Eu sei que há um padrão por trás de todas as implementações do compilador C, portanto, não deve haver recursos ocultos. Apesar disso, tenho certeza de que todos os desenvolvedores de C têm truques ocultos / secretos que usam o tempo todo.
c
hidden-features
bernardn
fonte
fonte
Respostas:
Ponteiros de função. Você pode usar uma tabela de ponteiros de função para implementar, por exemplo, aceleradores de código de thread indireto (FORTH) rápidos ou distribuidores de código de bytes, ou para simular métodos virtuais semelhantes a OO.
Depois, existem gemas ocultas na biblioteca padrão, como qsort (), bsearch (), strpbrk (), strcspn () [as duas últimas sendo úteis para implementar uma substituição de strtok ()].
Um erro de C é que o estouro aritmético assinado é um comportamento indefinido (UB). Portanto, sempre que você vê uma expressão como x + y, ambas sendo assinadas, ela pode potencialmente estourar e causar UB.
fonte
Mais um truque do compilador GCC, mas você pode fornecer dicas de indicação de ramificação para o compilador (comum no kernel do Linux)
veja: http://kerneltrap.org/node/4705
O que eu gosto sobre isso é que ele também acrescenta alguma expressividade a algumas funções.
fonte
Esse é um item opcional no padrão, mas deve ser um recurso oculto, porque as pessoas os redefinem constantemente. Uma base de código em que trabalhei (e ainda o faço, por enquanto) tem várias redefinições, todas com identificadores diferentes. Na maioria das vezes, é com macros de pré-processador:
E assim por diante. Isso me faz querer arrancar meus cabelos. Basta usar os inteiros padrão typedefs!
fonte
O operador de vírgula não é amplamente utilizado. Certamente pode ser abusado, mas também pode ser muito útil. Esse uso é o mais comum:
Mas você pode usar esse operador em qualquer lugar. Observar:
Cada instrução é avaliada, mas o valor da expressão será o da última instrução avaliada.
fonte
inicializando a estrutura para zero
isso zerará todos os elementos da estrutura.
fonte
memset
/calloc
do "todos os bytes zero" (ou seja, zeros físicos), o que de fato não é definido para todos os tipos.{ 0 }
é garantido para intilizar tudo com valores lógicos zero adequados . Os ponteiros, por exemplo, são garantidos para obter seus valores nulos adequados, mesmo que o valor nulo na plataforma fornecida seja0xBAADFOOD
.memset
isso que faz (com0
o segundo argumento). Você obtém zero lógico ao inicializar / atribuir0
(ou{ 0 }
) ao objeto no código-fonte. Esses dois tipos de zeros não produzem necessariamente o mesmo resultado. Como no exemplo com ponteiro. Quando você fazmemset
um ponteiro, recebe um0x0000
ponteiro. Porém, quando você atribui0
a um ponteiro, obtém um valor nulo , que no nível físico pode ser0xBAADF00D
ou qualquer outra coisa.double
,. Geralmente é implementado de acordo com o padrão IEEE-754, no qual o zero lógico e o zero físico são os mesmos. Mas o IEEE-754 não é exigido pelo idioma. Portanto, pode acontecer que, quando você fizerdouble d = 0;
(zero lógico), fisicamente alguns bits na memória ocupada pord
não sejam zero.Constantes com vários caracteres:
Isso define
x
como0x41424344
(ou0x44434241
, dependendo da arquitetura).Edição: Esta técnica não é portátil, especialmente se você serializar o int. No entanto, pode ser extremamente útil criar enums de auto-documentação. por exemplo
Isso torna muito mais simples se você estiver olhando para um despejo de memória bruta e precisar determinar o valor de uma enumeração sem precisar procurar.
fonte
Eu nunca usei campos de bits, mas eles parecem legais para coisas de nível ultra baixo.
Isso significa que
sizeof(cat)
pode ser tão pequeno quantosizeof(char)
.Comentários incorporados de Aaron e leppie , obrigado pessoal.
fonte
C possui um padrão, mas nem todos os compiladores C são totalmente compatíveis (ainda não vi nenhum compilador C99 totalmente compatível!).
Dito isso, os truques que eu prefiro são aqueles que não são óbvios e são portáveis entre plataformas, pois contam com a semântica C. Eles geralmente são sobre macros ou aritmética de bits.
Por exemplo: trocando dois números inteiros não assinados sem usar uma variável temporária:
ou "estendendo C" para representar máquinas de estados finitos como:
isso pode ser alcançado com as seguintes macros:
Em geral, porém, eu não gosto dos truques que são inteligentes, mas tornam o código desnecessariamente complicado de ler (como o exemplo de troca) e adoro os que tornam o código mais claro e transmitem diretamente a intenção (como o exemplo do FSM) .
fonte
Estruturas de entrelaçamento como o dispositivo de Duff :
fonte
Eu gosto muito de inicializadores designados, adicionados no C99 (e suportados no gcc por um longo tempo):
A inicialização do array não depende mais da posição. Se você alterar os valores de FOO ou BAR, a inicialização do array corresponderá automaticamente ao novo valor.
fonte
O C99 possui uma incrível inicialização de estrutura em qualquer ordem.
fonte
estruturas e matrizes anônimas é a minha favorita. (cf. http://www.run.montefiore.ulg.ac.be/~martin/resources/kung-f00.html )
ou
pode até ser usado para instanciar listas vinculadas ...
fonte
O gcc possui várias extensões para o idioma C que eu gosto, que podem ser encontradas aqui . Alguns dos meus favoritos são atributos de função . Um exemplo extremamente útil é o atributo format. Isso pode ser usado se você definir uma função personalizada que usa uma sequência de caracteres no formato printf. Se você habilitar esse atributo de função, o gcc fará verificações nos seus argumentos para garantir que a sequência e os argumentos do formato sejam correspondentes e gerará avisos ou erros, conforme apropriado.
fonte
o recurso (oculto) que me "chocou" quando vi pela primeira vez é sobre printf. esse recurso permite usar variáveis para formatar os especificadores de formato. procure o código, você verá melhor:
o caractere * atinge esse efeito.
fonte
Bem ... acho que um dos pontos fortes da linguagem C é a portabilidade e a padronização; portanto, sempre que encontro algum "truque oculto" na implementação que estou usando atualmente, tento não usá-lo porque tento manter meu Código C o mais padrão e portátil possível.
fonte
Asserções em tempo de compilação, como já discutido aqui .
fonte
Concatenação de cadeia constante
Fiquei bastante surpreso por não ter visto tudo nas respostas, pois todos os compiladores que conheço o apóiam, mas muitos programadores parecem ignorá-lo. Às vezes, é realmente útil e não apenas ao escrever macros.
Caso de uso que tenho no meu código atual: Eu tenho um
#define PATH "/some/path/"
em um arquivo de configuração (realmente é definido pelo makefile). Agora eu quero construir o caminho completo, incluindo nomes de arquivos para abrir recursos. Apenas vai para:Em vez do horrível, mas muito comum:
Observe que a solução horrível comum é:
fonte
Bem, eu nunca o usei e não tenho certeza se recomendaria a alguém, mas acho que essa pergunta seria incompleta sem uma menção ao truque co-rotineiro de Simon Tatham .
fonte
Ao inicializar matrizes ou enumerações, você pode colocar uma vírgula após o último item na lista de inicializadores. por exemplo:
Isso foi feito para que, se você estiver gerando código automaticamente, não precise se preocupar em eliminar a última vírgula.
fonte
A atribuição de estruturas é legal. Muitas pessoas parecem não perceber que as estruturas também são valores e podem ser atribuídas ao redor, não há necessidade de usar
memcpy()
, quando uma tarefa simples faz o truque.Por exemplo, considere alguma biblioteca gráfica 2D imaginária, ela pode definir um tipo para representar uma coordenada da tela (inteira):
Agora, você faz coisas que podem parecer "erradas", como escrever uma função que cria um ponto inicializado a partir dos argumentos da função e a retorna da seguinte maneira:
Isso é seguro, desde que, é claro, o valor de retorno seja copiado por valor usando a atribuição de estrutura:
Dessa forma, você pode escrever um código ish bastante limpo e orientado a objetos, tudo no padrão C.
fonte
Indexação de vetor estranho:
fonte
Os compiladores C implementam um dos vários padrões. No entanto, ter um padrão não significa que todos os aspectos da linguagem sejam definidos. Dispositivo de Duff , por exemplo, é um recurso 'oculto' favorito que se tornou tão popular que os compiladores modernos têm código de reconhecimento de finalidade especial para garantir que as técnicas de otimização não obtenham o efeito desejado desse padrão frequentemente usado.
Em geral, os recursos ocultos ou os truques de linguagem são desencorajados, enquanto você está rodando na borda dos padrões C, que seu compilador usa. Muitos desses truques não funcionam de um compilador para outro e, geralmente, esses tipos de recursos falham de uma versão de um conjunto de compiladores de um determinado fabricante para outra versão.
Vários truques que quebraram o código C incluem:
Outros problemas e questões que surgem sempre que os programadores fazem suposições sobre os modelos de execução especificados na maioria dos padrões C como comportamento 'dependente do compilador'.
fonte
Ao usar o sscanf, você pode usar% n para descobrir onde deve continuar lendo:
Aparentemente, você não pode adicionar outra resposta, então incluirei uma segunda aqui, você pode usar "&&" e "||" como condicionais:
Este código produzirá:
fonte
usar INT (3) para definir o ponto de interrupção no código é o meu favorito de todos os tempos
fonte
Meu recurso "oculto" favorito de C é o uso de% n no printf para gravar de volta na pilha. Normalmente printf exibe os valores dos parâmetros da pilha com base na string de formato, mas% n pode gravá-los de volta.
Confira a seção 3.4.2 aqui . Pode levar a muitas vulnerabilidades desagradáveis.
fonte
Verificação de suposições em tempo de compilação usando enumerações: exemplo estúpido, mas pode ser realmente útil para bibliotecas com constantes configuráveis em tempo de compilação.
fonte
#define CompilerAssert(exp) extern char _CompilerAssert[(exp)?1:-1]
)O Gcc (c) possui alguns recursos divertidos que você pode ativar, como declarações de funções aninhadas e a forma a?: B do operador?: Que retorna a se a não for falso.
fonte
Descobri recentemente 0 campos de bits.
que dará um layout de
em vez de sem o: 0;
O campo de largura 0 indica que os seguintes campos de bits devem ser definidos na próxima entidade atômica (
char
)fonte
Macros de argumento variável no estilo C99, também conhecido como
que seria usado como
Aqui eu também uso o operador stringize e a concatentação constante de strings, outros recursos que realmente gosto.
fonte
Variáveis automáticas de tamanho variável também são úteis em alguns casos. Estes foram adicionados no nC99 e são suportados no gcc há muito tempo.
Você acaba com um buffer na pilha, com espaço para o cabeçalho de protocolo de tamanho fixo mais dados de tamanho variável. Você pode obter o mesmo efeito com alloca (), mas essa sintaxe é mais compacta.
Você deve garantir que extraPadding seja um valor razoável antes de chamar essa rotina, ou você acaba explodindo a pilha. Você precisaria verificar os argumentos antes de chamar o malloc ou qualquer outra técnica de alocação de memória, portanto isso não é realmente incomum.
fonte