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

nun*_*nos 34 c

我正在开始一个在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;
}
Run Code Online (Sandbox Code Playgroud)

Mic*_*urr 83

你想用来strcmp() == 0比较字符串而不是简单字符串==,如果指针是相同的(在这种情况下它们不是这样),它只会比较.

args[i]是指向字符串的指针(指向空格终止的字符数组的指针),原样"&""<".

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

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

  • 在几个小时内+1.完美简单的答案. (2认同)

Rar*_*arr 8

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

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

  • args[i] 是一个“char*”,而不是一个“char”。 (2认同)

Ear*_*rlz 6

if (args[i] == "&")
Run Code Online (Sandbox Code Playgroud)

好吧,让我们来看看它的作用.

args是一个指针数组.所以,在这里你要比较args[i](指针)"&"(也是一个指针).嗯,这一切都是真实的唯一方法就是如果你拥有的某个地方args[i]="&",即便如此,"&"也不能保证在任何地方指向同一个地方.

我相信你真正想要的是strcmp比较整个字符串或者你想做什么if (*args[i] == '&')来比较args[i]字符串的第一个字符和&字符


blu*_*her 5

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

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


jfs*_*jfs 5

  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
    
    Run Code Online (Sandbox Code Playgroud)

    它建议替换x == ystrcmp(x,y) == 0.

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


Phi*_*hin 5

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

字符串文字如

"a"
Run Code Online (Sandbox Code Playgroud)

或者

"This is a string"
Run Code Online (Sandbox Code Playgroud)

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

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

所以如果我有

char *s1 = "This is a string";
Run Code Online (Sandbox Code Playgroud)

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

现在,如果我有

char *s2 = "This is a string";
Run Code Online (Sandbox Code Playgroud)

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

但如果我有

char *s3 = malloc( 17 );
strcpy(s3, "This is a string");
Run Code Online (Sandbox Code Playgroud)

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

说明性示例:

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

s1 == s2 // True: we are comparing two pointers that contain the same address
Run Code Online (Sandbox Code Playgroud)

但以下将评估为 false

s1 == s3 // False: Comparing two pointers that don't hold the same address.
Run Code Online (Sandbox Code Playgroud)

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

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

if( type == "Car" )
   //blah1
else if( type == "Motorcycle )
   //blah2
Run Code Online (Sandbox Code Playgroud)

你不应该这样做,因为它不能保证有效。即使您知道类型始终使用字符串文字设置。

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

A.type = "Car";
Run Code Online (Sandbox Code Playgroud)

然后 blah1 被执行,“摩托车”也类似。你可以做类似的事情

if( A.type == B.type )
Run Code Online (Sandbox Code Playgroud)

但这太可怕了。我写这篇文章是因为我认为了解它为什么有效很有趣,并且有助于理解为什么你不应该这样做。

解决方案:

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

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

enum type {CAR = 0, MOTORCYCLE = 1}
Run Code Online (Sandbox Code Playgroud)

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

char *types[] = {"Car", "Motorcycle"};
Run Code Online (Sandbox Code Playgroud)

现在我想一想,这很容易出错,因为必须小心保持类型数组中的相同顺序。

因此最好这样做

char *getTypeString(enum type t)
{
    switch(t)
    case CAR: return "Car";
    case MOTORCYCLE: return "Motorcycle"
    default: return NULL;
}
Run Code Online (Sandbox Code Playgroud)