sourcecode

문자열 리터럴로 초기화되었지만 "char s[]"가 아닌 "char *s"에 쓸 때 세그멘테이션 장애가 발생하는 이유는 무엇입니까?

copyscript 2022. 8. 12. 23:33
반응형

문자열 리터럴로 초기화되었지만 "char s[]"가 아닌 "char *s"에 쓸 때 세그멘테이션 장애가 발생하는 이유는 무엇입니까?

다음 코드는 회선 2에서 세그먼트 장애를 수신합니다.

char *str = "string";
str[0] = 'z';  // could be also written as *str = 'z'
printf("%s\n", str);

이 방법은 완벽하게 작동하지만,

char str[] = "string";
str[0] = 'z';
printf("%s\n", str);

MSVC 및 GCC에서 테스트 완료.

C FAQ, 질문 1.32 참조

Q: 이들 초기화 간의 차이점은 무엇입니까?
char a[] = "string literal";
char *p = "string literal";
됩니다.p[i].

A: 문자열 리터럴(C 소스의 이중 따옴표로 둘러싸인 문자열의 정식 용어)은 약간 다른 두 가지 방법으로 사용할 수 있습니다.

  1. 로서 「의 .char a[]는, 그 배열내의 문자의 초기치(필요한 경우는 그 사이즈)를 지정합니다.
  2. 그 외의 장소에서는, 이름 없는 정적 문자 배열로 바뀝니다.이 이름 없는 배열은 읽기 전용 메모리에 보존될 가능성이 있기 때문에, 반드시 변경할 필요는 없습니다.표현 컨텍스트에서 어레이는 통상대로 한 번에 포인터로 변환되므로(섹션 6 참조), 두 번째 선언은 이름 없는 어레이의 첫 번째 요소를 가리키도록 p를 초기화한다.

일부 컴파일러에는 문자열 리터럴의 쓰기 가능 여부(오래된 코드 컴파일용)를 제어하는 스위치가 있습니다.또한 일부 컴파일러에는 문자열 리터럴을 const char의 배열로 정식으로 취급하는 옵션이 있습니다(오류 검출의 향상을 위해서).

일반적으로 문자열 리터럴은 프로그램 실행 시 읽기 전용 메모리에 저장됩니다.이는 실수로 문자열 상수를 변경하는 것을 방지하기 위한 것입니다. 번째 예에서는 " " " 입니다."string"는 읽기 되어 있습니다.또, 「읽기 전용 메모리」에 격납되어 있습니다.*str첫 번째 문자를 가리킵니다.는 첫 를 segfault로 합니다.'z'.

예에서는 " " " " " " 입니다."string"컴파일러에 의해 읽기 전용 홈에서str[]배열. 그러면 첫 번째 문자를 변경할 수 있습니다.할 수 .

printf("%p", str);

또, 사이즈를 인쇄하는 것은str을 나타냅니다.

printf("%d", sizeof(str));

문자열에 쓸 때 분할 오류가 발생하는 이유는 무엇입니까?

C99 N1256 드래프트

