Características ocultas de C

2 minutos de lectura

Sé que hay un estándar detrás de todas las implementaciones del compilador C, por lo que no debería haber funciones ocultas. A pesar de eso, estoy seguro de que todos los desarrolladores de C tienen trucos ocultos/secretos que usan todo el tiempo.

  • Sería genial si usted/alguien editara la “pregunta” para indicar la selección de las mejores funciones ocultas, como en las versiones C# y Perl de esta pregunta.

    – Becarios Donal

    26 de mayo de 2010 a las 13:19

int8_t
int16_t
int32_t
uint8_t
uint16_t
uint32_t

Estos son un elemento opcional en el estándar, pero debe ser una característica oculta, porque la gente los redefine constantemente. Una base de código en la que he trabajado (y todavía lo hago, por ahora) tiene múltiples redefiniciones, todas con diferentes identificadores. La mayoría de las veces es con macros de preprocesador:

#define INT16 short
#define INT32  long

Y así. Me dan ganas de tirarme de los pelos. ¡Solo usa las malditas definiciones estándar de enteros!

  • Creo que son C99 más o menos. No he encontrado una forma portátil de garantizar que estos estén disponibles.

    – akauppi

    25 de septiembre de 2008 a las 19:25

  • Son una parte opcional de C99, pero no conozco proveedores de compiladores que no implementen esto.

    – Ben Collins

    25 de septiembre de 2008 a las 21:07

  • stdint.h no es opcional en C99, pero seguir el estándar C99 aparentemente lo es para algunos proveedores (tos microsoft).

    – Ben Combee

    22 de octubre de 2008 a las 17:54

  • @Pete, si quieres ser anal: (1) Este hilo no tiene nada que ver con ningún producto de Microsoft. (2) Este hilo nunca tuvo nada que ver con C++. (3) No existe tal cosa como C++ 97.

    – Ben Collins

    1 de marzo de 2009 a las 21:20

  • Mira esto azillionmonkeys.com/qed/pstdint.h — un stdint.h casi portátil

    – gnud

    16 de abril de 2009 a las 14:16

  • ¡En 20 años de CI NUNCA he visto eso!

    – Martín Beckett

    22 de junio de 2009 a las 14:55

  • En C++ puedes incluso sobrecargarlo.

    – Wouter Lievens

    1 de julio de 2009 a las 10:45

  • puede ! = debería, por supuesto. El peligro de sobrecargarlo es que lo incorporado ya se aplica a todo, incluido void, por lo que nunca fallará al compilar por falta de sobrecarga disponible. Es decir, le da mucha cuerda al programador.

    – Aarón

    6 de julio de 2009 a las 17:45

  • El int dentro del bucle no funcionará con C: es una mejora de C++. ¿Es “,” la misma operación que para (i=0,j=10;i

    – Aif

    26 de noviembre de 2010 a las 14:00


inicializando la estructura a cero

struct mystruct a = {0};

esto pondrá a cero todos los elementos de la estructura.

  • Sin embargo, no pone a cero el relleno, si lo hay.

    – Mikeage

    1 de marzo de 2009 a las 13:49

  • @simonn, no, no tiene un comportamiento indefinido si la estructura contiene tipos no integrales. memset con 0 en la memoria de un flotante/doble seguirá siendo cero cuando interprete el flotante/doble (flotante/doble están diseñados así a propósito).

    – Trevor Boyd Smith

    11 de junio de 2009 a las 13:59

  • @Andrés: memset/calloc hacer “todos los bytes cero” (es decir, ceros físicos), que de hecho no está definido para todos los tipos. { 0 } está garantizado para intilaize todo con la debida lógico valores cero. Se garantiza que los punteros, por ejemplo, obtendrán sus valores nulos adecuados, incluso si el valor nulo en la plataforma dada es 0xBAADFOOD.

    – Ant

    28 de octubre de 2009 a las 10:12

  • @nvl: obtienes físico cero cuando simplemente establece a la fuerza toda la memoria ocupada por el objeto en el estado de todos los bits cero. Esto es lo que memset hace (con 0 como segundo argumento). Usted obtiene lógico cero al inicializar/asignar 0 ( o { 0 }) al objeto en el código fuente. Estos dos tipos de ceros no necesariamente producen el mismo resultado. Como en el ejemplo con puntero. Cuando tu lo hagas memset en un puntero, obtienes un 0x0000 puntero. Pero cuando asignas 0 a un puntero, obtienes valor de puntero nuloque a nivel físico podría ser 0xBAADF00D O algo más.

    – Ant

    26 de febrero de 2010 a las 16:29


  • @nvl: Bueno, en la práctica, la diferencia suele ser solo conceptual. Pero en teoría, prácticamente cualquier tipo puede tenerlo. Por ejemplo, double. Por lo general, se implementa de acuerdo con el estándar IEEE-754, en el que el cero lógico y el cero físico son iguales. Pero el idioma no requiere IEEE-754. Así que puede suceder que cuando lo hagas double d = 0; (cero lógico), físicamente algunos bits en memoria ocupados por d no será cero.

    – Ant

    26 de febrero de 2010 a las 19:17

Punteros de función. Puede utilizar una tabla de punteros de función para implementar, por ejemplo, intérpretes de código de subprocesos indirectos rápidos (FORTH) o despachadores de código de bytes, o para simular métodos virtuales similares a OO.

Luego hay gemas ocultas en la biblioteca estándar, como qsort(),bsearch(), strpbrk(), strcspn() [the latter two being useful for implementing a strtok() replacement].

Una característica incorrecta de C es que el desbordamiento aritmético con signo es un comportamiento indefinido (UB). Entonces, cada vez que vea una expresión como x+y, ambas firmadas como ints, podría desbordarse y causar UB.

  • Sin embargo, no pone a cero el relleno, si lo hay.

    – Mikeage

    1 de marzo de 2009 a las 13:49

  • @simonn, no, no tiene un comportamiento indefinido si la estructura contiene tipos no integrales. memset con 0 en la memoria de un flotante/doble seguirá siendo cero cuando interprete el flotante/doble (flotante/doble están diseñados así a propósito).

    – Trevor Boyd Smith

    11 de junio de 2009 a las 13:59

  • @Andrés: memset/calloc hacer “todos los bytes cero” (es decir, ceros físicos), que de hecho no está definido para todos los tipos. { 0 } está garantizado para intilaize todo con la debida lógico valores cero. Se garantiza que los punteros, por ejemplo, obtendrán sus valores nulos adecuados, incluso si el valor nulo en la plataforma dada es 0xBAADFOOD.

    – Ant

    28 de octubre de 2009 a las 10:12

  • @nvl: obtienes físico cero cuando simplemente establece a la fuerza toda la memoria ocupada por el objeto en el estado de todos los bits cero. Esto es lo que memset hace (con 0 como segundo argumento). Usted obtiene lógico cero al inicializar/asignar 0 ( o { 0 }) al objeto en el código fuente. Estos dos tipos de ceros no necesariamente producen el mismo resultado. Como en el ejemplo con puntero. Cuando tu lo hagas memset en un puntero, obtienes un 0x0000 puntero. Pero cuando asignas 0 a un puntero, obtienes valor de puntero nuloque a nivel físico podría ser 0xBAADF00D O algo más.

    – Ant

    26 de febrero de 2010 a las 16:29


  • @nvl: Bueno, en la práctica, la diferencia suele ser solo conceptual. Pero en teoría, prácticamente cualquier tipo puede tenerlo. Por ejemplo, double. Por lo general, se implementa de acuerdo con el estándar IEEE-754, en el que el cero lógico y el cero físico son iguales. Pero el idioma no requiere IEEE-754. Así que puede suceder que cuando lo hagas double d = 0; (cero lógico), físicamente algunos bits en memoria ocupados por d no será cero.

    – Ant

    26 de febrero de 2010 a las 19:17

Constantes de varios caracteres:

int x = 'ABCD';

esto establece x para 0x41424344 (o 0x44434241dependiendo de la arquitectura).

EDITAR: Esta técnica no es portátil, especialmente si serializas el int. Sin embargo, puede ser extremadamente útil crear enumeraciones autodocumentadas. p.ej

enum state {
    stopped = 'STOP',
    running = 'RUN!',
    waiting = 'WAIT',
};

Esto hace que sea mucho más simple si está viendo un volcado de memoria sin formato y necesita determinar el valor de una enumeración sin tener que buscarlo.

  • Estoy bastante seguro de que esto no es una construcción portátil. El resultado de crear una constante de varios caracteres está definido por la implementación.

    –Mark Bessey

    17 de octubre de 2008 a las 4:18

  • Los comentarios “no portátiles” pierden el punto por completo. Es como criticar un programa por usar INT_MAX solo porque INT_MAX “no es portátil” 🙂 Esta función es tan portátil como debe ser. La constante de caracteres múltiples es una característica extremadamente útil que proporciona una forma legible de generar ID de enteros únicos.

    – Ant

    28 de octubre de 2009 a las 10:36

  • @Chris Lutz: estoy bastante seguro de que la coma final se remonta a K&R. Se describe en la segunda edición (1988).

    – Ferruccio

    28 de octubre de 2009 a las 11:15

  • @Ferruccio: Debes estar pensando en la coma final en las listas de inicializadores agregados. En cuanto a la coma final en las declaraciones de enumeración, es una adición reciente, C99.

    – Ant

    28 de octubre de 2009 a las 17:59

  • Olvidaste ‘HANG’ o ‘BSOD’ 🙂

    – JBRWilkinson

    2 de noviembre de 2009 a las 16:30

¿Ha sido útil esta solución?

Esta web utiliza cookies propias y de terceros para su correcto funcionamiento y para fines analíticos y para mostrarte publicidad relacionada con sus preferencias en base a un perfil elaborado a partir de tus hábitos de navegación. Al hacer clic en el botón Aceptar, acepta el uso de estas tecnologías y el procesamiento de tus datos para estos propósitos. Configurar y más información
Privacidad