스택 배열의 포인터에 액세스할 수 없는 이유는 무엇입니까?
아래 코드를 확인해주시기 바랍니다.배열을 다음과 같이 전달하려고 합니다.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
는 배열에 대한 포인터입니다.포인터에 대한 포인터가 아닙니다.
배열이 포인터라는 말을 들었을 수 있지만 이는 올바르지 않습니다.배열의 이름은 전체 개체(모든 요소)의 이름입니다.첫 번째 요소에 대한 포인터가 아닙니다.대부분의 식에서 배열은 첫 번째 요소의 포인터로 자동 변환됩니다.그것은 종종 유용한 편리함입니다.그러나 이 규칙에는 세 가지 예외가 있습니다.
- 배열은 다음의 피연산자입니다.
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
, 주소를 취할 수 있는 포인터가 없으므로, 이것은 a를 생성할 수 없습니다.char **
. A를 만들기 위해서는char **
, 당신은 먼저 그것을 가지고 있어야 합니다.char *
.
왜냐면test
는 포인터가 아닙니다.
&test
배열에 대한 포인터를 가져옵니다. 유형은 다음과 같습니다.char (*)[256]
, 와 호환되지 않는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
따라서 실제로 a를 가리키는 값입니다.(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
같은 가치를 가지다
이것은 컴파일러입니다.나는 이것이 언어에 의해 정의되는지 의심스럽습니다.
언급URL : https://stackoverflow.com/questions/60016986/why-cant-i-access-a-pointer-to-pointer-for-a-stack-array
'programing' 카테고리의 다른 글
JDBC 준비 상태에 매개 변수 전달 (0) | 2023.10.19 |
---|---|
부모가 소유한 루트의 백분율 계산 (0) | 2023.10.19 |
판다 버전 rbind (0) | 2023.10.19 |
스크롤 높이는 어떻게 결정합니까? (0) | 2023.10.19 |
glibc 사용 여부를 구분하는 방법 (0) | 2023.10.19 |