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


216

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

  • char *p = "hello"

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

  • char p[] = "hello"

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

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

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

5
유효하지 않습니다. char p[3] = "hello";초기화 문자열이 선언 한 배열의 크기에 비해 너무 깁니다. 오식?
코디 그레이

16
아니면 char p[]="hello";충분할 것입니다!
deepdive


1
가능한 중복 C에서 char s []와 char * s의 차이점무엇입니까? 사실, 이것은 또한 함수 매개 변수에 대해 구체적으로 묻지 만 구체적이지 않습니다 char.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

1
그것들은 근본적으로 다르다는 것을 이해해야합니다. 이것의 유일한 공통점은 arry p []의베이스가 포인터를 통해 배열 p []에 액세스 할 수있게하는 const 포인터라는 것입니다. p [] 자체는 문자열에 대한 메모리를 보유하지만 * p는 단지 ONE CHAR의 첫 번째 요소의 주소를 가리 킵니다 (즉, 이미 할당 된 문자열의 기본을 가리킴). 이를 더 잘 설명하려면 다음을 고려하십시오. char * cPtr = { 'h', 'e', ​​'l', 'l', 'o', '\ 0'}; ==> cPtr은 문자에 대한 포인터이므로 오류입니다. cBuff [] = { 'h', 'e', ​​'l', 'l', 'o', '\ 0'}; ==> 이것은 괜찮습니다. bcos cBuff 자체는 char 배열입니다
Ilavarasan

답변:


223

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

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

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

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

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

2012 년부터 지금까지 변경된 것이 있습니다. 문자 배열의 경우 "s"는 전체 배열을 인쇄합니다. "hello"
Bhanu Tez

@BhanuTez 아니요, 데이터 저장 방법 및 데이터로 수행되는 작업은 별도의 관심사입니다. 이 예제 printf%s형식 문자열을 처리하는 방법이므로 전체 문자열을 인쇄합니다 . 제공된 주소에서 시작하여 null 종료자가 발생할 때까지 계속합니다. %c예를 들어 하나의 문자 만 인쇄하려면 형식 문자열을 사용할 수 있습니다 .
iX3 2016 년

char [] 배열의 경우와 같이 char *p = "abc";NULL 문자 \0가 자동으로 추가 되는지 묻고 싶 습니까?
KPMG

왜 내가 설정할 char *name; name="123";수는 있지만 int유형 과 동일하게 할 수 있습니까? 그리고 %cprint 을 사용한 후에 name는 출력을 읽을 수없는 문자열입니다 : ?
TomSawyer

83

보자 :

#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 페이지


sizeof (q)에서 @Jon이 그의 답변에서 언급했듯이 q가 포인터로 붕괴되지 않는 이유는 무엇입니까?
garyp

sizeof가 함수가 아닌 연산자이기 때문에 @garyp q는 포인터로 붕괴되지 않습니다 (sizeof가 함수 인 경우에도 q는 함수가 char 포인터를 기대하는 경우에만 붕괴합니다).
GiriB

고맙지 만 printf ( "% zu \ n"대신 printf ( "% u \ n"대신 z를 제거해야한다고 생각합니다.
Zakaria

33

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

2
@ leszek.hanusz 정의되지 않은 동작 stackoverflow.com/questions/2766731/… Google "C language UB";-)
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

9

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


6

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

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

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

char *p = "hello";

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

char p[10] = "hello";

codeplusplus.blogspot.com/2007/09/… "그러나 변수를 초기화하는 것은 배열에 큰 성능과 공간을 차지합니다"
leef

@leef : 변수의 위치에 달려 있다고 생각합니다. 정적 메모리에 있으면 배열과 데이터가 EXE 이미지에 저장 될 수 있으며 초기화가 전혀 필요하지 않습니다. 그렇지 않으면, 데이터를 할당 한 다음 정적 데이터를 복사해야 할 경우 확실히 느려질 수 있습니다.
Jonathan Wood

3

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

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

진실한 진술이다


2
배열을 메모리 블록의 주소에 대한 포인터로 설명합니다. 따라서 왜 *(arr + 1)당신을의 두 번째 회원으로 데려다 줄 것입니다 arr. 경우 *(arr)예를 들면 32 비트 메모리 어드레스, 포인트는 bfbcdf5e, 다음 *(arr + 1)에 점 bfbcdf60(번째 바이트). 따라서 OS가 segfault하지 않으면 배열 범위를 벗어나면 이상한 결과가 발생하는 이유는 무엇입니까? int a = 24;주소에 있으면 segfault가 먼저 발생하지 않는다고 가정하면 bfbcdf62액세스 가을 arr[2]반환 할 수 있습니다 24.
Braden Best

3

에서 APUE , 5.14 절 :

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

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

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


1

char p[3] = "hello"? 해야한다char p[6] = "hello"C에서 "문자열"의 끝에 '\ 0'문자가 것을 기억 .

어쨌든 C의 배열은 메모리에있는 adjust 객체의 첫 번째 객체에 대한 포인터 일뿐입니다. 시맨틱에있는 유일한 다른 것입니다. 메모리의 다른 위치를 가리 키도록 포인터의 값을 변경할 수 있지만 배열을 만든 후에는 항상 같은 위치를 가리 킵니다.
또한 배열을 사용할 때 "새로 만들기"및 "삭제"가 자동으로 수행됩니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.