Por exemplo, recentemente deparei com isso no kernel do linux:
/ * Força um erro de compilação se a condição for verdadeira * / #define BUILD_BUG_ON (condição) ((nulo) sizeof (char [1 - 2 * !! (condição)]))
Portanto, no seu código, se você tem alguma estrutura que deve ser, digamos um múltiplo de 8 bytes de tamanho, talvez por causa de algumas restrições de hardware, você pode:
BUILD_BUG_ON ((tamanho da estrutura)% 8)! = 0);
e não será compilado, a menos que o tamanho da estrutura mystruct seja múltiplo de 8 e, se for múltiplo de 8, nenhum código de tempo de execução será gerado.
Outro truque que conheço é do livro "Graphics Gems", que permite que um único arquivo de cabeçalho declare e inicialize variáveis em um módulo enquanto em outros módulos usando esse módulo, apenas as declara como externas.
#ifdef DEFINE_MYHEADER_GLOBALS #define GLOBAL #define INIT (x, y) (x) = (y) #outro #define GLOBAL extern #define INIT (x, y) #fim se GLOBAL int INIT (x, 0); GLOBAL int de alguma forma (int a, int b);
Com isso, o código que define x e somefunc faz:
#define DEFINE_MYHEADER_GLOBALS #include "the_above_header_file.h"
enquanto o código que está apenas usando x e somefunc () faz:
#include "the_above_header_file.h"
Portanto, você obtém um arquivo de cabeçalho que declara as instâncias de protótipos globais e de função onde elas são necessárias e as declarações externas correspondentes.
Então, quais são seus truques favoritos de programação C nesse sentido?
BUILD_BUG_ON
macro, o que há de errado em usar#error
inside e#if
?Respostas:
O C99 oferece coisas muito legais usando matrizes anônimas:
Removendo Variáveis Inúteis
torna-se
Passando uma quantidade variável de argumentos
Listas vinculadas estáticas
Qualquer um que eu tenha certeza de muitas outras técnicas legais em que não pensei.
fonte
&(int){1}
, se você quiser deixar um pouco mais claro qual é a sua intenção aqui.Ao ler o código-fonte do Quake 2, vim com algo assim:
(mais ou menos, não tenho o código à mão para verificá-lo agora).
Desde então, um novo mundo de uso criativo do pré-processador se abriu diante dos meus olhos. Não incluo mais apenas cabeçalhos, mas pedaços inteiros de código de vez em quando (melhora muito a reutilização) :-p
Obrigado John Carmack! xD
fonte
Eu gosto de usar
= {0};
para inicializar estruturas sem precisar chamar memset.Isso inicializará todos os membros da estrutura (ou matriz) para zero (mas não quaisquer bytes de preenchimento - use memset se você precisar zerá-los também).
Mas você deve estar ciente de que existem alguns problemas com isso para estruturas grandes e dinamicamente alocadas .
fonte
const struct something zero_something = { 0 };
e, em seguida, posso redefinir uma variávelstruct something X = zero_something;
rapidamente ou no meio de uma rotina em que posso usar 'X = zero_something;'. A única objeção possível é que envolve a leitura de dados de algum lugar; hoje em dia, um 'memset ()' pode ser mais rápido - mas eu gosto da clareza da atribuição e também é possível usar valores diferentes de zero no inicializador também (e memset () seguido de ajustes no membro individual pode ser mais lento que uma cópia simples).Se estamos falando de truques c, meu favorito deve ser o dispositivo de Duff para desenrolar o loop! Só estou esperando a oportunidade certa para eu realmente usá-lo com raiva ...
fonte
usando
__FILE__
e__LINE__
para depuraçãofonte
__FUNCTION__
é apenas um apelido para__func__
e__func__
está em c99. Bastante útil.__PRETTY_FUNCTION__
em C (GCC) é apenas outro apelido para__func__
, mas em C ++ ele fornecerá a assinatura completa da função.Em C99
fonte
Uma vez um colega meu e eu redefinimos o retorno para encontrar um bug de corrupção de pilha complicado.
Algo como:
fonte
DoSomeStackCheckStuff
funções que deseja rastrear.#define return if((DoSomeStackCheckStuff) && 0) ; else return
... tão louco que eu acho!Eu gosto do "struct hack" por ter um objeto de tamanho dinâmico. Este site também explica muito bem (embora eles se refiram à versão C99, na qual você pode escrever "str []" como o último membro de uma estrutura). você poderia criar uma string "objeto" assim:
aqui, alocamos uma estrutura do tipo X no heap que é do tamanho de um int (para len), mais o tamanho do "hello world" e mais 1 (desde str 1 está incluído no (X).
Geralmente é útil quando você deseja ter um "cabeçalho" antes de alguns dados de comprimento variável no mesmo bloco.
fonte
str[1]
(nãostr[]
), o 1 byte de str está incluído no arquivosizeof(struct X)
. Isso inclui qualquer preenchimento entrelen
estr
.str
. OK, quando eu alocarsizeof(struct X) + 10
Então isso tornastr
efetivamente10 - sizeof(int)
(ou mais, já que dissemos que há preenchimento) grande. Isso se sobrepõe astr
qualquer preenchimento posterior. A única maneira que isso teria alguma diferença é se houvesse um membro depois dostr
qual a coisa terminasse, membros flexíveis devem ser os últimos. Qualquer preenchimento no final só pode causar muito a ser alocado. Forneça um exemplo específico de como isso pode realmente dar errado.Código orientado a objeto com C, emulando classes.
Basta criar uma estrutura e um conjunto de funções que levam um ponteiro para essa estrutura como primeiro parâmetro.
fonte
Ao invés de
Usar
fonte
Usando um truque estúpido de macro para facilitar a manutenção das definições de registro.
fonte
Para criar uma variável que é somente leitura em todos os módulos, exceto aquele em que está declarada:
fonte
Source2.c
, o compilador pode assumir queMyVar
isso não muda, mesmo em uma chamada de função paraSource1.c
. (Note-se que isto, como uma variável const real, diferente de um ponteiro para const Neste último caso, o aguçado-a objeto pode ainda ser modificados por meio de um ponteiro diferente..)Os desvios de bits são definidos apenas até um valor de desvio de 31 (em um número inteiro de 32 bits).
O que você faz se deseja ter um turno computado que também precisa trabalhar com valores de turno mais altos? Aqui está como o videocodec Theora faz isso:
Ou muito mais legível:
Executar a tarefa da maneira mostrada acima é muito mais rápido do que usar um ramo como este:
fonte
v
, enquanto ohalfshift
truque apenas dobra o intervalo permitido para 63 em uma arquitetura de 32 bits e 127 em uma de 64 bits.Declarando matriz de ponteiro para funções para implementar máquinas de estados finitos.
A vantagem mais agradável é que é simples forçar cada estímulo / estado a verificar todos os caminhos de código.
Em um sistema incorporado, frequentemente mapeio um ISR para apontar para uma tabela e a reveciono conforme necessário (fora do ISR).
fonte
Outro bom "truque" do pré-processador é usar o caractere "#" para imprimir expressões de depuração. Por exemplo:
editar: o código abaixo funciona apenas em C ++. Agradecimentos a smcameron e Evan Teran.
Sim, a afirmação do tempo de compilação é sempre ótima. Também pode ser escrito como:
fonte
Eu realmente não chamaria isso de um truque favorito, já que nunca o usei, mas a menção de Duff's Device me lembrou este artigo sobre a implementação de Coroutines em C. Ele sempre me dá uma risada, mas tenho certeza que poderia seja útil por algum tempo.
fonte
static
variável, mas aloco uma estrutura dinamicamente e passo um ponteiro para isso na função da corotina. Um monte de macros tornam isso mais agradável. Não é legal, mas melhor do que a versão assíncrona / de retorno de chamada que se espalha por todo o lugar. Eu usaria linhas verdes (viaswapcontext()
* nixes) se eu pudesse.O tempo (0); não tem efeito no programa, mas o compilador emitirá um aviso sobre "isso não faz nada", o que é suficiente para que eu olhe para a linha incorreta e veja o verdadeiro motivo pelo qual eu queria chamar atenção.
fonte
Sou fã de xor hacks:
Troque 2 ponteiros sem o terceiro indicador temporário:
Ou eu realmente gosto da lista vinculada xor com apenas um ponteiro. (http://en.wikipedia.org/wiki/XOR_linked_list)
Cada nó na lista vinculada é o Xor do nó anterior e o próximo nó. Para avançar, o endereço dos nós é encontrado da seguinte maneira:
etc.
ou para recuar:
etc.
Embora não seja muito útil (você não pode começar a atravessar a partir de um nó arbitrário), acho muito legal.
fonte
Este vem do livro 'Bastante corda para se dar um tiro no pé':
No cabeçalho declare
No seu código, coloque as instruções de teste, por exemplo:
O do / while ajuda no caso de o conteúdo da macro se expandir para várias instruções.
A instrução será impressa apenas se o sinalizador '-D RELEASE' do compilador não for usado.
Você pode, por exemplo, passe a bandeira para o seu makefile etc.
Não sei como isso funciona no Windows, mas no * nix funciona bem
fonte
#define D(x) do { } while(0)
vez lida com esse caso (e pode ser aplicado para o ramo que inserex
, bem como para a consistência)O Rusty realmente produziu todo um conjunto de condicionais de compilação no ccan , confira o módulo de construção de declaração :
Existem muitas outras macros úteis no cabeçalho real, fáceis de colocar no lugar.
Eu tento, com todas as minhas forças, resistir à atração do lado sombrio (e ao abuso do pré-processador) aderindo principalmente às funções embutidas, mas eu gosto de macros inteligentes e úteis, como as que você descreveu.
fonte
Dois bons livros-fonte para esse tipo de coisa são The Practice of Programming and Writing Solid Code . Um deles (não me lembro qual) diz: Prefira enum a #define onde você puder, porque enum é verificado pelo compilador.
fonte
Não é específico para C, mas sempre gostei do operador XOR. Uma coisa legal que ele pode fazer é "trocar sem um valor temporário":
Resultado:
fonte
Consulte "Recursos ocultos de C" pergunta .
fonte
Eu gosto do conceito de
container_of
usado, por exemplo, em listas. Basicamente, você não precisa especificarnext
elast
campos para cada estrutura que vai estar na lista. Em vez disso, você anexa o cabeçalho da estrutura da lista aos itens vinculados reais.Veja
include/linux/list.h
exemplos da vida real.fonte
Eu acho que o uso de ponteiros de dados do usuário é bem legal. Uma moda perdendo terreno hoje em dia. Não é tanto um recurso C, mas é muito fácil de usar em C.
fonte
Eu uso X-Macros para permitir que o pré-compilador gere código. Eles são especialmente úteis para definir valores de erro e cadeias de erro associadas em um único local, mas podem ir muito além disso.
fonte
Nossa base de código tem um truque semelhante a
que permite o rastreamento de vazamentos de memória no modo de depuração. Eu sempre achei isso legal.
fonte
Diversão com macros:
fonte
Aqui está um exemplo de como tornar o código C completamente inconsciente sobre o que realmente é usado pelo HW para executar o aplicativo. O main.c faz a configuração e, em seguida, a camada livre pode ser implementada em qualquer compilador / arco. Eu acho que é bastante interessante abstrair um pouco o código C, por isso não chega a ser específico.
Adicionando um exemplo compilável completo aqui.
fonte
Preencha os espaços em branco para que nem olá nem olá apareçam na saída.
ans:
fclose(stdout)
fonte
{}
botão da barra de ferramentas (eu fiz isso por você). O botão "Cotação" não mantém espaços em branco nem aplica realce de sintaxe.