문자열 리터럴에는 다음 두 가지 다른 용도가 있습니다.

  1. 「」를 합니다.char[]:

    char c[] = "abc";      
    

    이는 "더 많은 매직"이며, 6.7.8/14 "초기화"에서 설명합니다.

    문자열 리터럴에 의해 문자 타입의 배열을 초기화할 수 있습니다(옵션으로 중괄호로 묶음).문자열 리터럴의 연속된 문자(공간이 있거나 배열 크기를 알 수 없는 경우 끝의 늘 문자 포함)는 배열 요소를 초기화합니다.

    즉, 이것은 다음의 숏컷에 불과합니다.

    char c[] = {'a', 'b', 'c', '\0'};
    

    다른 일반 어레이와 마찬가지로c변경할 수 있습니다.

  2. 기타 모든 장소: 다음 항목이 생성됩니다.

    그래서 글을 쓸 때:

    char *c = "abc";
    

    이것은 다음과 같습니다.

    /* __unnamed is magic because modifying it gives UB. */
    static char __unnamed[] = "abc";
    char *c = __unnamed;
    

    「」의 .char[]로로 합니다.char *그것은 항상 합법이다.

    , 「」를 는,c[0] , , , , , , , , , , , , , , , , , , , , , , , , , , , , , , .__unnamed (UB).

    이는 6.4.5 "String Literals"에서 문서화되어 있습니다.

    5 변환 단계7에서는 문자열 리터럴 또는 리터럴에서 생성되는 각 멀티바이트 문자 시퀀스에 값 0의 바이트 또는 코드가 부가됩니다.그런 다음 멀티바이트 문자 시퀀스를 사용하여 시퀀스를 포함하기에 충분한 정적 스토리지 기간 및 길이의 배열을 초기화합니다.문자열 리터럴의 경우 배열 요소는 type char를 가지며 멀티바이트 문자 시퀀스의 개별 바이트로 초기화됩니다[...]

    6 이들 어레이의 요소가 적절한 값을 가지고 있는 경우 이들 어레이가 구별되는지 여부는 명시되어 있지 않습니다.프로그램이 이러한 배열을 수정하려고 하면 동작은 정의되지 않습니다.

6.7.8/32 "초기화"는 다음과 같은 직접적인 예를 제시합니다.

예 8: 선언

char s[] = "abc", t[3] = "abc";

"" objects ": "defined" char "defined" char 。s ★★★★★★★★★★★★★★★★★」t이치노

이 선언은 다음 선언과 동일합니다.

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

어레이의 내용은 변경할 수 있습니다.한편, 선언문은

char *p = "abc";

정의하다p char하고, 그 리터럴로 되어 있는 의 「 of char라고 하는 합니다.「」를 하려고 했을 .p어레이의 내용을 수정하는 동작은 정의되어 있지 않습니다.

GCC 4.8 x86-64 ELF 구현

프로그램:

#include <stdio.h>

int main(void) {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

컴파일 및 디컴파일:

gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o

출력 내용:

 char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata

: 스토어 : GCC 스토어char* 있다.rodata에 없음, 에 없습니다..text.

char[]:

 char s[] = "abc";

입수처:

17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)

스택에 예:%rbp를 참조해 주세요.

, 에는 ", " "가 기재되어 ..rodata ★★★★★★★★★★★★★★★★★」.text실행은 되지만 쓰기 권한이 없는 세그먼트(segment)에 있습니다.이는 다음 방법으로 확인할 수 있습니다.

readelf -l a.out

다음 내용이 포함됩니다.

 Section to Segment mapping:
  Segment Sections...
   02     .text .rodata

이 답변들은 대부분 맞지만 좀 더 명확히 하기 위해...

사람들이 말하는 '읽기 전용 메모리'는 ASM 용어로 텍스트세그먼트입니다메모리의 명령어가 로드되어 있는 장소와 같습니다.이것은 보안과 같은 명백한 이유로 읽기 전용입니다.문자열로 초기화된 문자*를 만들면 문자열 데이터가 텍스트 세그먼트로 컴파일되고 프로그램에서 텍스트 세그먼트를 가리키도록 포인터가 초기화됩니다.그래서 바꾸려고 하면 카붐이야.세그먼트 폴트

배열로 작성되면 컴파일러는 대신 초기화된 문자열 데이터를 데이터 세그먼트에 배치합니다. 데이터 세그먼트는 글로벌 변수와 같은 위치입니다.이 메모리는 데이터 세그먼트(segment)에 명령이 없기 때문에 변경할 수 있습니다.이 시점에서 컴파일러는 문자 배열(여전히 문자*일 뿐)을 초기화할 때 텍스트 세그먼트가 아닌 데이터 세그먼트를 가리키고 있습니다.이 세그먼트는 런타임에 안전하게 변경할 수 있습니다.

