Qual é o seu truque de programação C favorito? [fechadas]

134

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?

smcameron
fonte
9
Isso parece mais com truques do pré-processador C.
jmucchiello
Sobre BUILD_BUG_ONmacro, o que há de errado em usar #errorinside e #if?
Ricardo

Respostas:

80

O C99 oferece coisas muito legais usando matrizes anônimas:

Removendo Variáveis ​​Inúteis

{
    int yes=1;
    setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, &yes, sizeof(int));
}

torna-se

setsockopt(yourSocket, SOL_SOCKET, SO_REUSEADDR, (int[]){1}, sizeof(int));

Passando uma quantidade variável de argumentos

void func(type* values) {
    while(*values) {
        x = *values++;
        /* do whatever with x */
    }
}

func((type[]){val1,val2,val3,val4,0});

Listas vinculadas estáticas

int main() {
    struct llist { int a; struct llist* next;};
    #define cons(x,y) (struct llist[]){{x,y}}
    struct llist *list=cons(1, cons(2, cons(3, cons(4, NULL))));
    struct llist *p = list;
    while(p != 0) {
        printf("%d\n", p->a);
        p = p->next;
    }
}

Qualquer um que eu tenha certeza de muitas outras técnicas legais em que não pensei.

Evan Teran
fonte
2
Acredito que seu primeiro exemplo também pode ser escrito como &(int){1}, se você quiser deixar um pouco mais claro qual é a sua intenção aqui.
Lily Ballard
67

Ao ler o código-fonte do Quake 2, vim com algo assim:

double normals[][] = {
  #include "normals.txt"
};

(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

fortran
fonte
13
Você não pode dizer carmack em um encadeamento de otimização sem mencionar o sqrt inverso rápido que estava na fonte do terremoto. en.wikipedia.org/wiki/Fast_inverse_square_root
pg1989
De onde ele tirou 0x5f3759df?
RSH1 14/10/11
2
@RoryHarvey: Pelo que pude encontrar ao pesquisar, parece que era puramente empírico. Alguns estudos (não me lembro onde os vi) demonstraram que estava próximo do ideal, mas não totalmente ideal. Da mesma forma, parece que para 64 bits o valor foi descoberto, em vez de computado.
Matthieu M.
50

Eu gosto de usar = {0};para inicializar estruturas sem precisar chamar memset.

struct something X = {0};

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 .

John Carter
fonte
A propósito, não é necessário para variáveis ​​globais.
284
5
Não é necessário para variáveis estáticas . Variáveis ​​globais podem ser zeradas, mas não é um requisito.
23709 Jamie
4
Às vezes, estendo isso para: const struct something zero_something = { 0 };e, em seguida, posso redefinir uma variável struct 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).
Jonathan Leffler
45

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 ...

Jackson
fonte
4
Eu usei uma vez para produzir um ganho de desempenho mensurável, mas hoje em dia não é útil em muito hardware. Sempre perfil!
Dan Olson
6
Sim, o tipo de pessoa que não entende o contexto em que o dispositivo de Duff foi criado: "legibilidade do código" é inútil se o código não for rápido o suficiente para funcionar. Provavelmente, nenhuma das pessoas que votaram contra você já teve que codificar em tempo real.
Rob K
1
+1, eu realmente precisei usar o dispositivo de Duff algumas vezes. A primeira vez foi um loop que basicamente apenas copiou as coisas e fez algumas pequenas transformações no caminho. Foi muito, muito mais rápido que um simples memcpy () nessa arquitetura.
30609 Makis
3
A raiva será dos seus colegas e sucessores que precisam manter seu código depois de você.
31416 Jonathan Leffler
1
Como eu disse, ainda estou esperando a oportunidade certa - mas ninguém me incomodou o suficiente ainda. Estou escrevendo C há cerca de 25 anos, acho que me deparei com o dispositivo de Duff no início dos anos 90 e ainda não precisei usá-lo. Como outros comentaram, esse tipo de truque é cada vez menos útil agora, à medida que os compiladores melhoram com esse tipo de otimização.
Jackson
42

usando __FILE__e __LINE__para depuração

#define WHERE fprintf(stderr,"[LOG]%s:%d\n",__FILE__,__LINE__);
Pierre
fonte
6
Em alguns compiladores, você também obtém FUNCTION .
JBRWilkinson 02/11/2009
11
__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.
sklnd
ARQUIVO mostra o caminho completo do arquivo para que eu use basename ( ARQUIVO )
Jeegar Patel
31

