Sé que hay un estándar detrás de todas las implementaciones del compilador de C, por lo que no debería haber características ocultas. A pesar de eso, estoy seguro de que todos los desarrolladores de C tienen trucos ocultos / secretos que usan todo el tiempo.
c
hidden-features
bernardn
fuente
fuente
Respuestas:
Punteros de función. Puede usar una tabla de punteros de función para implementar, por ejemplo, intérpretes rápidos de código de hilos indirectos (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 () [estas dos últimas son útiles para implementar un reemplazo strtok ()].
Una característica errónea de C es que el desbordamiento aritmético con signo es un comportamiento indefinido (UB). Por lo tanto, cada vez que vea una expresión como x + y, ambas son entradas firmadas, podría desbordarse y causar UB.
fuente
Es más un truco del compilador GCC, pero puede dar indicaciones de ramificación al compilador (común en el kernel de Linux)
ver: http://kerneltrap.org/node/4705
Lo que me gusta de esto es que también agrega algo de expresividad a algunas funciones.
fuente
Se trata de un elemento opcional en el estándar, pero debe ser una característica oculta, ya que las personas los redefinen 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:
Y así. Me dan ganas de arrancarme el pelo. ¡Solo use los malditos tipos de letra enteros estándar!
fuente
El operador de coma no se usa ampliamente. Ciertamente se puede abusar de él, pero también puede ser muy útil. Este uso es el más común:
Pero puede usar este operador en cualquier lugar. Observar:
Cada declaración se evalúa, pero el valor de la expresión será el de la última declaración evaluada.
fuente
inicializando la estructura a cero
esto pondrá a cero todos los elementos de estructura.
fuente
memset
/calloc
do "todos los bytes cero" (es decir, ceros físicos), que de hecho no está definido para todos los tipos.{ 0 }
está garantizado para intilar todo con los valores lógicos cero adecuados . Los punteros, por ejemplo, están garantizados para obtener sus valores nulos adecuados, incluso si el valor nulo en la plataforma dada es0xBAADFOOD
.memset
hace (con un0
segundo argumento). Obtiene cero lógico cuando inicializa / asigna0
(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 lo hacesmemset
con un puntero, obtienes un0x0000
puntero. Pero cuando asigna0
a un puntero, obtiene un valor de puntero nulo , que a nivel físico podría ser0xBAADF00D
o cualquier otra cosa.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. Entonces, puede suceder que cuando lo hagadouble d = 0;
(cero lógico), físicamente algunos bits en la memoria ocupada pord
no sean cero.Constantes de varios caracteres:
Esto se establece
x
en0x41424344
(o0x44434241
, dependiendo de la arquitectura).EDITAR: Esta técnica no es portátil, especialmente si serializa el int. Sin embargo, puede ser extremadamente útil crear enumeraciones autodocumentadas. p.ej
Esto lo hace mucho más simple si está viendo un volcado de memoria sin procesar y necesita determinar el valor de una enumeración sin tener que buscarlo.
fuente
Nunca usé campos de bits, pero suenan bien para cosas de nivel ultra bajo.
Esto significa que
sizeof(cat)
puede ser tan pequeño comosizeof(char)
.Comentarios incorporados de Aaron y Leppie , gracias chicos.
fuente
C tiene un estándar, pero no todos los compiladores C son totalmente compatibles (¡todavía no he visto ningún compilador C99 totalmente compatible!).
Dicho esto, los trucos que prefiero son aquellos que no son obvios y portátiles en todas las plataformas, ya que dependen de la semántica en C. Por lo general, se trata de macros o aritmética de bits.
Por ejemplo: intercambiar dos enteros sin signo sin usar una variable temporal:
o "extender C" para representar máquinas de estados finitos como:
eso se puede lograr con las siguientes macros:
Sin embargo, en general, no me gustan los trucos que son inteligentes, pero hacen que el código sea innecesariamente complicado de leer (como el ejemplo de intercambio) y me encantan los que aclaran el código y transmiten directamente la intención (como el ejemplo de FSM) .
fuente
Estructuras entrelazadas como el dispositivo de Duff :
fuente
Soy muy aficionado a los inicializadores designados, agregados en C99 (y soportados en gcc durante mucho tiempo):
La inicialización de la matriz ya no depende de la posición. Si cambia los valores de FOO o BAR, la inicialización de la matriz corresponderá automáticamente a su nuevo valor.
fuente
C99 tiene una increíble inicialización de estructura de cualquier orden.
fuente
Las estructuras y matrices anónimas son mis favoritas. (cf. http://www.run.montefiore.ulg.ac.be/~martin/resources/kung-f00.html )
o
incluso se puede usar para instanciar listas vinculadas ...
fuente
gcc tiene varias extensiones del lenguaje C que disfruto, que se pueden encontrar aquí . Algunos de mis favoritos son atributos de función . Un ejemplo extremadamente útil es el atributo de formato. Esto se puede usar si define una función personalizada que toma una cadena de formato printf. Si habilita este atributo de función, gcc verificará sus argumentos para asegurarse de que su cadena de formato y argumentos coincidan y generará advertencias o errores según corresponda.
fuente
La característica (oculta) que me "sorprendió" cuando vi por primera vez es sobre printf. Esta característica le permite utilizar variables para formatear los especificadores de formato. busque el código, verá mejor:
el * personaje logra este efecto.
fuente
Bueno ... creo que uno de los puntos fuertes del lenguaje C es su portabilidad y estandarización, así que cada vez que encuentro algún "truco oculto" en la implementación que estoy usando actualmente, trato de no usarlo porque trato de mantener mi Código C lo más estándar y portátil posible.
fuente
Aserciones en tiempo de compilación, como ya se discutió aquí .
fuente
Concatenación constante de cuerdas
Me sorprendió bastante no verlo ya en las respuestas, ya que todos los compiladores que conozco lo respaldan, pero muchos programadores parecen ignorarlo. A veces es realmente útil y no solo al escribir macros.
Caso de uso que tengo en mi código actual: tengo un
#define PATH "/some/path/"
archivo de configuración (realmente está configurado por el archivo MAKE). Ahora quiero construir la ruta completa, incluidos los nombres de archivo para abrir recursos. Solo va a:En lugar de lo horrible, pero muy común:
Tenga en cuenta que la solución horrible común es:
fuente
Bueno, nunca lo he usado, y no estoy seguro de si alguna vez se lo recomendaría a alguien, pero creo que esta pregunta estaría incompleta sin mencionar el truco de rutina de Simon Tatham .
fuente
Al inicializar matrices o enumeraciones, puede poner una coma después del último elemento en la lista de inicializadores. p.ej:
Esto se hizo para que si está generando código automáticamente no tenga que preocuparse por eliminar la última coma.
fuente
La asignación de estructura es genial. Muchas personas no parecen darse cuenta de que las estructuras también son valores, y pueden asignarse alrededor, no hay necesidad de usarlas
memcpy()
, cuando una tarea simple hace el truco.Por ejemplo, considere una biblioteca imaginaria de gráficos 2D, podría definir un tipo para representar una coordenada de pantalla (entera):
Ahora, hace cosas que pueden parecer "incorrectas", como escribir una función que crea un punto inicializado a partir de argumentos de función, y lo devuelve, así:
Esto es seguro, siempre y cuando (por supuesto) el valor de retorno se copie por valor utilizando la asignación de estructura:
De esta manera, puede escribir código bastante limpio y orientado a objetos, todo en un estándar simple C.
fuente
Extraña indexación vectorial:
fuente
Los compiladores de C implementan uno de varios estándares. Sin embargo, tener un estándar no significa que todos los aspectos del lenguaje estén definidos. El dispositivo de Duff , por ejemplo, es una característica 'oculta' favorita que se ha vuelto tan popular que los compiladores modernos tienen un código de reconocimiento de propósito especial para garantizar que las técnicas de optimización no afecten el efecto deseado de este patrón de uso frecuente.
En general, se desaconsejan las funciones ocultas o los trucos de lenguaje, ya que se está ejecutando en el filo de cualquier estándar C que utilice su compilador. Muchos de estos trucos no funcionan de un compilador a otro, y a menudo este tipo de características fallarán de una versión de un conjunto de compiladores de un fabricante determinado a otra versión.
Varios trucos que han roto el código C incluyen:
Otros problemas y cuestiones que surgen cada vez que los programadores hacen suposiciones sobre los modelos de ejecución que se especifican en la mayoría de los estándares de C como comportamiento "dependiente del compilador".
fuente
Cuando use sscanf, puede usar% n para averiguar dónde debe continuar leyendo:
Aparentemente, no puede agregar otra respuesta, así que incluiré una segunda aquí, puede usar "&&" y "||" como condicionales:
Este código generará:
fuente
Usar INT (3) para establecer el punto de interrupción en el código es mi favorito de todos los tiempos
fuente
Mi característica "oculta" favorita de C es el uso de% n en printf para volver a escribir en la pila. Normalmente printf muestra los valores de los parámetros de la pila en función de la cadena de formato, pero% n puede escribirlos de nuevo.
Consulte la sección 3.4.2 aquí . Puede provocar muchas vulnerabilidades desagradables.
fuente
Comprobación de suposiciones en tiempo de compilación usando enumeraciones: ejemplo estúpido, pero puede ser realmente útil para bibliotecas con constantes configurables en tiempo de compilación.
fuente
#define CompilerAssert(exp) extern char _CompilerAssert[(exp)?1:-1]
)Gcc (c) tiene algunas características divertidas que puede habilitar, como declaraciones de funciones anidadas y la forma a?: B del operador?: Que devuelve a si a no es falso.
fuente
Descubrí recientemente 0 bitfields.
que dará un diseño de
en lugar de sin el: 0;
El campo de ancho 0 indica que los siguientes campos de bits deben establecerse en la siguiente entidad atómica (
char
)fuente
Macros de argumento variable de estilo C99, también conocido como
que se usaría como
Aquí también utilizo el operador stringize y la concatenación constante de string, otras características que realmente me gustan.
fuente
Las variables automáticas de tamaño variable también son útiles en algunos casos. Estos se agregaron en n99 y se han admitido en gcc durante mucho tiempo.
Termina con un búfer en la pila con espacio para el encabezado del protocolo de tamaño fijo más datos de tamaño variable. Puede obtener el mismo efecto con alloca (), pero esta sintaxis es más compacta.
Debe asegurarse de que extraPadding sea un valor razonable antes de llamar a esta rutina, o terminará volando la pila. Tendría que verificar los argumentos antes de llamar a malloc o cualquier otra técnica de asignación de memoria, por lo que esto no es realmente inusual.
fuente