2010-07-07 8 views
8

Estoy en el proceso de aprender C. Tengo un método que requiere 3 cadenas y las combina para realizar algunas operaciones. A continuación fue mi primera implementación usando un compilador de GCC.Manipulación de cadenas y asignación de memoria - C

void foo(const char *p1, const char *p2, const char *p3) 
{ 
    size_t length = strlen(p1) + strlen(p2) + strlen(p3); 
    char combined[length + 1]; 
    memset(combined, 0, length + 1); 
    strcat(combined, p1); 
    strcat(combined, p2); 
    strcat(combined, p3); 
    printf("Result : %s", combined); 
} 

int main() 
{ 
    foo("hello ", "world ", "how"); 
    return 0; 
} 

Esto funciona bien. Pero cuando compilé esto usando, cc -Wall -pedantic -g foo.c -o foo, comencé a recibir advertencias como ISO C90 forbids variable length array ‘combined’. MSVC no estaba compilando este código. Se ha cambiado el código como

void foo(const char *p1, const char *p2, const char *p3) 
{ 
    size_t length = strlen(p1) + strlen(p2) + strlen(p3); 
    char *combined = (char *) malloc(length + 1); 
    memset(combined, 0, length + 1); 
    strcat(combined, p1); 
    strcat(combined, p2); 
    strcat(combined, p3); 
    printf("Result : %s", combined); 
    free(combined); 
} 

Preguntas

  1. Es esta la aplicación correcta?
  2. Si las matrices de longitud variable no son parte del estándar, ¿por qué GCC lo implementó? Si se espera que el código se compile solo en GCC, el uso de matrices de variables será una mejor alternativa que el uso de malloc.
  3. Creo que la regla del pulgar es que, si la memoria requerida es conocida en tiempo de compilación, use matrices o use malloc para asignar la memoria requerida. ¿Es esto correcto?
  4. Mi código se espera compilar en GCC y MSVC. Desarrollaré en GCC generalmente. Entonces, ¿cuáles son los indicadores del compilador que garantizan la máxima portabilidad? Actualmente estoy usando -Wall -pedantic. ¿Debo usar también -ansi? ¿Cuáles serían las banderas equivalentes disponibles en MSVC?
  5. ¿Cuáles son las otras cosas comunes a considerar cuando se escribe código C portátil?
+0

Ver http: // stackoverflow.com/questions/3093049/different-behavior-of-compilers-with-array-allocation – PeterK

+1

(1) Sí, como mencionó en el punto 3 (2) Porque gcc también es compatible con otras normas además de ISO C90. (5) http://www.google.com/search?q=writing+portable+C+code Siempre es mejor asignar dinámicamente VLA porque puede verificar el valor de retorno de (m/c) alloc a vea si la asignación fue exitosa o no – itisravi

+0

@itisravi - Debe responder, en lugar de responder en un comentario. Debido a las restricciones de longitud de los comentarios, su recomendación sobre VLAs frente a la asignación dinámica es un poco escasa. Estoy de acuerdo con eso, y sé a lo que te refieres, pero no está claro porque los comentarios son demasiado cortos para las respuestas. Es por eso que tenemos respuestas. ;) –

Respuesta

8

Esto funciona bien. Pero cuando compilé este uso, cc -Wall -pedantic -g foo.c -o foo, comencé a recibir advertencias, como ISO C90, prohíbe el array de longitud variable "combinado".

Intente compilar con la opción -std=c99 (gcc).

MSVC no estaba compilando este código. Cambió el código como

Si las matrices de longitud variable no son parte del estándar, ¿por qué GCC lo implementó?

Los VLA son parte de ISO C99 (gcc y g ++ (como extensión) admiten VLA). MSVC todavía solo es compatible con C89.

Mi código se espera que se compile en GCC y MSVC.

Entonces no debe usar VLA en su código IMHO.

3

Las matrices de longitud variable no formaban parte del primer estándar ISO C (a las que se hace referencia como "C89", "C90" o "ANSI C"). Sin embargo, son y forman parte del último estándar ISO C (conocido como "C99").

GCC puede compilar el código en varios modos, incluyendo "estricta C90", "C90-con-GNU-C-extensions", y "C99" (aunque no totalmente aplicar C99, que es lo suficientemente cerca para la mayoría de los propósitos prácticos).

Por defecto, GCC usa "C90-with-GNU-C-extensions", por lo que su código se compila sin quejarse. El uso de -pedantic le indica que emita todas las advertencias requeridas por la norma pertinente (en este caso, C90), y tal advertencia es requerida por su código. Si otorga a GCC las banderas -std=c99 -pedantic, para decirle que compile contra la norma base C99 y emita todas las advertencias requeridas, su código compilará bien.

