C언어 - 가벼운 개념 정리 (포인터, 동적할당)

@developer/takealittle.time·2024년 10월 8일
1

Jungle

목록 보기
9/21


01. 포인터

int main (void)
{
	int num = 7;
    int *pnum;	// 포인터 변수 pnum 선언
    pnum = # // num의 주솟값을 포인터 변수 pnum에 저장
}
  • 정수형 변수 num을 선언하고, 7을 할당한다.
  • 정수형 변수의 포인터 pnum을 선언하고, '&' 기호를 이용 해 변수 num의 주소를 불러와 pnum에 할당했다.
  • 위와 같이 c 코드를 작성했다고 할 때, 다음과 같이 추상화 할 수 있다.
	// pnum에는 num의 주솟값이 할당되어 있다.
    // 이를 아래와 같이 출력할 수 있다.
    printf("%p",pnum);
    // 포인터 pnum을 가지고, 변수 num 값에 접근할 수 있다.
    // 이를 '역참조'라고 하며, 다음과 같이 * 기호를 이용할 수 있다.
    printf("%d",*pnum);
  • 위에서 보는 것처럼, pnum으로 변수 num의 주소를 가지고 있으면, 포인터 pnum을 가지고 '*' 기호를 통해 변수 num값에 접근할 수 있다. (역참조)

  • c에서 포인터의 개념은 이렇듯 해당 주소에 존재하는 값을 역참조 하여 값을 변경하는 등의 동작에 이용된다.

  • 포인터는 왜/어디에 쓰는걸까?
    C를 통해 알고리즘, 자료 구조 등을 구현 해보면 필요성을 느낄 수 있을 것이다.

'포인터의 개념 자체'가 어렵지는 않다.

  • 다만, (포인터 + 함수), (포인터 + 배열), (포인터 + 구조체) 등 다른 개념과 포인터가 혼용되기 시작하면 그 때부터 어렵게 느껴지기 시작한다.

  • 필자의 Tip : 작은 개념부터 천천히 확장해가며 이해하는 것이 개인적인 팁.

02. 포인터와 배열의 이름

02-1. 배열의 이름은 그 자체로 포인터이다.

  • 배열의 인덱스 표현과 포인터의 증감연산 표현을 통한 값의 참조는 혼용이 가능하다.
  • arr[i] = *(arr+i)

    위의 두 표현은 같은 표현이다.
  • 단, 배열의 이름에는 다른 변수의 주솟값을 할당할 수 없다.
    이 부분이 일반적인 포인터와는 다른 점이다. (이 외에 사용은 똑같다.)

02-2. 배열을 인자로 전달받는 함수의 선언

  • 정수형 배열을 함수의 인자로 받는 상황을 예로 들어보자.
void showArrayElem (int *param, int len)
void showArrayElem (int param[], int len)
  • 위와 같이 배열을 함수의 인자로 전달하는 방법에는 두 가지 방법이 있다.

3. Call-by-Value vs. Call-by-Address

3-1. Call-by-Value (값에 의한 호출)

  • 함수 호출 시 인자 값을 복사하여 함수에 전달
  • 함수 내에서 인자 값을 변경해도 원본 변수 값은 변경되지 않는다.
void increment(int x){
	x += 1; //원본 값은 변경되지 않음.
}

int main () {
	int a = 10;
    increment(a); // 함수 호출해도 a의 값은 여전히 10
    return 0;
}

3-2. Call-by-Address (주소에 의한 호출)

  • 함수의 인자로 변수의 주소를 전달 → 해당 주소를 통해 변수의 값을 변경. (간접 참조)
  • 포인터를 이용, 원본 변수에 접근 해 값을 수정함.
void increment (int* x) {
	*x += 1; // 포인터를 통해 원본변수의 값을 변경.
}
int main () {
	int a = 10;
    increment(&a); // 'a'의 값은 11로 변경 됨.
    return 0;
}
  • C++ 등에서의 'Call-by-Reference' 개념을 흉내 냄.

4. 포인터 대상의 const 선언

  • const : 변수의 상수화. (값을 못 바꿈)
const int* ptr = # // 포인터 '참조 대상' 변경 불가
int* const ptr = # // '포인터 변수'의 상수화 → 할당된 주소 값 변경 불가.

