
들어가며
오늘은 좀 피곤하네 ^,^
오늘도 간단하게 수업을 하고 왔다. 이번 주제는 다중 포인터! 피어한테 설명해주면서 배열등가포인터 또는 배열을 가리키는 포인터를 다른 포인터 변수에 담아주는 경우에, 해당 변수로는 배열의 크기를 전달해줄 수 없다는 결론에 이르렀다… 나는 당연히 될 줄 알고 “이렇게 하면 될 것 같은데요?!” 라면서 코드를 두드려봤는데, 시간을 날리고 클로드에게 물어보니 안되는거라고 하더라.. 😢
ㅤ
오늘의 키워드
배열의 이름
배열의 이름을 이용해 연산자를 호출하면 조금 특별하게 작동함.
ㅤ
int main(void) {
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
printf("%d \n", sizeof(a)); // 48
printf("%d \n", sizeof(a[1])); // 16
printf("%d \n", sizeof(a[0][1])); // 4
printf("%d \n", sizeof(*a)); // 16
printf("%d \n", sizeof(*(a+1))); // 16
printf("%d \n", sizeof(a+1)); // 4 정수연산으로 pointer
printf("%d \n", sizeof(&a)); // 4 &배열명은 주소는 같지만 pointer
printf("%d \n", sizeof(*&a)); // 48 배열 자기자신
printf("%d \n", sizeof(*&a)); // 48 배열포인터의 값 => 배열
printf("%d \n", sizeof(&*a)); // 4 배열 첫 원소값의 주소 => 포인터
printf("----------\n");
int (*p)[4] = a;
printf("%d \n", sizeof(p)); // 4 얘는 pointer
printf("%d \n", sizeof(p[1])); // 16 근데 얘는 배열이네? 타고 들어가서 그런가보오
printf("%d \n", sizeof(p[0][1])); // 4
printf("%d \n", sizeof(*p)); // 16
printf("%d \n", sizeof(*(p + 1))); // 16
printf("%d \n", sizeof(p + 1)); // 4
printf("%d \n", sizeof(&p)); // 4
printf("%d \n", sizeof(*&p)); // 4 포인터 자기자신 => 포인터
printf("%d \n", sizeof(&*p)); // 4 첫 원소의 주소값 => 포인터
return 0;
}
ㅤ
sizeof(포인터상수)의 결과는 상수(배열)가 사용하는 메모리의 크기sizeof(&포인터상수)의 결과는 포인터가 사용하는 메모리의 크기- 다만 &의 연산 결과가 포인터 상수인 경우에는 상수가 사용하는 메모리크기를 뱉음
ㅤ
아래 수식의 결과는 다르다!!! 단순히 *&와 &*는 컴파일러에 의해 알아서 상쇄되는 연산이 아니라, 각각 순서대로 연산이 처리되는 무언가라는 것을 반드시 기억하기!! 결과 보고 깜짝 놀랐다!!!
`printf("%d \n", sizeof(*&a)); // 48`
`printf("%d \n", sizeof(&*a)); // 4`
그런데 두 값 모두 같은 주소를 가리키고 있다. 타입은 다음과 같다.
*&a는int(**)[3][4]타입의 포인터가 가리키는 값이므로int(**)[4]&*a는int*타입의 값의 주소이므로 그냥 ‘포인터’ 이다.

