포인터 레벨

KWANHO PARK·2025년 10월 19일
#include <stdio.h>


int iNum = 100;
int* ipNum1 = &iNum;

int main(void)
{
	int** ipNum2 = &ipNum1;
	printf("iNum Address: %p\n", &iNum);
	printf("ipNum1 Address: %p\n", &ipNum1);
	printf("iNum2 Address: %p\n", &ipNum2);

	int num = 1;
	int* pNum = &num;
	int* pNum1 = &pNum;
	printf("num: %d\n", num);
	printf("num Address: %p\n", *pNum);
	printf("pNum Address: %p\n", *pNum1);

	return 0;
}

위의 코드를 컴파일하면

C:\Users\User\Desktop\Test>cl sysbolTable.c /link /DYNAMICBASE:NO
x86용 Microsoft (R) C/C++ 최적화 컴파일러 버전 19.44.35217
Copyright (c) Microsoft Corporation. All rights reserved.

sysbolTable.c
sysbolTable.c(23): warning C4477: 'printf' : 서식 문자열 '%p'에 'void *' 형식의 인수가 필요하지만 variadic 인수 1의 형식이 'int'입니다.
sysbolTable.c(24): warning C4477: 'printf' : 서식 문자열 '%p'에 'void *' 형식의 인수가 필요하지만 variadic 인수 1의 형식이 'int'입니다.
sysbolTable.c(21): warning C4047: '초기화 중': 'int *'의 간접 참조 수준이 'int **'과(와) 다릅니다.
Microsoft (R) Incremental Linker Version 14.44.35217.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:sysbolTable.exe
/DYNAMICBASE:NO
sysbolTable.obj

위와 같은 warning 문구가 뜬다.
이유가 뭘까?

포인터 레벨

  1. warning C4047: int* pNum1 = &pNum;
  • 원인: 포인터 레벨이 맞지 않음

  • pNum의 타입은 int*, 즉 int를 가리키는 포인터, 1단계 포인터

  • &pNum은 포인터 변수 pNum 자체의 주소이므로, 타입은 int**, 즉int 포인터를 가리키는 포인터, 2단계 포인터가 되어야 함

  • 이 int* 타입의 값을 int 타입의 변수 pNum1에 넣으려고 하니까, 컴파일러가 포인터의 간접 참조 수준이 다르다고 경고함

비유

  • pNum : 집 주소가 적힌 쪽지
  • &pNum : 그 쪽지가 있는 곳의 주소
  • 현재 상황 : pNum1이라는 집 주소를 담는 봉투에 쪽지가 있는 곳의 주소를 넣으려고 하니까 종류가 맞지 않는 것
  1. warning C4477: printf("...", pNum); printf("...", pNum1);
  • 원인: %p 서식 지정자는 주소(포인터)를 출력하라는 의미인데, 주소가 아닌 값(int)을 전달

  • 포인터(*)연산자는 포인터가 가리키는 주소로 찾아가서 그 안의 값을 가져오라는 역참조(Dereference) 연산자

  • *pNum은 pNum이 가리키는 num의 주소로 찾아가서 그 안의 값, 즉 정수 1을 가져옴

  • *pNum1도 마찬가지로 pNum1이 가리키는 곳의 값을 가져오는데, 이는 pNum 변수에 저장된 값(즉, num의 주소). 하지만 타입 불일치 때문에 컴파일러는 이 또한 int로 해석하여 경고

결론적으로 printf 함수에게 주소(%p)를 출력하라고 시켜놓고 정수(int) 값을 넘겨주고 있으니, 타입이 맞지 않는다고 경고

수정된 코드

#include <stdio.h>


int iNum = 100;
int* ipNum1 = &iNum;

int main(void)
{
	
	// int* ipNum2 = 0x200;
	int** ipNum2 = &ipNum1;
	printf("iNum Address: %p\n", &iNum);
	printf("ipNum1 Address: %p\n", &ipNum1);
	printf("iNum2 Address: %p\n", &ipNum2);

	int num = 1;
	int* pNum = &num;
	int** pNum1 = &pNum;	// 수정
	printf("num: %d\n", num);
	printf("num Address: %p\n", pNum);		// 수정
	printf("pNum Address: %p\n", pNum1);	// 수정

	return 0;
}
  • 이제 경고가 안뜬다.
