내가 다음 코드 조각은 세그먼트 오류를주는 이유를 이해하려고 노력하고는 strtok를 내부적으로 char
의 배열 인 문자열 "this is a test"
을 직접 가리 킵니다. 배열에 복사하지 않고 토큰 화 line
이 있습니까?세그먼트 오류
세그먼트 오류
답변
문제는 문자열 리터럴을 수정하려는 것입니다. 이렇게하면 프로그램 동작이 정의되지 않습니다.
문자열 리터럴을 수정할 수 없다고하면 단순화됩니다. 해당 문자열 리터럴이 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와의 하위 호환성을 염려하지 않았습니다.)
큰 설명 !!! – ademar111190
downvoter는 설명해 주길 바래요? –
앞에서 말씀 드린대로 strtok
은 문자열 리터럴을 수정할 수 없습니다. 당신은
char str[] = "this is a test";
tokenize(str);
이 배열 str
를 생성하고 this is a test\0
로를 초기화합니다 및 tokenize
에 그것에 대한 포인터를 전달을해야한다.
나는 이것에 대해 두들겨 맞을 것이라고 확신하지만 ... "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);
}
strtok의 안전하지 않은 특성을 가져 오려면 strncpy가 strcpy보다 훨씬 안전하다는 것을 기억해야합니다. strcpy는 컴파일 타임 상수 문자열에 대해 완벽하게 안전하지만 나중에 리팩토링하면 strcpy 호출이 버퍼 오버 플로우 취약점으로 바뀔 수 있습니다. –
Strok 그것을 토큰 화하기 위해 첫 번째 인자를 수정합니다. 따라서 형식이 const char *
이고 리터럴 문자열을 전달할 수 없으므로 수정할 수 없으므로 정의되지 않은 동작입니다. 문자열 리터럴을 수정할 수있는 char 배열로 복사해야합니다.
컴파일 타임 상수 문자열을 토큰 화하려고하면 세그먼트 화 오류가 발생합니다. 상수 문자열은 읽기 전용 메모리에 있습니다.
C 컴파일러는 컴파일 타임 상수 문자열을 실행 파일에 저장하고 운영 체제는이를 읽기 전용 메모리 (* nix ELF 파일의 .rodata)로로드합니다. 이 메모리는 읽기 전용으로 표시되고 strtok이 전달한 문자열에 쓰기 때문에 읽기 전용 메모리에 쓰는 세그먼트 오류가 발생합니다.
"...은 내부적으로 배열이 char
"입니다.
"this is a test"
이 내부적으로 char
의 배열 인 사실은 전혀 바뀌지 않습니다. 그것은 여전히 문자열 리터럴입니다 (모든 문자열 리터럴은 char의 수정 불가능한 배열입니다). strtok
은 여전히 문자열 리터럴을 토큰 화하려고합니다. 이것이 충돌하는 이유입니다.
난 후에 printf를 사용하여 Segmentation Fault 오류가 발생하여 토큰을 출력하려고 시도했습니다. (cmd
) NULL이되었습니다.
Dude - "this is a test"는 STRING LITERAL입니다. 의미는 * READ ONLY * "char of array"입니다. 특정 플랫폼에서 충돌하지 않고 수정하려고 시도 할 수도 있습니다. 그러나 확실히 ANY * 플랫폼에 대한 no-no입니다. – paulsm4