que programas para ejecutar con compilar compilador como c

programas - ¿Hay un compilador de C que no compila esto?



programas para compilar (13)

Estuve un rato en mi perfilador tratando de descubrir cómo acelerar un analizador sintáctico común que estaba embotellado en torno al análisis de la fecha, y probé varios algoritmos para acelerar las cosas.

Lo que probé que fue más rápido para mí también fue, con mucho, el más fácil de leer, pero potencialmente no estándar.

Esto funcionó bastante bien en GCC , icc y mi compilador SGI realmente viejo y exigente. Como es una optimización bastante legible, ¿dónde no hace lo que yo quiero?

static int parseMonth(const char *input) { int rv=-1; int inputInt=0; int i=0; for(i=0; i<4 && input[i]; i++) { inputInt = (inputInt << 8) | input[i]; } switch(inputInt) { case ''Jan/'': rv=0; break; case ''Feb/'': rv=1; break; case ''Mar/'': rv=2; break; case ''Apr/'': rv=3; break; case ''May/'': rv=4; break; case ''Jun/'': rv=5; break; case ''Jul/'': rv=6; break; case ''Aug/'': rv=7; break; case ''Sep/'': rv=8; break; case ''Oct/'': rv=9; break; case ''Nov/'': rv=10; break; case ''Dec/'': rv=11; break; } return rv; }


El compilador CVI 8.5 para Windows de National Instruments falla en su código original con múltiples advertencias:

Warning: Excess characters in multibyte character literal ignored.

y errores de la forma:

Duplicate case label ''77''.

Tiene éxito en el código de Jonathan.


Como lo mencionaron otros, ese código arroja un montón de advertencias y probablemente no sea endian-safe.

¿Su manuscrito original de la fecha también fue escrito a mano? ¿Has probado strptime (3)?


Dejando a un lado las cuestiones del tamaño de la palabra, su compilador puede promover la entrada [i] a un entero negativo que simplemente establecerá los bits superiores de inputInt con o la operación, por lo que le sugiero que sea explícito sobre la firma de las variables char.

Pero dado que en los Estados Unidos, a nadie le importa el octavo bit, probablemente no sea un problema para usted.


El hecho de que una constante de cuatro caracteres sea equivalente a un entero particular de 32 bits es una característica no estándar que se ve a menudo en los compiladores para computadoras MS Windows y Mac (y PalmOS, AFAICR).

En estos sistemas, una cadena de cuatro caracteres se usa comúnmente como una etiqueta para identificar trozos de archivos de datos, o como un identificador de aplicación / tipo de datos (por ejemplo, "APPL").

Es una ventaja para el desarrollador que puedan almacenar dicha cadena en varias estructuras de datos sin preocuparse por la terminación de cero bytes, punteros, etc.


Estás calculando un hash de esos cuatro personajes. ¿Por qué no predefinir algunas constantes enteras que calculan el hash de la misma manera y las usan? La misma legibilidad y no depende de ninguna idiosincrasia específica de implementación del compilador.