Si desea asegurarse de que su código es compatible con el estándar C90 base, a continuación, utilizar -std=c90 -pedantic (o -ansi -pedantic: -ansi es un sinónimo de -std=c90 al compilar código C). Tenga en cuenta que MSVC no es compatible con C99.

7
  1. Sí, lo es. No hay violaciones específicas del estándar allí. El memset es una pérdida de tiempo, sin embargo, ya que se va a sobrescribir de todos modos (realice su primer strcat en un strcpy). Y debe siempre comprobar malloc devolviendo NULO. ¡No importa qué!
  2. C89/90 no es el estándar actual, C99 es. Y C1x no está tan lejos. GCC se mantiene al ritmo de la vanguardia.
  3. Solo use matrices locales si no las necesita para sobrevivir más allá del final de la función. De lo contrario, malloc es su mejor opción, particularmente si desea devolver la secuencia combinada.
  4. Creo que gcc tiene la bandera -std=c89 o algo similar. En cualquier caso, MSVC no siempre sigue el estándar :-)
  5. compilar y probar en ambas plataformas con frecuencia. Esa es la única forma de estar seguro.

optaría por:

void foo (const char *p1, const char *p2, const char *p3) { 
    size_t length = strlen(p1) + strlen(p2) + strlen(p3); 
    char *combined = (char *) malloc(length + 1); 
    if (combined == NULL) { 
     printf("Result : <unknown since I could't get any memory>\n"); 
    } else { 
     strcpy(combined, p1); 
     strcat(combined, p2); 
     strcat(combined, p3); 
     printf("Result : %s", combined); 
     free(combined); 
    } 
} 

o, ya que no está realmente hacer nada con la cadena a excepción de imprimirlo:

void foo (const char *p1, const char *p2, const char *p3) { 
    printf("Result : %s%s%s", p1, p2, p3); 
} 

:-)

Otro La estrategia que he visto es la estrategia "solo asignar si es necesario":

void foo (const char *p1, const char *p2, const char *p3) { 
    char str1k[1024]; 
    char *combined; 
    size_t length = strlen (p1) + strlen (p2) + strlen (p3) + 1; 
    if (length <= sizeof(str1k)) 
     combined = str1k; 
    else 
     combined = malloc (length); 
    if (combined == NULL) { 
     printf ("Result : <unknown since I couldn't get any memory>\n"); 
    } else { 
     strcpy (combined, p1); 
     strcat (combined, p2); 
     strcat (combined, p3); 
     printf ("Result : %s", combined); 
    } 
    if (combined != str1k) 
     free (combined); 
} 

que utiliza almacenamiento de pila si la cadena combinada se ajustará y solo asigna memoria si no lo hace. Esto a menudo puede conducir a una mejora de velocidad sustancial si el grueso de las cadenas se combinan en menos del límite.

+0

Excelente respuesta. Muchas gracias. Usaré la cuerda para realizar algunas operaciones además de solo imprimir. Esos son omitidos por fáciles de explicar. –

0

Un modismo muy común para evitar estos problemas es permitir que la persona que llama gestione la memoria. Por lo tanto, en lugar de asignar usted mismo la memoria (ya sea usando una matriz de longitud variable en la pila o por malloc 'algo, o lo que sea), espera que la persona que llama proporcione memoria. Considere esto:

int foo(const char *p1, const char *p2, const char *p3, char *buf, size_t bufsize) 
{ 
    size_t requiredSize = strlen(p1) + strlen(p2) + strlen(p3) + 1; 
    if (!buf) 
     return requiredSize; 
    if (requiredSize > bufsize) 
     return -1; 
    buf[0] = '\0'; 
    strcat(buf, p1); 
    strcat(buf, p2); 
    strcat(buf, p3); 
    return requiredSize; 
} 

int main() 
{ 
    /* simple case: caller knows that the buffer is large enough. */ 
    char buf[ 1024 ]; 
    foo("Hello", "World", "Bar", buf, sizeof(buf)); 
    printf("Result : %s\n", buf); 

    /* complicated case: caller wants to allocate buffer of just the right size */ 
    size_t bufsize = foo("Hello", "World", "Bar", NULL, 0); 
    char *buf2 = (char *)malloc(bufsize); 
    foo("Hello", "World", "Bar", buf2, bufsize); 
    free(buf2); 
} 

La ventaja de este enfoque es que foo nunca habrá fugas. Además de eso, la persona que llama puede usar una matriz simple basada en la pila en caso de que funcione para él. Si quiere saber el tamaño exacto, puede llamar al foo y pasar NULL como cuarto argumento.