Estou usando a versão mais recente do g++ para compilar um código de teste envolvendo um array de ponteiros struct onde os structs contêm um array. A compilação falha quando tento inicializar o array de ponteiros struct em uma linha, mas se eu atribuir os structs a variáveis e então pegar seus endereços ao criar o array, o g++ não reclama. Quero entender o porquê. Este é meu primeiro post, então peço desculpas se houver informações faltando.
Procurei por perguntas semelhantes e não encontrei nada tão específico.
#include <stdint.h>
typedef struct {
uint16_t num_elements;
uint8_t elements[];
} HasArray;
HasArray x = {.num_elements = 2, .elements = {51, 17}};
HasArray y = {.num_elements = 4, .elements = {1, 42, 88, 73}};
const HasArray* collection[] = {&x, &y};
// main() somewhere
O código acima funciona, o que eu presumo que seja porque o g++ pode alocar memória para x
e y
em tempo de compilação (ou algo similar). Se eu tentar inicializar collection
assim:
// HasArray cast is necessary so I can take the address, doing `&` on just the `{}`
// didn't work
// Also I would use a macro for these declarations, I know it's clunky
const HasArray* collection[] = {
&(HasArray){.num_elements = 2, .elements = {51, 17}},
&(HasArray){.num_elements = 4, .elements = {1, 42, 88, 73}}
};
g++ lança
test.c:16:56: error: non-static initialization of a flexible array member
16 | &(HasArray){.num_elements = 2, .elements = {51, 17}},
| ^
test.c:17:63: error: non-static initialization of a flexible array member
17 | &(HasArray){.num_elements = 4, .elements = {1, 42, 88, 73}}
| ^
O que torna essas situações diferentes? No meu cérebro, a segunda sintaxe ainda alocará memória em tempo de compilação, mas parece que não é o caso. Eu entendo que eu poderia alocar memória em tempo de execução com malloc()
, mas a primeira situação não requer isso e eu sei o tamanho das structs em tempo de compilação, então como posso indicar o tamanho para o compilador sem criar variáveis separadas para cada struct?
Editar: Eu de fato marquei a pergunta incorretamente, opa. Isso é para código c, mas tenho que usar g++ para compilá-lo. @ChrisMM forneceu uma resposta muito boa nos comentários. Para contornar o erro, atribuirei os arrays a variáveis e pegarei seus endereços como na primeira situação.
Primeiro, inicializar um membro de array flexível é uma extensão não padrão. O GCC permite isso em casos de objetos com duração de armazenamento estático , ou seja, aqueles declarados no escopo do arquivo ou com a
static
palavra-chave, pois ele pode reservar a quantidade apropriada de espaço para ele em tempo de compilação.Sobre o motivo pelo qual isso gera um erro:
Há ambiguidades em relação à duração de armazenamento de literais compostos antes de C23. No GCC em particular, eles sempre têm duração de armazenamento automática . E como o GCC só permite inicialização flexível de membros de array para objetos com duração de armazenamento estática, você obtém um erro.
O padrão C23 esclarece isso ao declarar que a duração de armazenamento de um literal composto é a do escopo envolvente. Então, se você fosse compilar com GCC 13 ou posterior, que tem suporte total a C23, o acima seria compilado de forma limpa.
No C padrão, membros de matriz flexíveis ( Os membros de matriz flexíveis são válidos em C++? ) são definidos como o último elemento de uma struct com um tamanho não especificado, permitindo dimensionamento dinâmico.
Isso ocorre porque membros flexíveis de array não podem ser inicializados dessa maneira. O padrão C determina que estruturas com FAMs devem ser alocadas dinamicamente para definir o tamanho do array flexível. Inicialização literal estática ou composta de FAMs não é permitida.
Na sua abordagem inicial:
Alguns compiladores permitem isso porque x e y são definidos com tamanhos conhecidos em tempo de compilação (porque toda vez você terá o mesmo tamanho de x e y; não é dinâmico como, por exemplo, um scanf com o tamanho ou algo assim). No entanto, esse uso não é padrão e depende de extensões específicas do compilador. O padrão C não define comportamento para inicializar FAMs dessa forma, tornando o código não portátil.
Portanto, para estar em conformidade com o padrão, você precisa alocar memória dinamicamente para estruturas com FAMs:
Então isso lhe dará: