Frequentemente vejo alguns amigos desenvolvendo em C e sempre se deparam com algumas construções que são completamente “esquisitas”. O esquisito está entre aspas pois são construções que não são válidas em ANSI C (ou no nosso bom e velho amigo C89) e completamente válidas em C99.
Em artigos anteriores eu utilizei algumas construções não muito usuais, e que gosto muito: inicialização de estruturas, de vetores e compound literals. Como os compiladores ANSI C utilizados em sistemas embarcados já provém suporte ao C99, nada como explorar isso e ver como tirar mais proveito do compilador.
Inicialização de vetores
Em C89 a inicialização dos elementos de vetores é em ordem fixa, respeitando a ordem dos elementos no vetor. Já em C99, especificando o elemento do vetor o qual queremos inicializar, podemos inicializá-lo em qualquer ordem. Para inicializar um elemento de vetor em C99, escreva ‘[índice] =’ antes do valor do elemento, por exemplo:
int a[6] =
{
[0] = 1,
[3] = 5,
[2] = 3
};
A construção acima é equivalente à inicialização a seguir em C89:
int a[6] =
{
1,0, 3, 5, 0, 0
};
Repare que os termos não inicializados explicitamente são inicializados com zero.
Inicialização de estruturas
Em C89 você pode inicializar uma estrutura assim:
static const struct STGpioOutputConfig stUserLedCfg[] =
{
{
SYSCTL_PERIPH_USR_LED1,
GPIO_BASE_USR_LED1,
GPIO_PIN_USR_LED1,
},
{
SYSCTL_PERIPH_USR_LED2,
GPIO_BASE_USR_LED2,
GPIO_PIN_USR_LED2,
},
{
SYSCTL_PERIPH_USR_LED3,
GPIO_BASE_USR_LED3,
GPIO_PIN_USR_LED3,
},
{
SYSCTL_PERIPH_USR_LED4,
GPIO_BASE_USR_LED4,
GPIO_PIN_USR_LED4,
},
};
O detalhe aqui é que os elementos devem ser inicializados na mesma ordem que eles são listados na declaração da struct:
struct STGpioOutputConfig
{
DWORD dwSYSCTL; /**< the SYSCTL value */
DWORD dwBase; /**< the PortBase value */
DWORD dwPin; /**< the Pin value */
};
Assim, quando declaramos uma variável do tipo struct STGpioOutputConfig, dwSYSCTL vem primeiro, dwBase depois e assim por diante. Isso pode ser um inconveniente pois como todo código de inicialização não é alterado nunca, todo código dependente da ordem dos inicializadores pode não funcionar mais. Em C99 você pode inicializar uma estrutura especificando os nomes dos elementos da struct:
static const struct STGpioOutputConfig stUserLedCfg[] =
{
[0] =
{
.dwSYSCTL = SYSCTL_PERIPH_USR_LED1,
.dwBase = GPIO_BASE_USR_LED1,
.dwPin = GPIO_PIN_USR_LED1,
}
...
};
Esta construção não somente desacopla a ordem dos inicializadores, mas torna o código mais legível. Como benefício adicionado, os elementos não inicializados são inicializados com zero (0). Como facilidade, se durante o projeto qualquer campo for adicionado à estrutura, a sua inicialização pode ser acrescentada sem medo de que os outros elementos da estrutura tenham o valor de inicialização alterados.
Neste ponto vou indicar uma forma de organização de projetos que utilizo frequentemente, que é a inicialização de vetores de estruturas de tal forma a representar algum aspecto de implementação. Repare que o vetor de estruturas declarado acima, stUserLedCfg, é a representação de vários LEDs presentes em um projeto. Dependendo do tamanho do projeto pode ser um pouco difícil de lembrar qual elemento do vetor representa um dado led. Assim, sempre que possível, declaro uma enumeração que cobre a faixa de elementos do vetor e, na inicialização, inicializo cada elemento explicitamente. Para o vetor de estrutura citado, ficaria assim:
typedef enum
{
USR_LED1,
USR_LED2,
USR_LED3,
USR_LED4
} USER_LEDS;
static const struct STGpioOutputConfig stUserLedCfg[] =
{
[USR_LED2]
{
...
},
[USR_LED1]
{
...
},
[USR_LED3]
{
...
},
[USR_LED4]
{
...
},
};
Com esta construção fica muito mais fácil de organizar o código e ele se torna totalmente extensível.
Compound literals em atribuição a estruturas
Esta nova sintaxe de inicialização de estruturas é fabulosa, mas não ajuda em tudo. Vamos a um exemplo prático: necessitamos inicializar uma estrutura qualquer, por exemplo, a GPIO_InitTypeDef presente na STDPeripheralLib da ST. Em C89, teríamos algo assim:
uint8_t LED_Init( void )
{
GPIO_InitTypeDef initGPIO;
GPIO_TypeDef* ugpioTD;
ugpioTD = Get_LED_GPIO( DEV_LED_0 );
if ( NULL == ugpioTD )
{
return EINVAL;
}
RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA , ENABLE);
initGPIO.GPIO_Mode = GPIO_Mode_OUT;
initGPIO.GPIO_OType = GPIO_OType_PP;
initGPIO.GPIO_PuPd = GPIO_PuPd_UP;
initGPIO.GPIO_Speed = GPIO_Speed_25MHz;
initGPIO.GPIO_Pin = 0;
GPIO_Init( ugpioTD, &initGPIO );
return 0;
}
Usando a construção de C99 chamada compound literal, você pode atribuir valores para a struct initGPIO da forma apresentada abaixo:
initGPIO = (GPIO_InitTypeDef)
{
.GPIO_Mode = GPIO_Mode_OUT,
.GPIO_OType = GPIO_OType_PP,
.GPIO_PuPd = GPIO_PuPd_UP,
.GPIO_Speed = GPIO_Speed_25MHz,
.GPIO_Pin = 0
};
Tenha em mente que esta construção sempre emprega uma declaração anônima. Debaixo do capô, o compilador faz algo do tipo:
GPIO_InitTypeDef anon =
{
.GPIO_Mode = GPIO_Mode_OUT,
.GPIO_OType = GPIO_OType_PP,
.GPIO_PuPd = GPIO_PuPd_UP,
.GPIO_Speed = GPIO_Speed_25MHz,
.GPIO_Pin = 0
};
Novamente, a coisa legal aqui é que os elementos não inicializados explicitamente são inicializados com zero.
Com a mesma construção, é possível inicializar uma struct com zero somente com a seguinte declaração:
initGPIO = (GPIO_InitTypeDef){0};
Compound literals com primitivas
Compound literals também podem ser aplicadas a primitivas. Em geral, a declaração abaixo não tem muita utilidade:
int i = (int){100};
Porém, devido ao compound literal estar associado a uma variável anônima, podemos usar o endereço dela:
int* iPrt = &(int){100};
Podemos aplicar isso em exemplos do tipo abaixo, que é uma construção C89:
int iValue = 100; TTimerRegisterCallBack( 100, TimerPeriodic, foo, &iValue, NULL );
A mesma construção poderia ser feita em C99 da forma mostrada abaixo:
TTimerRegisterCallBack( 100,
TimerPeriodic,
foo,
&(int){100},
NULL );
Conclusão – C89 vs C99
Enquanto C99 possui alguns updates em relação ao padrão C89, é interessante habilitá-lo em seu compilador e começar a usar. O seu uso tornará as suas implementações mais legíveis e portáveis. Além disso, atente-se à documentação do seu compilador, pois podemos ter alguns ganhos de velocidade ou de uso de memória se utilizarmos compound literals e outras construções de C99.










Dias, excelente artigo! Compound literals são um dos aspectos menos explorados do C99, e permitem construções de alto nível, com declaração no ponto-de-uso. Um dos aspectos que mais gosto em compound literals é o uso de pointers para structs como passagem de parâmetros em funções. Neste caso, o uso de compound literals permite a especificação de named parameters, similar a linguagens mais estruturadas (como ADA, por exemplo). Esta construção é C99: // SECURE_STATUS_T ecdsa_verify(ECDSA_VECTOR_T *vector, ECDSA_CMD_EVENT_T *event); SECURE_STATUS_T ret = ecdsa_verify( &(ECDSA_VECTOR_T){ .key_type = ECC_KEY_SYS_MASTER, // master public key .msg_hash = ecdsa.msg_hash, // ptr to the 32bytes sha-256 message hash… Leia mais »
Muito esclarecedor. Parabéns pelo artigo!
obrigado, Mestre!