2010-04-08 28 views
27

我開始在C語言中編寫一個簡化的shell for linux的項目。我完全沒有熟練使用C語言和Linux,這正是我認爲這將是一個好的原因理念。警告:與字符串文字的比較會導致未指定的行爲

從解析器開始,我已經遇到了一些問題。

代碼應該很直接,這就是爲什麼我沒有包含任何評論。

我收到警告與海灣合作委員會:「比較與字符串文字導致未指定的行爲」在行註釋「警告這裏」(見下面的代碼)。

我不知道爲什麼這會導致一個警告,但真正的問題是,即使我是一個比較「<」到「<」是沒有的,如果...

我裏面得到我正在尋找解釋問題的答案,但是如果您在應該改進的代碼中看到某些內容,請說出來。請記住我並不那麼精通,而且這仍然是一項正在進行的工作(或者更好的是,這是一項開始的工作)。

在此先感謝。

#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

如果你真的只想檢查單個字符,你可以做'如果(參數[I] && ARGS [I] [0] ==「< 「)'。 – GManNickG 2010-04-08 20:11:43

回答

69

代表你想用strcmp() == 0來比較,而不是一個簡單的==字符串,這只是比較指針是否相同(在這種情況下它們不會)。

args[i]是一個指向字符串的指針(指向字符數組的空指針終止),如同"&""<"

表達式argc[i] == "&"檢查兩個指針​​是否相同(指向相同的內存位置)。

表達式strcmp(argc[i], "&") == 0將檢查兩個字符串的內容是否相同。

+1

在幾個小時內+1。完美的簡單答案。 – GManNickG 2010-04-08 20:08:43

+0

一個體面的編譯器可以告訴它;)http://stackoverflow.com/questions/2603039/warning-comparison-with-string-literals-results-in-unspecified-behaviour/2603201#2603201 – jfs 2010-04-08 20:29:24

+0

@GMman - 拯救獨角獸:爲你+1。似乎我們需要設置一個交換。 :) – 2010-04-08 20:35:44

5

'a'"a"區分:

  • 'a'指字符a的值。
  • "a"表示存儲字符串"a"(通常位於程序內存空間的數據部分)的內存位置的地址。在該內存位置,您將有兩個字節 - 字符'a'和字符串的空終止符。
+1

args [i]是'char *',而不是'char'。 – 2010-04-08 20:07:02

4

您不能在C中與字符串==進行比較。對於C,字符串只是(零終止)數組,因此您需要使用字符串函數來比較它們。請參閱手冊頁strcmp()strncmp()

如果你想比較一個字符,你需要比較一個字符,而不是字符串。 "a"是字符串a,它佔兩個字節(a和終止空字節),而性格a'a'在C.

+0

他在比較字符,而不是字符串。 – WhirlWind 2010-04-08 20:07:31

+0

@WhirlWind:這是不正確的。也就是說,你不能用'='做任何比較,但你可以用'=='來進行比較。 – GManNickG 2010-04-08 20:07:58

+0

args [i] ==「>」 - 好的,很好,有兩種方法可以解決這個問題。或者將其更改爲與字符串func進行的字符串比較,或者將字符與字符進行比較... – WhirlWind 2010-04-08 20:09:50

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

好的,讓我們來看看它的功能。

args是一個指針數組。所以,你在這裏比較args[i](一個指針)和"&"(也是一個指針)。那麼,唯一的方法是每個都是真實的,如果你有args[i]="&"的地方,那麼,"&"不能保證指向同一個地方。

我相信你實際上是在尋找或者是strcmp整個字符串比較或你想要做if (*args[i] == '&')args[i]字符串的第一個字符比較的&字符

2
  1. clang有優勢錯誤報告&恢復。

    $ 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 
    

    這表明通過strcmp(x,y) == 0更換x == y

  2. gengetopt爲您寫入命令行選項解析器。

3

這一個老問題,但我有最近解釋給別人,我想在這裏記錄的答案將是有益的,至少在瞭解Ç是如何工作的。

"a" 

"This is a string" 

字符串文字把你的程序的文本或數據段。

C中的字符串實際上是指向字符的指針,字符串被理解爲內存中的後續字符,直到遇到NUL字符爲止。也就是說,C並不真正瞭解字符串。

所以,如果我有

char *s1 = "This is a string"; 

那麼s1是指向字符串的第一個字節。現在

,如果我有

char *s2 = "This is a string"; 

這也是一個指針,指向程序的文本或數據段串的首字節相同。

但是,如果我有

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

然後S3是一個指向內存中的另一個地方到我複製到其他琴絃的所有字節。

說明性的例子:

雖然,作爲你的編譯器正確地指出,你不應該這樣做,下面的值爲true:

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

但下面將評估爲假

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

雖然這可能是這樣的誘惑:

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

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

你不應該這樣做,因爲它不是保證工作的東西。即使您知道該類型將始終使用字符串文字來設置。

我測試過它,它工作。如果我做

A.type = "Car"; 

然後blah1得到執行,類似的「摩托車」。而且你可以做像

if(A.type == B.type) 

但是這太可怕了。我正在寫這篇文章,因爲我認爲知道它爲什麼有效很有意思,並且它有助於理解你爲什麼不應該這樣做。

解決方案:

在你的情況,你需要做的是用strcmp(a,b) == 0更換a == b

在我的例子的情況下,你應該使用一個枚舉。

enum type {CAR = 0, MOTORCYCLE = 1} 

用繩子前面的東西是有用的,因爲你可以打印類型,所以你可能有這樣

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

而現在,我想它的數組,這是錯誤,因爲一個容易必須小心在類型數組中保持相同的順序。

因此,它可能會更好做

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