Diário de aprendizagem C / C ++: definição de macro da linguagem C ## conector e # uso de símbolo

Prefácio: Como usar macros em linguagem C. Macros em C (e C ++) pertencem à categoria de pré-processamento do compilador e pertencem ao conceito de tempo de compilação (não ao conceito de tempo de execução). A seguir está um breve resumo dos problemas de uso de macro freqüentemente encontrados.

Sobre # e ##

 

Na macro da linguagem C, a função de # é restringir os parâmetros da macro que a seguem, basta colocar, após substituir a variável macro a que se refere, adicionar aspas duplas nos lados esquerdo e direito dela . Por exemplo, a macro no seguinte código:

#define WARN_IF(EXP)    do{ if (EXP)    fprintf(stderr, "Warning: " #EXP "\n"); }  while(0)

Em seguida, o seguinte processo de substituição aparecerá em uso real:

WARN_IF (divisor == 0);被 替换 为do {if (divisor == 0) fprintf (stderr, "Warning" "divider == 0" "\ n");} while (0);

Dessa forma, uma mensagem de prompt será gerada no fluxo de erro padrão sempre que o divisor (divisor) for 0.

E ## é chamado de concatenador, que é usado para conectar dois tokens em um token. Observe que o objeto conectado aqui é um Token, não necessariamente uma variável de macro. Por exemplo, você deseja fazer uma matriz de estrutura composta de nomes de comando de item de menu e ponteiros de função, e deseja ter uma relação intuitiva e de nome entre os nomes de função e os nomes de comando de item de menu. Então, o código a seguir é muito prático:

struct command

{

char * name;

void (*function) (void);

};

#define COMMAND(NAME) { NAME, NAME ## _command }

// 然后你就用一些预先定义好的命令来方便的初始化一个command结构的数组了:

struct command commands[] = {

COMMAND(quit),

COMMAND(help),

...

}

A macro COMMAND atua como um gerador de código aqui, o que pode reduzir a densidade do código até certo ponto e, indiretamente, também pode reduzir erros causados ​​por desatenção. Também podemos conectar n + 1 tokens com n ## símbolos, que também não está disponível nos símbolos #. tal como:

#define LINK_MULTIPLE(a,b,c,d) a##_##b##_##c##_##d

typedef struct _record_type LINK_MULTIPLE(name,company,position,salary);

// 这里这个语句将展开为:

// typedef struct _record_type name_company_position_salary;

Em relação ao uso de

... Chamada de macro variável na macro C, que é uma macro de parâmetro variável. tal como:

#define myprintf (templt, ...) fprintf (stderr, templt, __ VA_ARGS__)

// ou

#define myprintf (templt, args ...) fprintf (stderr, templt, args)

Como o parâmetro da variável não foi nomeado na primeira macro, nós o substituímos pela macro padrão __VA_ARGS__. Na segunda macro, nomeamos explicitamente o parâmetro variável args, para que possamos usar args para nos referir ao parâmetro variável na definição da macro. Como o stdcall na linguagem C, os parâmetros variáveis ​​devem aparecer como o último item na lista de parâmetros. Quando podemos fornecer apenas o primeiro modelo de parâmetro na macro acima, o padrão C exige que escrevamos:

myprintf (templt,);

Formato. O processo de substituição neste momento é:

 

myprintf ("Erro! \ n",); Substitua por: fprintf (stderr, "Erro! \ n",);

Este é um erro de sintaxe e não pode ser compilado normalmente. Geralmente, existem duas soluções para esse problema. Primeiro, a solução fornecida pelo GNU CPP permite que a macro chamada acima seja escrita como:

myprintf (templt);

E será substituído por:

fprintf (stderr, "Erro! \ n",);

Obviamente, ainda haverá erros de compilação (em alguns casos, exceto neste exemplo, os erros de compilação não ocorrerão). Além deste método, tanto c99 quanto GNU CPP suportam os seguintes métodos de definição de macro:

