Предупреждение: сравнение со строковыми литералами приводит к неопределенному поведению - PullRequest
31 голосов
/ 09 апреля 2010

Я начинаю проект написания упрощенной оболочки для linux на C. Я совсем не разбираюсь в C и в Linux, именно поэтому я решил, что это будет хорошей идеей.

Начиная с парсера, я уже столкнулся с некоторыми проблемами.

Код должен быть простым, поэтому я не включил никаких комментариев.

Я получаю предупреждение с помощью gcc: «сравнение со строковыми литералами приводит к неопределенному поведению» в строках с комментариями «WARNING HERE» (см. Код ниже).

Я понятия не имею, почему это вызывает предупреждение, но реальная проблема заключается в том, что даже если я сравниваю "<" с "<", это не попадает в if ... </p>

Я ищу ответ на объясненную проблему, однако, если в коде есть что-то, что должно быть улучшено, скажите, пожалуйста. Просто помните, что я не настолько опытен и что эта работа еще не завершена (или еще лучше, начальная работа).

Заранее спасибо.

#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 ]

81 голосов
/ 09 апреля 2010

Вы хотите использовать strcmp() == 0 для сравнения строк вместо простого ==, который будет просто сравниваться, если указатели совпадают (чего не будет в этом случае).

args[i] - указатель на строку (указатель на массив символов с нулевым символом в конце), как "&" или "<".

Выражение argc[i] == "&" проверяет, совпадают ли два указателя (указывают на одну и ту же ячейку памяти).

Выражение strcmp( argc[i], "&") == 0 проверит, совпадает ли содержимое двух строк.

6 голосов
/ 09 апреля 2010
if (args[i] == "&")

Хорошо, давайте рассмотрим, что это делает.

args - это массив указателей. Итак, здесь вы сравниваете args[i] (указатель) с "&" (также указатель). Ну, единственный способ, которым все это будет правдой, - это если где-то у вас есть args[i]="&", и даже тогда "&" не гарантирует, что он будет указывать на одно и то же место везде.

Я полагаю, что вы на самом деле ищете либо strcmp для сравнения всей строки, либо ваше желание if (*args[i] == '&') сравнить первый символ строки args[i] с & символом

6 голосов
/ 09 апреля 2010

Существует различие между 'a' и "a":

  • 'a' означает значение символа a.
  • "a" означает адрес ячейки памяти, в которой хранится строка "a" (которая обычно находится в разделе данных в области памяти вашей программы). В этой ячейке памяти у вас будет два байта - символ 'a' и нулевой терминатор для строки.
5 голосов
/ 09 апреля 2010

Вы не можете сравнивать строки с == в C. Для C строки - это просто массивы с нулевым символом в конце, поэтому вам нужно использовать строковые функции для их сравнения. См. Справочную страницу для strcmp () и strncmp () .

Если вы хотите сравнить символ, вам нужно сравнить его с символом, а не строкой. "a" - это строка a, занимающая два байта (a и завершающий нулевой байт), а символ a представлен 'a' в C.

4 голосов
/ 09 апреля 2010
  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 пишет для вас анализатор параметров командной строки.

3 голосов
/ 30 октября 2016

Это старый вопрос, но мне недавно пришлось его кому-то объяснить, и я подумал, что запись ответа здесь будет полезна, по крайней мере, для понимания того, как работает С.

Строковые литералы, такие как

"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.

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;
}
0 голосов
/ 21 января 2019

Сегодня я столкнулся с этой проблемой, работая с клиентской программой. Программа работает FINE в 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 для указания на те же адреса, что может работать Любые строки, созданные на лету после времени компиляции, будут иметь РАЗНЫЕ адреса, поэтому не удастся сравнить. Where Compiler settings are Изменение параметра «Только база данных программ» приведет к сбору программы.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...