Подтвердить что ты не робот

Предупреждение: сравнение со строковыми литералами приводит к неопределенному поведению

Я начинаю проект написания упрощенной оболочки для linux в C. Я совсем не владею 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;
}
4b9b3361

Ответ 1

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

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

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

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

Ответ 2

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

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

Ответ 3

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

Хорошо, дайте понять, что это делает.

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

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

Ответ 4

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

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

Ответ 5

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

  • gengetopt пишет для вас синтаксический анализатор командной строки.

Ответ 6

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

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

"a"

или

"This is a string"

помещаются в текстовые или информационные сегменты вашей программы.

Строка в C на самом деле является указателем на char, а строка понимается как последующие символы в памяти до тех пор, пока не встретится NUL char. То есть, 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

но следующее будет оцениваться как 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 выполняется и аналогично для "Мотоцикла". И вы сможете делать такие вещи, как

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

Ответ 7

Я столкнулся с этой проблемой сегодня, работая с клиентской программой. Программа работает 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 Если изменить настройку только на базу данных программы, программа будет скомпилирована так, что она не будет выполнена.