#define myprintf (templt, ...) fprintf (stderr, templt, ## __ VAR_ARGS__)

Neste momento, a função do símbolo de link ## é eliminar a vírgula na frente quando __VAR_ARGS__ está vazio. Então, o processo de tradução neste momento é o seguinte:

myprintf (templt); é transformado em: fprintf (stderr, templt);

Portanto, se o modelo for legal, nenhum erro de compilação será gerado. Aqui está uma lista de algumas áreas propensas a erros no uso de macros e métodos de uso apropriados.

Aninhamento errado-aninhamento incorreto

As definições de macro não precisam ter parênteses completos e correspondentes, mas para evitar erros e melhorar a legibilidade, é melhor evitar esse uso.

O problema causado pela precedência do operador - Problema de precedência do operador

Uma vez que a macro é apenas uma substituição simples, se o parâmetro da macro for uma estrutura composta, então, após a substituição, a prioridade do operador entre os vários parâmetros pode ser maior do que a prioridade do operador da interação entre as partes do parâmetro único, se não Os colchetes protegem cada parâmetro da macro, o que pode causar situações inesperadas. tal como:

 

Se você quiser ver mais informações, por favor, olhe a imagem acima ou junte-se ao meu círculo de pinguins. Há mais projetos e cursos de código aberto gratuitos esperando por você para assistir!

#define ceil_div (x, y) (x + y - 1) / y

Então

a = ceil_div (b & c, sizeof (int));

Será transformado em:

a = (b & c + sizeof (int) - 1) / sizeof (int);

// Como a prioridade de +/- é maior do que a prioridade de &, a fórmula acima é equivalente a:

a = (b & (c + sizeof (int) - 1)) / sizeof (int);

Obviamente, essa não é a intenção original do chamador. Para evitar isso, você deve escrever mais alguns parênteses:

#define ceil_div (x, y) (((x) + (y) - 1) / (y))

Elimine ponto-e-vírgula desnecessário - engolir ponto-e-vírgula

Normalmente, para fazer uma macro semelhante a uma função parecer uma chamada de linguagem C normal na superfície, geralmente adicionamos um ponto e vírgula após a macro, como a macro a seguir com parâmetros:

MY_MACRO (x);

Mas se for a seguinte situação:

#define MY_MACRO(x) { /* line 1 */ /* line 2 */ /* line 3 */ }

//...

if (condition())

MY_MACRO(a);

else

{...}

Isso causará erros de compilação devido ao ponto-e-vírgula extra. A fim de evitar essa situação, mantendo MY_MACRO (x); desta forma de escrever, precisamos definir a macro desta forma:

#define MY_MACRO (x) do {

/ * linha 1 * / / * linha 2 * / / * linha 3 * /} enquanto (0)

Portanto, contanto que você sempre use ponto-e-vírgula, não terá problemas.

Duplicação de efeitos colaterais

O efeito colateral aqui significa que o parâmetro da macro pode ser avaliado várias vezes (ou seja, valor) quando a macro é expandida, mas se o parâmetro da macro for uma função, ele pode ser chamado várias vezes para obter resultados inconsistentes ou mesmo Erros mais sérios ocorrerão. tal como:

# define min (X, Y) ((X)> (Y)? (Y): (X))

// ...

 

c = min (a, foo (b));

Nesse momento, a função foo () é chamada duas vezes. Para resolver esse problema potencial, devemos escrever a macro min (X, Y) assim:

#define min (X, Y) ({typeof (X) x_ = (X); typeof (Y) y_ = (Y); (x_ <y_)? x_: y_;})

A função de ({...}) é devolver o valor do último dos vários comandos internos.Também permite que as variáveis ​​sejam declaradas internamente (porque forma um âmbito local através de chaves).

 

Acho que você gosta

Origin blog.csdn.net/weixin_45713725/article/details/109358846
Recomendado
Clasificación