uint8_t - ¿Qué es este signo más macro misterioso en stdint.h?
stdint.h download (4)
Por favor vea mi código:
#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;
}
Encuentro que la definición de macro en <stdint.h>
es:
#define INT8_C(val) ((int8_t) + (val))
¿Cuál es el significado o significado de este signo más?
El fragmento:
((int8_t) + (78));
es una expresión, una que toma el valor 78
, aplica el +
unario, luego lo int8_t
un tipo int8_t
, antes de tirarlo. No es realmente diferente a las expresiones legales:
42;
a + 1;
que también evalúa las expresiones y luego desecha el resultado (aunque estas posibilidades se optimizarán si el compilador puede decir que no hay efectos secundarios).
Estas expresiones "desnudas" son perfectamente válidas en C y generalmente útiles solo cuando tienen efectos secundarios, como con i++
, que calcula i
y lo desecha con el efecto secundario de que incrementa el valor.
La forma en que deberías usar esa macro es más parecida a:
int8_t varname = INT8_C (somevalue);
La razón para el operador unario +
aparentemente redundante se puede encontrar en el estándar. 6.5.3.3 Unary arithmetic operators /1
C99 6.5.3.3 Unary arithmetic operators /1
:
El operando del operador unario + o - debe tener tipo aritmético;
Y, en 6.2.5 Types, /18
:
Los tipos enteros y flotantes se denominan colectivamente tipos aritméticos.
En otras palabras, el unario +
impide utilizar todos los demás tipos de datos en la macro, como punteros, números complejos o estructuras.
Y, finalmente, la razón por la cual
signed char + 78;
El snippet no funciona porque no es lo mismo. Este está comenzando a declarar una variable de tipo con signed char
pero se ahoga cuando llega al +
ya que no es un nombre de variable legal. Para hacerlo equivalente a su fragmento de trabajo, usaría:
(signed char) + 78;
que es la conversión del valor +78
para escribir signed char
.
Y, según C99 7.8.14 Macros for integer constants /2
, también debe tener cuidado con el uso de no constantes en esas macros, no se garantiza que funcionen:
El argumento en cualquier instancia de estas macros será una constante entera sin relleno (como se define en 6.4.4.1) con un valor que no exceda los límites para el tipo correspondiente.
6.4.4.1
simplemente especifica los diversos formatos de enteros (decimal / octal / hexadecimal) con los diversos sufijos ( U
, UL
, ULL
, L
, LL
y los equivalentes en minúsculas, según el 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
lo que permitirá que su macro INT8_C
funcione bien, pero el texto INT64_C(val)
se procesaría previamente en cualquiera de los valL
o valLL
, ninguno de los cuales desearía.
Es un operador unario +
. Obtiene 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 puntero.
Pero tu expresion expresiva
INT8_C(val);
Tiene un comportamiento indefinido. Se INT8_C()
que el argumento de INT8_C()
sea "una constante entera no corregida ... con un valor que no exceda los límites para el tipo correspondiente" ( N1256 7.18.4). La intención de estas macros es permitirte escribir algo como INT64_C(42)
y expandirlo a 42L
si int64_t
es long
, o a 42LL
si int64_t
es `largo, y así sucesivamente.
Pero escribiendo tampoco
((int8_t) + (78));
o
((int8_t) + (val));
En tu propio código es perfectamente legal.
EDITAR :
La definición para INT8_C()
en la pregunta:
#define INT8_C(val) ((int8_t) + (val))
es válido en C99, pero no es conforme con el último borrador N1256, como resultado de un cambio en una de las correcciones técnicas.
El estándar C99 original, sección 7.18.4.1p2, dice:
La macro INT *** N * _C ** ( valor ) se expandirá a una constante de entero con signo con el valor especificado y el tipo int_least *** N * _t **.
N1256 cambió esto a (párrafo 3):
Cada invocación de una de estas macros se expandirá a una expresión constante de entero adecuada para usar en directivas de preprocesamiento #if . El tipo de expresión tendrá el mismo tipo que una expresión del tipo correspondiente convertido de acuerdo con las promociones de números enteros. El valor de la expresión será el del argumento.
El cambio se realizó en respuesta al Informe de Defectos # 209 .
EDIT2 : Pero vea la respuesta de R ..; En realidad es válido en N1256, por razones bastante oscuras.
Esa es una ventaja unaria.
Sólo hay dos cosas que podría estar haciendo.
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.
Restringe el operando a los tipos para los cuales se definen los operadores aritméticos. Esto parece ser la razón más probable.
Parece que todos se han perdido el punto del operador unario más aquí, que es hacer que el resultado sea válido en las directivas de preprocesador #if
. Dado:
#define INT8_C(val) ((int8_t) + (val))
las directivas
#define FOO 1
#if FOO == INT8_C(1)
expandir como
#if 1 == ((0) + (1))
y así trabajar como se espera. Esto se debe al hecho de que cualquier símbolo de #define
d en una directiva #if
expande a 0
.
Sin el plus unario (que se convierte en un binario plus en #if
directivas #if
), la expresión no sería válida en #if
directivas #if
.