본문 바로가기

C

C 배열과 포인터 그리고 함수 적용

개인 복습용입니다
더 디테일한 사항은 생략된 것이 많습니다 


개인적으로 알기만 해도 좋은 포인터에 대한 개념과
함수에 적용시키며 어려웠던 점을 기술해본다

 

 

C 의 꽃은 포인터인가?

듣기로는 C 에서 많은 사람들이 포기하겠습니다를 외치는 부분이 포인터라고 한다

이유는 간단하다

컴퓨터의 작동 원리를 이해하면서 동작 시켜야 하는데

솔직히 코드를 글처럼 타닥타닥 치다가

갑자기 원리를 이해하고 어떻게 동작하는지 알아야 한다 하면

너무나 어렵다...

 

코딩이 마치 논리에 의한 소설을 적는 기분이었다면

포인터를 적용 시킬땐 진짜 안맞는 사람이랑 대화하는 기분이 든다

 

나 : "너 이거 왜 못해?"

컴퓨터 : "니가 이상하게 말했는데?"

나 : "아닌데? 이거 분명 돼야되는데?"

컴퓨터 : "아닌데? 이거 잘못됐는데?"

나 : ???

 

포인터를 조금 쉽게 나만의 방식으로 이해해보자

 

 

 

1. 포인터의 개념

int a = 10;
int b[] = {1, 2, 3, 4, 5};
int *px = &x;
int *pa = &a;
int *pb = &b;

예시 코드로 살펴보자

 

왜 처음보는 * 가 있는거지?

&는 scanf 함수에 변수로 받아들일때 쓰거나 and 논리연산 쓸 때 쓰는거 아닌가?

일단 다 무시해보자

 

변수명이 너무 어렵다

가장 첫 두줄에 있는 변수는 배열까지 배우신 분들이라면 충분히 납득 가능할 것이다

 

문제는 *가 들어가 있는 것들인데

내가 보고 있는 책에는 *pa 라고 변수를 해서 더 헷갈린다

 

한마디로 말해 * 자체가 포인터로 가리키고 있다라는 뜻이고,

& 기호는 a 변수의 주소를 나타내는 것이며,

* 기호는 a 변수의 주소의 값, 즉 위 예시 코드에서의( a = 10) 10을 가리키는 것이다

 

컴퓨터의 메모리에는 주소라는 것이 할당되어

내가 a = 10 이라는 변수를 선언하면 수많은 메모리의 주소 중 하나의 주소에 그 값이 선언되는 것이다

 

그래서 그 주소를 부르려면 &a 를 사용하는 것이고,

그 주소에 들어있는 값을 부르려면 *a 를 사용하는 것이다

 

주소 = 우체통 / 값 = 집

 

단번에 이해하기 어렵겠지만 위와 같이 예시를 들어보았다

& 기호로 우체통, 집 앞의 지번을 찾아가고

* 기호로 집 그 자체를 찾아간다는 식의 비유이다

 

 

이것저것 설명하면서 그 주소가 어떻게 이루어져있고

주소란 무엇이고 어쩌구 저쩌구 말이 많지만

위의 개념부터 착실히 잡혀야 추후의 것들이 이해된다고 생각한다

 

 

 

 

2. 포인터 연산

포인터는 연산이 가능하다

*p 라는 포인터가 있다면 해당 포인터에 1을 더하거나 뺄 수 있다

#include <stdio.h>
int main(void)
{
	int data[5] = { 1, 3, 5, 7, 9 };
	int* p = &data[0];

	printf("%d\n", *p + 1);			// * 포인터의 위치의 값에서 +1
	printf("%d\n", *(p + 1));		// * (포인터의 위치 +1)한 위치의 값
	printf("%d\n", (*p + 1) + 2);		// * (포인터의 위치의 값) + 1
	printf("%d", *(p)+1);			// * (포인터의 위치의 값) + 1
}

 

실행 결과

 

무슨 말인지 이해가 가지 않는다

 

* p 는 포인터의 위치에 있는 주소의 값을 불러오는데, 위에 집 모양의 그림을 예시로 들어보자면

& 는 우체통으로 주소를 불러오지만 * 는 그 주소의 값을 불러온다 하니,

* p 는 실질적으로 집 안에 들어있는 데이터를 불러온다

 

그런데 * p 는 어떤 데이터를 가리키는 걸까?

값이 하나라면 그 값을 가져오지만 값이 배열이라면 0번 인덱스의 값을 가리키고 있다

 

따라서 아래와 같이 표현할 수 있다

* p = &data[0];

p 변수에 data[0] 의 주소 & 를 넣어주고

* 기호로 그 주소 안의 값을 불러오게 되니

* p 를 호출하면 data[0] 의 값을 불러온다

 

그럼 위 코드에서 연산된 결과를 살펴보면,

1) * p 의 값에서 +1 한 값은 == data[0] : 1 의 값에서 1을 더한값 → 2
2) * (p + 1) == data[0 + 1] == data[1] → 3
3) (* p + 1) == data[0] == 1 + 1 → 2    2 + 2 → 4
4) *(p) + 1 == data[0] == 1                   1+ 1 → 2

이렇게 된다

 

 

 

3. SWAP(별개 내용)

포인터 관련된 문제를 풀면서

정말정말 많이 쓰일것 같은 것이 swap 이다

 

배열의 순서를 오름차순, 내림차순으로 정렬시키거나

기존의 인덱스와 특정 인덱스의 순서를 바꾸는 것인데,

이전 수업에서도 swap 관련된 내용을 얼핏 들은 기억이 났다

 

포인터에 한정된 것은 아니나 swap에 대해 기록해두려 한다

 

데이터 SWAP

일반적으로 생각하기론 data[0] 인덱스와 data[1] 인덱스를 서로 바꿔주면 

그것이 바로 swap 이라고 생각이 들 것이다

 

나도 그렇게 생각했고,

당연히 둘을 그냥 바꿔주기만 하면 자리가 바뀌겠거니 했다

 

하지만 아니다

 

컴퓨터의 메모리는 저런식으로 생각하고 data[0] 인덱스의 데이터를를 data[1] 인덱스로 넣어주면

data[0]의 데이터는 그대로 있고 data[1] 인덱스로 data[0]의 데이터가 복사되는것 뿐이다

 

즉, data[1] 인덱스의 데이터는 사라지고

data[0], data[1] 인덱스 모두 data[0] 의 데이터가 남아있다

 

따라서 컴퓨터에서의 swap은 아래와 같이 이루어진다

 

데이터 SWAP 과정

하나의 임의 변수에 data[0]을 넣고,

data[1]을 data[0]으로

그리고 data[1] 자리에 임의 변수에 들어있던 data[0]을 넣는다

 

temp = data[0];
data[0] = data[1];
data[1] = temp;

코드로 짜보면 대략 위와 같이 된다

 

 

 

4. 포인터와 함수

배열의 함수와 크게 다를것은 없다

* 포인터 자체는 배열의 [0] 인덱스의 주소를 부른다는 것만 알면

쉽게 응용이 가능하다

 

함수는 기본적으로 인자를 넘기면 함수 안에서만 데이터에 대한 변형이 이루어지고,

함수를 빠져 나오는 순간 계산시킨 값은 리턴이 아니면 돌아오지 않는다

 

하지만 C에서의 리턴은 1개의 값만 리턴하게 되니

여러개의 값에 대한 변형을 시키려면 포인터로 구성해야 한다

 

즉, a[10] 배열에 대한 주소 값을 함수의 인자로 넘겨주면

함수 안에서의 계산 결과가

직접 a[10] 배열의 값을 바꾸는 것으로 작용한다는 것이다

 

int swap(int *arr)
{
	int temp;
	temp = arr[0];
	arr[0] = arr[1];
	arr[1] = temp;

	return 1;
}

int main(void)
{
	int i;
	int data[SIZE] = { 1, 3, 5, 7, 9 };
	swap(&data[0]);		// 인자로 그냥 data 만 써도 동일한 내용

	for (i = 0; i < SIZE; i++)
		printf("%d ", data[i]);
}

 

실행 결과

인수로 넘겨주는 &data[0] 과 그냥 data 는 동일한 의미를 가진다

 

나는 함수로 data 배열을 넘겨주고 그 안에서 스왑을 시켰음에도

함수를 빠져 나온뒤 data 배열을 출력하면

의도한대로 swap 되어 값의 [0] 인덱스와 [1] 인덱스의 순서가 바뀌어있는 것을 알 수 있다

 

 

 

 

5. 연습문제

[정렬 전]과 같은 배열을 만들고, 정렬하는 함수를 만들어 정렬하고
[정렬 후] 배열을 만들어 그 결과를 출력하도록 해보자

정렬 전 : 92 34 76 32 15 28 41 55 89 62
정렬 후 : 15 28 32 34 41 55 62 76 89 92

 

 

 

#include <stdio.h>
#define SIZE 10

int print_array(int *arr, int size);
int swap_array(int *arr, int size);

int main(void)
{
	int arr[SIZE] = { 92, 34, 76, 32, 15, 28, 41, 55, 89, 62 };

	print_array(&arr[0], SIZE);
	swap_array(&arr[0], SIZE);

	print_array(arr, SIZE);
}

int swap_array(int *arr, int size)
{
	int i, j, temp, index;
	for (i = 0; i < size - 1; i++) {
		index = i;
		for (j = i + 1; j < size; j++) {
			if (arr[index] > arr[j])
				index = j;

		}
		if (i != index) {
			temp = arr[i];
			arr[i] = arr[index];
			arr[index] = temp;
		}
	}
	return 0;
}

int print_array(int *arr, int size)
{
	int i;
	for (i = 0; i < size; i++)
		printf("%d ", arr[i]);
	printf("\n");
	return 1;
}

 

 

<< 어려웠던 점 >>

함수에 배열 데이터를 넣고 포인터로 불러들일 때 

아직 함수 활용에 익숙치 않아 데이터를 맞게 인수로 준거 같은데도 불구하고 

계속해서 오류가 발생했다

 

어떤 원인인지 잘 모르고 계속 만지던 탓에 

결국 프로그램이 뒤죽박죽 되어 처음부터 시작하기도 했지만

해당하는 오류가 말하는 원인이 무엇인지 알게 되었고

해결할 수 있는 방법 그리고 함수 적용에 있어 많이 익숙해졌음에 뿌듯하다

 

+ 배열의 순차 정렬 등에 활용하는 프로그램은

처음 봤을 때 이해하기 어려웠다

 

하지만 내가 그린 그림처럼 이해하니 이해가 쉬웠고

이전에 설명해주셨던 내용에서 문득 기억이 나 적용하게 된 프로그램이라 그런지

더욱 감회가 새롭다

'C' 카테고리의 다른 글

C vs Python 함수와 배열  (0) 2023.02.23
C vs Python 조건문, 반복문  (4) 2023.02.22
C 입출력 ASCII 코드 그리고 비트 연산자  (1) 2023.02.20