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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2603039/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 05:06:47  来源:igfitidea点击:

Warning: comparison with string literals results in unspecified behaviour

c

提问by nunos

I am starting a project of writing a simplified shell for linux in C. I am not at all proficient with C nor with Linux that's exactly the reason I decided it would be a good idea.

我正在开始一个用 C 为 linux 编写简化 shell 的项目。我对 C 和 Linux 一点都不精通,这正是我决定这将是一个好主意的原因。

Starting with the parser, I have already encountered some problems.

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

The code should be straightforward that's why I didn't include any comments.

代码应该很简单,这就是我没有包含任何注释的原因。

I am getting a warning with gcc: "comparison with string literals results in unspecified behaviour" at the lines commented with "WARNING HERE" (see code below).

我在 gcc 中收到警告:“与字符串文字的比较导致未指定的行为”在用“警告这里”注释的行(见下面的代码)。

I have no idea why this causes an warning, but the real problem is that even though I am comparing an "<" to an "<" is doesn't get inside the if...

我不知道为什么这会导致警告,但真正的问题是,即使我将“<”与“<”进行比较,也没有进入 if...

I am looking for an answer for the problem explained, however if there's something that you see in the code that should be improved please say so. Just take in mind I am not that proficient and that this is still a work in progress (or better yet, a work in start).

我正在寻找所解释问题的答案,但是如果您在代码中看到应该改进的内容,请说出来。请记住,我不是那么精通,而且这仍然是一项正在进行的工作(或者更好的是,一项开始的工作)。

Thanks in advance.

提前致谢。

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

回答by Michael Burr