C:\Users\User\Desktop\Test>cl sysbolTable.c /link /DYNAMICBASE:NO
x86용 Microsoft (R) C/C++ 최적화 컴파일러 버전 19.44.35217
Copyright (c) Microsoft Corporation. All rights reserved.

sysbolTable.c
Microsoft (R) Incremental Linker Version 14.44.35217.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:sysbolTable.exe
/DYNAMICBASE:NO
sysbolTable.obj

주소를 포인터로 쓰는 법

  • 위의 sysbolTable.c 를 실행하면
C:\Users\User\Desktop\Test>sysbolTable.exe
iNum Address: 0041C000
ipNum1 Address: 0041C004
iNum2 Address: 0019FF28
iNum2: 00000100
num: 1
num Address: 0019FF24
pNum Address: 0019FF20
  • 위와 같이 출력된다.

그럼 iNum2의 주소인 0019FF28라는 값을 직접 사용해서 해당 주소공간에 값을 할당할 수 있을까?

  • 우선 아래 코드를 추가해서 컴파일 해보자
(int *)0x0019FF28 = 0x100;
printf("iNum2: %p\n", ipNum2);
  • 했더니 아래처럼 바로 경고와 에러가 뜬다
C:\Users\User\Desktop\Test>cl sysbolTable.c /link /DYNAMICBASE:NO
x86용 Microsoft (R) C/C++ 최적화 컴파일러 버전 19.44.35217
Copyright (c) Microsoft Corporation. All rights reserved.

sysbolTable.c
sysbolTable.c(16): warning C4047: '=': 'int *'의 간접 참조 수준이 'int'과(와) 다릅니다.
sysbolTable.c(16): error C2106: '=': 왼쪽 피연산자는 l-value이어야 합니다.

L-Value, R-Value

C언어에서 모든 표현식은 L-value 또는 R-value로 구분됨

  • L-value (Left-value): 대입 연산자(=)의 왼쪽에 올 수 있는 것. 이름이 있고 값을 저장할 수 있는 메모리 공간이나 변수

예: int num; num = 100; (여기서 num이 L-value)

  • R-value (Right-value): 대입 연산자(=)의 오른쪽에만 올 수 있는 것. 임시 값이나 리터럴(literal)처럼 고유한 메모리 공간을 갖지 않는 값

예: num = 100; (여기서 100이 R-value)

경고, 에러 원인

  • 임시 값(R-Value)에 데이터를 저장하려고 시도했기 때문

  • 문제 발생 부분 : (int *)0x0019FF28

0x0019FF28: 이건 그냥 숫자(정수 리터럴)임. R-value임

(int *)0x0019FF28: 이건 그냥 정수 리터럴 값을 int를 가리키는 주소로 형변환했을 뿐임. 즉 여전히 임시적인 포인터 값이므로 R-value임

결과적으로 이 코드는 (임시 값) = 100; 과 같은 형태가 되어버림. 컴파일러는 값을 저장할 수 있는 메모리 공간(L-value)이 아닌, 정체불명의 임시 값(R-value)에 데이터를 넣으려고 하니까 경고 및 에러 발생

해결

  • 아래와 같이 코드 수정
*(int *)0x0019FF28 = 0x100;
printf("iNum2: %p\n", ipNum2);
  • (int *)0x0019FF28: 0x0019FF28번지 주소를 의미하는 임시 포인터 값 (R-value).

  • *: 그 주소가 가리키는 메모리 공간으로 찾아가라는 명령.

  • *(int *)0x0019FF28: 0x0019FF28번지 메모리 공간 그 자체를 의미. 이것은 값을 저장할 수 있는 공간이므로 L-value

  • 컴파일 결과

C:\Users\User\Desktop\Test>cl sysbolTable.c /link /DYNAMICBASE:NO
x86용 Microsoft (R) C/C++ 최적화 컴파일러 버전 19.44.35217
Copyright (c) Microsoft Corporation. All rights reserved.

sysbolTable.c
Microsoft (R) Incremental Linker Version 14.44.35217.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:sysbolTable.exe
/DYNAMICBASE:NO
sysbolTable.obj
  • 이제 경고나 에러가 안 뜬다.

0개의 댓글