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 []는 서로 다른 유형이며 컴파일러에 의해 다르게 처리됩니다 (포인터 = 주소 + 포인터 유형의 표현, 배열 = 포인터 + 배열의 선택적 길이, 예를 들어 배열이 정적으로 할당 된 경우) ), 세부 사항은 표준에서 찾을 수 있습니다. 그리고 런타임 수준에서 그들 사이의 차이는 없습니다 (어셈블러에서는 거의 아래를 참조하십시오).
Q : 이러한 초기화의 차이점은 무엇입니까?
char a[] = "string literal"; char *p = "string literal";
p [i]에 새 값을 할당하려고하면 프로그램이 충돌합니다.
A : 문자열 리터럴 (C 소스에서 큰 따옴표로 묶인 문자열의 공식 용어)은 두 가지 방식으로 사용할 수 있습니다.
- char a [] 선언에서와 같이 char 배열의 초기화 프로그램으로, 해당 배열에있는 문자의 초기 값 (필요한 경우 크기)을 지정합니다.
- 다른 곳에서는 명명되지 않은 정적 문자 배열로 바뀌며이 명명되지 않은 배열은 읽기 전용 메모리에 저장 될 수 있으므로 반드시 수정할 수는 없습니다. 표현식 컨텍스트에서, 배열은 평소와 같이 한 번에 포인터로 변환되므로 (섹션 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 초안
문자열 리터럴에는 두 가지 용도가 있습니다.
-
초기화
char[]
:char c[] = "abc";
이것은 “더 많은 마법”이며 6.7.8 / 14 “초기화”에 설명되어 있습니다.
문자 유형의 배열은 선택적으로 중괄호로 묶인 문자열 리터럴로 초기화 될 수 있습니다. 문자열 리터럴의 연속 문자 (공간이 있거나 배열의 크기를 알 수없는 경우 종료 널 문자 포함)는 배열의 요소를 초기화합니다.
따라서 이것은 바로 가기입니다.
char c[] = {'a', 'b', 'c', '\0'};
다른 일반 배열과 마찬가지로
c
수정할 수 있습니다. -
다른 곳에서는 :
- 이름이없는
- char 배열 C와 C ++에서 문자열 리터럴의 유형은 무엇입니까?
- 정적 스토리지
- 수정 된 경우 UB (정의되지 않은 동작)를 제공합니다.
그래서 당신이 쓸 때 :
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 인 바이트 또는 코드가 추가됩니다. 그런 다음 멀티 바이트 문자 시퀀스는 시퀀스를 포함하기에 충분한 정적 저장 기간 및 길이의 배열을 초기화하는 데 사용됩니다. 문자열 리터럴의 경우 배열 요소에는 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의 설명과 일치합니다.