2011-10-04 16 views
19

Por favor, vea mi código:¿Qué es este misterioso signo más macro en stdint.h?

#include <stdint.h> 

int main(int argc, char *argv[]) 
{ 
unsigned char s = 0xffU; 
char ch = 0xff; 
int val = 78; 
((int8_t) + (78)); /*what does this mean*/ 

INT8_C(val); /*equivalent to above*/ 

signed char + 78; /*not allowed*/ 

return 0; 
} 

me parece que la definición de la macro en <stdint.h> es:

#define INT8_C(val) ((int8_t) + (val)) 

¿Cuál es el sentido o el significado de este signo más?

+1

Cabe señalar que esta definición de 'INT8_C' es incorrecta: 7.18.4 párrafo 3: "Cada invocación de una de estas macros (NOTA: refiriéndose a' INTN_C') se ampliará a una expresión constante constante ** adecuada para su uso en las directivas de preprocesamiento '# if'. **" Ese molde no funcionará en una directiva de preprocesador, ya que los tipos (y, por lo tanto, los moldes) no existen durante el preprocesamiento. ¿Qué compilador/plataforma estás usando? Debería considerar la presentación de un informe de error sobre esto. –

+4

@ChrisLutz: era válido en C99; se convirtió en inválido en el Corrigendum técnico 1, que está incorporado en N1256. Esto fue en respuesta a [DR # 209] (http://www.open-std.org/jtc1/sc22/wg14/www/docs/dr_209.htm). –

+0

@KeithThompson - Ah. Tengo una copia de TC2, y hasta hace muy poco tiempo no estaba realmente al tanto de los Corrientes Técnicos. –

Respuesta

27

El fragmento:

((int8_t) + (78)); 

es un expresión, uno que toma el valor 78, se aplica el operador unario +, entonces arroja que a un tipo int8_t, antes de tirarlo. No es real, diferente a las expresiones legales:

42; 
a + 1; 

que también evaluar las expresiones luego tirar el resultado (aunque éstas posiblemente se optimizarán dejar de existir si el compilador puede decir que no hay efectos secundarios).

Estas expresiones "desnudos" son perfectamente válidas en C y generalmente útiles sólo cuando tienen efectos secundarios, tales como con i++, que calcula i y lo lanza lejos con el efecto secundario es que se incrementa el valor.

La forma en que debe a utilizar esa macro es más a lo largo de las líneas de:

int8_t varname = INT8_C (somevalue); 

La razón de la aparentemente redundante + operador unitario se pueden encontrar en la norma. Citando 6.5.3.3 Unary arithmetic operators /1:

El operando del operador unario + o - debe tener un tipo aritmético;

Y, en 6.2.5 Types, /18:

enteros y tipos flotantes se denominan colectivamente tipos aritméticos.

En otras palabras, el unario + no le permite usar todos los otros tipos de datos en la macro, tales como punteros, números complejos o estructuras.

Y, por último, la razón de su:

signed char + 78; 

fragmento no funciona es porque no es lo mismo. Éste está comenzando a declarar una variable de tipo signed char pero se ahoga cuando llega al + ya que ese no es un nombre de variable legal. Para que sea equivalente a su fragmento de trabajo, se debería utilizar:

(signed char) + 78; 

que es la fundición del valor +78 para escribir signed char.

Y, según 7.8.14 Macros for integer constants /2 C99, también se debe tener cuidado con el uso de los no constantes en esas macros, que no está garantizado para trabajar:

El argumento en cualquier instancia de estas macros se realizará una constante entera no reajustada (como definida en 6.4.4.1) con un valor que no supera los límites para el tipo correspondiente.

6.4.4.1 simplemente especifica los diversos formatos de enteros (decimal/octal/hex) con los diversos sufijos (U, UL, ULL, L, LL y los equivalentes minúsculas, dependiendo del tipo). La conclusión es que tienen que ser constantes en lugar de variables.

Por ejemplo, glibc tiene:

# define INT8_C(c)  c 
# define INT16_C(c)  c 
# define INT32_C(c)  c 
# if __WORDSIZE == 64 
# define INT64_C(c) C## L 
# else 
# define INT64_C(c) C## LL 
# endif 

que permitirá que su INT8_C macro para funcionar bien, pero el texto INT64_C(val) se pre-procesado en cualquiera valL o valLL, ninguno de los cuales usted quiere.

+0

En realidad, la definición que el OP da para 'INT8_C' viola el estándar, por mi lectura. 7.18.4 párrafo 3: "Cada invocación de una de estas macros se ampliará a una expresión constante constante ** adecuada para su uso en las directivas de preprocesamiento '# if'. **" La definición original de 'INT8_C' no puede utilizarse en una directiva '# if', y por lo tanto es incorrecta. –

+3

@Chris: el + lo hace válido en las directivas de preprocesamiento. 'int8_t' se expande a 0, y' (0) + (val) 'es simplemente' val'. –

+0

¿Cómo se expande 'int8_t' a 0? Pensé que era un tipo dedef? o_O –

10

Es un operador único +. Otorga el valor de su operando, pero solo se puede aplicar a tipos aritméticos. El propósito aquí es evitar el uso de INT8_C en una expresión de tipo de puntero.

Sin embargo, su declaración de la expresión

INT8_C(val); 

ha indefinido comportamiento. Se requiere que el argumento INT8_C() sea "una constante entera no confirmada ... con un valor que no exceda los límites para el tipo correspondiente" (N1256 7.18.4). La intención de estas macros es permitirle escribir algo como INT64_C(42) y expandirlo a 42L si int64_t es , o 42LL si int64_t es `largo, y así sucesivamente.

Pero escribir bien

((int8_t) + (78)); 

o

((int8_t) + (val)); 

en su propio código es perfectamente legal.

EDITAR:

La definición de INT8_C() en la pregunta:

#define INT8_C(val) ((int8_t) + (val)) 

es válido en el C99, pero es no conforme de acuerdo con el posterior proyecto N1256, como resultado de un cambio en uno de los corrigenda técnicos.

El estándar original C99, sección 7.18.4.1P2, dice:

La macro INT *** N * _C ** (valor) se expanda a un entero constante con el valor ed especificidad y escriba int_least *** N * _t **.

N1256 cambió esto a (párrafo 3):

Cada invocación de una de estas macros deberá expandirse a un número entero expresión constante adecuado para su uso en #if directivas de preprocesamiento. El tipo de expresión tendrá el mismo tipo que una expresión del tipo correspondiente convertido de acuerdo con las promociones enteras . El valor de la expresión será el del argumento .

El cambio se realizó en respuesta a Defect Report #209.

EDIT2: Pero vea la respuesta de R ...; en realidad es válido en N1256, por razones bastante oscuras.

+1

No solo es incorrecto el código del OP, la copia del OP de 'stdint.h' es incorrecta (si esa es realmente su definición de' INT8_C'). –

+0

@ChrisLutz: ¿Qué tiene de malo? –

+0

Viola el párrafo 7.18.4. Consulte mi respuesta a continuación para no seguir enviando ese párrafo por correo electrónico. –

0

Eso es un plus unario.

Hay solo dos cosas que podría estar haciendo.

  1. Se aplica lo que se conoce como las conversiones aritméticas habituales al operando. Esto establece un tipo real común para un resultado. No puedo pensar en ninguna razón para forzar esto si el resultado está a punto de convertirse en algo específico.

  2. Restringe el operando a tipos para los que se definen operadores aritméticos. Esta parece ser la razón más probable.

18

parece que cada uno ha perdido el punto de que el operador unario más aquí, que es hacer que el resultado válido en #if directivas del preprocesador. Teniendo en cuenta:

#define INT8_C(val) ((int8_t) + (val)) 

las directivas:

#define FOO 1 
#if FOO == INT8_C(1) 

expandirse a medida:

#if 1 == ((0) + (1)) 

y por lo tanto trabajar como se esperaba. Esto se debe al hecho de que cualquier símbolo #define d en una directiva #if se expande a 0.

Sin el plus unario (que se convierte en un binario más en las directivas #if), la expresión no sería válida en las directivas #if.

+0

Eso es realmente extraño, y desafortunadamente funciona. ¿Esto cuenta como código C ofuscado? Creo que tener '+' ser un operador unario en una situación y un operador binario en otra, depender de '(int8_t)' interpretarse como un molde o como un símbolo indefinido, está al borde de la ofuscación ... –

+0

Si piensas esto está ofuscado, no lea tgmath.h .... –

+1

Eso es a la vez hermoso y feo. –

Cuestiones relacionadas