¿Qué causa si un char
en C (usando gcc) está firmado o sin firmar? Sé que el estándar no dicta uno sobre el otro y que puedo verificar CHAR_MIN
y CHAR_MAX
de limites.h pero quiero saber qué desencadena uno sobre el otro cuando se usa gcc
Si leo limites.h de libgcc-6 veo que hay una macro __CHAR_UNSIGNED__
que define un carácter "predeterminado" firmado o sin firmar, pero no estoy seguro de si el compilador lo establece en (su) tiempo de compilación.
Traté de listar los makros predefinidos de GCC con
$ gcc -dM -E -x c /dev/null | grep -i CHAR
#define __UINT_LEAST8_TYPE__ unsigned char
#define __CHAR_BIT__ 8
#define __WCHAR_MAX__ 0x7fffffff
#define __GCC_ATOMIC_CHAR_LOCK_FREE 2
#define __GCC_ATOMIC_CHAR32_T_LOCK_FREE 2
#define __SCHAR_MAX__ 0x7f
#define __WCHAR_MIN__ (-__WCHAR_MAX__ - 1)
#define __UINT8_TYPE__ unsigned char
#define __INT8_TYPE__ signed char
#define __GCC_ATOMIC_WCHAR_T_LOCK_FREE 2
#define __CHAR16_TYPE__ short unsigned int
#define __INT_LEAST8_TYPE__ signed char
#define __WCHAR_TYPE__ int
#define __GCC_ATOMIC_CHAR16_T_LOCK_FREE 2
#define __SIZEOF_WCHAR_T__ 4
#define __INT_FAST8_TYPE__ signed char
#define __CHAR32_TYPE__ unsigned int
#define __UINT_FAST8_TYPE__ unsigned char
pero no pude encontrar __CHAR_UNSIGNED__
Antecedentes:tengo un código que compilo en dos máquinas diferentes:
PC de escritorio:
- Debian GNU/Linux 9.1 (extendido)
- gcc versión 6.3.0 20170516 (Debian 6.3.0-18)
- Intel(R) Core(TM) i3-4150
- libgcc-6-dev:6.3.0-18
char
está firmado
Frambuesa Pi3 :
- Raspbian GNU/Linux 9.1 (extendido)
- gcc versión 6.3.0 20170516 (Raspbian 6.3.0-18+rpi1)
- Procesador ARMv7 versión 4 (v7l)
- libgcc-6-dev:6.3.0-18+rpi
char
no está firmado
Así que la única diferencia obvia es la arquitectura de la CPU...
Respuestas:
Según el estándar C11 (léase n1570), char
puede ser signed
o unsigned
(así que en realidad tienes dos sabores de C). Lo que es exactamente es específico de la implementación.
Algunos procesadores y arquitecturas de conjuntos de instrucciones o interfaces binarias de aplicaciones favorecen un signed
tipo de carácter (byte) (por ejemplo, porque se asigna muy bien a alguna instrucción de código de máquina), otros favorecen un unsigned
uno.
gcc
tiene incluso algo de -fsigned-char
o -funsigned-char
opción que casi nunca debería usar (porque cambiarla rompe algunos casos de esquina en las convenciones de llamada y ABI) a menos que vuelva a compilar todo, incluida su biblioteca estándar de C.
Podrías usar feature_test_macros(7) y <endian.h>
(vea endian(3)) o autoconf en Linux para detectar lo que tiene su sistema.
En la mayoría de los casos, debe escribir código C portátil, que no depende de esas cosas. Y puede encontrar bibliotecas multiplataforma (p. ej., glib) para ayudarlo en eso.
Por cierto gcc -dM -E -x c /dev/null
también da __BYTE_ORDER__
etc., y si desea un byte de 8 bits sin firmar, debe usar <stdint.h>
y su uint8_t
(más portátil y más legible). Y limites estándar.h define CHAR_MIN
y SCHAR_MIN
y CHAR_MAX
y SCHAR_MAX
(podría compararlos por igualdad para detectar signed char
s implementaciones), etc...
Por cierto, debería preocuparse por la codificación de caracteres, pero la mayoría de los sistemas actuales usan UTF-8 en todas partes. Las bibliotecas como libunistring son útiles. Vea también esto y recuerde que, en términos prácticos, un carácter Unicode codificado en UTF-8 puede abarcar varios bytes (es decir, char
-s).