본문 바로가기
C++/유튜브 어소트락 게임아카데미 C++무료강의

9. 포인터 변수 문제 풀이

by GREEN나무 2025. 4. 15.
728x90

강의 링크


🧩 문제 1.

short sArr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* pI = (int*)sArr;
int iData = ((short*)(pI + 2));
printf("1번 문제 정답 : %d\n", iData);

🖨️ 출력 결과

1번 문제 정답 : 5

🔍 해설

  • short sArr[10]은 2바이트 정수 10개로 구성된 배열입니다.
  • int* pI = (int*)sArr;
    → short*을 int*로 강제로 캐스팅하였기에, 포인터의 연산 단위가 4바이트로 바뀝니다.
  • pI + 2는 4바이트 * 2 → 8바이트(=4개의 short) 만큼 증가합니다.
    즉, sArr[4]와 sArr[5]를 가리키게 됩니다. 즉 {1,2,3,4,👉5,6,7,8,9,10} 중에서 5,6 위치.
  • 이후 (short*)(pI + 2)로 다시 short*로 바꿔서 역참조합니다.
    → short* 관점에서 보면 5가 위치한 주소가 되고, iData에는 5가 들어갑니다.

정답: 5


🧩 문제 2.

char cArr[2] = { 1, 1 };
short* pS = (short*) cArr;
iData = *pS;
printf("2번 문제 정답 : %d\n", iData);

🖨️ 출력 결과

2번 문제 정답 : 257

🔍 해설

  • char cArr[2] = {1, 1}; → 각각 1바이트씩 1이 들어 있음.
  • short* pS = (short*)cArr;
    → char[2]을 short*로 바꾸었으니, 2바이트 단위로 읽게 됩니다.
  • iData = *pS;
    → cArr[0] = 0x01, cArr[1] = 0x01 → 리틀 엔디안에서는 0x0101

※ 리틀 엔디안(Little Endian)

더보기

 📦 리틀 엔디안 vs 빅 엔디안

 

🔸 엔디안이란?

엔디안(Endianness)은 2바이트 이상인 데이터(예: int, short 등)를 메모리에 어떤 순서로 저장할 것인지를 결정하는 방식입니다.
쉽게 말해, 숫자의 바이트 순서를 메모리에 어떻게 나열할까? 라는 문제입니다.

예를 들어, 0x1234라는 2바이트 데이터를 저장할 때

  • 앞에서부터 저장할까?
  • 뒤에서부터 저장할까?

이 선택에 따라 리틀 엔디안 또는 빅 엔디안이 됩니다.


🔹 리틀 엔디안 (Little Endian)

"작은 값(LSB)을 먼저 저장해요!"

주소 저장 값
0x00 0x34
0x01 0x12

📌 즉, 역순으로 저장됩니다.


🔹 빅 엔디안 (Big Endian)

"큰 값(MSB)을 먼저 저장해요!"

주소 저장 값
0x00 0x12
0x01 0x34

📌 우리가 일반적으로 숫자를 읽는 순서와 같아요.


🎯문제2 로 다시 보기

char cArr[2] = { 1, 1 };   // 10진수로 [1, 1]
short* pS = (short*) cArr;
int iData = *pS;

🔍 메모리에 저장된 모습 (리틀 엔디안 기준):

주소 값 (16진수)
cArr[0] 0x01
cArr[1] 0x01

📌 short* 포인터로 읽으면 두 바이트를 한꺼번에 읽기 때문에,

👉 0x01(하위 바이트) + 0x01(상위 바이트) → 0x0101

💡 결과적으로 0x0101 = 257 (10진수)가 됩니다!


🧪 만약 빅 엔디안이었다면?

주소
cArr[0] 0x01 (상위 바이트)
cArr[1] 0x01 (하위 바이트)