char *str = "string";  

은 「」입니다.str 그대로를 "string"프로그램의 바이너리 이미지에 하드 코딩되어 있으며, 메모리에서 읽기 전용으로 플래그가 설정되어 있을 가능성이 있습니다.

★★★★★★★★★★★★★★★★★.str[0]=응용 프로그램의 읽기 전용 코드에 쓰려고 합니다.아마 컴파일러에 의존한 것 같습니다.

고정 메모리

문자열 리터럴은 기본적으로 읽기 전용이므로 메모리의 상수 부분에 저장됩니다.저장된 데이터는 변경할 수 없습니다. 즉, 변경할 수 없습니다.따라서 C 코드에 정의되어 있는 모든 문자열 리터럴은 여기서 읽기 전용 메모리주소를 취득합니다.

스택 메모리

메모리의 Stack 부분은 로컬 변수(예: 함수에 정의된 변수)의 주소가 존재하는 부분입니다.


@matli의 답변에서 알 수 있듯이 이 일정한 문자열을 사용하는 방법에는 두 가지가 있습니다.

(1) 문자열 리터럴로의 포인터

문자열 리터럴에 대한 포인터를 정의할 때는 스택메모리에 존재하는 포인터 변수를 만듭니다.기본 문자열 리터럴이 있는 읽기 전용 주소를 가리킵니다.

#include <stdio.h>

int main(void) {
  char *s = "hello";
  printf("%p\n", &s);  // Prints a read-only address, e.g. 0x7ffc8e224620
  return 0;
}

「 」를 ,s

s[0] = 'H';

는 우 a a a a a를 받는다.Segmentation fault (core dumped)접근해서는 안 되는 메모리에 접근하려고 합니다. 주소의 인 「읽기 전용 주소」의 있습니다.0x7ffc8e224620.

(2) 문자 배열

