programing

chars [ ]와 char *의 차이점은 무엇입니까?

randomtip 2022. 9. 11. 16:43
반응형

chars [ ]와 char *의 차이점은 무엇입니까?

C에서는 다음과 같은 선언으로 문자열 리터럴을 사용할 수 있습니다.

char s[] = "hello";

또는 다음과 같습니다.

char *s = "hello";

그럼 차이점은 무엇일까요?컴파일 시 및 실행 시 스토리지 지속 시간 측면에서 실제로 어떤 일이 일어나는지 알고 싶습니다.

여기서의 차이점은 이다.

char *s = "Hello world";

"Hello world"기억의 읽기 전용 부분과s이 메모리에 대한 쓰기 작업을 불법으로 만드는 포인터입니다.

실행 중:

char s[] = "Hello world";

는, 리터럴 문자열을 읽기 전용 메모리에 격납해, 스택상에서 새롭게 할당된 메모리에 그 문자열을 카피합니다.이렇게 해서

s[0] = 'J';

합법적인.

우선 함수 인수에서 이들은 정확히 동일합니다.

void foo(char *x);
void foo(char x[]); // exactly the same in all respects

문맥에서는 in른 in in in in inchar *는 포인터를 「」는 「」로 합니다.char []을 사용하다앞의 케이스에서는 끈이 어디에 들어가느냐고 묻는다.컴파일러는 문자열 리터럴을 유지하기 위해 정적 익명 배열을 비밀리에 할당합니다. ★★★★★★★★★★★★★★★★:

char *x = "Foo";
// is approximately equivalent to:
static const char __secret_anonymous_array[] = "Foo";
char *x = (char *) __secret_anonymous_array;

이 포인터를 사용하여 이 익명 배열의 내용을 수정하려고 하면 안 됩니다. 효과는 정의되어 있지 않습니다(대부분 크래시를 의미합니다.

x[1] = 'O'; // BAD. DON'T DO THIS.

어레이 구문을 사용하면 어레이가 새 메모리에 직접 할당됩니다.따라서 수정은 안전합니다.

char x[] = "Foo";
x[1] = 'O'; // No problem.

, 에서 이 대신에, 「 」, 「 」, 「 」, 「 」, 「 」, 「 」strdup()또는 이와 유사합니다.어레이가 글로벌 범위에서 할당되어 있는 경우는 물론 문제 없습니다.

이 선언:

char s[] = "hello";

하나의 오브젝트를 만듭니다.achar 배열이라고 6사이즈의 s값 「」으로이 되어 있습니다.'h', 'e', 'l', 'l', 'o', '\0'이 어레이가 메모리에 할당되어 있는 장소와 지속 시간은 선언이 표시되는 위치에 따라 달라집니다.선언이 함수 내에 있는 경우 선언이 선언된 블록의 마지막까지 존속하며, 거의 확실하게 스택에 할당됩니다.함수 외부에 있는 경우 선언은 프로그램 실행 시 실행 파일에서 쓰기 가능한 메모리로 로드되는 "초기화된 데이터 세그먼트" 에 저장됩니다.

한편, 이 선언은 다음과 같습니다.

char *s ="hello";

개의 개체를 만듭니다.

  • 읽기 전용 배열 6개char " " " 가 되어 있습니다.'h', 'e', 'l', 'l', 'o', '\0'이름은 없고 정적 저장 기간(프로그램의 전체 수명 동안 지속된다는 의미)이 있습니다.
  • s이름 없는 읽기 전용 배열의 첫 번째 문자 위치로 초기화됩니다.

이름 없는 읽기 전용 배열은 일반적으로 프로그램의 "텍스트" 세그먼트에 위치합니다. 즉, 디스크에서 읽기 전용 메모리로 코드 자체와 함께 로드됩니다. " " " "s메모리의 포인터 변수는 선언이 나타나는 위치에 따라 달라집니다(첫 번째 예시와 같음).

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.

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

readelf -l a.out

다음 내용이 포함됩니다.

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

char[]:

 char s[] = "abc";

입수처:

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

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

선언에 따라

char *s0 = "hello world";
char s1[] = "hello world";

맵 ~ 3 의 를 들어, 이 컬럼은, 「Dada」의 행 주소로부터 오프셋0 ~ 3 의 문자를 나타내고 있습니다).★★★★★★★★★★★★★★★★★,0x00에 주소가 .0x0001000C + 3=0x0001000F

+0    +1    +2    +30x00008000: 'h' 'e' 'l' 'l'0x00008004: 'o' 'w' 'o'0x00008008: 'r' 'l' 'd' 0x00...
s0: 0x00010000: 0x00 0x00 0x80 0x00s1: 0x00010004: 'h' 'e' 'l'0x00010008: 'o' 'w' 'o'0x0001000C: 'r' 'l' 'd' 0x00

리터럴 " " ""hello world"로 되어 있습니다.char )const char(C++)의 경우, 정적 스토리지 지속 시간을 사용합니다.즉, 프로그램 시작 시 메모리가 할당되고 프로그램이 종료될 때까지 할당된 상태로 유지됩니다.문자열 리터럴의 내용을 수정하려고 하면 정의되지 않은 동작이 호출됩니다.

회선

char *s0 = "hello world";

정의하다s0charduration ( 「」를 합니다)s0는 선언된 범위에 대해서만 존재하며 스트링 리터럴의 주소를 복사합니다(0x00008000(이 예에서는) 에 접속합니다.「 」이후로 해 주세요.s0리터럴을 이 문자열 리터럴을에 대한 인수를 들어, 「」는 「」등)로 사용할 수 .strtok(),strcat(),strcpy()등 ) 。

