Como inicializar uma estrutura de acordo com os padrões da linguagem de programação C

466

Eu quero inicializar um elemento struct, dividido em declaração e inicialização. Isto é o que eu tenho:

typedef struct MY_TYPE {
  bool flag;
  short int value;
  double stuff;
} MY_TYPE;

void function(void) {
  MY_TYPE a;
  ...
  a = { true, 15, 0.123 }
}

É assim que se declara e inicializa uma variável local de MY_TYPE acordo com os padrões da linguagem de programação C (C89, C90, C99, C11, etc.)? Ou há algo melhor ou pelo menos funcionando?

Atualização Acabei tendo um elemento de inicialização estático, onde defini todos os subelementos de acordo com minhas necessidades.

Vergonhoso
fonte
3
você realmente deve aceitar uma resposta melhor, vejo que você tinha que usar alguma má codificação guia, mas você ainda não deve sugerir a outras pessoas que esse é o caminho certo para fazê-lo ..
Karoly Horvath
@KarolyHorvath bem, a maioria das boas respostas é específica para C99. Talvez minha pergunta seja uma duplicata do stackoverflow.com/questions/6624975/… ?
cringe
se essa era sua intenção original, provavelmente sim, mas 1) os votos seriam muito enganosos. 2) a partir das principais pesquisas, este é o único que mostra o caminho C99 ... seria melhor reutilizar esta página para a demonstração C99 ... (aparentemente as pessoas começaram a vincular esta página para mostrar como )
Karoly Horvath
10
Interessante que a resposta aceita (e altamente votada) na verdade não responda à pergunta, mesmo como publicada originalmente. Inicializadores designados não abordam o problema do OP, que é dividir a declaração da inicialização. Para C antes de 1999, a única solução real é atribuir a cada membro; para C99 e posteriores, um literal composto, como na resposta de CesarB, é a solução. (É claro que um inicializador real, com ou sem designadores, seria ainda melhor, mas, aparentemente, o OP foi selado com um padrão de codificação muito ruim.)
Keith Thompson
32
A rigor, o termo "ANSI C" agora se refere ao padrão ISO de 2011, que o ANSI adotou. Mas, na prática, o termo "ANSI C" geralmente se refere ao padrão (oficialmente obsoleto) de 1989. Por exemplo, "gcc -ansi" ainda aplica o padrão de 1989. Como a ISO publicou os padrões de 1990, 1999 e 2011, é melhor evitar o termo "ANSI C" e consultar a data do padrão, se houver alguma possibilidade de confusão.
Keith Thompson

Respostas:

728

No (ANSI) C99, você pode usar um inicializador designado para inicializar uma estrutura:

MY_TYPE a = { .flag = true, .value = 123, .stuff = 0.456 };