리터럴을 '''로 합니다."Hello"되어 있는 전용 를 가지고 있습니다.0x7ffc8e224620.

#include <stdio.h>

int main(void) {
  // We create an array from a string literal with address 0x7ffc8e224620.
  // C initializes an array variable in the stack, let's give it address
  // 0x7ffc7a9a9db2.
  // C then copies the read-only value from 0x7ffc8e224620 into 
  // 0x7ffc7a9a9db2 to give us a local copy we can mutate.
  char a[] = "hello";

  // We can now mutate the local copy
  a[0] = 'H';

  printf("%p\n", &a);  // Prints the Stack address, e.g. 0x7ffc7a9a9db2
  printf("%s\n", a);   // Prints "Hello"

  return 0;
}

주의: 1과 같이 문자열 리터럴 포인터를 사용하는 경우 베스트 프랙티스는const 「 」, 「 」등)const *s = "hello"이것은 읽기 쉽고 컴파일러가 위반했을 때 도움이 됩니다..error: assignment of read-only location ‘*s’ faultseg fault. fault가 아닌 fault.seg fault가 아닌에디터의 린터도 코드를 수동으로 컴파일하기 전에 에러를 검출할 가능성이 있습니다.

첫 번째는 수정할 수 없는 하나의 상수 문자열입니다.두 번째는 값이 초기화된 배열이므로 수정할 수 있습니다.

액세스 할 수 없는 메모리에 액세스 하려고 하면, 세그먼트화 장해가 발생합니다.

char *str는 변경할 수 없는 문자열에 대한 포인터입니다(세그 폴트를 취득한 이유).

, 「」입니다.char str[]는 어레이이며 변경할 수 있습니다.

현악기들은,

char a[] = "string literal copied to stack";
char *p  = "string literal referenced by p";

첫 번째 경우 리터럴은 'a'가 범위에 들어오면 복사됩니다.여기서 'a'는 스택에 정의된 배열입니다.즉, 스트링이 스택에 작성되고 그 데이터는 코드(텍스트) 메모리에서 복사됩니다.이것은 통상 읽기 전용입니다(이것은 실장 마다 다릅니다.컴파일러는 읽기 전용의 프로그램 데이터를 읽기 가능 메모리에 격납할 수도 있습니다).

두 번째 경우 p는 스택(로컬 스코프)에 정의되어 다른 위치에 저장된 문자열 리터럴(프로그램 데이터 또는 텍스트)을 참조하는 포인터입니다.일반적으로 이러한 기억을 수정하는 것은 좋은 관행도 권장도 되지 않습니다.

첫 번째 코드에서 "string"은 문자열 상수이며 문자열 상수는 읽기 전용 메모리에 배치되는 경우가 많기 때문에 절대 수정하지 마십시오."str"는 상수를 수정하기 위해 사용되는 포인터입니다.

두 번째 코드에서 "string"은 어레이 이니셜라이저로, 일종의 짧은 손입니다.

char str[7] =  { 's', 't', 'r', 'i', 'n', 'g', '\0' };

"str"는 스택에 할당된 배열로 자유롭게 변경할 수 있습니다.

의 of의 "whatever"는 " " 입니다.const char *(비정수 문자*에 할당하는 경우에도), 즉, 이 문자에는 쓰지 않는 것이 좋습니다.

컴파일러는 이 스트링을 메모리의 읽기 전용 부분에 삽입함으로써 segfault를 생성합니다.

이 오류 또는 문제를 이해하려면 먼저 포인터와 어레이의 차이를 알아야 합니다.그러므로 먼저 차이점을 설명하겠습니다.

문자열 배열

 char strarray[] = "hello";

memory array로 memory array로 된다.[h][e][l][l][o][\0] =>[]는 1 이 연속 은 strarray so 라는 할 수 있습니다.서 strarray 1 char, strarray here.so 는 strarray here.so 입니다.이치노strarrayin 에되어 있습니다.이 경우는, it.in 를 참조해 주세요.네, 네, 네, 네, 네, 네, 네, 네, 기기를요요요요요요 。"hello" 각 하여 각 문자에 으로써 메모리 내용을 쉽게 할 수 .

`strarray[0]='m'` it access character at index 0 which is 'h'in strarray

값이 요.'m'이 strarray로 되었습니다."mello";

할 점은 할 수 있지만 다른 할 수는 입니다.strarray="new string"입니다.

포인터

포인터가 메모리 내의 메모리 위치를 가리키고 있는 것을 알고 있듯이 초기화되지 않은 포인터는 랜덤 메모리 위치를 가리키기 때문에 초기화 후에는 특정 메모리 위치를 가리킵니다.

char *ptr = "hello";

은 문자열 ptr로 됩니다."hello"은 읽기 메모리에 저장되어 있기 에 ROM(Read Only Memory)에 저장되어 있습니다."hello"ROMOM에되어 있기 할 수 .

되어 고정 ptr을 가리키고 ."hello"

읽기 전용 메모리에 액세스할 수 없으므로 ptr[0]='m'이 잘못되었습니다.

그러나 ptr은 포인터일 뿐이므로 다른 문자열 값으로 직접 초기화할 수 있으므로 데이터 유형 변수의 메모리 주소를 가리킬 수 있습니다.

ptr="new string"; is valid
char *str = "string";

포인터를 문자열 리터럴에 할당합니다.이 문자열 리터럴은 컴파일러가 실행 파일의 수정할 수 없는 부분에 삽입합니다.

char str[] = "string";

변경 가능한 로컬 어레이를 할당 및 초기화합니다.

@matli가 링크한 C FAQ에 기재되어 있습니다만, 아직 여기에 아무도 기재되어 있지 않습니다.구체적인 설명을 위해서, 문자열 리터럴(소스내의 큰따옴표로 둘러싸인 문자열)이 문자 배열을 초기화하기 위해서(즉, 올바르게 동작하는 @Mark의 두 번째 예) 이외의 장소에서 사용되고 있는 경우, 그 문자열은 컴파일러에 의해 특별한 정적 문자열 테이블에 저장됩니다.기본적으로 익명(변수 "이름"이 없음)인 글로벌 정적 변수(물론 읽기 전용)를 만드는 것과 관련이 있습니다.읽기 전용 부분은 중요한 부분이며, 이것이 @Mark의 첫 번째 코드 예시 segfaults입니다.

 char *str = "string";

line은 포인터를 정의하고 리터럴 문자열을 가리킵니다.리터럴 문자열은 쓸 수 없으므로 쓸 수 있는 경우:

  str[0] = 'z';

세그먼트(seg)에 오류가 발생합니다.일부 플랫폼에서는 리터럴이 쓰기 가능한 메모리에 있기 때문에 세그먼트 폴트가 표시되지 않을 수 있습니다.그러나 이 리터럴은 유효하지 않은 코드(정의되지 않은 동작의 결과)입니다.

행:

char str[] = "string";

는 문자 배열을 할당하고 리터럴 문자열을 해당 배열에 복사합니다.이 배열은 완전히 쓰기 가능하므로 후속 업데이트는 문제 없습니다.

"string"과 같은 문자열 리터럴은 실행 파일의 주소 공간에 읽기 전용 데이터(give or take)로 할당될 수 있습니다.만지면 수영복 구역이 이상해져 세그먼트 결함으로 알립니다.

첫 번째 예에서는 해당 Const 데이터에 대한 포인터를 얻을 수 있습니다.두 번째 예에서는 const 데이터 복사본을 사용하여 7자 배열을 초기화합니다.

// create a string constant like this - will be read only
char *str_p;
str_p = "String constant";

// create an array of characters like this 
char *arr_p;
char arr[] = "String in an array";
arr_p = &arr[0];

// now we try to change a character in the array first, this will work
*arr_p = 'E';

// lets try to change the first character of the string contant
*str_p = 'G'; // this will result in a segmentation fault. Comment it out to work.


/*-----------------------------------------------------------------------------
 *  String constants can't be modified. A segmentation fault is the result,
 *  because most operating systems will not allow a write
 *  operation on read only memory.
 *-----------------------------------------------------------------------------*/

//print both strings to see if they have changed
printf("%s\n", str_p); //print the string without a variable
printf("%s\n", arr_p); //print the string, which is in an array. 

★★★★★★★★★★★★★★★★★★.str라고 하는 것은 입니다."string"이것으로 가 트리거되었을 입니다.은, 「」를 이므로, 정말가 있습니다.★★★★★★★★★★★★★★★★★★,const char * a까지char * 경고가 비활성화되어 있습니까?아니면 그냥 무시한 것입니까?)

만들고, 그 배열을 액세스할 수 합니다.이것은 완전한 액세스권을 가진 메모리입니다.이 어레이는"string"를 작성하다char[7](글자에 6개, 끝의 '0'에 1개), 원하는 대로 사용할 수 있습니다.

Section 5.5 Character Pointers and FunctionsK&R에서는 다음 토픽에 대해서도 설명합니다.

이들 정의에는 중요한 차이가 있습니다.

char amessage[] = "now is the time"; /* an array */
char *pmessage = "now is the time"; /* a pointer */

amessage배열로, 문자 시퀀스를 저장할 수 있을 정도로 크고,'\0'초기화됩니다.어레이 내의 개개의 문자를 변경할 수 있지만amessage는 항상 같은 스토리지를 참조합니다.반면에,pmessage는 문자열 상수를 가리키도록 초기화된 포인터입니다.이 후 포인터는 다른 곳을 가리키도록 변경될 수 있지만 문자열 내용을 변경하려고 하면 결과는 정의되지 않습니다.

언급URL : https://stackoverflow.com/questions/164194/why-do-i-get-a-segmentation-fault-when-writing-to-a-char-s-initialized-with-a

반응형