#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 = #
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 문구가 뜬다.
이유가 뭘까?
원인: 포인터 레벨이 맞지 않음
pNum의 타입은 int*, 즉 int를 가리키는 포인터, 1단계 포인터
&pNum은 포인터 변수 pNum 자체의 주소이므로, 타입은 int**, 즉int 포인터를 가리키는 포인터, 2단계 포인터가 되어야 함
이 int* 타입의 값을 int 타입의 변수 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 = #
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
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
(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이어야 합니다.
C언어에서 모든 표현식은 L-value 또는 R-value로 구분됨
예: int num; num = 100; (여기서 num이 L-value)
예: 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