➡️ 역시 0x0101이지만,
만약 값이 { 0x01, 0x02 }였다면?

  • 🐥 리틀 엔디안: 0x0201 → 513
  • 🦁 빅 엔디안: 0x0102 → 258

😨 이처럼 동일한 데이터라도 해석 결과가 달라질 수 있기 때문에,
💾 네트워크, 바이너리 파일, 포인터 캐스팅 등에서는 엔디안 정리가 꼭 필요합니다!


🧠 한 줄 요약

📝 리틀 엔디안은 하위 바이트(작은 값)가 앞에 오는 방식입니다.
대부분의 시스템 (특히 x86 기반)은 리틀 엔디안을 사용하고 있습니다.

 

🧠 2진수로 보면:

00000001 (LSB)
00000001 (MSB)
=> 00000001 00000001 = 257 (10진수)

정답: 257

💡 의도적으로 1바이트짜리 배열을 2바이트 단위로 읽게끔 변환했기 때문에, 이처럼 예상치 못한 값이 나올 수 있습니다.


🧩 문제 3.

void Test1(int a) {
	a = 500;
}

void Test2(int* a) {
	*a = 500;
}

int main() {
	int a = 100;
	Test1(a);
	printf("Test1 : %d\n", a);

	Test2(&a);
	printf("Test2 : %d\n", a);
	return 0;
}

🖨️ 출력 결과

Test1 : 100
Test2 : 500

🔍 해설

  • Test1(int a)
    값에 의한 전달(Call by Value)
    → main 함수의 변수 a 값을 복사해서 사용하므로, 원본에는 영향이 없습니다.
  • Test2(int* a)
    주소에 의한 전달(Call by Reference)
    → 실제 변수 a의 주소를 전달받았기 때문에, 그 주소에 있는 값을 직접 바꿀 수 있습니다.

✅ Test1은 값이 안 바뀌고, Test2는 실제 변수 a가 변경됨.


🧠 한 줄 요약

  • 🔧 값을 바꾸고 싶다면 반드시 포인터를 써야 한다!
  • 📌 scanf() 함수도 마찬가지로 주소를 요구하는 이유가 바로 여기에 있음.

📝 포인터 개념 확장 — scanf 계열 함수 요약

구분 기본 함수 보안 강화 함수 locale 지원 함수 사용 예시
콘솔 입력 scanf scanf_s _scanf_s_l scanf("%d", &num);
파일 입력 fscanf fscanf_s ❌ (locale 지원 없음) fscanf(fp, "%d", &num);
문자열 입력 sscanf sscanf_s _sscanf_s_l sscanf(str, "%d", &num);
길이 제한 _snscanf _snscanf_s _snscanf_s_l _snscanf_s(str, 10, "%s", buf, size);

⚠️ _snscanf_s, _sscanf_s_l 등은 Visual Studio 전용이므로 GCC, Clang에서는 사용 불가입니다.


🧠 마무리 한 줄 요약

포인터는 메모리를 직접 다루는 칼이다.
값을 읽고 바꾸는 모든 행위는 주소에서 시작되며, 형(type) 변환에 따라 해석 방식이 전혀 달라질 수 있으므로 항상 메모리 크기와 정렬을 고려하자.


💡 실전 Tip

  • 포인터가 가리키는 대상의 크기 단위가 연산의 기본이 됩니다.
  • 무조건 외우기보다, "왜 이렇게 나올까?"를 메모리 입장에서 상상해보는 것이 중요합니다.
  • 시각화 도구(VS Memory Window, 그림 등)를 활용해보면 더 빠르게 체득됩니다.

 

'C++ > 유튜브 어소트락 게임아카데미 C++무료강의' 카테고리의 다른 글

11. void 포인터 (void*)  (0) 2025.04.19
10. 포인터와 const  (0) 2025.04.19
8. 포인터  (0) 2025.04.15
7. 지역변수, 전역변수  (0) 2025.04.02
6. 구조체(Structure)  (0) 2025.03.30