5. 이중 포인터 (Double Pointer)

  • 이중 포인터는 단일 포인터를 한 번 더 포인터로 지칭하는 wrapping 일 뿐, 어렵게 생각하지 말자.

  • 아래 예제 코드를 보고 이해할 수 있으면 된다.

int main () {
	double num = 3.14;
    double *ptr = #
    double **dptr = &ptr;
    double *ptr2;
    
    printf("%9p %9p \n",ptr,*dptr);
    printf("%9g %9g \n",ptr,**dptr);
    ptr2 = *dptr;
    *ptr2 = 10.99;
    printf("%9g %9g \n",num, **dptr);
    return 0;
}

<실행 결과>

0032FD00	0032FD00
    3.14	    3.14
   10.99	   10.99
  • 코드만 보고 이해가 어려울 때, 개인적인 팁은 참조 관계를 그림으로 나타내는 것이다.

int main () {
	double num = 3.14;
    double *ptr = &num;
    double **dptr = &ptr;
    double *ptr2;
 }
  • 출력을 시작하기 전의 선언부만 살펴보면, 위와 같이 참조 관계가 만들어진다.

6. 동적 할당 (malloc, calloc, realloc)

6-1. C언어의 메모리 구조

  • 동적 할당 개념을 살펴보기 전에, C언어의 메모리 구조를 간단히 살펴보자.

  • 코드 영역: 프로그램 코드 자체를 저장하는 영역

  • 데이터 영역: 전역 변수, static 변수 등 프로그램 종료 시까지 남아있는 데이터들을 저장하는 영역

  • 힙 영역: 작성자가 데이터를 원하는 시점에 생성 / 소멸시킬 수 있는 영역.
    → "동적 할당에 이용하는 영역"

  • 스택 영역: 특정 함수에서 사용되는 지역 변수, 매개변수 등
    함수를 빠져나가면 삭제되는 데이터를 저장하는 영역.

동적 할당의 개념을 이해하기 위해 주목해야 할 메모리 영역은 '힙 영역'이다.

6-2. 동적 할당

#include <stdlib.h>

int *ptr1 = (int*)malloc(sizeof(int));
free(ptr1);
  • C언어에서는 malloc( ), calloc( ) 등을 이용해 힙 영역에 원하는 크기만큼의 메모리를 할당하고, 원하는 시점에 free( )를 호출 해 이 영역을 소멸시킬 수 있다.

  • 위처럼 동적 할당을 이용해 할당한 메모리 공간은 보통 포인터로 연결하고, 역참조를 통해 원본 값을 접근하며 사용한다.

6-2-1. calloc( )

#include <stdlib.h>
// int형 5개 만큼의 메모리 공간 할당, 0으로 초기화
int *arr = (int*)calloc(5,sizeof(int));
  • calloc( )은 malloc( )의 사촌 뻘 되는 함수로, 동작 자체는 malloc과 같이 동적 할당한 메모리를 반환해주는 동작을 한다.

  • 차이점이 있다면, 우선 인자를 두 개를 받는다. 할당할 메모리의 블럭 개수와 블럭의 사이즈를 위와 같이 전달 받는다.

  • calloc은 자동으로 할당한 메모리의 초기값들을 0으로 초기화 해준다.

6-2-2. realloc()

// 길이가 7인 int형 배열로 확장
arr = (int*)realloc(arr,sizeof(int)*7);
  • realloc( )은 이미 동적 할당된 메모리의 영역을 재선언 할 때 사용한다.
    예를 들어, 위와 같이 작성하면 arr에 할당된 int형 메모리 공간을7개로 늘릴 수 있는 것이다.

동적 할당을 사용하는 이유?

  1. 동적 할당을 이용한 메모리 공간의 생성/소멸 시점을 작성자가 조절할 수 있다.
  2. 크기가 가변적인 배열의 선언을 구현할 수 있다.

이 포스팅은 필자가 개념을 돌아보기 위해 요점만 가볍게 작성하였으며, 디테일 한 설명은 부족할 수 있습니다.

  • C언어 : 포인터의 이해를 돕는 짤.jpg

참고 자료 / 이미지 출처 ::

  • 저서: 윤성우의 열혈 C 프로그래밍
profile
능동적으로 사고하고, 성장하기 위한. 🌱

0개의 댓글

관련 채용 정보