2012-01-22 5 views
10

내가 다음 코드 조각은 세그먼트 오류를주는 이유를 이해하려고 노력하고는 strtok를 내부적으로 char의 배열 인 문자열 "this is a test"을 직접 가리 킵니다. 배열에 복사하지 않고 토큰 화 line이 있습니까?세그먼트 오류

+2

Dude - "this is a test"는 STRING LITERAL입니다. 의미는 * READ ONLY * "char of array"입니다. 특정 플랫폼에서 충돌하지 않고 수정하려고 시도 할 수도 있습니다. 그러나 확실히 ANY * 플랫폼에 대한 no-no입니다. – paulsm4

답변

14

문제는 문자열 리터럴을 수정하려는 것입니다. 이렇게하면 프로그램 동작이 정의되지 않습니다.

문자열 리터럴을 수정할 수 없다고하면 단순화됩니다. 해당 문자열 리터럴이 const 인 경우 올바르지 않습니다. 그들은 아니다.

경고 : 다음과 같은 오류가 발생합니다.

문자열 리터럴 "this is a test"char[15] (길이는 14이고 종료는 '\0' 인 경우 1) 형식입니다.이 표현식을 포함한 대부분의 문맥에서, 그러한 표현식은 암시 적으로 char* 유형의 배열의 첫 번째 요소에 대한 포인터로 변환됩니다.

문자열 리터럴이 참조하는 배열을 수정하는 동작은 정의되지 않습니다. 이는 const이 아니기 때문에가 아니라 C 표준에서 명확히 정의되지 않았기 때문입니다.

일부 컴파일러는이 문제를 해결할 수도 있습니다. 귀하의 코드는 실제로 리터럴에 해당하는 정적 배열을 수정할 수 있습니다 (나중에 큰 혼란을 야기 할 수 있음).

그러나 대부분의 최신 컴파일러는 실제 ROM이 아닌 가상 메모리 시스템에 의한 수정으로부터 보호되는 메모리 영역에 배열을 읽기 전용 메모리에 저장합니다. 이러한 메모리를 수정하려고하면 일반적으로 세그먼테이션 오류와 프로그램 충돌이 발생합니다.

이 아닌 문자열 리터럴 const입니까? 당신이 정말로 그것들을 수정하려고 시도해서는 안되기 때문에, 그것은 확실히 이해 될 것입니다 - 그리고 C++은 문자열 리터럴을 만듭니다 const. 그 이유는 역사적이다. const 키워드는 1989 ANSI C 표준에 소개되기 전에는 존재하지 않았습니다 (하지만 그 전에는 일부 컴파일러에서 구현되었을 수도 있음). 그래서 미리 ANSI 프로그램은 다음과 같습니다 print_string이 문자열이 s가 가리키는 수정할 수 없습니다 사실을 적용 할 수있는 방법이 없었다

#include <stdio.h> 

print_string(s) 
char *s; 
{ 
    printf("%s\n", s); 
} 

main() 
{ 
    print_string("Hello, world"); 
} 

. ANSI C에서 문자열 리터럴 const을 작성하면 기존 코드가 손상되어 ANSI C위원회에서 피하려고 매우 힘들었습니다. 그 이후로 언어에 그런 변화를 줄 수있는 좋은 기회는 없었습니다. (C++의 디자이너 인 Bjarne Stroustrup은 C와의 하위 호환성을 염려하지 않았습니다.)

+0

큰 설명 !!! – ademar111190

+1

downvoter는 설명해 주길 바래요? –

2

앞에서 말씀 드린대로 strtok은 문자열 리터럴을 수정할 수 없습니다. 당신은

char str[] = "this is a test"; 
tokenize(str); 

이 배열 str를 생성하고 this is a test\0로를 초기화합니다 및 tokenize에 그것에 대한 포인터를 전달을해야한다.

0

나는 이것에 대해 두들겨 맞을 것이라고 확신하지만 ... "strtok()"은 본질적으로 안전하지 못하고 액세스 위반과 같은 일들을하기 쉽습니다.

여기에서 대답은 거의 확실하게 문자열 상수를 사용하고 있습니다.

대신을 시도해보십시오

void tokenize(char* line) 
{ 
    char* cmd = strtok(line," "); 

    while (cmd != NULL) 
    { 
     printf ("%s\n",cmd); 
     cmd = strtok(NULL, " "); 
    } 
} 

int main(void) 
{ 
    char buff[80]; 
    strcpy (buff, "this is a test"); 
    tokenize(buff); 
} 
+1

strtok의 안전하지 않은 특성을 가져 오려면 strncpy가 strcpy보다 훨씬 안전하다는 것을 기억해야합니다. strcpy는 컴파일 타임 상수 문자열에 대해 완벽하게 안전하지만 나중에 리팩토링하면 strcpy 호출이 버퍼 오버 플로우 취약점으로 바뀔 수 있습니다. –

1

Strok 그것을 토큰 화하기 위해 첫 번째 인자를 수정합니다. 따라서 형식이 const char *이고 리터럴 문자열을 전달할 수 없으므로 수정할 수 없으므로 정의되지 않은 동작입니다. 문자열 리터럴을 수정할 수있는 char 배열로 복사해야합니다.

2

컴파일 타임 상수 문자열을 토큰 화하려고하면 세그먼트 화 오류가 발생합니다. 상수 문자열은 읽기 전용 메모리에 있습니다.

C 컴파일러는 컴파일 타임 상수 문자열을 실행 파일에 저장하고 운영 체제는이를 읽기 전용 메모리 (* nix ELF 파일의 .rodata)로로드합니다. 이 메모리는 읽기 전용으로 표시되고 strtok이 전달한 문자열에 쓰기 때문에 읽기 전용 메모리에 쓰는 세그먼트 오류가 발생합니다.

1

"...은 내부적으로 배열이 char"입니다.

"this is a test"이 내부적으로 char의 배열 인 사실은 전혀 바뀌지 않습니다. 그것은 여전히 ​​문자열 리터럴입니다 (모든 문자열 리터럴은 char의 수정 불가능한 배열입니다). strtok은 여전히 ​​문자열 리터럴을 토큰 화하려고합니다. 이것이 충돌하는 이유입니다.

0

난 후에 printf를 사용하여 Segmentation Fault 오류가 발생하여 토큰을 출력하려고 시도했습니다. (cmd) NULL이되었습니다.