Inicializar / redefinir estrutura para zero / nulo


92
struct x {
    char a[10];
    char b[20];
    int i;
    char *c;
    char *d[10];
};

Estou preenchendo esta estrutura e usando os valores. Na próxima iteração, desejo redefinir todos os campos para 0ou nullantes de começar a reutilizá-los.

Como eu posso fazer isso? Posso usar memsetou devo passar por todos os membros e depois fazer isso individualmente?

Respostas:


109

Defina uma instância estática const da estrutura com os valores iniciais e, em seguida, simplesmente atribua esse valor à sua variável sempre que quiser redefini-la.

Por exemplo:

static const struct x EmptyStruct;

Aqui estou contando com a inicialização estática para definir meus valores iniciais, mas você pode usar um inicializador de estrutura se quiser valores iniciais diferentes.

Então, cada vez que fizer o loop, você pode escrever:

myStructVariable = EmptyStruct;

1
@David Heffernan: isso é melhor do que usar memsetse eu só quiser redefinir tudo para 0??
hari

9
@hari Eu mesmo prefiro essa abordagem. Usar memsetme faz sentir suja. Prefiro deixar o compilador se preocupar com o layout da memória sempre que possível. Estritamente falando, esse uso de memsetnão é portátil, mas na prática eu ficaria surpreso se você compilasse seu código em qualquer lugar que importasse. Portanto, você provavelmente pode usar o memset com segurança, se preferir.
David Heffernan

2
@hari, é conceitualmente melhor, já que você fornece valores de inicialização padrão (algo como um padrão de fábrica de objetos.)
Diego Sevilla

1
@cnicutar Eu sei disso. Observe que minha resposta recomenda não usar o memset. Observe também o comentário sobre onde aponto a não portabilidade de usar o memset como um meio de atribuir valores nulos. Meu comentário anterior apenas aponta a realidade de que bits 0, invariavelmente, correspondem a zeros de ponto flutuante.
David Heffernan

1
@ kp11 citação, por favor
David Heffernan

85

A maneira de fazer isso quando você tem um C moderno (C99) é usar um literal composto .

a = (const struct x){ 0 };

Isso é um pouco semelhante à solução de David, só que você não precisa se preocupar em declarar uma estrutura vazia ou se deve declará-la static. Se você usar o constcomo eu fiz, o compilador ficará livre para alocar o literal composto estaticamente no armazenamento somente leitura, se apropriado.


Isso cria uma instância de x na pilha para depois copiá-la para a? Para estruturas grandes / pequenas pilhas, isso pode ser um problema.
Étienne

1
Como todas as otimizações, isso é completamente dependente do compilador, então você terá que verificar o que o seu compilador produz. "Normalmente" em compiladores modernos isso não acontece, eles podem rastrear inicializações muito bem e fazer apenas o que é necessário, não mais. (E não pense que essas coisas são problemas antes de medir uma verdadeira desaceleração. Geralmente não são.)
Jens Gustedt

3
avisos de "inicialização ausente" são realmente falsos. O padrão C prescreve exatamente o que deve acontecer e prevê o { 0 }como o inicializador padrão. Desligue esse aviso, é um alarme falso espúrio.
Jens Gustedt,

1
@JensGustedt Este typecasting é realmente necessário? Não posso escrever assim? struct xa = (const) {0};
Patrick

1
@Patrick, embora a sintaxe seja semelhante, não é um elenco, mas um "literal composto". E você está misturando inicialização e atribuição.
Jens Gustedt de

58

Melhor do que tudo acima, é sempre usar a especificação C padrão para inicialização da estrutura:

struct StructType structVar = {0};

Aqui estão todos os bits zero (sempre).


13
Eu não acho que você pode fazer isso cada vez em torno de um loop
David Heffernan

