警告:与字符串文字进行比较会导致未指定的行为

时间:2010-04-08 20:02:59

标签: c

我正在开始一个用C语言编写Linux简化shell的项目。我完全不熟悉C,也不熟悉Linux,这正是我认为这是个好主意的原因。

从解析器开始,我已经遇到了一些问题。

代码应该是直截了当的,这就是为什么我没有包含任何评论。

我收到gcc的警告:“与字符串文字的比较导致未指明的行为”在注释为“WARNING HERE”的行(请参阅下面的代码)。

我不知道为什么会引起警告,但真正的问题是即使我正在比较“<”到“<”是不会进入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;
}

7 个答案:

答案 0 :(得分:81)

您希望使用strcmp() == 0来比较字符串而不是简单的==,只会比较指针是否相同(在这种情况下它们不会是这样)。

args[i]是指向字符串的指针(指向空格终止的字符数组的指针),"&""<"也是如此。

表达式argc[i] == "&"检查两个指针​​是否相同(指向相同的内存位置)。

表达式strcmp( argc[i], "&") == 0将检查两个字符串的内容是否相同。

答案 1 :(得分:6)

'a'"a"之间存在区别:

  • 'a'表示字符a
  • 的值
  • "a"表示存储字符串"a"的内存位置的地址(通常位于程序内存空间的数据部分中)。在该内存位置,您将有两个字节 - 字符'a'和字符串的空终止符。

答案 2 :(得分:6)

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

好的,让我们来看看它的作用。

args是一个指针数组。所以,在这里你要将args[i](指针)与"&"(也是一个指针)进行比较。嗯,唯一的方法是,如果某个地方有args[i]="&",即便如此,"&"也无法保证指向同一个地方。

我相信您实际需要的是strcmp来比较整个字符串,或者您想要if (*args[i] == '&')来比较args[i]字符串的第一个字符与{{} 1}}字符

答案 3 :(得分:5)

您无法在C中将字符串与==进行比较。对于C,字符串只是(零终止)数组,因此您需要使用字符串函数来比较它们。请参阅strcmp()strncmp()的手册页。

如果要比较需要与字符比较的字符,而不是字符串。 "a"是字符串a,占用两个字节(a和终止空字节),而字符a在C中由'a'表示

答案 4 :(得分:4)

  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
    

    建议将x == y替换为strcmp(x,y) == 0

  2. gengetopt为您编写命令行选项解析器。

答案 5 :(得分:3)

这是一个古老的问题,但我最近不得不向某人解释,我认为在这里记录答案至少在了解C的工作原理方面会有所帮助。

这样的字符串文字
"a"

"This is a string"

放在程序的文本或数据段中。

C中的字符串实际上是指向char的指针,该字符串被理解为内存中的后续字符,直到遇到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

但以下将评估为false

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被执行并且类似于&#34; Motorcycle&#34;。而且你可以做像

这样的事情
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;
}

答案 6 :(得分:0)

我今天在处理客户程序时遇到了这个问题。 该程序使用以下命令在VS6.0中可以进行FINE: (我已经对其稍作更改)

//
// 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中内置了带有“程序并继续”的程序数据库。 比较APPEAR起作用。使用此设置,将启用STRING池,并且编译器会优化指向相同地址的所有STRING指针,因此可以正常工作。在编译时间后动态创建的任何字符串都将具有不同的地址,因此将使比较失败。 Where Compiler settings are 将设置更改为“仅程序数据库”将生成程序,从而使其失败。