Em C99

typedef struct{
    int value;
    int otherValue;
} s;

s test = {.value = 15, .otherValue = 16};

/* or */
int a[100] = {1,2,[50]=3,4,5,[23]=6,7};
Jasper Bekkers
fonte
28

Uma vez um colega meu e eu redefinimos o retorno para encontrar um bug de corrupção de pilha complicado.

Algo como:

#define return DoSomeStackCheckStuff, return
Andrew Barrett
fonte
4
Espero que isso tenha sido # definido no corpo da função e # indefinido no final!
Strager 01/03/09
Não gosto muito disso - a primeira coisa que me vem à mente é que o DoSomeStackCheckStuff estraga a memória por causa de algum bug e quem está lendo o código não está ciente da redefinição do retorno e se pergunta o que está acontecendo.
Gilligan
8
@strager Mas isso tornaria basicamente inútil. O ponto principal é adicionar algum rastreio a cada chamada de função. Caso contrário, você apenas adicionaria uma chamada às DoSomeStackCheckStufffunções que deseja rastrear.
Clueless
1
@ Giligan Eu não acho que esse é o tipo de coisa que você deixa ativada o tempo todo; parece bastante útil para o trabalho de depuração de uma só vez.
sunetos 14/10
isso realmente funciona? :) eu teria escrito #define return if((DoSomeStackCheckStuff) && 0) ; else return... tão louco que eu acho!
Paolo Bonzini 14/10
22

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:

struct X {
    int len;
    char str[1];
};

int n = strlen("hello world");
struct X *string = malloc(sizeof(struct X) + n);
strcpy(string->str, "hello world");
string->len = n;

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.

Evan Teran
fonte
Pessoalmente, acho mais fácil usar apenas malloc () e realloc () e usar strlen () sempre que precisar encontrar o comprimento, mas se você precisar de um programa que nunca saiba o comprimento da string e provavelmente precisará encontrá-la muitas vezes, esta é provavelmente a melhor estrada.
31720 Chris Lutz
4
"... a versão C99 onde você pode escrever" str [] "" Eu vi matrizes de tamanho zero nesse contexto, como str [0]; com bastante frequência. Eu acho que é C99. Eu sei que os compiladores mais antigos se queixam de matrizes de tamanho zero.
smcameron
3
Eu também gosto deste, no entanto, você deve usar algo como malloc (offsetof (X, str) + numbytes), caso contrário, as coisas darão errado por causa de problemas de preenchimento e alinhamento. Por exemplo, sizeof (struct X) pode ser de 8, não 5.
Fozi
3
@Fozi: Na verdade, não acho que isso seria um problema. Como esta versão possui str[1](não str[]), o 1 byte de str está incluído no arquivo sizeof(struct X). Isso inclui qualquer preenchimento entre lene str.
Evan Teran
2
@Rusky: Como isso afetaria negativamente alguma coisa? Suponha que exista "preenchimento" depois str. OK, quando eu alocar sizeof(struct X) + 10Então isso torna strefetivamente 10 - sizeof(int)(ou mais, já que dissemos que há preenchimento) grande. Isso se sobrepõe a str qualquer preenchimento posterior. A única maneira que isso teria alguma diferença é se houvesse um membro depois do strqual 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.
quer
17

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.

Brian R. Bondy
fonte
2
Ainda existe algo por aí que traduz C ++ em C, como o cfront costumava fazer?
217 MarkJ
11
Isso dificilmente é orientação a objetos. Para OO com herança, você precisará adicionar algum tipo de tabela de função virtual à sua estrutura de objeto, que pode ser sobrecarregada por "subclasses". Existem muitas estruturas de estilo "C com classes" semi-cozidas disponíveis para esse fim, mas eu recomendo ficar de fora.
ExDM69
Era preciso ser dito. +1 para isso.
quer
3
@ exDM69, a orientação a objetos é tanto uma maneira de pensar sobre um problema quanto um paradigma de codificação; você pode fazer isso com sucesso sem herança. Eu fiz isso em alguns projetos antes de pular em C ++.
Mark Ransom
16

Ao invés de

printf("counter=%d\n",counter);

Usar

#define print_dec(var)  printf("%s=%d\n",#var,var);
print_dec(counter);
Const
fonte
14

Usando um truque estúpido de macro para facilitar a manutenção das definições de registro.

#define COLUMNS(S,E) [(E) - (S) + 1]