Editar: Outros membros são inicializados como zero: "Membros de campo omitidos são inicializados implicitamente da mesma forma que objetos que têm duração de armazenamento estático". ( https://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html )

filante
fonte
87
incrível, obrigado. você pode até aninhar em: estático oxeRay2f ray = {.unitDir = {.x = 1.0f, .y = 0.0f}};
orion elenzil
4
Isso não responde à pergunta. O OP deseja separar a inicialização da declaração.
Osvein 04/04
3
C99! = ANSI C - Portanto, isso não funciona no C89, nem no C90.
Tim Wißmann
3
C99 é ANSI C, ver a este
Cutberto Ocampo
3
@CutbertoOcampo Entendo o que aconteceu agora. A pergunta original estava pedindo soluções em ANSI C, obviamente ele queria respostas apenas para o C89. Em 2016, a pergunta foi editada e o "ANSI C" foi removido, o que torna difícil entender por que essa resposta e os comentários mencionam "(ANSI) C99".
Étienne
198

Você pode fazer isso com um literal composto . De acordo com essa página, ele funciona em C99 (que também conta como ANSI C ).

MY_TYPE a;

a = (MY_TYPE) { .flag = true, .value = 123, .stuff = 0.456 };
...
a = (MY_TYPE) { .value = 234, .stuff = 1.234, .flag = false };

As designações nos inicializadores são opcionais; você também pode escrever:

a = (MY_TYPE) { true,  123, 0.456 };
...
a = (MY_TYPE) { false, 234, 1.234 };
CesarB
fonte
Portanto, os inicializadores designados são para quando você declara e define ao mesmo tempo, mas literais compostos são para quando você define uma variável já declarada?
Geremia 24/03
@ Geremia, você precisa primeiro definir a estrutura nos dois casos, mas com os inicializadores designados, você torna o código mais legível e mais resistente a erros, no caso de alterações na estrutura.
hoijui
2
Isso é um pouco complicado. Muitas vezes (algumas bem-sucedidas) tentei usar inicializadores designados. No entanto, agora ficou claro (graças ao exemplo de your e @ philant) que deve existir uma conversão se o inicializador não for usado no momento da criação do objeto. No entanto, agora também aprendi que, se os inicializadores forem usados ​​em outro momento que não a criação de objetos (como no seu exemplo), ele será chamado de literal composto , não estritamente um inicializador designado . ideone.com/Ze3rnZ
sherrellbc
93

Vejo que você já recebeu uma resposta sobre o ANSI C 99, portanto, discutirei o ANSI C 89. O ANSI C 89 permite que você inicialize uma estrutura dessa maneira:

typedef struct Item {
    int a;
    float b;
    char* name;
} Item;

int main(void) {
    Item item = { 5, 2.2, "George" };
    return 0;
}

É importante lembrar que, no momento em que você inicializa até um objeto / variável na estrutura, todas as outras variáveis ​​serão inicializadas com o valor padrão.

Se você não inicializar os valores em sua estrutura, todas as variáveis ​​conterão "valores de lixo".

Boa sorte!

Ron Nuni
fonte
3
É possível usar variáveis ​​na inicialização?
Cyan
2
@ Cyan É para objetos com duração de armazenamento automático. Veja 6.7.9 13) em open-std.org/jtc1/sc22/wg14/www/docs/n1570.pdf . Para objetos globais é praticamente restrito a literais. Você não pode nem usar outros objetos globais, mesmo que sejam const.
PSKocik 09/10
Então a única diferença com o C 99 é que você não pode especificar as designações?
Akaisteph7 24/02
42

a = (MYTYPE){ true, 15, 0.123 };

faria bem em C99

qrdl
fonte
1
Sinceramente, acho que a resposta é a mais útil, mas tanto faz.
user12211554
22

Você quase conseguiu ...

MY_TYPE a = { true,15,0.123 };

A pesquisa rápida em 'struct initialize c' mostra-me isto

Kieveli
fonte
1
Eu acho que isso é verdade para o padrão C, mas não para o ANSI C (99?). Também estou vinculado a regras de codificação que não me permitem fazer declaração e inicialização de uma só vez; portanto, tenho que dividir e inicializar cada subelemento.
cringe
8
A inicialização só pode acontecer no ponto da declaração. É isso que significa "inicializar". Caso contrário, você está permitindo que um valor indefinido seja o valor inicial de sua variável / estrutura e, em seguida, atribuindo um valor mais tarde.
Kieveli
8
hum ... Você tem regras de codificação que são deliberadamente ruins? Talvez você deva apresentar o sujeito que os escreveu a "Aquisição de recursos é inicialização" ( en.wikipedia.org/wiki/RAII )
James Curran
Confie em mim, eu realmente, realmente, não posso mudar um pouco dessas regras neste momento. É horrível codificá-lo. E existem muitas outras regras desde os anos 70, como cabeçalhos de código estático com informações de gerenciamento como o número da revisão. Mudanças para cada lançamento, mesmo se a fonte não mudou ...
encolher
19

A norma de linguagem de programação C ISO / IEC 9899: 1999 (comumente conhecida como C99) permite usar um inicializador designado para inicializar membros de uma estrutura ou união da seguinte maneira:

MY_TYPE a = { .stuff = 0.456, .flag = true, .value = 123 };

É definido na paragraph 7seção 6.7.8 Initializationda norma ISO / IEC 9899: 1999 como:

Se um designador tiver o formulário
. identificador
, o objeto atual (definido abaixo) deve ter estrutura ou tipo de união e o identificador deve ser o nome de um membro desse tipo.

Observe que paragraph 9na mesma seção afirma que:

Exceto quando indicado explicitamente em contrário, para os fins desta subcláusula, os membros não nomeados dos objetos de estrutura e tipo de união não participam da inicialização. Membros sem nome de objetos de estrutura têm valor indeterminado, mesmo após a inicialização.

Na implementação do GNU GCC, no entanto, os membros omitidos são inicializados como zero ou valor apropriado para o tipo zero. Conforme indicado na seção 6.27 Inicializadores designados da documentação do GNU GCC:

Membros de campo omitidos são inicializados implicitamente da mesma forma que objetos que têm duração de armazenamento estático.

