[★C] 포인터

방법이있지·2025년 6월 8일
post-thumbnail

포인터는 다른 변수의 주소를 저장하는 변수입니다. 흔히 '포인터가 변수를 가리킨다'고도 표현합니다.

주소와 포인터

주소와 주소 연산자(&)

  • 메모리의 위치를 식별하는 주소는 바이트 단위로 구분됨
  • 2바이트 이상의 크기의 변수는 여러 연속된 주소에 걸쳐 할당됨
    • e.g., int형 변수 a가 메모리 100 ~ 103번지까지 4바이트에 걸쳐 할당됨

  • 변수의 주소는 변수가 할당된 메모리 공간의 **첫 번째 바이트 주소**를 의미함
    • e.g., inta의 주소는 100
  • 주소 연산자 &: &변수명으로 변수명의 주소를 구할 수 있음

포인터와 간접참조 연산자(*)

  • 포인터: 주소를 저장하는 변수
  • 자료형 *포인터명 형태로 포인터 선언
    • 자료형엔 저장하려는 주소에 위치한 변수의 자료형을 적음
    • e.g., int *pa: int형 변수의 주소를 저장하는 포인터 pa를 선언
  • 자료형을 가리키는 포인터를 (자료형) *형 포인터라고도 부름
    • e.g., paint *
int a;      // 변수 a 선언
int *pa;    // 포인터 pa 선언
pa = &a;    // pa에 a의 주소 대입 -> 포인터 pa는 변수 a를 가리킴
  • pa = &a로 포인터 pa에 변수 a의 주소 (&a)를 저장
    • int *pa = &a와 같이 포인터의 선언과 동시에 초기화도 가능
  • 이와 같이, 포인터가 변수의 주소를 저장하는 것을 포인터가 변수를 가리킨다라고 부름
    • 위 예제에선 paa를 가리킴 (pa -> a)

// 위 코드에서 계속
*pa = 22;   // 포인터로 변수 a에 22 대입

printf("변수명으로 a의 값 출력: %d\n", a);
printf("포인터로 a의 값 출력: %d\n", *pa);

// [출력]
// 변수명으로 a의 값 출력: 22
// 포인터로 a의 값 출력: 22
  • 간접참조 연산자 *: *포인터명으로 포인터명이 가리키는 변수를 사용할 수 있음
    • *pa = 22: pa가 가리키는 a22를 대입
    • printf("%d\n", *pa): pa가 가리키는 a의 값 22를 출력
  • 포인터 선언할 때 *는 간접참조 연산자 *와 동일한 기호지만 의미가 다름!!

포인터의 특징

포인터는 변수, 주소는 상수

  • 특정 변수의 주소는 상수, 값이 바뀌지 않음
    • &a = &b와 같이 a의 주소를 b의 주소로 바꾸는 건 불가능
  • 포인터는 상수, 다른 주소를 대입해 값을 바꿀 수 있음

int a = 5, b = 10;   // int형 변수 a, b 선언
int* p = &a;         // int*형 포인터 p 선언: a를 가리킴
p = &b;      // p에 b의 주소 대입해 값을 바꿈

포인터와 주소의 크기

int a;
printf("주소의 크기: %d바이트\n", sizeof(&a));
printf("int형 변수의 주소: %p\n", &a);
// int형 변수의 주소: 00000000005FFE8C
// 주소의 크기: 8바이트
  • 64비트 시스템 기준, 주소의 크기는 8바이트
    • 주소 위치의 자료형과 상관없이, 항상 8바이트
  • 실제로 주소는 16진수 16자리로 표현됨
    • 16진수 1자리는 4비트를 나타내므로, 16자리는 4×16=644 \times 16 = 64비트 -> 88바이트
// 위 코드에서 계속
int *pa = &a;
printf("포인터의 크기: %d바이트\n", sizeof(pa));
// 주소의 크기: 8바이트
  • 포인터는 주소를 저장하는 변수이므로, 포인터의 크기도 주소의 크기와 동일한 8바이트
    • 포인터가 가리키는 자료형과 관계없이, 항상 8바이트
  • 특정 자료형의 주소 또는 해당 자료형을 가리키는 포인터는, (자료형) * 형이라고 부름
    • 즉 위 코드의 pa, &a는 모두 int *

