source

경고: 문자열 리터럴과 비교하면 지정되지 않은 동작이 발생합니다.

goodcode 2022. 8. 18. 23:29
반응형

경고: 문자열 리터럴과 비교하면 지정되지 않은 동작이 발생합니다.

C에 Linux용 간이 셸을 작성하는 프로젝트를 시작하고 있습니다.저는 C도 Linux도 전혀 능숙하지 않기 때문에 좋은 아이디어라고 판단했습니다.

파서부터 이미 몇 가지 문제가 발생했습니다.

코드는 간단해야 하기 때문에 코멘트를 넣지 않았습니다.

"WARNING HERE"로 코멘트된 행에 "문자열 리터럴과 비교하면 지정되지 않은 동작이 발생한다"라는 경고가 gcc와 함께 표시됩니다(아래 코드 참조).

이것이 왜 경고를 발생시키는지 알 수 없지만, 진짜 문제는 "<"를 "<"와 비교해도 만약의 경우 안에 들어가지 않는다는 것입니다.

설명한 문제에 대한 답을 찾고 있습니다만, 코드에 개선해야 할 점이 있으면 그렇게 말씀해 주십시오.저는 그다지 능숙하지 않으며, 이것은 아직 진행 중인 작업(또는 시작 단계에서의 작업)이라는 점을 유념하십시오.

잘 부탁드립니다.

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

하려고 합니다.strcmp() == 0한 비교가 비교하다==포인터가 같은 경우(이 경우에는 해당되지 않음)를 비교합니다.

args[i]terminated 에 대한 입니다. 「」라고 하는 의미입니다."&" ★★★★★★★★★★★★★★★★★」"<".

.argc[i] == "&"는, 2개의 포인터가 같은지를 확인합니다(같은 메모리 위치를 가리키고 있습니다).

.strcmp( argc[i], "&") == 0두 문자열의 내용이 동일한지 확인합니다.

이 있다'a' ★★★★★★★★★★★★★★★★★」"a":

  • 'a'입니다.a.
  • "a"는, 메모리 위치의 주소는 다음과 같습니다."a"저장된다(일반적으로 프로그램 메모리 공간의 데이터 섹션에 있음).위치에는 2바이트입니다.★★★★'a'및 문자열의 늘 터미네이터를 지정합니다.
if (args[i] == "&")

좋아, 이걸로 뭘 할 수 있는지 알아보자.

포인터의 입니다. 이번에는.args[i] ~ 에 (지 ( 포인터)"&"(일부러) 이 일 수 있는 은 당신이 에 '아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아.args[i]="&" ★★★★★★★★★★★★★."&"모든 곳에서 같은 장소를 가리킬 수 있는 것은 아닙니다.

이 있는 은 ★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★★.strcmp전체 문자열을 비교하거나 하고 싶은 것을 비교하다if (*args[i] == '&')의 첫 글자를 비교하다args[i]에 줄을 매다&성격

이것은 오래된 질문이지만, 최근 누군가에게 설명해야 했기 때문에, 여기에 답을 기록하는 것이 적어도 C의 구조를 이해하는 데 도움이 될 것이라고 생각했습니다.

스트링 리터럴은 다음과 같습니다.

"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는 다른 문자열의 모든 바이트를 복사하는 메모리 내의 다른 위치에 대한 포인터입니다.

예시:

컴파일러가 올바르게 지적하고 있듯이, 이것을 실행해서는 안 됩니다.다만, 다음과 같이 평가됩니다.

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이 실행되고 'Motorcycle'도 마찬가지입니다.그리고 당신은 이런 것들을 할 수 있을 것이다.

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

스트링을 비교할 수 없습니다.==C의 경우 문자열은 단지 (제로 종단된) 배열이므로 문자열 함수를 사용하여 비교해야 합니다.strcmp()strncmp()의 man 페이지를 참조해 주세요.

문자를 비교하려면 문자열이 아닌 문자와 비교해야 합니다. "a"입니다.a(2바이트)를 차지합니다a및 를 지정합니다.단, 「」는 「」를 참조해 주세요.a라고 표현합니다.'a'로 있습니다.

나는 오늘 클라이언트 프로그램과 함께 일하면서 이 문제를 우연히 발견했다.이 프로그램은 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 포인터를 POINT TO THE SAME ADDRESS로 최적화하여 동작합니다.컴파일 시간 후 즉시 작성된 문자열은 주소가 다르므로 비교에 실패합니다.컴파일러 설정 위치 설정을 Program Database only(프로그램 데이터베이스만)로 변경하면 프로그램이 구축되어 오류가 발생합니다.

  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는 명령줄 옵션 파서를 대신 작성합니다.

언급URL : https://stackoverflow.com/questions/2603039/warning-comparison-with-string-literals-results-in-unspecified-behaviour

반응형