2010-04-08 22 views
27

Estoy empezando un proyecto de escribir un shell simplificado para Linux en C. No soy competente en C ni en Linux, esa es precisamente la razón por la que decidí que sería una buena idea.Advertencia: la comparación con literales de cadena da como resultado un comportamiento no especificado

Comenzando con el analizador, ya he encontrado algunos problemas.

El código debe ser sencillo, es por eso que no incluí ningún comentario.

Aparece una advertencia con gcc: "la comparación con literales de cadena da como resultado un comportamiento no especificado" en las líneas comentadas con "ADVERTENCIA AQUÍ" (ver el código a continuación).

no tengo ni idea de por qué esto provoca una advertencia, pero el verdadero problema es que a pesar de que estoy comparando un "<" a un "<" es no recibe dentro del si ...

I Estoy buscando una respuesta para el problema explicado, sin embargo, si hay algo que ve en el código que debería mejorarse, dígalo. Solo tenga en cuenta que no soy tan competente y que esto todavía es un trabajo en progreso (o mejor aún, un trabajo en el comienzo).

Gracias de antemano.

#include <stdio.h> 
#include <unistd.h> 
#include <string.h> 

typedef enum {false, true} bool; 

typedef struct { 
    char **arg; 
    char *infile; 
    char *outfile; 
    int background; 
} Command_Info; 

int parse_cmd(char *cmd_line, Command_Info *cmd_info) 
{ 
    char *arg; 
    char *args[100];  

    int i = 0; 
    arg = strtok(cmd_line, " \n"); 
    while (arg != NULL) { 
     args[i] = arg; 
     arg = strtok(NULL, " \n"); 
     i++; 
    } 

    int num_elems = i; 

    cmd_info->infile = NULL; 
    cmd_info->outfile = NULL; 
    cmd_info->background = 0; 

    int iarg = 0; 
    for (i = 0; i < num_elems; i++) 
    { 
     if (args[i] == "&") //WARNING HERE 
      return -1;  
     else if (args[i] == "<") //WARNING HERE 
      if (args[i+1] != NULL) 
       cmd_info->infile = args[i+1]; 
      else 
       return -1; 

     else if (args[i] == ">") //WARNING HERE 
      if (args[i+1] != NULL) 
       cmd_info->outfile = args[i+1]; 
      else 
       return -1;   

     else 
      cmd_info->arg[iarg++] = args[i]; 
    } 

    cmd_info->arg[iarg] = NULL; 

    return 0; 
} 

void print_cmd(Command_Info *cmd_info) 
{ 
    int i; 
    for (i = 0; cmd_info->arg[i] != NULL; i++) 
     printf("arg[%d]=\"%s\"\n", i, cmd_info->arg[i]); 
    printf("arg[%d]=\"%s\"\n", i, cmd_info->arg[i]);  
    printf("infile=\"%s\"\n", cmd_info->infile); 
    printf("outfile=\"%s\"\n", cmd_info->outfile); 
    printf("background=\"%d\"\n", cmd_info->background); 
} 

int main(int argc, char* argv[]) 
{ 
    char cmd_line[100]; 
    Command_Info cmd_info; 

    printf(">>> "); 

    fgets(cmd_line, 100, stdin); 

    parse_cmd(cmd_line, &cmd_info); 

    print_cmd(&cmd_info); 

    return 0; 
} 
+0

Si realmente sólo desea comprobar un solo carácter, que puede hacer 'si (args [i] && args [i] [0] == '< ') '. – GManNickG

Respuesta

69

Desea utilizar strcmp() == 0 para comparar cadenas en lugar de un simple ==, que simplemente comparará si los punteros son los mismos (lo que no será en este caso).

args[i] es un puntero a una cadena (un puntero a una matriz de caracteres nulo terminado), como es "&" o "<".

La expresión argc[i] == "&" comprueba si los dos punteros son iguales (señale la misma ubicación de memoria).

La expresión strcmp(argc[i], "&") == 0 comprobará si el contenido de las dos cadenas es el mismo.

+1

+1 en pocas horas. Respuesta simple y perfecta – GManNickG

+0

Un compilador decente podría decirlo;) http://stackoverflow.com/questions/2603039/warning-comparison-with-string-literals-results-in-unspecified-behaviour/2603201#2603201 – jfs

+0

@GMman - Save the Unicorns: Hizo +1 por ti. Parece que necesitamos configurar un intercambio. :) –

5

Hay una distinción entre 'a' y "a":

  • 'a' significa que el valor del carácter a.
  • "a" significa la dirección de la ubicación de la memoria donde se almacena la cadena "a" (que generalmente estará en la sección de datos del espacio de memoria de su programa). En esa ubicación de memoria, tendrá dos bytes: el carácter 'a' y el terminador nulo para la cadena.
+1

args [i] es un 'char *', no un 'char'. –

4

No se puede comparar cadenas con == en C. Para C, las cadenas son sólo (terminados en cero) matrices, por lo que es necesario utilizar las funciones de cadena para compararlos. Consulte la página del manual para strcmp() y strncmp().