ㅤ
포인터를 잘 사용하려면
- 포인터 상수인지 포인터 변수인지 파악한다.
char* p = "abc"⇒p[1] = 'x'를 할 수 없음.- 요거슨 P가 상수포인터이기 때문임 (read-only memory에 저장됨)
char p[4] = “abc”처럼 작성하면 복사해서 스택에 넣기 때문에 p 값 변경이 가능함!
- 포인터는 타입이 중요함.
- 포인터의 값 : 가리키는 데이터의 시작 주소
- 포인터의 타입 : 주소의 데이터를 읽고 쓰는 단위
- 포인터의 자료형을 바꿀 수 있다.
- 데이터는 1byte, 4byte, 8byte 등의 여러 크기로 저장되지만, 포인터는 항상 동일한 4byte 포인터에 담긴다.
- 포인터가 가리키는 값을 어떤 크기 단위로 읽어올지는 자료형이 결정한다.
- 내가 작성한 데이터를 어떤 단위로 읽어올지는 타입캐스팅을 통해 변경 가능하다.
ㅤ
여전히 포인터 상수와 상수 포인터가 헷갈린다…
둘 뒤에 일단 ‘가’를 붙여서, 상수 포인터는 상수가(리키는) 포인터 / 포인터 상수는 포인터가 상수 라고 우선은 기억을 하고, 좀 더 익숙해질 필요가 있겠ㄷr…
배열명은 포인터상수!!!
”문자열”을 가리키면 상수포인터!!!
ㅤ
&를 붙일 수 있는 건 l-value 뿐
&의 뒤에는 l-value OR 배열명만 올 수 있다.
⇒ 연산결과인 숫자 등은 못온다.
&a: 가능&(a+3): 불가능
ㅤ
함수의 파라미터로 배열의 포인터를 넘길 때는 [배열표현]을 지양하자.
- 함수의 파라미터에는 가능하면 배열형식으로 작성하기보다는 포인터 형식으로 작성하자.
void func(int arr[][4])
void func(int ** arr)
ㅤㅤ
배열이름의 타입과 이중 포인터는 다르다.
배열 등가 포인터로 다중 포인터를 만들면 행 단위 이동을 할 수 없다
int (*p)[4] != int** p
ㅤ
이중포인터는 주로 포인터 배열에 대한 연산을 처리하기 위해 사용한다.
char* str[5];
char** p = str;
ㅤ
포인터 변수로는 사이즈를 전달할 수 없다.
과제를 위해 대가리를 박으며 배웠다. 나는 될 줄 알았다.
sizeof(배열명) 을 하면 배열이 메모리에서 차지하는 크기를 구할 수 있다. 그리고 나는 &배열명 의 형식으로 포인터로 넘겨 함수 블럭 내에서 sizeof(배열명) 을 통해 배열의 크기를 구해보고 싶었다. 그러나 열심히 여러가지 방법들을 츄라이 해봤지만 타이틀의 결론을 내리게 되었다.
포인터 변수는 결국 포인터이고, 배열의 크기를 담아서 함수에게 전달해줄 방법은 없는 것 같다. ;ㅅ;
ㅤ
⇒ 배열명은 2가지 정보를 한 번에 담고있음
- 포인터로서의 배열명 : 배열의 첫 주소값 + 가리키는 대상의 타입
- 배열로서의 배열명 : 배열의 첫 주소값 + 배열의 전체 크기
배열등가포인터에 이 주소를 담는다면 “포인터로서의 배열명”에 대한 정보만 남게된다!
딥다이브
배열의 이름을 보내는 대신, 배열의 포인터(&ary)를 넘기면 함수에서 사이즈를 찾을 수 있다.
다만, 행 단위 이동에 대한 정보를 표현하기 위해 (사이즈를 알기위해) 인자에 해당 정보를 입력해둬야한다.
void print_aryy(int(*ary)[3][4])
{
int r = sizeof(*ary) / sizeof(**ary);
int c = sizeof(**ary) / sizeof(***ary);
int (*pp)[4] = ary;
for (int i = 0; i < r; i++)
{
for (int j = 0; j < c; j++)
{
printf("%d ", pp[i][j]);
}
}
printf("\n");
}
...
int array[3][4] = {1, 2, ... , 12};
print_ary(&array);
// 사이즈를 넘길 필요가 없다!'TIL' 카테고리의 다른 글
| [250822] Day 12 - 메모리 동적할당과의 한 판 승부 (0) | 2025.08.24 |
|---|---|
| [250821] Day 11 - C언어는 모든게 포인터인가요 (0) | 2025.08.22 |
| [250819] Day 9 - 개인교사 데뷔전 (0) | 2025.08.20 |
| [250818] Day 8 - 알고리즘 재활치료 시작 (3) | 2025.08.18 |
| [250814] Day 4 - 포인터지옥 벌써 시작 (7) | 2025.08.15 |