표시되는 이유는 무엇입니까 세그먼트 오류를 작성할 때 문자열과 함께 초기화"char*s"이지만"char s[]"?

StackOverflow https://stackoverflow.com/questions/164194

  •  03-07-2019
  •  | 
  •  

문제

다음 코드를 받 seg 에 오류 line2:

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 및 프로그램입니다.

도움이 되었습니까?

해결책

보 C FAQ 질문 1.32

Q:의 차이점은 무엇입한 이러한 초기화가?
char a[] = "string literal";
char *p = "string literal";
내 프로그램으로 충돌하려 하는 경우에 새 값을 할당 p[i].

A:문자열 리터럴이(공식적인 용어 에 대한 따옴표 문자열 C 소스)을 사용할 수 있습에서 두 개의 약간 다른 방법:

  1. 으로의 이니셜라이저 배열의 char,으로서의 선언 char a[] 지정합니다 초기 값 의 캐릭터에서는 array(고, 필요한 경우,그 크기).
  2. 다른 곳에서,그것으로 이름,정체되는 문자 배열, 이름 배열이 저장될 수 있습니다 기 전에,메모리,그리고는 따라서 반드시 없 수정했습니다.식 컨텍스트에, 배열 변환 한 번에 포인터로,평소(섹션 참조 6),그래서 두 번째는 선언을 초기화합니다 p 하여름 편의 첫번째 요소입니다.

일부 컴파일러가 있 스위치 여부를 제어하는 문자열 은 쓰기 또는지(컴파일을 위한 세 코드),으며,일부는 비공개일 수 있습 옵션 원인 문자열을 공식적으로 으로 처리되는 배열의 const char(한 더 오류를 잡기).

다른 팁

일반적으로 문자열 리터럴은 프로그램이 실행될 때 읽기 전용 메모리에 저장됩니다. 이것은 실수로 문자열 상수를 변경하는 것을 방지하기위한 것입니다. 첫 번째 예에서 "string" 읽기 전용 메모리에 저장됩니다 *str 첫 번째 캐릭터를 가리 킵니다. Segfault는 첫 번째 캐릭터를 변경하려고 할 때 발생합니다. 'z'.

두 번째 예에서는 문자열입니다 "string" ~이다 복사 읽기 전용 가정에서 str[] 정렬. 그런 다음 첫 번째 문자를 변경하는 것이 허용됩니다. 각각의 주소를 인쇄하여 확인할 수 있습니다.

printf("%p", str);

또한 크기를 인쇄합니다 str 두 번째 예에서는 컴파일러가 7 바이트를 할당했음을 보여줍니다.

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

이 답변의 대부분은 정확하지만 조금 더 선명도를 더하기 위해 ...

사람들이 언급하는 "메모리 전용 메모리 읽기"는 ASM 용어의 텍스트 세그먼트입니다. 지침이로드되는 메모리와 같은 장소입니다. 이것은 보안과 같은 명백한 이유로 읽기 전용입니다. 문자열에 초기화 된 char*를 만들 때 문자열 데이터가 텍스트 세그먼트로 컴파일되고 프로그램은 포인터를 초기화하여 텍스트 세그먼트를 가리 킵니다. 따라서 변경하려고하면 Kaboom. Segfault.

배열로 작성된 경우 컴파일러는 초기화 된 문자열 데이터를 대신 데이터 세그먼트에 배치합니다. 이는 전역 변수 및 해당 라이브와 동일합니다. 이 메모리는 데이터 세그먼트에 지침이 없기 때문에 변이 가능합니다. 이번에는 컴파일러가 문자 배열을 초기화 할 때 (여전히 Char*) 텍스트 세그먼트 대신 데이터 세그먼트를 가리키며 런타임에 안전하게 변경할 수 있습니다.

문자열에 쓸 때 왜 분할 결함을 얻습니까?