Si quiere comparar un personaje, necesita compararlo con un personaje, no con una cadena. "a" es la cadena a, que ocupa dos bytes (el byte a y el terminador nulo), mientras que el carácter a está representado por 'a' en C.

+0

Está comparando personajes, no cadenas. – WhirlWind

+0

@WhirlWind: Eso es incorrecto. Dicho esto, no puedes hacer comparaciones con '=', pero puedes con '=='. – GManNickG

+0

args [i] == ">" - bien, bien, hay dos formas de arreglar eso. O lo cambias a una comparación de cuerdas con un func de cuerdas, o una comparación de personajes con un personaje ... – WhirlWind

5
if (args[i] == "&") 

Bien, disectemos lo que hace.

args es una matriz de punteros. Por lo tanto, aquí está comparando args[i] (un puntero) a "&" (también un puntero). Bueno, la única manera de que esto sea cierto todo es que si en algún lugar se tiene args[i]="&" e incluso entonces, "&" no está garantizada para que apunte al mismo lugar en todas partes.

Creo que lo que en realidad se está buscando es bien strcmp a comparar toda la cadena o el querer hacer if (*args[i] == '&') para comparar el primer carácter de la cadena args[i] al carácter &

2
  1. clang tiene ventajas en informe de errores & recuperación.

    $ clang errors.c 
    errors.c:36:21: warning: result of comparison against a string literal is unspecified (use strcmp instead) 
         if (args[i] == "&") //WARNING HERE 
            ^~ ~~~ 
          strcmp(, ) == 0 
    errors.c:38:26: warning: result of comparison against a string literal is unspecified (use strcmp instead) 
         else if (args[i] == "<") //WARNING HERE 
             ^~ ~~~ 
           strcmp(, ) == 0 
    errors.c:44:26: warning: result of comparison against a string literal is unspecified (use strcmp instead) 
         else if (args[i] == ">") //WARNING HERE 
             ^~ ~~~ 
           strcmp(, ) == 0 
    

    Se sugiere reemplazar por x == ystrcmp(x,y) == 0.

  2. gengetopt escribe de línea de comandos analizador de opciones para usted.

3

Este una vieja pregunta, pero he tenido que explicar a alguien hace poco y pensé que la grabación de la respuesta en este caso sería de gran ayuda, al menos en la comprensión de cómo funciona C.

literales de cadena como

"a" 

o

"This is a string" 

se ponen en los segmentos de texto o datos de su programa.

Una cadena en C es en realidad un puntero a un char, y la cadena se entiende que los caracteres posteriores en la memoria hasta hasta que se encuentra un char NUL. Es decir, C realmente no sabe sobre cadenas.

Así que si tengo

char *s1 = "This is a string"; 

entonces S1 es un puntero al primer byte de la cadena.

Ahora, si tengo

char *s2 = "This is a string"; 

esto también es un puntero a la misma primer byte de esa cadena en el segmento de texto o datos del programa.

Pero si tengo

char *s3 = malloc(17); 
strcpy(s3, "This is a string"); 

continuación S3 es un puntero a otro lugar de la memoria en la que copio todos los bytes de las otras cuerdas.

ejemplos ilustrativos:

Aunque, como su compilador señala con razón, que no debe hacer esto, el siguiente se evaluará como true:

s1 == s2 // True: we are comparing two pointers that contain the same address 

pero el siguiente será evaluada como falsa

s1 == s3 // False: Comparing two pointers that don't hold the same address. 

Y aunque podría ser tentador para tener algo como esto:

struct Vehicle{ 
    char *type; 
    // other stuff 
} 

if(type == "Car") 
    //blah1 
else if(type == "Motorcycle) 
    //blah2 

No debe hacerlo porque no es algo que se garantice que funcione. Incluso si sabe que ese tipo siempre se establecerá utilizando un literal de cadena.

Lo he probado y funciona. Si lo hago

A.type = "Car"; 

entonces blah1 se ejecuta y de manera similar para "Motocicleta". Y usted sería capaz de hacer cosas como

if(A.type == B.type) 

pero esto es simplemente terrible. Estoy escribiendo sobre esto porque creo que es interesante saber por qué funciona, y ayuda a entender por qué no debes hacerlo.

Soluciones:

En su caso, lo que se quiere hacer es usar strcmp(a,b) == 0 para reemplazar a == b

En el caso de mi ejemplo, se debe utilizar una enumeración.

enum type {CAR = 0, MOTORCYCLE = 1} 

Lo anterior con una cuerda era útil porque se puede imprimir el tipo, lo que podría tener una serie como esta

char *types[] = {"Car", "Motorcycle"}; 

Y ahora que lo pienso, esto es propenso a errores, ya que uno debe tener cuidado de mantener el mismo orden en la matriz de tipos.

Por lo tanto, podría ser mejor que hacer

char *getTypeString(int type) 
{ 
    switch(type) 
    case CAR: return "Car"; 
    case MOTORCYCLE: return "Motorcycle" 
    default: return NULL; 
} 
Cuestiones relacionadas