[c] C에서 char 배열과 char 포인터의 차이점은 무엇입니까?

C의 포인터를 이해하려고 노력하고 있지만 현재 다음과 혼동됩니다.

  • char *p = "hello"

    이것은 h 에서 시작하여 문자 배열을 가리키는 문자 포인터 입니다.

  • char p[] = "hello"

    hello 를 저장하는 배열입니다 .

이 두 변수를이 함수에 전달할 때의 차이점은 무엇입니까?

void printSomething(char *p)
{
    printf("p: %s",p);
}



답변

char*그리고 char[] 다른 종류가 있습니다 ,하지만 모든 경우에 즉시 분명 아니다. 배열이 포인터로 붕괴 되기 때문입니다 . 즉, 형식 char[]중 하나 char*가 예상 되는 위치 에 형식 표현식 이 제공 되면 컴파일러는 배열을 첫 번째 요소에 대한 포인터로 자동 변환합니다.

예제 함수 printSomething는 포인터를 기대하므로 다음과 같이 배열을 전달하려고하면 :

char s[10] = "hello";
printSomething(s);

컴파일러는 다음과 같이 작성한 척합니다.

char s[10] = "hello";
printSomething(&s[0]);


답변

보자 :

#include <stdio.h>
#include <string.h>

int main()
{
    char *p = "hello";
    char q[] = "hello"; // no need to count this

    printf("%zu\n", sizeof(p)); // => size of pointer to char -- 4 on x86, 8 on x86-64
    printf("%zu\n", sizeof(q)); // => size of char array in memory -- 6 on both

    // size_t strlen(const char *s) and we don't get any warnings here:
    printf("%zu\n", strlen(p)); // => 5
    printf("%zu\n", strlen(q)); // => 5

    return 0;
}

foo *와 foo []는 서로 다른 유형이며 컴파일러에 의해 다르게 처리됩니다 (포인터 = 주소 + 포인터 유형의 표현, 배열 = 포인터 + 배열의 선택적 길이, 예를 들어 배열이 정적으로 할당 된 경우) ), 세부 사항은 표준에서 찾을 수 있습니다. 그리고 런타임 수준에서 그들 사이의 차이는 없습니다 (어셈블러에서는 거의 아래를 참조하십시오).

또한 C FAQ 에 관련 질문 이 있습니다 .

Q : 이러한 초기화의 차이점은 무엇입니까?

char a[] = "string literal";
char *p  = "string literal";   

p [i]에 새 값을 할당하려고하면 프로그램이 충돌합니다.

A : 문자열 리터럴 (C 소스에서 큰 따옴표로 묶인 문자열의 공식 용어)은 두 가지 방식으로 사용할 수 있습니다.

  1. char a [] 선언에서와 같이 char 배열의 초기화 프로그램으로, 해당 배열에있는 문자의 초기 값 (필요한 경우 크기)을 지정합니다.
  2. 다른 곳에서는 명명되지 않은 정적 문자 배열로 바뀌며이 명명되지 않은 배열은 읽기 전용 메모리에 저장 될 수 있으므로 반드시 수정할 수는 없습니다. 표현식 컨텍스트에서, 배열은 평소와 같이 한 번에 포인터로 변환되므로 (섹션 6 참조) 두 번째 선언은 p를 초기화하여 명명되지 않은 배열의 첫 번째 요소를 가리 키도록합니다.

일부 컴파일러에는 문자열 리터럴을 쓸 수 있는지 여부를 제어하는 ​​스위치가 있으며 (오래된 코드를 컴파일하기 위해) 문자열 리터럴을 형식적으로 const char 배열로 처리하도록하는 옵션이있을 수 있습니다 (더 나은 오류 포착).

질문 1.31, 6.1, 6.2, 6.8 및 11.8b도 참조하십시오.

참고 문헌 : K & R2 Sec. 5.5 p. 104

ISO 초 6.1.4, 섹션 6.5.7

이론적 근거 3.1.4

H & S 섹션 2.7.4 31-2 페이지


답변

C에서 char 배열과 char 포인터의 차이점은 무엇입니까?

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]하면 __unnamedUB 도 수정 됩니다.

    이것은 6.4.5 “문자열 리터럴”에 문서화되어 있습니다 :

    5 변환 단계 7에서는 문자열 리터럴 또는 리터럴에서 생성 된 각 멀티 바이트 문자 시퀀스에 값이 0 인 바이트 또는 코드가 추가됩니다. 그런 다음 멀티 바이트 문자 시퀀스는 시퀀스를 포함하기에 충분한 정적 저장 기간 및 길이의 배열을 초기화하는 데 사용됩니다. 문자열 리터럴의 경우 배열 요소에는 char 유형이 있으며 멀티 바이트 문자 시퀀스의 개별 바이트로 초기화됩니다 […]

    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“pointer to char”유형으로 정의 하고 길이가 4 인 “array 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실행이 동일한 세그먼트,하지만 쓰기 권한이있다. 이것은 다음과 같이 볼 수 있습니다.

readelf -l a.out

포함하는:

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


답변

문자열 상수의 내용을 변경할 수 없습니다 p. 첫 번째가 가리키는 것입니다. 두 번째 p는 문자열 상수로 초기화 된 배열 이며 내용을 변경할 있습니다.


답변

이와 같은 경우 효과는 동일합니다. 첫 번째 문자의 주소를 문자열로 전달합니다.

선언은 분명히 동일하지 않습니다.

다음은 문자열과 문자 포인터의 메모리를 따로 설정 한 다음 문자열의 첫 번째 문자를 가리 키도록 포인터를 초기화합니다.

char *p = "hello";

다음은 문자열에 대해서만 메모리를 따로 설정합니다. 따라서 실제로 적은 메모리를 사용할 수 있습니다.

char p[10] = "hello";


답변

내가 기억할 수있는 한 배열은 실제로 포인터 그룹입니다. 예를 들어

p[1]== *(&p+1)

진실한 진술이다


답변

에서 APUE , 5.14 절 :

char    good_template[] = "/tmp/dirXXXXXX"; /* right way */
char    *bad_template = "/tmp/dirXXXXXX";   /* wrong way*/

… 첫 번째 템플릿의 경우 배열 변수를 사용하기 때문에 이름이 스택에 할당됩니다. 그러나 두 번째 이름에는 포인터를 사용합니다. 이 경우 포인터 자체의 메모리 만 스택에 상주합니다. 컴파일러는 문자열이 실행 파일의 읽기 전용 세그먼트에 저장되도록 정렬합니다. 때 mkstemp함수가 문자열을 수정하려고 세그먼트 오류가 발생합니다.

인용 된 텍스트는 @Ciro Santilli의 설명과 일치합니다.