C99 N1256 초안

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

  1. 초기화 char[]:

    char c[] = "abc";      
    

    이것은 "더 많은 마법"이며 6.7.8/14 "초기화"에 설명되어 있습니다.

    문자 유형의 배열은 문자열 문자 문자로 초기화 될 수 있으며, 선택적으로 교정기로 둘러싸여 있습니다. 캐릭터 문자열 리터럴의 연속 문자 (공간이 있거나 배열이 알려지지 않은 경우 종단 null 문자 포함) 배열의 요소를 초기화하십시오.

    따라서 이것은 단지 바로 가기입니다.

    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 "문자 리터럴"으로 문서화되어 있습니다.

    5 번역 단계 7에서, 바이트 또는 값 코드 0은 문자열 리터럴 또는 리터럴에서 비롯된 각 다중 바이트 문자 시퀀스에 추가됩니다. 그런 다음 멀티 바이트 문자 시퀀스는 정적 저장 시간의 배열을 초기화하는 데 사용됩니다. 캐릭터 문자 리터럴의 경우, 배열 요소는 유형 숯을 가지며 멀티 바이트 문자 시퀀스의 개별 바이트로 초기화됩니다 [...

    6 요소가 적절한 값을 가지고 있다면이 배열이 뚜렷한 지 지정되지 않습니다. 프로그램이 이러한 배열을 수정하려고하면 동작이 정의되지 않습니다.

6.7.8/32 "초기화"는 직접적인 예를 제공합니다.

예 8 : 선언

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

"일반"숯 배열 개체를 정의합니다 s 그리고 t 그의 요소는 문자열 리터럴로 초기화됩니다.

이 선언은 동일합니다

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

배열의 내용은 수정 가능합니다. 반면에, 선언

char *p = "abc";

정의합니다 p "포인터에 대한 포인터"유형의 경우 초기 초기에 초기화 된 "char"유형의 객체를 가리키며 길이 4가있는 요소가 문자열 문자 문자로 초기화됩니다. 사용 시도가 이루어지면 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 실행되었지만 쓰기 권한이없는 동일한 세그먼트에서. 이것은 다음과 같이 관찰 할 수 있습니다.

readelf -l a.out

포함하는:

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

첫 번째 코드에서 "String"은 문자열 상수이며 문자열 상수는 종종 읽기 전용 메모리에 배치되므로 수정해서는 안됩니다. "STR"은 상수를 수정하는 데 사용되는 포인터입니다.

두 번째 코드에서 "String"은 배열 이니셜 라이저이며, 짧은 손입니다.

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

"str"는 스택에 할당 된 배열이며 자유롭게 수정할 수 있습니다.

유형이기 때문에 "whatever" 첫 번째 예제의 맥락에서 const char * (당신이 그것을 맹렬한 숯*에 할당하더라도), 당신은 그것을 시도하고 쓰지 말아야한다는 것을 의미합니다.

컴파일러는 문자열을 메모리의 읽기 전용 부분에 넣음으로써이를 시행하므로 Segfault가 생성됩니다.

이 오류 또는 문제를 이해하려면 먼저 차이 B/W를 알아야합니다. 포인터와 배열은 먼저 여기에 차이점을 설명합니다.

문자열 배열

 char strarray[] = "hello";

메모리 배열에서 연속 메모리 셀에 저장되며 [h][e][l][l][o][\0] =>[] 1 숯 바이트 크기 메모리 셀 이며이 연속 메모리 셀은 Strarray라는 이름으로 액세스 할 수 있습니다. strarray 문자열의 모든 문자가 포함 된 자체가 초기화되었습니다. "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" read only memory (rom)에 저장된 상수 문자열이므로 "hello" ROM에 저장되어 변경할 수 없습니다.

그리고 PTR은 스택 섹션에 저장되고 상수 문자열을 가리키는 것입니다. "hello"

따라서 ptr [0] = 'm'은 읽기 전용 메모리에 액세스 할 수 없으므로 유효하지 않습니다.

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

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

위의 세트 str 문자 그대로의 가치를 가리 킵니다 "string" 프로그램의 이진 이미지에서 하드 코딩되어 있으며 메모리에서 읽기 전용으로 표시 될 수 있습니다.

그래서 str[0]= 응용 프로그램의 읽기 전용 코드에 편지를 쓰려고합니다. 나는 이것이 아마도 컴파일러 의존적 일 것이라고 생각한다.

char *str = "string";

문자열 리터럴에 대한 포인터를 할당하며, 컴파일러는 실행 파일의 수정 불가능한 부분을 넣습니다.

char str[] = "string";

수정 가능한 로컬 어레이를 할당하고 초기화합니다

@matli가 언급 한 C FAQ는 언급했지만 아직 다른 사람은 아직 없습니다. 설명을 위해 : 문자열 리터럴 (소스의 두 배에 따른 문자열)이 어디서나 사용되는 경우 이것 말고도 문자 배열 (예 : @Mark의 두 번째 예제, 올바르게 작동)을 초기화하려면 해당 문자열이 컴파일러에 의해 스페셜로 저장됩니다. 정적 문자열 테이블, 이는 본질적으로 익명 (변수 "이름"이없는 글로벌 정적 변수) (물론 읽기 전용)을 만드는 것과 유사합니다. 그만큼 읽기 전용 부분은 중요한 부분이며, @Mark의 첫 번째 코드 예제가 Segfaults의 이유입니다.

그만큼

 char *str = "string";

선은 포인터를 정의하고 문자 그대로 문자열을 가리 킵니다. 문자 그대로 문자열은 쓸 수 없습니다.

  str[0] = 'z';

당신은 seg 결함을 얻습니다. 일부 플랫폼에서는 리터럴이 쓰기 가능한 메모리에있을 수 있으므로 segfault가 표시되지 않지만, 유효하지 않은 코드 (정의되지 않은 동작 결과).

라인 :

char str[] = "string";

캐릭터 배열을 할당합니다 사본 해당 배열에 문자열이 완전히 쓰여질 수 있으므로 후속 업데이트는 문제가되지 않습니다.

"문자열"과 같은 문자열 리터럴은 실행 파일의 주소 공간에 읽기 전용 데이터 (컴파일러를 주거나 가져 가면)로 할당 될 수 있습니다. 당신이 그것을 만질 때, 그것은 당신이 수영복 지역에 있다는 사실을 놀라게하고 Seg 결함으로 알려줍니다.

첫 번째 예에서는 해당 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". 당신은 a를 만들고 있습니다 char[7] (문자의 경우 6 개, 하나는 종료 된 ' 0'), 당신은 당신이 좋아하는 모든 일을합니다.

줄이

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

첫 번째 경우, 'a'가 범위에 들어올 때 문자를 복사해야합니다. 여기 'A'는 스택에 정의 된 배열입니다. 이는 문자열이 스택에 생성되고 데이터가 코드 (텍스트) 메모리에서 복사되며 일반적으로 읽기 전용입니다 (이 구현 구체에 따라 컴파일러는이 읽기 전용 프로그램 데이터를 읽기 작성 가능한 메모리에도 배치 할 수 있습니다. ).

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

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

TYR이 액세스 할 수없는 메모리에 액세스하기 위해 TYR이 발생합니다.

char *str 수정할 수없는 문자열에 대한 포인터입니다 (Seg 결함을 얻는 이유) ..

반면 char str[] 배열이며 수정할 수 있습니다 ..

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top