O compilador do Microsoft Visual C ++ deve oferecer suporte a inicializadores designados desde a versão 2013, de acordo com o post oficial do blog C ++ Conformance Roadmap . O artigo Parágrafo Initializing unions and structsdo inicializador na documentação do MSDN Visual Studio sugere que membros sem nome inicializassem com valores apropriados de zero-zero, de maneira semelhante ao GNU GCC.

A norma ISO / IEC 9899: 2011 (comumente conhecida como C11) que substituiu a ISO / IEC 9899: 1999 mantém os inicializadores designados na seção 6.7.9 Initialization. Ele também mantém paragraph 9inalterado.

O novo padrão ISO / IEC 9899: 2018 (conhecido como C18) que substituiu o ISO / IEC 9899: 2011 mantém os inicializadores designados na seção 6.7.9 Initialization. Ele também mantém paragraph 9inalterado.

PF4Public
fonte
3
Inicialmente, sugeri isso como edição da resposta aceita, mas ela foi rejeitada. Assim, eu o posto como resposta.
usar o seguinte código
Acredito que "membro sem nome" não significa "campos omitidos", mas "membros anônimos", como a união em struct thing { union { char ch; int i; }; };. O padrão diz mais tarde: "Se houver menos inicializadores em uma lista entre chaves, do que elementos ou membros de um agregado, ou menos caracteres em uma cadeia de caracteres literal usada para inicializar uma matriz de tamanho conhecido do que os elementos da matriz, o restante do agregado deve ser inicializado implicitamente da mesma forma que os objetos com duração de armazenamento estático. "
emersão
14

como Ron Nuni disse:

typedef struct Item {
    int a;
    float b;
    char* name;
} Item;

int main(void) {
    Item item = {5, 2.2, "George"};
    return 0;
}

Uma coisa importante a lembrar: no momento em que você inicializa até um objeto / variável na estrutura, todas as outras variáveis ​​serão inicializadas com o valor padrão.

Se você não inicializar os valores em sua estrutura (por exemplo, se você apenas declarar essa variável), todos variable.membersconterão "valores de lixo", apenas se a declaração for local!

Se a declaração for global ou estática (como neste caso), todos os não inicializados variable.membersserão inicializados automaticamente para:

  • 0 para números inteiros e ponto flutuante
  • '\0'para char(é claro que isso é exatamente o mesmo que 0e charé um tipo inteiro)
  • NULL para ponteiros.
r_goyal
fonte
Interessante sobre o local / global, isso também se aplica aos valores de tempo da estrutura. Eu tinha um local e, em um caso, o horário de verão estava ativo; em outro, não estava, não devido a qualquer coisa que fiz. Eu não estava usando o DST (o campo tm_isdst), isso era do horário do strptime, mas quando eu converti para time_t, havia uma hora de folga.
27675 Alan Alany
3
void function(void) {
  MY_TYPE a;
  a.flag = true;
  a.value = 15;
  a.stuff = 0.123;
}
Robert
fonte
24
depois de ouvir 'inicialização é diferente de atribuição'. então isso não é uma atribuição, e não uma inicialização?
Hayri Uğur Koltuk 12/03/12
2

Se a MS não tiver atualizado para C99, MY_TYPE a = {true, 15,0.123};

eddyq
fonte
2

Encontrei outra maneira de inicializar estruturas.

A estrutura:

typedef struct test {
    int num;
    char* str;
} test;

Inicialização:

test tt = {
    num: 42,
    str: "nice"
};

De acordo com a documentação do GCC, essa sintaxe é obsoleta desde o GCC 2.5 .

MichaelWang
fonte
2

Eu não gostei de nenhuma dessas respostas, então criei as minhas. Não sei se isso é ANSI C ou não, é apenas o GCC 4.2.1 no modo padrão. Como nunca me lembro do bracketing, começo com um subconjunto dos meus dados e luto com as mensagens de erro do compilador até que ele seja encerrado. A legibilidade é minha primeira prioridade.

    // in a header:
    typedef unsigned char uchar;

    struct fields {
      uchar num;
      uchar lbl[35];
    };

    // in an actual c file (I have 2 in this case)
    struct fields labels[] = {
      {0,"Package"},
      {1,"Version"},
      {2,"Apport"},
      {3,"Architecture"},
      {4,"Bugs"},
      {5,"Description-md5"},
      {6,"Essential"},
      {7,"Filename"},
      {8,"Ghc-Package"},
      {9,"Gstreamer-Version"},
      {10,"Homepage"},
      {11,"Installed-Size"},
      {12,"MD5sum"},
      {13,"Maintainer"},
      {14,"Modaliases"},
      {15,"Multi-Arch"},
      {16,"Npp-Description"},
      {17,"Npp-File"},
      {18,"Npp-Name"},
      {19,"Origin"}
    };

