簡體   English   中英

警告:與字符串文字的比較會導致未指定的行為

[英]Warning: comparison with string literals results in unspecified behaviour

我正在開始一個項目,為 C 中的 linux 編寫一個簡化的 shell。我對 C 和 Linux 都不精通,這正是我認為這是個好主意的原因。

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

代碼應該簡單明了,這就是為什么我沒有包含任何注釋。

我收到 gcc 的警告:“與字符串文字的比較會導致未指定的行為”,注釋為“此處警告”的行(請參見下面的代碼)。

我不知道為什么這會導致警告,但真正的問題是即使我將“<”與“<”進行比較也不會進入 if...

我正在尋找所解釋問題的答案,但是如果您在代碼中看到應該改進的地方,請說出來。 請記住,我不是那么精通,而且這仍在進行中(或者更好的是,開始工作)。

#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;
}

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

args[i]是指向字符串的指針(指向以空字符結尾的字符數組的指針),就像"&""<"

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

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

'a'"a"之間有區別:

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

你不能在 C 中用==比較字符串。對於 C,字符串只是(以零結尾的)數組,所以你需要使用字符串函數來比較它們。 請參閱strcmp()strncmp()的手冊頁。

如果你想比較一個字符,你需要比較一個字符,而不是一個字符串。 "a"是字符串a ,它占據兩個字節( a和終止的空字節),而字符a在 C 中由'a'表示。

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

好的,讓我們分析一下這是做什么的。

args 是一個指針數組。 因此,在這里您將args[i] (一個指針)與"&" (也是一個指針)進行比較。 好吧,這一切都成立的唯一方法是,如果您在某個地方有args[i]="&"並且即使如此, "&"也不能保證在任何地方都指向同一個地方。

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

  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為您編寫命令行選項解析器。

這是一個老問題,但我最近不得不向某人解釋它,我認為在這里記錄答案至少有助於理解 C 的工作原理。

字符串文字如

"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 是指向內存中另一個位置的指針,我將其他字符串的所有字節復制到其中。

示例:

雖然,正如您的編譯器正確指出的那樣,您不應該這樣做,但以下內容將評估為真:

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"};

現在我考慮了一下,這很容易出錯,因為必須小心地在 types 數組中保持相同的順序。

因此最好這樣做

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

我今天在使用客戶程序時遇到了這個問題。 該程序在 VS6.0 中運行良好,使用以下內容:(我稍微更改了它)

//
// This is the one include file that every user-written Nextest programs needs.
// Patcom-generated files will also look for this file.
//
#include "stdio.h"
#define IS_NONE( a_key )   ( ( a_key == "none" || a_key == "N/A" ) ? TRUE : FALSE )

//
// Note in my environment we have output() which is printf which adds /n at the end
//
main {
    char *psNameNone = "none";
    char *psNameNA   = "N/A";
    char *psNameCAT  = "CAT";

    if (IS_NONE(psNameNone) ) {
        output("psNameNone Matches NONE");
        output("%s psNameNoneAddr 0x%x  \"none\" addr 0x%X",
            psNameNone,psNameNone,
            "none");
    } else {
        output("psNameNone Does Not Match None");
        output("%s psNameNoneAddr 0x%x  \"none\" addr 0x%X",
            psNameNone,psNameNone,
            "none");
    }

    if (IS_NONE(psNameNA) ) {
        output("psNameNA Matches N/A");
        output("%s psNameNA 0x%x  \"N/A\" addr 0x%X",
        psNameNA,psNameNA,
        "N/A");
    } else {
        output("psNameNone Does Not Match N/A");
        output("%s psNameNA 0x%x  \"N/A\" addr 0x%X",
        psNameNA,psNameNA,
        "N/A");
    }
    if (IS_NONE(psNameCAT)) {
        output("psNameNA Matches CAT");
        output("%s psNameNA 0x%x  \"CAT\" addr 0x%X",
        psNameNone,psNameNone,
        "CAT");
    } else {
        output("psNameNA does not match CAT");
        output("%s psNameNA 0x%x  \"CAT\" addr 0x%X",
        psNameNone,psNameNone,
        "CAT");
    }
}

如果在 VS6.0 中內置帶有編輯並繼續的程序數據庫。 比較似乎可以工作。 使用此設置啟用 STRING 池,並且編譯器優化所有 STRING 指針指向相同地址,因此這可以工作。 編譯后即時創建的任何字符串都將具有不同的地址,因此比較將失敗。 編譯器設置在哪里 將設置更改為 Program Database only 將構建程序,因此它會失敗。

或者可能是這樣的:

bool stringEquals(const char *__lhs, const char *__rhs) {
    return strcmp(__lhs, __rhs) == 0;
}

出於可讀性原因...

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM