[C] 씹어먹는 C 언어 - <12 - 1~3. 포인터 >

Kim Dongil·2022년 10월 18일
0

C

목록 보기
16/28

10/18
보고 또 보고 또 봐도 제대로 이해를 하지 못했다. (12-3 중후반 부분)
가슴이 답답하고 마음 한 구석에 응어리가 있다.
내일 다시 처음(12-1)부터 읽어봐야 겠다.

10/19

1.

이 문장에선 parr 이라는 포인터변수를 선언하고 arr의 주소를 가리키게 했고
parr++ 를 했을때 int형 포인트 이므로 1*4 를해서 주소값에 4바이트 만큼 증가시키고 정수는 1 증가가 가능하다고 한다

그런데 어째서 arr 배열을 선언하고 arr++ 를 하면 오류가 나는지 이해가 안된다

배열의 이름이 첫 번째 원소를 가리키는 포인터로 타입 변경 된다고 했을 때, 이는 단순히 배열의 첫 번째 원소를 가리키는 주소값 자체가 될 뿐입니다. 따라서 arr++ 문장은 C 컴파일러 입장에서 다음을 수행한 것과 같습니다.
첫 번째 원소를 가리키는 주소값 이 된다면 ++ 를 했을때 어째서 오류가 나는 것일까?
그저 정말 딱 저 문장만 약속으로 해둬서 그런걸까?

  1. 해답 : 배열은 배열이고 포인터는 포인터 입니다. arr 은 배열이고, 포인터와는 아예 별개 입니다. 다만 arr 을 사용할 때 마치 배열의 시작 주소값을 나타내는 포인터 처럼 변환될 뿐입니다. arr++ 은 배열에서 애초에 지원되지 않는 연산입니다.

2.


parr 은 int* 를 가리키는 포인터이고, int*의 크기는 8바이트 이기 때문에 parr+1 은 주소값이 8 증가해서 세번째 원소의 주소값을 갖게되어서 3이 된다는건 이해를 했다.
그런데 *(parr+1)+1 부분에서 int 크기만큼 주소값이 4가 늘어낫는데 주소값이 총 12가 늘었다는 뜻 아닌가?
4가 아닌 7이 되었다는게 이해가 안된다.

그렇다면 이 코드는 무슨 일을 했었을 까요? << 에서의 예제와 내용중에 int*가 64bit이기때문에 parr+1을 했을 경우 8byte가 증가한다는건 이해가 됩니다. 그런데 그다음에 *(parr + 1) + 1 에서 int 크기 만큼 증가를 하신다고 하셨는데 왜 3의 다음인 4가 아니라 index가 4만큼 증가인 7이 나오는건가요 제가 어느부분이 모잘라서 그런지 모르겠습니다. 혹시 여기에 관해서 설명이 가능할까요 int * 타입이라고 하셨는데 왜 int 크기 만큼 증가인지 잘모르겠습니다. 정말 너무 궁금합니다

문제 1. * 와 & 연산자의 역할이 무엇인지 말해보세요 (난이도 : 下)

* :
1. 단행 연산자로 쓰일 때에는 주소값에 저장되어있는 값을 불러오게 됩니다.
2. 피연산자가 두 개일 때는, 두 값을 곱해주는 산술 연산자로 역할하게 됩니다.

& :
1. 단행 연산자로 쓰일 때에는 해당 값이 자장되어있는 주소값을 불러오게 됩니다.
2. 피연산자가 두 개일 때는 AND를 의미하는 논리 연산자로 역할합니다.

문제 2. int **a; 와 같은 이중 포인터(double-pointer) 에 대해 생각해 보세요 (난이도 : 中上)

포인터는 변수의 주소값을 저장합니다.
포인터도 변수입니다.
포인터도 주소값을 가집니다.
이중포인터는 포인터의 주소값을 가리킵니다.

const int =
1. 가리키는 대상의 값을 바꿀 수 없다.
2. 가리키는 대상(주소)은 바꿀 수 있다.
int
const =
1. 가리키는 대상의 값을 바꿀수 있다.
2. 가리키는 대상(주소)은 바꿀 수 없다.

암기 팁 : 앞에 있는 수식어를 생각 하자

문제 3. int arr[3][3]; 과 같은 배열은 내부적으로 어떻게 처리되는지 생각해보세요 (난이도 : 中)

*(*(arr+3)+3)


처음에 정답을 보고 위에 사람처럼 의문이 생겼다
그리고서 아래의 글과 코드를 보고 이해가 됐다.

arr +1 라는 코드의 의미는 배열의 원소가 가진 크기만큼 1번 더하라 라는 의미입니다.
arr의 크기는 arr[x]의 경우엔 16, arr[x][y]의 경우에는 4가 나옵니다.
여기서 arr은 arr[x]의 첫번째 원소를 가르키는 주소값을 나타내죠. 따라서 첫번째 원소의 크기만큼 16의 주소값이 이동하게 됩니다. arr + 1은 결국 arr[x+1]의 주소값이 되는 셈이죠. 이후에도 마찬가지로 arr[3][0]의 값을 나타내는 *(a+3)에 3을 더하는 행위는 arr[3][y]의 크기인 4만큼 3번을 더하라는 소리가 되어 12만큼의 주소값이 이동하게 됩니다.
+1이라는것이 배열의 한칸 한칸을 이동하라는 의미가 아닌 배열의 원소의 크기(메모리적인)만큼 이동하라는 의미라고 생각하시면 됩니다.

#include <iostream>

int main() {
	int arr[4][4] = { 0 };

	for (int i = 0; i <4; i++)
		for (int j = 0; j < 4; j++)
			printf_s("arr[%d][%d]의 주소값 = %p\n", i, j, &arr[i][j]);



	printf_s("*(arr+3)의 주소값 = %p\n", arr + 3);

	printf_s("*arr 의 크기 = %d\n", sizeof(*arr));
	printf_s("*(*(arr+3)+3)의 주소값 = %p\n", *(arr + 3)+3);

	return 0;
}

위 코드를 돌려보면 아래와 같은 결과값이 나옵니다.

arr[0][0]의 주소값 = 0019FC24
arr[0][1]의 주소값 = 0019FC28
arr[0][2]의 주소값 = 0019FC2C
arr[0][3]의 주소값 = 0019FC30
arr[1][0]의 주소값 = 0019FC34
arr[1][1]의 주소값 = 0019FC38
arr[1][2]의 주소값 = 0019FC3C
arr[1][3]의 주소값 = 0019FC40
arr[2][0]의 주소값 = 0019FC44
arr[2][1]의 주소값 = 0019FC48
arr[2][2]의 주소값 = 0019FC4C
arr[2][3]의 주소값 = 0019FC50
arr[3][0]의 주소값 = 0019FC54
arr[3][1]의 주소값 = 0019FC58
arr[3][2]의 주소값 = 0019FC5C
arr[3][3]의 주소값 = 0019FC60
*(arr + 3)의 주소값 = 0019FC54
*arr 의 크기 = 16
* (*(arr + 3) + 3)의 주소값 = 0019FC60

문제 4. int* arr[3]; 과 같은 배열이 가지는 의미는 무엇일까요? (난이도 : 中)

int* 변수 3 개를 가지는 배열

문제 5. 3 차원 배열의, 배열이름과 동일한 포인터는 어떻게 정의될 것인가? (난이도 : 中) (참고 : 2 차원 배열에선 int (*arr)[4]; 와 같은 꼴이었다)

  1. 3차원 배열은 2차원 배열의 구조를 n개 가지고 있는 거기 때문에, int(*arr)[2][3]으로 선언 할 수 있을 것 같습니다. 배열의 크기는 [2][3]으로 알려주고 포인터가 가리키는 값이 int형 타입이라는 것을 컴파일러에게 알려줍니다.
  2. 다차원 배열에 헷갈리지 않으려면 한 가지만 확실히 생각하면 될 것 같습니다. 개념적으로 우리는 머리속으로 다차원으로 생각하지만 실제 메모리에서는 1차원으로 저장할 수 밖에 없다는 것.

따라서 아무리 차원이 높아져도 결국 메모리에는 1차원으로 죽 이어서 나열하게 되는 것이고, 포인터는 그 배열의 시작 주소를 가리키면 됩니다.
즉, 배열의 차원이 아무리 높아져도 포인터의 차원에는 아무런 영향이 없다는 뜻이고, 다시 말해, 배열의 차원이 높아진다고 포인터가 이중 포인터가 되거나 하는 것과는 전혀 연관이 없다는 것 입니다.
따라서, 배열의 차원이 높아질 때마다 포인터의 정의에 그 차원에 따른 부가적인 정보만 더해주면 될 것 같습니다.

  • 2차원 배열:1차원 배열에서 부가적으로 열의 정보 x가 필요하므로,
    int(*parr)[x] 와 같이 정의한다.
  • 3차원 배열: 2차원 배열에서 부가적으로 3차원 y의 정보가 필요하므로,
    int(*parr)[x][y]와 같이 정의될 것이다.
  • 4차원 배열: 마찬가지로, int(*parr)[x][y][z]로 될 것이다.

배열의 차원이 높아지더라도 실제 메모리에서는 1차원이고, 포인터는 단지 그 배열의 시작주소를 가리키는 것이라고 생각하면 헷갈리지 않는 것 같습니다.

문제 6. 포인터 간의 형변환은 무엇을 의미하는가? 그리고, C 언어에서 포인터 간의 형변환이 위험한 것인가? (난이도 : 中) (참고적으로, 포인터간의 형 변환은 아직 이야기 한 적이 없으나 한 번 시도는 해보세요)

  1. 포인터 간의 형변환은 포인터가 가리키는 시작 주소는 같으나 그 시작 주소로 부터 읽어오는 크기가 다르기 때문에, 참조되지 않은 메모리 값을 참조할 수 있기 때문에 위험할 것 같다.

  2. 포인터 간의 형변환은 큰 크기 타입(double,int) 을 작은 타입(char)로 나눔으로써 데이터 값을 정교하게 바꿀 수 있다는 것을 의미. 작은 값(char형)을 큰 값(ex.int형)으로 바꾸는 것은 위험하지 않지만, 그 반대는 값을 손실 할 수 있어 위험하다.

씹어먹는 C 언어 - <12 - 1. 포인터는 영희이다! (포인터)>

0개의 댓글