2
Isso realmente deve funcionar. Mas, infelizmente, gcc & g ++ reclamam disso. gcc gera avisos, enquanto g ++ gera um erro. Eu sei que gcc & g ++ são os responsáveis ​​por isso (eles devem seguir a especificação do padrão C), mas, no entanto, para uma boa portabilidade, é obrigatório levar essa limitação em consideração.
Cyan

3
@Cyan Você pode usar {}em C ++. Os desenvolvedores do gcc parecem inseguros se o C ++ deve suportar{0} e não estou familiarizado com essa parte do padrão.
Mateus Leia

@Matthew: Sim, na verdade, acabei usando memset (), porque havia muitos problemas de portabilidade com {0}ou {}. Não tenho certeza se os padrões C e C ++ são claros e em sincronia neste assunto, mas aparentemente, os compiladores não estão.
Cyan,

isso funcionará em tal caso? struct StructType structVar = malloc (sizeof(StructType)); structVar ={0}
Sam Thomas

34

Em C, é um idioma comum zerar a memória para um structusando memset:

struct x myStruct;
memset(&myStruct, 0, sizeof(myStruct));

Tecnicamente falando, não acredito que isso seja portátil porque pressupõe que o NULLponteiro em uma máquina seja representado pelo valor inteiro 0, mas é amplamente usado porque na maioria das máquinas é esse o caso.

Se você mudar de C para C ++, tome cuidado para não usar essa técnica em todos os objetos. C ++ só torna isso válido em objetos sem funções de membro e sem herança.


2
O padrão C afirma que NULL é sempre 0. Se a máquina for projetada de forma que um endereço inválido predeterminado não seja literalmente 0, o compilador para essa arquitetura deve se ajustar durante a compilação.
Kevin M

8
@KevinM Sim, o símbolo "0" sempre corresponde ao ponteiro NULL, mesmo que seja totalmente zero; mas não há nada que o compilador possa fazer se você definir os bits como zero usando o memset.
Adrian Ratnapala

"C ++ só torna isso válido em objetos sem funções de membro e sem herança." É sobre se o tipo é considerado 'dados simples e antigos'. Os critérios dependem da versão da linguagem C ++.
Max Barraclough

19

Se você tiver um compilador compatível com C99, você pode usar

mystruct = (struct x){0};

caso contrário, você deve fazer o que David Heffernan escreveu, ou seja, declarar:

struct x empty = {0};

E no circuito:

mystruct = empty;

6

Você pode usar memsetcom o tamanho da estrutura:

struct x x_instance;
memset (&x_instance, 0, sizeof(x_instance));

1
Não acho que o elenco seja necessário aqui. É isso?
templatetypedef

Bem, estou tão acostumado com C ++ que ... bem, funcionará também em C ++, então não vejo isso como um fardo.
Diego Sevilla

Sim, eu não fui específico o suficiente. É qualquer ponteiro para cv qualificado T pode ser convertido para cv qualificado void *. Qualquer ponteiro de dados, ponteiros de função são um assunto completamente diferente. Kudos @Kevin
Marcus Borkenhagen

memseté uma opção, mas ao usá-lo, você deve ter certeza de que a memória gravada é exatamente do mesmo tamanho que o terceiro parâmetro, por isso é melhor se referir ao tamanho do objeto real, não o tamanho do tipo que você sei que atualmente é. IOW memset (&x_instance, 0, sizeof(x_instance));é uma escolha muito melhor. BTW: o (void*)elenco é supérfluo em C ++ também.
Wolf

(desculpe, eu perdi o atendimento da pergunta, é melhor agora)
Wolf

5

Eu acredito que você pode simplesmente atribuir o conjunto vazio ( {}) à sua variável.

struct x instance;

for(i = 0; i < n; i++) {
    instance = {};
    /* Do Calculations */
}

0
 struct x myX;
 ...
 memset(&x, 0, sizeof(myX));

5
Eu acho que OP sabe como chamar o memset, mas a questão é se fazer isso é ou não sensato
David Heffernan

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.