👉🏻 변수, 포인터 기초 개념) https://goldenriver42.tistory.com/57?category=923014

 

변수, 포인터 개념 헷갈림 (관련 질문 아래)

https://stackoverflow.com/questions/35954132/what-is-the-meaning-of-the-address-of-a-pointer

 

요약

- 이름을 가진 변수 선언하기만 해도 (쓰레기값으로 채워진) 메모리 할당됨. 해당 메모리 위치 가리키는 주소도 얻기 가능

- 정의(definition)해야 쓰레기값 대신 쓸모있는 값이 채워짐

 

 

오랜만에 OS코드를 C언어로 짜며 동적할당을 고민하다보니, 포인터 개념이 매우 헷갈렸다.

 

 

포인터는 메모리에 저장된 값의 주소를 가리킨다. 그러면 딱히 메모리에 저장할 필요가 없는 alias? 숫자?가 아닌가?

 

근데 그렇다기엔 포인터는 8바이트 크기를 가진다고들 하잖아 (64비트 시스템).

또 포인터의 포인터도 있잖아? 그러면 포인터도 메모리에 저장되는 하나의 값인건가?

 

    int i = 5;
    int j;
    int* ptr1 = &i;
    int* ptr2; 
    int** pptr1 = &ptr1;
    int** pptr2 = &ptr2;

0xcccccccc는 초기화되지 않은 지역변수를 의미. http://slaveofcod.egloos.com/v/744445

 

j와 ptr2에 주목하자. 단순 선언만 해도 과연 메모리를 할당받을까?

&j 를 찍어보거나, pptr2를 통해 그렇다는 것을 알 수 있다.

j는 int형이니 4바이트 공간을 할당받았을테고, ptr2는 포인터니 8바이트 공간을 할당받았을 것이다.

 

보통 요런 지역 변수들은 stack에 저장되어있다가, 함수가 종료되면 알아서 사라지니 따로 free해줄 필요가 없다.

(오랜만이라 heap 동적할당과 잠시 헷갈렸나보다)

 


더 재밌는 것 : 포인터 선언으로 할당받은 공간에 문자열 넣기

 

64비트 시스템에서 포인터는 8바이트 크기를 가진다.

포인터 선언만 해도 메모리에 8바이트를 할당해준다는 소리인데, 그럼 이 공간에 문자열을 넣을수도 있겠지?

const char* src = "abcdefg"; // null문자 (\0)까지 합해서 길이 8
void* testPtr; // testPtr은 8바이트 메모리 할당받음
void** pptr = &testPtr; // 그 주소를 Get - 이게 char* 역할을 할 것
strcpy((char*)pptr, src); // 포인터 선언으로 할당받은 공간에 문자열 넣기

// 잘 됐는지 체크
cout << src << "\n";
cout << *(char *)pptr << "\n"; // 이러면 문자 하나만 출력되네?
cout << (const char *)pptr << "\n";

pptr에 문자열을 리틀 엔디안 방식으로 저장;&amp;amp;nbsp;https://tcpschool.com/c/c_refer_endian

보면, pptr은 성공적으로 testPtr이 할당받은 8바이트 메모리의 주소를 따올 수 있었고,

이를 char*로 캐스팅해서 8바이트 위치에 길이 8 (널문자 포함) 짜리 문자열을 복사해 넣을 수 있었다. 

 

cout으로 출력해보니 아주 잘 된다!

 

당연하지만, const char* src = "abcdefgh"; 로 바꿔서 길이가 8바이트를 넘어가면 stack corruption에러가 뜬다.

(C++은) 지역변수를 스택에 저장하므로, stack corruption이 뜨는 것 같다.  

 

+) 맨 처음에 visual studio의 build configuration이 Win32로 되어있어서 포인터가 4바이트 크기를 가지더라.

어쩐지 문자열 크기가 4를 넘어가면 stack corruption 뜨더만.

 

 

 

💡 이게 가능한 이유는, 시스템은 메모리를 type별로 구분해서 나눠놓지 않기 때문.

메모리는 그냥 contiguous memory이고, 프로그래머가 어떤 type으로 메모리를 참조하느냐에 따라 참조하는 영역이 달라진다. (이걸 무슨 개념이라고 하는지 잘 모르겠다)

ex) double* a = 1; 처럼 8바이트를 할당받고, *(int *)a로 참조하면 4바이트만 참조하겠지

그래서 일단 void 포인터로 주소 받은 다음에, 내 입맛대로 캐스팅해서 원하는 데이터 넣을 수 있다!

 

이 개념을 OS 코드에서 잘 써먹었지.

rsp는 stack 포인터인데, 일단은 8바이트 값으로 나타내고 있었다.

일단 void 포인터로 rsp 주소를 얻은 다음에, rsp를 변경하거나 rsp가 가리키는 스택 위치에 값을 집어넣을 수 있었다.

void **rspp = &_if.rsp; // 로 _if.rsp값 바꿀 수 있다. (rsp는 포인터로 해석되는 8바이트 값)
(*rspp)--;
(*rspp) -= 8;

// rsp가 가리키는 스택 위치에 새로운 value 삽입도 가능
**(char **)rspp = 'a'; // char (c 문자열 = array of char이므로, 문자 하나씩 삽입 노가다)
**(uint8_t **)rspp = (uint8_t)0; // 숫자
**(char ***)rspp = argv[0]; // char pointer 넣기 (놀랍게도, 됨)

 

 

(15Feb22) 기훈이 C언어 질문 (pass 2D array as function parameter) 답해주다가 발견. 

일단 배열 선언하면, WxH 크기만큼 contiguous memory 할당해줌. (Visual Studio 디버그-창-메모리 클릭해서, 메모리 주소에 들어있는 값 보는 창 띄우기 가능)

거기에 값 채워넣기 가능!

#include <stdio.h>

void printArray(int a, int b, int arr[a][b]){
    for(int x = 0; x < a; x++) {
		for(int y = 0; y < b; y++) printf("%d ", arr[x][y]);
		printf("\n");
	}
}

int main(void) {
	int arr[3][4]; // ❓ 선언만 하고 값 채워넣기 가능? → ✔️ 가능!
	for(int x = 0; x < 3; x++) for(int y = 0; y < 4; y++) arr[x][y] = x*4 + y;

	// printArray(3, 4, arr);
	printArray(sizeof(arr)/sizeof(arr[0]), sizeof(arr[0])/sizeof(int), arr);

	return 0;	
}

'<언어> > [C]' 카테고리의 다른 글

How is constant stored in the memory? (+ Python Integer caching)  (0) 2022.01.29
배열(Array) 생성  (0) 2021.10.07
memset 초기화  (0) 2021.08.12
Passing C-style 1-D/2-D array as a parameter  (0) 2021.03.17
후위 증감연산자와 *ptr++  (0) 2021.03.17

+ Recent posts