Os dados podem iniciar a vida como um arquivo delimitado por tabulação que você substitui para massagear outra coisa. Sim, isso é material do Debian. Portanto, um par externo de {} (indicando a matriz) e outro par para cada estrutura interna. Com vírgulas entre. Colocar as coisas em um cabeçalho não é estritamente necessário, mas eu tenho cerca de 50 itens na minha estrutura, então eu os quero em um arquivo separado, para manter a bagunça fora do meu código e para facilitar a substituição.

Alan Corey
fonte
3
Não havia dúvida sobre a inicialização do conjunto de estruturas! Quero dizer, sua resposta contém despesas gerais.
Victor Signaevskyi
Eu acho que meu ponto foi declarar a estrutura com valores já nela vs. usando = para definir cada valor posteriormente.
Alan Corey
Esse princípio se aplica, seja uma estrutura ou uma matriz deles. Usar = não é realmente inicialização, eu não pensaria.
Alan Corey
0

A estrutura em C pode ser declarada e inicializada assim:

typedef struct book
{
    char title[10];
    char author[10];
    float price;
} book;

int main() {
    book b1={"DS", "Ajay", 250.0};

    printf("%s \t %s \t %f", b1.title, b1.author, b1.price);

    return 0;
}
Ajay Parashar
fonte
0

Eu li a documentação do Microsoft Visual Studio 2015 para inicializar tipos agregados ainda, todas as formas de inicialização com {...}são explicadas lá, mas a inicialização com ponto, denominada '' designador '' não é mencionada lá. Também não funciona.

O capítulo C.7 da norma C99 6.7.8 Inicialização explica a possibilidade de designadores, mas, na minha opinião, não é realmente claro para estruturas complexas. O padrão C99 como pdf .

Na minha opinião, pode ser melhor

  1. Use a = {0};inicialização para todos os dados estáticos. É menos esforço para o código da máquina.
  2. Use macros para inicializar, por exemplo

    typedef MyStruct_t{ int x, int a, int b; } MyStruct; define INIT_MyStruct(A,B) { 0, A, B}

A macro pode ser adaptada, sua lista de argumentos pode ser independente do conteúdo da estrutura alterado. É adequado se menos elementos forem inicializados. Também é adequado para estrutura aninhada. 3. Um formulário simples é: Inicialize em uma sub-rotina:

void init_MyStruct(MyStruct* thiz, int a, int b) {
  thiz->a = a; thiz->b = b; }

Essa rotina se parece com ObjectOriented em C. Use thiz, não thispara compilá-la com C ++ também!

MyStruct data = {0}; //all is zero!
init_MyStruct(&data, 3, 456);
Hartmut Schorrig
fonte
0

Eu tenho procurado uma boa maneira de inicializar minha estrutura, e eu tenho que usar o abaixo (C99). Isso me permite inicializar uma única estrutura ou uma matriz de estruturas da mesma maneira que os tipos simples.

typedef struct {
    char *str;
    size_t len;
    jsmntok_t *tok;
    int tsz;
} jsmn_ts;

#define jsmn_ts_default (jsmn_ts){NULL, 0, NULL, 0}

Isso pode ser usado no código como:

jsmn_ts mydata = jsmn_ts_default; /* initialization of a single struct */

jsmn_ts myarray[10] = {jsmn_ts_default, jsmn_ts_default}; /* initialization of
                                                    first 2 structs in the array */
div0man
fonte
Mas isso não inicializa uma matriz de estruturas com os valores padrão. Inicializa a primeira estrutura na matriz com os valores fornecidos em jsmn_ts_default, mas o restante das estruturas é inicializado como se a matriz tivesse duração de armazenamento estático, o que significa que os membros foram inicializados em NULLe 0. Mas se você alterar #define jsmn_ts_default (jsmn_ts){"default", sizeof "default", NULL, 0}, verá que apenas o primeiro elemento da matriz é inicializado.
ex nihilo
Sim, eu acabei de voltar de mais testes, queria editar o post :) btw, mesmo comportamento ocorre com int a[10] = {1};apenas o elemento 1 será==1
div0man