You want to use strcmp() == 0to compare strings instead of a simple ==, which will just compare if the pointers are the same (which they won't be in this case).

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

args[i]is a pointer to a string (a pointer to an array of chars null terminated), as is "&"or "<".

args[i]是指向字符串的指针(指向以 null 结尾的字符数组的指针),如"&""<"

The expression argc[i] == "&"checks if the two pointers are the same (point to the same memory location).

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

The expression strcmp( argc[i], "&") == 0will check if the contents of the two strings are the same.

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

回答by RarrRarrRarr

There is a distinction between 'a'and "a":

'a'和之间有区别"a"

  • 'a'means the value of the character a.
  • "a"means the address of the memory location where the string "a"is stored (which will generally be in the data section of your program's memory space). At that memory location, you will have two bytes -- the character 'a'and the null terminator for the string.
  • 'a'表示字符的值a
  • "a"表示存储字符串的内存位置的地址"a"(通常位于程序内存空间的数据部分)。在那个内存位置,您将有两个字节——'a'字符串的字符和空终止符。

回答by Earlz

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

Ok, let's disect what this does.

好的,让我们分析一下这是做什么的。

args is an array of pointers. So, here you are comparing args[i](a pointer) to "&"(also a pointer). Well, the only way this will every be true is if somewhere you have args[i]="&"and even then, "&"is not guaranteed to point to the same place everywhere.

args 是一个指针数组。因此,在这里您将args[i](指针)与"&"(也是指针)进行比较。好吧,这一切都成立的唯一方法是,如果您拥有某个地方args[i]="&",即使如此,"&"也不能保证在任何地方都指向同一个地方。

I believe what you are actually looking for is either strcmpto compare the entire string or your wanting to do if (*args[i] == '&')to compare the first character of the args[i]string to the &character

我相信你实际上是在寻找或者是strcmp整个字符串比较或你想要做的if (*args[i] == '&')到的第一个字符比较args[i]字符串的&字符

回答by bluebrother

You can't compare strings with ==in C. For C, strings are just (zero-terminated) arrays, so you need to use string functions to compare them. See the man page for strcmp()and strncmp().

你不能==在 C 中比较字符串。对于 C,字符串只是(以零结尾的)数组,所以你需要使用字符串函数来比较它们。请参阅strcmp()strncmp()的手册页。

If you want to compare a character you need to compare to a character, not a string. "a"is the string a, which occupies two bytes (the aand the terminating null byte), while the character ais represented by 'a'in C.

如果你想比较一个字符,你需要比较一个字符,而不是一个字符串。"a"是字符串a,它占用两个字节(thea和终止空字节),而字符在 Ca中用 表示'a'

回答by jfs

  1. clanghas advantages in error reporting & recovery.

    $ 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
    

    It suggests to replace x == yby strcmp(x,y) == 0.

  2. gengetoptwrites command-line option parser for you.

  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 == ystrcmp(x,y) == 0.

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

回答by Philippe Carphin

This an old question, but I have had to explain it to someone recently and I thought recording the answer here would be helpful at least in understanding how C works.

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

String literals like

字符串文字如

"a"

or

或者

"This is a string"

are put in the text or data segments of your program.

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

A string in C is actually a pointer to a char, and the string is understood to be the subsequent chars in memory up until a NUL char is encountered. That is, C doesn't really know about strings.

C 中的字符串实际上是一个指向字符的指针,该字符串被理解为内存中的后续字符,直到遇到 NUL 字符为止。也就是说,C 并不真正了解字符串。

So if I have

所以如果我有

char *s1 = "This is a string";

then s1 is a pointer to the first byte of the string.

那么 s1 是指向字符串第一个字节的指针。

Now, if I have

现在,如果我有

char *s2 = "This is a string";

this is also a pointer to the same first byte of that string in the text or data segment of the program.

这也是指向程序文本或数据段中该字符串的相同第一个字节的指针。

But if I have

但如果我有

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

then s3 is a pointer to another place in memory into which I copy all the bytes of the other strings.

然后 s3 是指向内存中另一个位置的指针,我将其他字符串的所有字节复制到其中。

Illustrative examples:

示例:

Although, as your compiler rightly points out, you shouldn't do this, the following will evaluate to true:

虽然,正如您的编译器正确指出的那样,您不应该这样做,但以下内容将评估为真:

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

but the following will evaluate to false

但以下将评估为假

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

And although it might be tempting to have something like this:

尽管拥有这样的东西可能很诱人:

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

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

You shouldn't do it because it's not something that is guarantied to work. Even if you know that type will always be set using a string literal.

你不应该这样做,因为它不是保证工作的东西。即使您知道该类型将始终使用字符串文字进行设置。

I have tested it and it works. If I do

我已经测试过它并且它有效。如果我做

A.type = "Car";

then blah1 gets executed and similarly for "Motorcycle". And you'd be able to do things like

然后 blah1 被执行,对于“摩托车”也是如此。你可以做这样的事情

if( A.type == B.type )

but this is just terrible. I'm writing about it because I think it's interesting to know why it works, and it helps understand why you shouldn't do it.

但这太可怕了。我写它是因为我认为知道它为什么起作用很有趣,它有助于理解为什么你不应该这样做。

Solutions:

解决方案:

In your case, what you want to do is use strcmp(a,b) == 0to replace a == b

在你的情况下,你想要做的是strcmp(a,b) == 0用来替换a == b

In the case of my example, you should use an enum.

在我的示例中,您应该使用枚举。

enum type {CAR = 0, MOTORCYCLE = 1}

The preceding thing with string was useful because you could print the type, so you might have an array like this

前面的字符串很有用,因为你可以打印类型,所以你可能有一个这样的数组

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

And now that I think about it, this is error prone since one must be careful to maintain the same order in the types array.

现在我考虑了一下,这很容易出错,因为必须小心地在 types 数组中保持相同的顺序。

Therefore it might be better to do

因此最好这样做

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

回答by Ross Youngblood

I ran across this issue today working with a clients program. The program works FINE in VS6.0 using the following: (I've changed it slightly)

我今天在使用客户程序时遇到了这个问题。该程序在 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");
    }
}

If built in VS6.0 with Program Database with Edit and Continue. The compares APPEAR to work. With this setting STRING pooling is enabled, and the compiler optimizes all STRING pointers to POINT TO THE SAME ADDRESSS, so this can work. Any strings created on the fly after compile time will have DIFFERENT addresses so will fail the compare. Where Compiler settings areChanging the setting to Program Database only will build the program so that it will fail.

如果在 VS6.0 中内置带有编辑并继续的程序数据库。比较似乎可以工作。使用此设置启用 STRING 池,并且编译器优化所有 STRING 指针指向相同地址,因此这可以工作。编译后即时创建的任何字符串都将具有不同的地址,因此比较将失败。 编译器设置在哪里将设置更改为 Program Database only 将构建程序,因此它会失败。