포인터 간 대입

  • 포인터의 크기는 모두 동일하지만, 항상 대입 연산이 가능하진 않음
  • 같은 자료형을 가리키는 포인터끼리만 대입 가능
// 가능한 경우
int a = 5;
int *pa, *pb;
pa = &a;   // pa는 a를 가리킴
pb = pa;   // pb에 pa의 값을 대입 -> pb도 a를 가리킴

// 불가능한 경우
double *pc;
pc = pa;

// [실행결과] error: assignment to 'double *' from incompatible pointer type 'int *'
  • double*형 포인터 pc에, int*형 포인터 pa의 값을 대입할 수 없음

두 변수의 값 바꾸기

  • 함수 간에 데이터를 공유하기 위해선, 포인터를 사용해야 함
  • 대표적인 예제로는, 두 변수의 값을 바꾸는 함수가 있음

잘못된 방법

#include <stdio.h>

void swap(int x, int y);

int main(void){
    int a = 33, b = 9;
    swap(a, b);
    printf("a: %d, b: %d\n", a, b);
    return 0;
}

void swap(int a, int b){
    int temp;
    temp = a;
    a = b;
    b = temp;
}

// [출력] a: 33, b: 9

  • swap을 호출하면 main 함수 a, b값만 복사되어 swap 함수의 a, b에 저장됨
    • 변수의 이름이 같아도 함수가 다르면, 별도의 저장 공간을 갖는 다른 변수임
  • swap 내부에선 복사본의 값만 변경되며, maina, b의 값에는 변동이 없음

포인터를 사용하는 경우

#include <stdio.h>

void swap(int *pa, int *pb);

int main(void){
    int a = 33, b = 9;      // int형 변수 초기화
    swap(&a, &b);           // a, b의 주소를 swap 함수에 인수로 보냄
    printf("a: %d, b: %d\n", a, b);
    return 0;
}

void swap(int *pa, int *pb){
    int temp;
    temp = *pa;             // temp에 pa가 가리키는 변수의 값 저장
    *pa = *pb;              // pa가 가리키는 변수에 pb가 가리키는 변수의 값을 저장
    *pb = temp;             // pb가 가리키는 변수에 temp의 값 저장
}

// [출력] a: 9, b: 33

  • swap 함수에 a의 주소 &ab의 주소 &b를 매개변수로 보냄
    • paa를, pbb를 가리킴
  • 함수 내에서 *pa, *pb를 사용해 해당 주소의 실제 변수 값을 변경

연습문제

키보드로 실수 3개를 입력한 후, 큰 숫자부터 작은 숫자로 정렬한 뒤 출력하는 프로그램을 작성하세요.

  • 버블 정렬 방식으로 양옆 값을 비교하고 필요하면 교환
  • 정렬 과정에서 두 변수의 값을 바꿔주기 위해, 앞선 swap 함수를 응용해서 구현
  • 함수 간 데이터 전달을 위해, 변수의 주소를 포인터 형태로 전달
#include <stdio.h>

// 두 실수의 값을 변경
void swap(double* pa, double* pb);

// 세 변수의 값을 정렬
void line_up(double* p1, double* p2, double* p3);

int main(void){
    double d1, d2, d3;

    printf("실수값 3개 입력: ");
    scanf("%lf %lf %lf", &d1, &d2, &d3);
    line_up(&d1, &d2, &d3);

    printf("%.1lf, %.1lf, %.1lf", d1, d2, d3);
    return 0;
}

void swap(double* pa, double* pb){
    double temp;
    temp = *pa;
    *pa = *pb;
    *pb = temp;
}

// 버블 정렬 방식으로 양옆 원소 교환
// swap이 주소를 매개변수로 받으므로
// 주소가 저장된 포인터를 인수로 보내면 됨
void line_up(double* p1, double* p2, double* p3){
    if (*p1 < *p2) swap(p1, p2);
    if (*p2 < *p3) swap(p2, p3);
    if (*p1 < *p2) swap(p1, p2);
}

// [입력] 실수값 3개 입력: 3.5 1.7 2.4
// [출력] 3.5, 2.4, 1.7
profile
뭔가 만드는 걸 좋아하는 개발자 지망생입니다. 프로야구단 LG 트윈스를 응원하고 있습니다.

0개의 댓글