다음 코드를 살펴보십시오. 배열을 char**
함수 로 전달하려고 합니다.
#include <stdio.h>
#include <stdlib.h>
static void printchar(char **x)
{
printf("Test: %c\n", (*x)[0]);
}
int main(int argc, char *argv[])
{
char test[256];
char *test2 = malloc(256);
test[0] = 'B';
test2[0] = 'A';
printchar(&test2); // works
printchar((char **) &test); // crashes because *x in printchar() has an invalid pointer
free(test2);
return 0;
}
이미 명시 적으로 캐스팅 &test2
하여 컴파일 할 수 있다는 사실은 char**
이 코드가 잘못되었음을 암시합니다.
아직도, 나는 그것에 대해 정확히 무엇이 잘못되었는지 궁금합니다. 동적으로 할당 된 배열에 대한 포인터에 대한 포인터를 전달할 수 있지만 스택의 배열에 대한 포인터에 대한 포인터는 전달할 수 없습니다. 물론 배열을 임시 변수에 먼저 할당하여 문제를 쉽게 해결할 수 있습니다.
char test[256];
char *tmp = test;
test[0] = 'B';
printchar(&tmp);
그래도 누군가 char[256]
가 char**
직접 전송 하지 않는 이유를 설명해 줄 수 있습니까?
답변
때문에이 test
포인터가 아닙니다.
&test
배열이 포인터가 아니기 때문에 char (*)[256]
호환되지 않는 유형의 배열에 대한 포인터를 가져 char**
옵니다. 결과적으로 정의되지 않은 동작이 발생합니다.
답변
test
포인터가 아닌 배열이며 배열에 대한 포인터 &test
입니다. 포인터를 가리키는 포인터가 아닙니다.
배열이 포인터라는 말을 들었을 수도 있지만 이는 잘못된 것입니다. 배열의 이름은 전체 객체, 모든 요소의 이름입니다. 첫 번째 요소에 대한 포인터가 아닙니다. 대부분의 표현식에서 배열은 첫 번째 요소에 대한 포인터로 자동 변환됩니다. 그것은 종종 유용한 편의입니다. 그러나이 규칙에는 세 가지 예외가 있습니다.
- 배열은의 피연산자입니다
sizeof
. - 배열은의 피연산자입니다
&
. - 배열은 배열을 초기화하는 데 사용되는 문자열 리터럴입니다.
에서는 &test
, 배열의 피연산자는 &
자동 전환이 발생하지 않도록. 의 결과 는 형식 이 아닌 형식 &test
이 256 char
인 배열에 대한 포인터 입니다.char (*)[256]
char **
포인터에 대한 포인터를 얻으려면 char
에서 test
먼저 포인터를 만들 필요가있다 char
. 예를 들면 다음과 같습니다.
char *p = test; // Automatic conversion of test to &test[0] occurs.
printchar(&p); // Passes a pointer to a pointer to char.
이것에 대해 생각하는 또 다른 방법 test
은 전체 객체의 이름, 즉 전체 배열의 이름을 256으로 인식하는 것 char
입니다. 포인터의 이름을 지정하지 않으므로에 &test
주소를 가져올 수있는 포인터가 없으므로을 (를) 생성 할 수 없습니다 char **
. 를 만들려면 char **
먼저가 있어야합니다 char *
.
답변
의 유형은 test2
입니다 char *
. 따라서 유형&test2
할 예정입니다 char **
매개 변수의 유형과 호환 x
의 printchar()
.
의 유형은 test
입니다 char [256]
. 그래서, 유형 &test
것입니다 수 char (*)[256]
있습니다 되지 매개 변수의 유형과 호환 x
의 printchar()
.
test
및 주소의 차이점을 보여 드리겠습니다 test2
.
#include <stdio.h>
#include <stdlib.h>
static void printchar(char **x)
{
printf("x = %p\n", (void*)x);
printf("*x = %p\n", (void*)(*x));
printf("Test: %c\n", (*x)[0]);
}
int main(int argc, char *argv[])
{
char test[256];
char *test2 = malloc(256);
test[0] = 'B';
test2[0] = 'A';
printf ("test2 : %p\n", (void*)test2);
printf ("&test2 : %p\n", (void*)&test2);
printf ("&test2[0] : %p\n", (void*)&test2[0]);
printchar(&test2); // works
printf ("\n");
printf ("test : %p\n", (void*)test);
printf ("&test : %p\n", (void*)&test);
printf ("&test[0] : %p\n", (void*)&test[0]);
// Commenting below statement
//printchar((char **) &test); // crashes because *x in printchar() has an invalid pointer
free(test2);
return 0;
}
산출:
$ ./a.out
test2 : 0x7fe974c02970
&test2 : 0x7ffee82eb9e8
&test2[0] : 0x7fe974c02970
x = 0x7ffee82eb9e8
*x = 0x7fe974c02970
Test: A
test : 0x7ffee82eba00
&test : 0x7ffee82eba00
&test[0] : 0x7ffee82eba00
여기를 참고하십시오 :
출력 (메모리 어드레스) test2
및 &test2[0]
인 수치 같은 그들의 타입 인도 동일하다 char *
.
그러나 test2
및&test2
주소가 다르고 유형도 다릅니다.
의 유형은 test2
입니다 char *
.
의 유형은 &test2
입니다 char **
.
x = &test2
*x = test2
(*x)[0] = test2[0]
출력 (메모리 어드레스) test
, &test
및 &test[0]
인 수치 동일 하지만, 그들의 형태는 다르다 .
의 유형은 test
입니다 char [256]
.
의 유형은 &test
입니다 char (*) [256]
.
의 유형은 &test[0]
입니다 char *
.
출력 도시 된 바와 같이 &test
동일하다 &test[0]
.
x = &test[0]
*x = test[0] //first element of test array which is 'B'
(*x)[0] = ('B')[0] // Not a valid statement
따라서 세그먼테이션 오류가 발생합니다.
답변
포인터가 아니기 때문에 포인터에 대한 포인터에 액세스 할 수 없습니다 &test
. 그것은 배열입니다.
배열의 주소를 가져 와서 배열과 배열의 주소를로 캐스팅 (void *)
하고 비교하면 가능한 포인터 포인터를 제외하고 동일합니다.
실제로하고있는 것은 이것과 비슷합니다 (다시 말해서 엄격한 앨리어싱 제외).
putchar(**(char **)test);
이것은 분명히 잘못된 것입니다.
답변
귀하의 코드는 인수 가 포함 된 메모리를 가리키는 인수 x
를 기대합니다 printchar
.(char *)
.
첫 번째 호출에서 이는 사용 된 스토리지 test2
를 가리 키므로 실제로 (char *)
는를 가리키는 값 이며, 후자는 할당 된 메모리를 가리 킵니다.
그러나 두 번째 호출에서는 이러한 (char *)
값이 저장 될 수있는 위치가 없으므로 해당 메모리를 가리킬 수 없습니다. (char **)
추가 한 캐스트에서 컴파일 오류 (로 변환 (char *)
하는 방법 에 대한 오류)를 제거 (char **)
했지만 저장 공간이 얇은 공기에서(char *)
시험의 첫 번째 문자를 가리 키도록 초기화. C에서 포인터 캐스팅은 포인터의 실제 값을 변경하지 않습니다.
원하는 것을 얻으려면 명시 적으로해야합니다.
char *tempptr = &temp;
printchar(&tempptr);
나는 당신의 예제가 훨씬 더 큰 코드의 증류라고 가정합니다. 예를 들어, 다음 호출에서 다음 문자가 인쇄되도록 전달 된 값이 가리키는 값 printchar
을 증가 시키려고 할 수 있습니다 . 그렇지 않은 경우 인쇄 할 문자를 가리 키거나 문자 자체를 전달 하지 않는 이유는 무엇입니까?(char *)
x
(char *)
답변
주소를받는 것은 주소를 test
받는 것과 test[0]
같습니다.
#include <stdio.h>
#include <stdlib.h>
static void printchar(char **x)
{
printf("[printchar] Address of pointer to pointer: %p\n", (void *)x);
printf("[printchar] Address of pointer: %p\n", (void *)*x);
printf("Test: %c\n", **x);
}
int main(int argc, char *argv[])
{
char test[256];
char *test2 = malloc(256);
printf("[main] Address of test: %p\n", (void *)test);
printf("[main] Address of the address of test: %p\n", (void *)&test);
printf("[main] Address of test2: %p\n", (void *)test2);
printf("[main] Address of the address of test2: %p\n", (void *)&test2);
test[0] = 'B';
test2[0] = 'A';
printchar(&test2); // works
printchar(&test); // crashes because *x in printchar() has an invalid pointer
free(test2);
return 0;
}
그것을 컴파일하고 실행하십시오 :
forcebru$ clang test.c -Wall && ./a.out
test.c:25:15: warning: incompatible pointer types passing 'char (*)[256]' to
parameter of type 'char **' [-Wincompatible-pointer-types]
printchar(&test); // crashes because *x in printchar() has an inva...
^~~~~
test.c:4:30: note: passing argument to parameter 'x' here
static void printchar(char **x)
^
1 warning generated.
[main] Address of test: 0x7ffeeed039c0
[main] Address of the address of test: 0x7ffeeed039c0 [THIS IS A PROBLEM]
[main] Address of test2: 0x7fbe20c02aa0
[main] Address of the address of test2: 0x7ffeeed039a8
[printchar] Address of pointer to pointer: 0x7ffeeed039a8
[printchar] Address of pointer: 0x7fbe20c02aa0
Test: A
[printchar] Address of pointer to pointer: 0x7ffeeed039c0
[printchar] Address of pointer: 0x42 [THIS IS THE ASCII CODE OF 'B' in test[0] = 'B';]
Segmentation fault: 11
따라서 세그먼테이션 오류의 궁극적 인 원인은이 프로그램이 절대 주소 0x42
(또는'B'
읽을 권한이없는 ) 입니다.
다른 컴파일러 / 머신을 사용하더라도 주소는 다를 수 있습니다. 온라인으로 시도하십시오! 하지만 어떤 이유로 든 여전히 얻을 수 있습니다.
[main] Address of test: 0x7ffd4891b080
[main] Address of the address of test: 0x7ffd4891b080 [SAME ADDRESS!]
그러나 분할 오류를 일으키는 주소는 매우 다를 수 있습니다.
[printchar] Address of pointer to pointer: 0x7ffd4891b080
[printchar] Address of pointer: 0x9c000000942 [WAS 0x42 IN MY CASE]
답변
표현은 char [256]
구현 의존적이다. 같지 않아야합니다.char *
.
주조 &test
유형 char (*)[256]
에char **
하면 정의되지 않은 동작이 발생합니다.
일부 컴파일러를 사용하면 예상대로 수행 할 수 있지만 다른 컴파일러에서는 그렇지 않을 수 있습니다.
편집하다:
gcc 9.2.1로 테스트 한 후 printchar((char**)&test)
실제로 test
값 캐스트로 전달되는 것으로 보입니다 char**
. 지시 인 것처럼 때문이다 printchar((char**)test)
. 이 printchar
함수 x
에서 첫 번째 문자에 대한 이중 포인터가 아니라 배열 테스트의 첫 번째 문자에 대한 포인터입니다. 이중 역 참조 x
는 배열의 첫 8 바이트가 유효한 주소에 해당하지 않기 때문에 세그먼트 화 오류가 발생합니다.
clang 9.0.0-2로 프로그램을 컴파일 할 때 정확히 동일한 동작과 결과를 얻습니다.
이것은 컴파일러 버그 또는 결과가 컴파일러에 따라 다를 수있는 정의되지 않은 동작의 결과로 간주 될 수 있습니다.
또 다른 예기치 않은 동작은 코드가
void printchar2(char (*x)[256]) {
printf("px: %p\n", *x);
printf("x: %p\n", x);
printf("c: %c\n", **x);
}
출력은
px: 0x7ffd92627370
x: 0x7ffd92627370
c: A
이상한 행동은 그 x
와 *x
같은 가치를 가지고 있습니다.
이것은 컴파일러입니다. 나는 이것이 언어로 정의되어 있는지 의심합니다.
![](http://daplus.net/wp-content/uploads/2023/04/coupang_part-e1630022808943-2.png)