uint32_t MONTH_JAN = ''J'' << 24 + ''a'' << 16 + ''n'' << 8 + ''/''; uint32_t MONTH_FEB = ''F'' << 24 + ''e'' << 16 + ''b'' << 8 + ''/''; ... static uint32_t parseMonth(const char *input) { uint32_t rv=-1; uint32_t inputInt=0; int i=0; for(i=0; i<4 && input[i]; i++) { inputInt = (inputInt << 8) | (input[i] & 0x7f); // clear top bit } switch(inputInt) { case MONTH_JAN: rv=0; break; case MONTH_FEB: rv=1; break; ... } return rv; }


Hay al menos 3 cosas que impiden que este programa sea portátil:

  1. Las constantes de múltiples caracteres están definidas por la implementación, por lo que diferentes compiladores pueden manejarlas de forma diferente.
  2. Un byte puede ser de más de 8 bits, hay un montón de hardware donde la unidad de memoria direccionable más pequeña es 16 o incluso 32 bits, a menudo se encuentra esto en los DSP, por ejemplo. Si un byte es más de 8 bits, también lo será char ya que char es por definición de un byte de longitud; su programa no funcionará correctamente en tales sistemas.
  3. Por último, hay muchas máquinas donde int es solo de 16 bits (que es el tamaño más pequeño permitido para int), incluidos los dispositivos integrados y las máquinas heredadas, su programa también fallará en estas máquinas.

Me encantaría ver el perfil que muestra que este es tu cuello de botella más significativo, pero en cualquier caso si vas a sacar algo como esto, usa una unión en lugar de 50 instrucciones que se repiten y cambian. Aquí hay un pequeño ejemplo de programa, te dejo encajarlo en tu programa.

/* union -- demonstrate union for characters */ #include <stdio.h> union c4_i { char c4[5]; int i ; } ; union c4_i ex; int main (){ ex.c4[0] = ''a''; ex.c4[1] = ''b''; ex.c4[2] = ''c''; ex.c4[3] = ''d''; ex.c4[4] = ''/0''; printf("%s 0x%08x/n", ex.c4, ex.i ); return 0; }

Aquí hay un ejemplo de salida:

bash $ ./union abcd 0x64636261 bash $


Recibo advertencias, pero no errores (gcc). Parece compilar y operar bien. Sin embargo, ¡puede que no funcione para sistemas big-endian!

Aunque no recomendaría este método. Tal vez puedas xor en lugar de or-shift, para crear un solo byte. Luego use la sentencia case en un byte (o, más rápido, use un LUT de los primeros N bits).


Solaris 10 - SPARC - Compilador SUN.

Código de prueba:

#include <stdio.h> static int parseMonth(const char *input) { int rv=-1; int inputInt=0; int i=0; for(i=0; i<4 && input[i]; i++) { inputInt = (inputInt << 8) | input[i]; } switch(inputInt) { case ''Jan/'': rv=0; break; case ''Feb/'': rv=1; break; case ''Mar/'': rv=2; break; case ''Apr/'': rv=3; break; case ''May/'': rv=4; break; case ''Jun/'': rv=5; break; case ''Jul/'': rv=6; break; case ''Aug/'': rv=7; break; case ''Sep/'': rv=8; break; case ''Oct/'': rv=9; break; case ''Nov/'': rv=10; break; case ''Dec/'': rv=11; break; } return rv; } static const struct { char *data; int result; } test_case[] = { { "Jan/", 0 }, { "Feb/", 1 }, { "Mar/", 2 }, { "Apr/", 3 }, { "May/", 4 }, { "Jun/", 5 }, { "Jul/", 6 }, { "Aug/", 7 }, { "Sep/", 8 }, { "Oct/", 9 }, { "Nov/", 10 }, { "Dec/", 11 }, { "aJ/n", -1 }, }; #define DIM(x) (sizeof(x)/sizeof(*(x))) int main(void) { size_t i; int result; for (i = 0; i < DIM(test_case); i++) { result = parseMonth(test_case[i].data); if (result != test_case[i].result) printf("!! FAIL !! %s (got %d, wanted %d)/n", test_case[i].data, result, test_case[i].result); } return(0); }

Resultados (GCC 3.4.2 y Sun):

$ gcc -O xx.c -o xx xx.c:14:14: warning: multi-character character constant xx.c:15:14: warning: multi-character character constant xx.c:16:14: warning: multi-character character constant xx.c:17:14: warning: multi-character character constant xx.c:18:14: warning: multi-character character constant xx.c:19:14: warning: multi-character character constant xx.c:20:14: warning: multi-character character constant xx.c:21:14: warning: multi-character character constant xx.c:22:14: warning: multi-character character constant xx.c:23:14: warning: multi-character character constant xx.c:24:14: warning: multi-character character constant xx.c:25:14: warning: multi-character character constant $ ./xx $ cc -o xx xx.c $ ./xx !! FAIL !! Jan/ (got -1, wanted 0) !! FAIL !! Feb/ (got -1, wanted 1) !! FAIL !! Mar/ (got -1, wanted 2) !! FAIL !! Apr/ (got -1, wanted 3) !! FAIL !! May/ (got -1, wanted 4) !! FAIL !! Jun/ (got -1, wanted 5) !! FAIL !! Jul/ (got -1, wanted 6) !! FAIL !! Aug/ (got -1, wanted 7) !! FAIL !! Sep/ (got -1, wanted 8) !! FAIL !! Oct/ (got -1, wanted 9) !! FAIL !! Nov/ (got -1, wanted 10) !! FAIL !! Dec/ (got -1, wanted 11) $

Tenga en cuenta que el último caso de prueba aún pasó, es decir, generó un -1.

Aquí hay una versión revisada - más detallada - de parseMonth () que funciona igual bajo el compilador GCC y Sun C:

#include <stdio.h> /* MONTH_CODE("Jan/") does not reduce to an integer constant */ #define MONTH_CODE(x) ((((((x[0]<<8)|x[1])<<8)|x[2])<<8)|x[3]) #define MONTH_JAN ((((((''J''<<8)|''a'')<<8)|''n'')<<8)|''/'') #define MONTH_FEB ((((((''F''<<8)|''e'')<<8)|''b'')<<8)|''/'') #define MONTH_MAR ((((((''M''<<8)|''a'')<<8)|''r'')<<8)|''/'') #define MONTH_APR ((((((''A''<<8)|''p'')<<8)|''r'')<<8)|''/'') #define MONTH_MAY ((((((''M''<<8)|''a'')<<8)|''y'')<<8)|''/'') #define MONTH_JUN ((((((''J''<<8)|''u'')<<8)|''n'')<<8)|''/'') #define MONTH_JUL ((((((''J''<<8)|''u'')<<8)|''l'')<<8)|''/'') #define MONTH_AUG ((((((''A''<<8)|''u'')<<8)|''g'')<<8)|''/'') #define MONTH_SEP ((((((''S''<<8)|''e'')<<8)|''p'')<<8)|''/'') #define MONTH_OCT ((((((''O''<<8)|''c'')<<8)|''t'')<<8)|''/'') #define MONTH_NOV ((((((''N''<<8)|''o'')<<8)|''v'')<<8)|''/'') #define MONTH_DEC ((((((''D''<<8)|''e'')<<8)|''c'')<<8)|''/'') static int parseMonth(const char *input) { int rv=-1; int inputInt=0; int i=0; for(i=0; i<4 && input[i]; i++) { inputInt = (inputInt << 8) | input[i]; } switch(inputInt) { case MONTH_JAN: rv=0; break; case MONTH_FEB: rv=1; break; case MONTH_MAR: rv=2; break; case MONTH_APR: rv=3; break; case MONTH_MAY: rv=4; break; case MONTH_JUN: rv=5; break; case MONTH_JUL: rv=6; break; case MONTH_AUG: rv=7; break; case MONTH_SEP: rv=8; break; case MONTH_OCT: rv=9; break; case MONTH_NOV: rv=10; break; case MONTH_DEC: rv=11; break; } return rv; } static const struct { char *data; int result; } test_case[] = { { "Jan/", 0 }, { "Feb/", 1 }, { "Mar/", 2 }, { "Apr/", 3 }, { "May/", 4 }, { "Jun/", 5 }, { "Jul/", 6 }, { "Aug/", 7 }, { "Sep/", 8 }, { "Oct/", 9 }, { "Nov/", 10 }, { "Dec/", 11 }, { "aJ/n", -1 }, { "/naJ", -1 }, }; #define DIM(x) (sizeof(x)/sizeof(*(x))) int main(void) { size_t i; int result; for (i = 0; i < DIM(test_case); i++) { result = parseMonth(test_case[i].data); if (result != test_case[i].result) printf("!! FAIL !! %s (got %d, wanted %d)/n", test_case[i].data, result, test_case[i].result); } return(0); }

Quería usar MONTH_CODE () pero los compiladores no cooperaron.


Solo sé lo que el Estándar C dice sobre esto (C99):

El valor de una constante de caracteres enteros que contiene más de un carácter (por ejemplo, ''ab''), o que contiene un carácter o secuencia de escape que no se correlaciona con un carácter de ejecución de un solo byte, está definido por la implementación. Si una constante de caracteres enteros contiene un único carácter o secuencia de escape, su valor es el que resulta cuando un objeto con tipo char cuyo valor es el del carácter único o secuencia de escape se convierte en tipo int.

(6.4.4.4/10 tomado de un borrador)

Entonces es implementación definida. Lo que significa que no se garantiza que funcione igual en todas partes, pero el comportamiento debe documentarse mediante la implementación. Por ejemplo, si int tiene solo 16 bits de ancho en una implementación particular, entonces ''Jan/'' ya no se puede representar como lo intentas ( char debe tener al menos 8 bits, mientras que un carácter literal siempre es de tipo int ).


Comeau compilador

Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for ONLINE_EVALUATION_BETA2 Copyright 1988-2008 Comeau Computing. All rights reserved. MODE:strict errors C99 "ComeauTest.c", line 11: warning: multicharacter character literal (potential portability problem) case ''Jan/'': rv=0; break; ^ "ComeauTest.c", line 12: warning: multicharacter character literal (potential portability problem) case ''Feb/'': rv=1; break; ^ "ComeauTest.c", line 13: warning: multicharacter character literal (potential portability problem) case ''Mar/'': rv=2; break; ^ "ComeauTest.c", line 14: warning: multicharacter character literal (potential portability problem) case ''Apr/'': rv=3; break; ^ "ComeauTest.c", line 15: warning: multicharacter character literal (potential portability problem) case ''May/'': rv=4; break; ^ "ComeauTest.c", line 16: warning: multicharacter character literal (potential portability problem) case ''Jun/'': rv=5; break; ^ "ComeauTest.c", line 17: warning: multicharacter character literal (potential portability problem) case ''Jul/'': rv=6; break; ^ "ComeauTest.c", line 18: warning: multicharacter character literal (potential portability problem) case ''Aug/'': rv=7; break; ^ "ComeauTest.c", line 19: warning: multicharacter character literal (potential portability problem) case ''Sep/'': rv=8; break; ^ "ComeauTest.c", line 20: warning: multicharacter character literal (potential portability problem) case ''Oct/'': rv=9; break; ^ "ComeauTest.c", line 21: warning: multicharacter character literal (potential portability problem) case ''Nov/'': rv=10; break; ^ "ComeauTest.c", line 22: warning: multicharacter character literal (potential portability problem) case ''Dec/'': rv=11; break; ^ "ComeauTest.c", line 1: warning: function "parseMonth" was declared but never referenced static int parseMonth(const char *input) { ^


char *months = "Jan/Feb/Mar/Apr/May/Jun/Jul/Aug/Sep/Oct/Nov/Dec/"; char *p = strnstr(months, input, 4); return p ? (p - months) / 4 : -1;


if ( !input[0] || !input[1] || !input[2] || input[3] != ''/'' ) return -1; switch ( input[0] ) { case ''F'': return 1; // Feb case ''S'': return 8; // Sep case ''O'': return 9; // Oct case ''N'': return 10; // Nov case ''D'': return 11; // Dec; case ''A'': return input[1] == ''p'' ? 3 : 7; // Apr, Aug case ''M'': return input[2] == ''r'' ? 2 : 4; // Mar, May default: return input[1] == ''a'' ? 0 : (input[2] == ''n'' ? 5 : 6); // Jan, Jun, Jul }

Un poco menos legible y no tanto valioso, pero tal vez incluso más rápido, ¿no?