typedef struct
{
    char studentNumber COLUMNS( 1,  9);
    char firstName     COLUMNS(10, 30);
    char lastName      COLUMNS(31, 51);

} StudentRecord;
EvilTeach
fonte
11

Para criar uma variável que é somente leitura em todos os módulos, exceto aquele em que está declarada:

// Header1.h:

#ifndef SOURCE1_C
   extern const int MyVar;
#endif

// Source1.c:

#define SOURCE1_C
#include Header1.h // MyVar isn't seen in the header

int MyVar; // Declared in this file, and is writeable

// Source2.c

#include Header1.h // MyVar is seen as a constant, declared elsewhere
Steve Melnikoff
fonte
Isso parece perigoso. Estas são declarações e definições que não correspondem. Ao compilar Source2.c, o compilador pode assumir que MyVarisso não muda, mesmo em uma chamada de função para Source1.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..)
Jilles
1
Isso não produz variável que é somente leitura em algumas unidades de compilação. Isso produz um comportamento indefinido (consulte a página 6.2.7.2 da ISO 9899 e também a página 6.7.3.5).
Ales Hakl
8

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:

unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
  return (a>>(v>>1))>>((v+1)>>1);
}

Ou muito mais legível:

unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
  unsigned int halfshift = v>>1;
  unsigned int otherhalf = (v+1)>>1;

  return (a >> halfshift) >> otherhalf; 
}

Executar a tarefa da maneira mostrada acima é muito mais rápido do que usar um ramo como este:

unsigned int shiftmystuff (unsigned int a, unsigned int v)
{
  if (v<=31)
    return a>>v;
  else
    return 0;
}
Nils Pipenbrinck
fonte
... e o gcc realmente o alinha :) :) +1
Tim Post
2
Na minha máquina, gcc-4.3.2 se livrar do ramo no segundo usando uma instrução CMOV (movimento condicional)
Adam Rosenfield
3
"muito mais rápido do que usar uma ramificação": a diferença é que a ramificação está correta para todos os valores de v, enquanto o halfshifttruque apenas dobra o intervalo permitido para 63 em uma arquitetura de 32 bits e 127 em uma de 64 bits.
Pascal Cuoq 14/10
8

Declarando matriz de ponteiro para funções para implementar máquinas de estados finitos.

int (* fsm[])(void) = { ... }

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).

Jamie
fonte
Uma técnica que eu gosto com isso é que, se você tiver uma função que requer inicialização, inicialize o ponteiro com uma chamada para a rotina de inicialização. Quando isso é executado, a última coisa que faz é substituir o ponteiro por um ponteiro para a função real e chamar essa função. Dessa forma, o inicializador é chamado automaticamente na primeira vez que a função é chamada, e a função real é chamada a cada vez subseqüente.
TMN
7

Outro bom "truque" do pré-processador é usar o caractere "#" para imprimir expressões de depuração. Por exemplo:

#define MY_ASSERT(cond) \
  do { \
    if( !(cond) ) { \
      printf("MY_ASSERT(%s) failed\n", #cond); \
      exit(-1); \
    } \
  } while( 0 )

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:

#define COMPILE_ASSERT(cond)\
     typedef char __compile_time_assert[ (cond) ? 0 : -1]
Gilad Naor
fonte
A macro COMPILE_ASSERT não pode ser usado duas vezes, porém, como ele polui o namespace com um typedef, ea 2ª uso recebe: Erro: redefinição do typedef '__compile_time_assert'
smcameron
Você realmente tentou isso? Você pode "typedef foo;" quantas vezes quiser. É assim que você faz as pré-declarações. Eu o uso há 2,5 anos em vários compiladores, tanto gcc, VC quanto um compilador para um ambiente incorporado, e nunca encontrei nenhuma dificuldade.
Gilad Naor
Eu odeio o pré-processador C ... :(
Hasen
1
Sim, eu tentei. Recortei e colei a mensagem de erro do compilador, que era o gcc.
smcameron
1
@ Gilad: é legal em c ++ ter typedefs redundantes, mas não em c.
Evan Teran
6

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.

Dan Olson
fonte
Na verdade, usei essa técnica na prática para tornar o código que conduz uma sequência de E / S assíncrona dependente, vagamente legível por humanos. A principal diferença é que eu não armazeno o estado da corotina em uma staticvariá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 (via swapcontext()* nixes) se eu pudesse.
Pmdj # 14/11
6
#if TESTMODE == 1    
    debug=1;
    while(0);     // Get attention
#endif

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.

gbarry
fonte
9
você não poderia usar #warning?
234 Stefano Borini #
Aparentemente, eu poderia. Não é completamente padrão, mas funcionou nos compiladores que eu uso. Curiosamente, o compilador incorporado traduziu um #define, enquanto o gcc não.
Gbarry #
6

Sou fã de xor hacks:

Troque 2 ponteiros sem o terceiro indicador temporário:

int * a;
int * b;
a ^= b;
b ^= a;
a ^= b;

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:

LLNode * first = head;
LLNode * second = first.linked_nodes;
LLNode * third = second.linked_nodes ^ first;
LLNode * fourth = third.linked_nodes ^ second;

etc.

ou para recuar:

LLNode * last = tail;
LLNode * second_to_last = last.linked_nodes;
LLNode * third_to_last = second_to_last.linked_nodes ^ last;
LLNode * fourth_to_last = third_to_last.linked_nodes ^ second_to_last;

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.

hamiltop
fonte
5

Este vem do livro 'Bastante corda para se dar um tiro no pé':

No cabeçalho declare

#ifndef RELEASE
#  define D(x) do { x; } while (0)
#else
#  define D(x)
#endif

No seu código, coloque as instruções de teste, por exemplo:

D(printf("Test statement\n"));

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

Simon Walker
fonte
Você pode expandir D (x) para {} quando RELEASE estiver definido, para que ele funcione bem com as instruções if. Caso contrário "se (a) D (x);" será expandido para apenas "if (a)" quando você tiver definido o RELEASE. Isso lhe dará alguns bugs legais na versão RELEASE
MarkJ
3
@MarkJ: NÃO. Do jeito que está, "se (a) D (x);" expande para "se (a);" o que é perfeitamente bom. Se você tivesse D (x) expandido para {}, então "if (a) if (b) D (x); else foo ();" expandiria INCORRETAMENTE para "if (a) if (b) {}; else foo ();", fazendo com que o "else foo ()" correspondesse ao segundo se, em vez do primeiro se.
237 Adam Rosenfield
Para ser sincero, uso principalmente essa macro para testar instruções de impressão ou, se eu tivesse uma instrução condicional, incluiria tudo, por exemplo. D (se (a) foo (););
Simon Walker
1
@AdamRosenfield: Usando #define D(x) do { } while(0)vez lida com esse caso (e pode ser aplicado para o ramo que insere x, bem como para a consistência)
rpetrich
3

O Rusty realmente produziu todo um conjunto de condicionais de compilação no ccan , confira o módulo de construção de declaração :

#include <stddef.h>
#include <ccan/build_assert/build_assert.h>

struct foo {
        char string[5];
        int x;
};

char *foo_string(struct foo *foo)
{
        // This trick requires that the string be first in the structure
        BUILD_ASSERT(offsetof(struct foo, string) == 0);
        return (char *)foo;
}

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.

Tim Post
fonte
Sim, recentemente me deparei com o ccan, e estava pensando em contribuir com algum código, mas ainda não entendi o "caminho do ccan". Obrigado pelo link, porém, mais motivação para analisar o ccan, o que eu realmente espero que tenha alguma tração.
smcameron
Bem, eu não ficaria muito preocupado com o 'caminho da lata' até que seja mais estabelecido ... agora o ccan-lint está sendo proposto como um projeto do GSOC. É um grupo pequeno e bastante amigável .. e um ótimo lugar para trechos despejo :)
Tim Publicar
BTW, notei que o BuILD_ASSERT do Rusty é exatamente como a macro do kernel do linux (sem surpresa), mas falta um dos "nots" (ou bangs, ou! 'S) e, percebendo isso, acho que meu exemplo de uso da macro que publiquei é incorreto. Deveria ter sido: "BUILD_BUG_ON ((sizeof (struct mystruct)% 8))"
smcameron
3

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.

Yuval F
fonte
1
AFAIK, em C89 / 90, NÃO há digitação de enumerações. enums são de alguma forma mais convenientes #defines.
cschol
Parte inferior da página 39, 2º ED K&R. Há pelo menos a oportunidade de verificar.
Jonathan Watmough
3

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":

int a = 1;
int b = 2;

printf("a = %d, b = %d\n", a, b);

a ^= b;
b ^= a;
a ^= b;

printf("a = %d, b = %d\n", a, b);

Resultado:

a = 1, b = 2

a = 2, b = 1

Karl
fonte
a = 1; b = 2; a = a + b; b = ab; a = ab; dá o mesmo resultado também
Grambot 14/10
Isso também trocará aeb: a ^ = b ^ = a ^ = b;
vikhyat
@TheCapn: a adição pode estourar, no entanto.
Michael Foukarakis 15/10
2

Eu gosto do conceito de container_of usado, por exemplo, em listas. Basicamente, você não precisa especificar nextelast 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.hexemplos da vida real.

Viliam
fonte
1

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.

epatel
fonte
1
Eu gostaria de entender o que você quis dizer aqui. Você poderia explicar mais? O que é um ponteiro de dados do usuário?
Zan Lynx
1
Por favor, veja aqui stackoverflow.com/questions/602826/…
epatel 03/03
é principalmente para retornos de chamada. São alguns dados que você gostaria de receber sempre que o retorno de chamada é acionado. Particularmente útil para passar um C ++ desse ponteiro para um retorno de chamada, para que você possa vincular um objeto a um evento.
Evan Teran
Ah sim. Obrigado. Eu uso muito isso, mas nunca chamei assim.
Zan Lynx
1

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.

JayG
fonte
1

Nossa base de código tem um truque semelhante a

#ifdef DEBUG

#define my_malloc(amt) my_malloc_debug(amt, __FILE__, __LINE__)
void * my_malloc_debug(int amt, char* file, int line)
#else
void * my_malloc(int amt)
#endif
{
    //remember file and line no. for this malloc in debug mode
}

que permite o rastreamento de vazamentos de memória no modo de depuração. Eu sempre achei isso legal.

jdizzle
fonte
1

Diversão com macros:

#define SOME_ENUMS(F) \
    F(ZERO, zero) \
    F(ONE, one) \
    F(TWO, two)

/* Now define the constant values.  See how succinct this is. */

enum Constants {
#define DEFINE_ENUM(A, B) A,
    SOME_ENUMS(DEFINE_ENUMS)
#undef DEFINE_ENUM
};

/* Now a function to return the name of an enum: */

const char *ToString(int c) {
    switch (c) {
    default: return NULL; /* Or whatever. */
#define CASE_MACRO(A, B) case A: return #b;
     SOME_ENUMS(CASE_MACRO)
#undef CASE_MACRO
     }
}
sanjoyd
fonte
0

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.

/* free.h */
#ifndef _FREE_H_
#define _FREE_H_
#include <stdio.h>
#include <string.h>
typedef unsigned char ubyte;

typedef void (*F_ParameterlessFunction)() ;
typedef void (*F_CommandFunction)(ubyte byte) ;

void F_SetupLowerLayer (
F_ParameterlessFunction initRequest,
F_CommandFunction sending_command,
F_CommandFunction *receiving_command);
#endif

/* free.c */
static F_ParameterlessFunction Init_Lower_Layer = NULL;
static F_CommandFunction Send_Command = NULL;
static ubyte init = 0;
void recieve_value(ubyte my_input)
{
    if(init == 0)
    {
        Init_Lower_Layer();
        init = 1;
    }
    printf("Receiving 0x%02x\n",my_input);
    Send_Command(++my_input);
}

void F_SetupLowerLayer (
    F_ParameterlessFunction initRequest,
    F_CommandFunction sending_command,
    F_CommandFunction *receiving_command)
{
    Init_Lower_Layer = initRequest;
    Send_Command = sending_command;
    *receiving_command = &recieve_value;
}

/* main.c */
int my_hw_do_init()
{
    printf("Doing HW init\n");
    return 0;
}
int my_hw_do_sending(ubyte send_this)
{
    printf("doing HW sending 0x%02x\n",send_this);
    return 0;
}
F_CommandFunction my_hw_send_to_read = NULL;

int main (void)
{
    ubyte rx = 0x40;
    F_SetupLowerLayer(my_hw_do_init,my_hw_do_sending,&my_hw_send_to_read);

    my_hw_send_to_read(rx);
    getchar();
    return 0;
}
eaanon01
fonte
4
Gostaria de elaborar, talvez explicando um uso prático?
Leonardo Herrera
Como exemplo, se eu tiver que escrever um programa de teste usando uma interface HW que gera interrupções no final. Em seguida, este módulo pode ser configurado para executar uma função fora do escopo normal como manipulador de sinal / interrupção.
Eaanon01 3/11/09
0
if(---------)  
printf("hello");  
else   
printf("hi");

Preencha os espaços em branco para que nem olá nem olá apareçam na saída.
ans:fclose(stdout)

justgo
fonte
você pode formatar o código com o {}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.
Álvaro González