포인터? 배열?
학부 1학년 때 제일 먼저 C언어를 배웠었다.
학기가 끝나보니 1학년 답게 연습문제와 평가만 풀 수 있었지 남는건 없었다.
그 이후 몇년동안 파이썬, 자바, CS를 거치고 다시 포인터를 보니 이제야 좀 이제 약?간 이해가 되는 듯 하다. (사실 잘 모름)
포인터
포인터는 간단하게 설명하면 주소를 값으로 갖는 변수이다.
선언은 (간접 접근시 얻을 수 있는 자료형)* (변수이름)으로해 int* a 와 같이 선언할 수 있다.

a의 주소를 포인터 변수 b에 저장하는 코드이다.
보면 lea를 통해 rax에 a의 주소를 부르고 이를 b에 저장하는 것을 볼 수 있다.

주소를 가지는 포인터형 변수는 저장하고 있는 주소값에 있는 값을 역참조(간접 참조)를 통해서 불러올 수 있다.
보는 것 처럼
1. b의 값(a의 주소)를 rax에 불러옴
2. a의 값(b의 값 = a의 주소에서 불러오는 것이므로) 을 eax에 저장
3. eax의 값을 c에 저장
이렇게 3단계를 통해 불러옴을 알 수 있다.
Swap?
스왑은 포인터를 처음 배우면 가장 먼저 해보는 예제이다.
a와 b를 파라미터로 받고 이를 서로 바꿔 잘 바뀌는 지 확인해본다.
파이썬에서는 a, b = b, a와 같이 간단히 되긴하지만 대부분 가능하지 않다.
Swap을 이해하기 위해서는 Call By Value와 Call By Reference를 이해해야 한다.
Call By?
#include <iostream>
using namespace std;
void Swap1(int a, int b)
{
int temp = a;
a = b;
b = a;
}
void Swap2(int* a, int* b)
{
int temp = *a;
*a = *b;
*b = temp;
}
int main()
{
int a = 10;
int b = 20;
Swap1(a, b);
Swap2(&a, &b);
cout << "a: " << a << ", b: " << b << endl;
}
간단한 어디 책에서 자주 봤을 법한 Swap 예시이다.
Swap1은 Call By Value로 넘겨주는 방식, Swap2는 Call By Reference로 넘겨주는 방식이다.
Swap1은 바뀌지 않고, Swap2는 값이 바뀐다.
당연히 Call By Value보다 Call By Reference같은 가치 판단은 없다.
상황마다 사용하는 방법이 다를 뿐이다.

위는 해당 코드를 디스어셈블리 한 것이다.
Swap1은 b, a의 값을 edx, ecx에 복사하고 이를 통해서 연산할 것이다.
Swap2는 b, a의 주소를 직접 rdx, rcx에 복사한다.(주소가 64bit이므로 rdx, rcx)
따라서 Swap1에서는 값을 바꾸지 못하고, Swap2에서는 해당 주소값을 통해 원본 값에 역참조를 통해 접근해 값을 Swap할 수 있다.
포인터의 자료형?
포인터는 메모리 주소를 값으로 저장하고, 그 앞에 붙는 int, char등의 자료형은 해당 주소를 역참조했을 때 가져올 값의 자료형이다.
int* a = &b라면 *a를 통해 역접근 했을 때 나오는 값은 int인 것이다.
포인터가 주소를 가지는 값이라는 것을 더 잘 보여주는 예시가 있다.


여기서 그림 상단의 Debug 옆에 x86, x64를 보면 된다.
이는 어느 컴퓨터 아키텍쳐를 통해 디버그,릴리즈 컴파일링 할지 결정하는 것인데
x86은 32bit 운영체제, x64는 64bit운영체제이다.
x86환경에서는 메모리 주소를 표현하기 위해 32비트가 사용되고, x64에서는 64비트를 사용한다.
따라서 sizeof를 통해 포인터의 크기를 출력하면 각각 4바이트와 8바이트가 출력된다.
++
포인터에 덧셈 뺄셈이 가능한데 이는 배열과 함께 보자
배열
배열은 동일한 자료형을 가지는 데이터를 메모리에 연속적으로 저장하는 자료구조이다.
각 데이터는 인덱스를 통해 랜덤 접근이 가능하다.
랜덤 접근이 가능하기 때문에 O(1)의 시간복잡도로 접근한다.
다만 기본적으로 크기가 정해져 있고, 삽입, 삭제 연산에서 비효율적일 수 있다.
인덱스가 0부터 시작하면 첫 번째 요소의 주소는 배열의 시작 주소와 동일하기 때문에 인덱스는 0부터 시작해서 0, 1, 2 ... 로 늘어난다.

기본적인 배열 선언과 삽입의 예시이다.
보는 것 처럼 eax에 4(자료형 int의 크기)를 확보하고 이것과 3(데이터를 삽입할 상대적 위치)을 곱한다.
그리고 배열의 시작주소(arr)과 ecx를 더해 해당 위치에 데이터를 삽입한다.

0x00AFF840은 배열 arr의 시작 주소인데, 여기서 3바이트 떨어진 0x00AFF84C에 2가 저장된 것을 볼 수 있다.(리틀엔디안)
함수 인자로 배열 사용
배열을 함수인자로 사용하면 내부적으로는 포인터와 같게 사용된다.
포인터? 배열?
포인터와 배열의 차이는 포인터는 주소 자체를 저장하지만, 배열은 자료를 여러개 저장한다는 것이다.
배열의 주소를 출력하면 배열의 시작주소(배열의 이름)와 같게 출력되지만,
포인터는 주소를 담는 것이고 실제 주소는 또 따로 있다.

말로는 풀기 뭔가 애매한데, 코드를 보면 이해가 쉽다.
막상 정리하고보니 잘 이해하고 있는지 모르겠다
이중 포인터와 이중 배열도 잘 알아봐야지