Atributos
Desde o C23
, foram adicionados os "atributos" a linguagem, que são uma forma de sinalizar detalhes adicionais ao compilador, uma funcionalidade que até então só existia no C++
(adicionada no C++11
).
Os atributos servem, similar ao #pragma
, como uma forma padronizada e unificada de indicar detalhes ao compilador, que normalmente eram efetuados utilizando extensões de compilador (como __attribute__
do GCC e IBM, ou __declspec
do MSVC).
Declaração de atributos :
[[atributo-padrao]] //1° forma
[[prefixo::atributo]] //2° forma
[[atributo-padrao(argumentos)]] //3° forma
[[prefixo::atributo(argumentos)]] //4° forma
Todos atributos específicos de compilador tem um prefixo
e ::
antes do nome efetivo do atributo, já os atributos padrão da linguagem são escritos sem prefixo.
Alguns atributos podem ter um ou mais argumentos, que ficam entre parênteses, caso haja mais de um, eles são separados por virgula.
Exemplo do uso de atributos:
float f1 = 5.4;
//Atributo padrão para inibir
//avisos do compilador sobre variável não utilizada
[[maybe_unused]]
float f2 = 9.2;
//Usando atributo para especificar
//exceção a regra de strict aliasing ao GCC (C23)
[[gnu::may_alias]]
int *i1 = (int*)&f1;
//Utilizando extensão de compilador (antes do C23)
__attribute((__may_alias__)) int *i2 = (int*)&f1;
Atributos padrão
Os seguintes atributos são considerados padrão da linguagem:
[[deprecated]]
: Indica que a função ou variável associada é depreciada e não é recomendada, gerando um aviso do compilador.[[deprecated("motivo")]]
: Versão do atributo acima, com um parâmetro indicando a mensagem a ser exibida ao gerar o aviso.[[fallthrough]]
: Indica que continuar a execução para o próximocase
por conta da ausência da palavra chavebreak
numa estrutura de controle de fluxoswitch
é intencional.[[nodiscard]]
: Faz com que o compilador gere um aviso se o valor de retorno for ignorado.[[nodiscard("motivo")]]
: Versão do atributo acima, com um parâmetro indicando a mensagem a ser exibida ao gerar o aviso.[[maybe_unused]]
: Impede o compilador de gerar avisos se a variável/função associada não for utilizada.[[noreturn]]
: Indica que a função não retorna.[[reproducible]]
: Indica que a função ésem efeito
eindependente
, isso permite que o compilador trate múltiplas chamadas em sequência como uma única chamada.[[unsequenced]]
: Indica que a função ésem efeito
,idempotente
,sem estado
eindependente
, permite que o compilador trate múltiplas chamadas em sequência como uma única chamada e também que elas sejam paralelizadas e reordenadas de forma arbitrária.
Para entender melhor os atributos unsequenced
e reproducible
, as funções podem ou não ser classificadas como :
Sem efeito
: Uma função ésem efeito
se ela modifica apenas variáveis locais, ou valores através de um único ponteiro repassado como parâmetro a função.Idempotente
: Uma função éidempotente
quando outra chamada dela pode ser executada logo em sequência sem alterar o resultado ou o estado observável de execução.Sem Estado
: Uma função é consideradasem estado
, se qualquer definição de variável de duraçãoestática
ou dethread
nela ou em alguma função que ela chame sejaconst
e não tenha o especificadorvolatile
.Independente
: Uma função éindependente
quando ela não modifica nenhum parâmetro via ponteiro, não modifica o estado de variáveis globais nem o estado global do programa ou sistema operacional.
Para compreender de maneira prática (quais funções poderiam ter os atributos unsequenced
ou reproducible
):
printf
não poderia ter nenhumstrlen
ememcmp
podem ser[[unsequenced]]
memcpy
pode ser[[reproducible]]
memmove
não pode ser nenhum dos dois, por que ele não éidempotente
para regiões de memória que se sobrepõemfabs
pode ser[[unsequenced]]
sqrt
não pode ser nenhum dos dois, pois ele modifica o ambiente de ponto flutuante e pode modificarerrno
Os exemplos acima foram retirados dessa resposta do StackOverflow.
Checar atributo
Podemos checar se um atributo existe utilizando __has_c_attribute
dentro de um #if
ou #elif
.
O valor resultante da expressão __has_c_attribute
difere entre atributos padrão e atributos específicos de compiladores :
- Atributos padrão tem tipo
long
e valor indicando o ano e mês em que o atributo foi adicionado aos documentos de especificação da linguagem, ex: um atributo adicionado em Abril de 2019 terá o valor201904L
. - Atributos específicos de compilador são considerados como existentes caso tenham valor diferente de zero.
- Atributos inexistentes terão o seu valor zerado.