회선

char s1[] = "hello world";

정의하다s1로 12자 char(length는 문자열 리터럴에서 가져온 길이) 자동 저장 기간을 사용하여 리터럴의 내용을 배열에 복사합니다.메모리 맵에서 알 수 있듯이 스트링의 복사본이 2개 있습니다."hello world"의 차이는 에 할 수 있다는 입니다. ; 에 포함되어 있는 문자열은 변경할 수 .s1.

s0 ★★★★★★★★★★★★★★★★★」s1컨텍스트에서 .을 사용하다

sizeof s0 == sizeof (char*)
sizeof s1 == 12

type of &s0 == char **
type of &s1 == char (*)[12] // pointer to a 12-element array of char

할 수 .s0다른 문자열 리터럴 또는 다른 변수를 가리킵니다.할 수 .s1하다

차이에 대한 예:

printf("hello" + 2); //llo
char a[] = "hello" + 2; //error

첫 번째 경우 포인터 산술이 동작하고 있습니다(포인터에 대한 함수 붕괴로 전달된 배열).

char s[] = "hello";

s로 늘어서 char를 유지할 수 입니다.char및합니다.

char *s = "hello";

s의 포인터가char는, 문자 의 「읽기 전용」을한 고정 전용)."hello".

char *s1 = "Hello world"; // Points to fixed character string which is not allowed to modify
char s2[] = "Hello world"; // As good as fixed array of characters in string so allowed to modify

// s1[0] = 'J'; // Illegal
s2[0] = 'J'; // Legal
char s[] = "Hello world";

서서,,s원하는 경우 덮어쓸 수 있는 문자 배열입니다.

char *s = "hello";

이 내의 에 이러한 하기 위해 됩니다.s을 사용하다여기서 오브젝트가 가리키는 오브젝트를 변경할 수 있습니다.단, 스트링 리터럴을 가리키고 있는 한, 그 오브젝트가 가리키는 문자의 블록은 변경할 수 없습니다.

여기서의 코멘트에 비추어 보면, char * s = "hello" ; 이것은 나쁜 생각이며 매우 좁은 범위에서 사용해야 합니다.

이는 "일관적 정확성"이 "좋은 일"이라는 것을 지적할 수 있는 좋은 기회일 수 있습니다.가능한 한 언제 어디서나 "const" 키워드를 사용하여 포인터가 재생될 때 가장 "완전"인 발신자 또는 프로그래머로부터 코드를 보호합니다.

멜로드라마는 충분합니다.여기서 포인터를 "const"로 장식할 때 얻을 수 있는 것은 다음과 같습니다.(주의:포인터 선언을 오른쪽에서 왼쪽으로 읽어야 한다.)다음은 포인터를 가지고 놀 때 자신을 보호하는 세 가지 방법입니다.

const DBJ* p means "p points to a DBJ that is const" 

즉, DBJ 오브젝트는 p를 통해 변경할 수 없습니다.

DBJ* const p means "p is a const pointer to a DBJ" 

즉, p를 통해 DBJ 객체를 변경할 수는 있지만 p 자체를 변경할 수는 없습니다.

const DBJ* const p means "p is a const pointer to a const DBJ" 

즉, 포인터 p 자체를 변경할 수도 없고 p를 통해 DBJ 객체를 변경할 수도 없습니다.

시도된 상수 돌연변이와 관련된 오류는 컴파일 시 포착됩니다.const 실행 시 공간이나 속도 패널티는 없습니다.

(물론 C++ 컴파일러를 사용하고 있다고 가정합니다.)

--DBJ

가 같기 에, 「」, 「따라서」, 「따라서」, 「따라서」, 「」의 어느 쪽인가로 , 할 수것을 고려해 .[] ★★★★★★★★★★★★★★★★★」*(<var> + <index>)★★★★

printf("%c", x[1]);     //Prints r

그리고:

printf("%c", *(x + 1)); //Prints r

물론, 당신이 하려고 한다면

*(x + 1) = 'a';

읽기 전용 메모리에 액세스하려고 하면 Segmentation Fault가 발생할 수 있습니다.

덧붙이자면, 사이즈에 따라 다른 값을 얻을 수 있습니다.

printf("sizeof s[] = %zu\n", sizeof(s));  //6
printf("sizeof *s  = %zu\n", sizeof(s));  //4 or 8

와 같이 '\0'최종 요소로 할당됩니다.

char *str = "Hello";

위의 str은 프로그램의 바이너리 이미지로 하드 코드 되어 있는 리터럴 값 "Hello"를 가리키도록 설정되어 있습니다.이것은, 이 String 리터럴의 변경이 부정하고, 세그먼트화의 장해가 발생하는 것을 의미합니다.

char str[] = "Hello";

는 스택 상에서 새로 할당된 메모리에 문자열을 복사합니다.따라서 그것을 변경하는 것은 허용되고 합법적이다.

means str[0] = 'M';

스트링을 "Mello"로 변경합니다.

상세한 것에 대하여는, 같은 질문을 해 주세요.

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

다음과 같은 경우:

char *x = "fred";

x는 l값입니다.연결할 수 있습니다.단, 다음과 같은 경우:

char x[] = "fred";

x는 l값이 아니라 r값입니다.할당할 수 없습니다.

언급URL : https://stackoverflow.com/questions/1704407/what-is-the-difference-between-char-s-and-char-s

반응형