본문 바로가기
IT/c언어

C언어 배열과 포인터, 이것만 알면 끝! 💡 개념부터 활용까지 쉽게 배우기

by 영리더 2025. 7. 9.
728x90
반응형

C언어 배열과 포인터, 이것만 알면 끝! 💡 개념부터 활용까지 쉽게 배우기 

C언어를 배우다 보면 가장 어렵다고 느끼는 개념 중 하나가 바로 **'배열(Array)'**과 **'포인터(Pointer)'**입니다. 이 두 개념은 서로 긴밀하게 연결되어 있어 더욱 헷갈리게 느껴질 수 있죠. 하지만 C언어의 강력함은 바로 이 배열과 포인터에서 나온다고 해도 과언이 아닙니다.

오늘은 **2025년 최신 기준**으로 **배열과 포인터가 무엇인지, 왜 중요한지, 그리고 이 둘이 어떻게 긴밀하게 연결되어 사용되는지**를 그림과 쉬운 예시 코드를 통해 명확하게 설명해 드리겠습니다. 이 글을 통해 C언어의 핵심 개념을 확실하게 잡고 더 깊은 프로그래밍의 세계로 나아가세요!

---

🌟 1. 배열 (Array): 값을 담는 연속된 상자들

배열은 **같은 종류의 데이터를 여러 개 한꺼번에 저장할 수 있는 공간**입니다. 쉽게 말해, 옆으로 쭉 이어진 **'칸막이 없는 서랍장'**이나 **'연속된 사물함'**이라고 생각할 수 있습니다.

배열의 특징

  • **동일한 자료형:** 배열 안에는 오직 한 가지 종류의 데이터(예: 정수만, 문자만)만 저장할 수 있습니다.
  • **연속된 메모리 공간:** 배열의 요소들은 메모리 상에 빈틈없이 연속적으로 저장됩니다. 이것이 포인터와 연결되는 중요한 지점입니다.
  • **인덱스(Index) 사용:** 각 칸에는 0부터 시작하는 고유한 번호(인덱스)가 붙어 있어, 이 번호를 통해 원하는 칸의 데이터에 접근할 수 있습니다.

예시 코드: 배열 선언 및 사용

#include <stdio.h>

int main() {
    // 5개의 정수를 저장할 수 있는 배열 선언
    int scores[5]; 

    // 배열에 값 저장
    scores[0] = 90; // 첫 번째 칸 (인덱스 0)
    scores[1] = 85; // 두 번째 칸 (인덱스 1)
    scores[2] = 70;
    scores[3] = 95;
    scores[4] = 80; // 마지막 칸 (인덱스 4)

    // 배열의 값 출력
    printf("첫 번째 점수: %d\n", scores[0]);
    printf("세 번째 점수: %d\n", scores[2]);

    // 반복문을 이용한 전체 출력
    printf("모든 점수: ");
    for (int i = 0; i < 5; i++) {
        printf("%d ", scores[i]);
    }
    printf("\n");

    return 0;
}
                

위 코드에서 `scores[5]`는 `scores[0]`부터 `scores[4]`까지 총 5개의 정수 공간을 의미합니다.

배열 메모리 구조 이미지 예시

▲ 그림 1: 배열이 메모리에 연속적으로 저장되는 모습 (예시)

---

✨ 2. 포인터 (Pointer): 메모리 주소를 가리키는 손가락

포인터는 **메모리 주소를 저장하는 특별한 변수**입니다. 컴퓨터 메모리는 모든 데이터가 저장되는 거대한 아파트 단지와 같고, 각 방에는 고유한 **'주소'**가 있죠. 포인터는 바로 이 방의 주소를 가리키는 **'손가락'** 또는 **'내비게이션'**이라고 생각하면 이해하기 쉽습니다.

포인터의 특징

  • **주소를 저장:** 일반 변수가 값을 저장하는 것과 달리, 포인터 변수는 다른 변수의 메모리 주소를 저장합니다.
  • **주소 연산자 (`&`):** 변수 앞에 `&`를 붙이면 해당 변수의 메모리 주소를 얻을 수 있습니다. (예: `&num`은 `num` 변수의 주소)
  • **역참조 연산자 (`*`):** 포인터 변수 앞에 `*`를 붙이면 포인터가 가리키는 **주소에 저장된 실제 값**을 얻을 수 있습니다. (예: `*ptr`은 `ptr`이 가리키는 곳의 값)
  • **자료형:** 포인터는 자신이 가리킬 값의 자료형을 명시해야 합니다. (예: `int *ptr;`은 정수형 변수의 주소를 저장하는 포인터)

예시 코드: 포인터 선언 및 사용

#include <stdio.h>

int main() {
    int num = 10; // 정수형 변수 num 선언 및 초기화
    int *ptr;     // 정수형 변수의 주소를 저장할 포인터 ptr 선언

    ptr = #   // num 변수의 주소를 ptr에 저장

    printf("num의 값: %d\n", num);         // 10
    printf("num의 주소: %p\n", &num);       // 예: 0x7ffee5c01b1c (주소는 실행마다 다름)
    printf("ptr의 값 (num의 주소): %p\n", ptr); // 예: 0x7ffee5c01b1c (num의 주소와 동일)
    printf("ptr이 가리키는 값: %d\n", *ptr);  // 10 (num의 값과 동일)

    *ptr = 20; // ptr이 가리키는 곳 (즉, num)의 값을 20으로 변경
    printf("num의 변경된 값: %d\n", num); // 20

    return 0;
}
                

포인터 `ptr`을 통해 변수 `num`의 값을 읽고 변경할 수 있습니다. 이것이 포인터의 핵심적인 역할입니다.

---

🤝 3. 배열과 포인터, 뗄레야 뗄 수 없는 관계

C언어에서 배열과 포인터는 매우 밀접하게 관련되어 있습니다. 사실상 **배열의 이름 자체가 그 배열의 시작 주소를 가리키는 포인터처럼 사용될 수 있습니다.**

핵심 관계

  1. **배열 이름 = 첫 번째 요소의 주소:** 배열의 이름은 그 배열의 첫 번째 요소(인덱스 0)의 메모리 주소를 나타냅니다.
    int arr[3] = {10, 20, 30};
    printf("arr의 주소: %p\n", arr);      // 배열 전체의 시작 주소
    printf("arr[0]의 주소: %p\n", &arr[0]); // 첫 번째 요소의 주소
    // 두 주소는 동일하게 출력됩니다.
                        
  2. **포인터로 배열 요소 접근:** 포인터를 사용하여 배열의 요소에 접근할 수 있습니다.
    int arr[3] = {10, 20, 30};
    int *ptr = arr; // ptr이 arr의 시작 주소를 가리킴 (ptr = &arr[0]과 같음)
    
    printf("첫 번째 요소: %d\n", *ptr);      // *ptr == arr[0]
    printf("두 번째 요소: %d\n", *(ptr + 1)); // *(ptr + 1) == arr[1]
    printf("세 번째 요소: %d\n", *(ptr + 2)); // *(ptr + 2) == arr[2]
                        
    포인터에 정수를 더하면, 포인터가 가리키는 자료형의 크기만큼 주소가 이동합니다. 예를 들어 `int` 포인터에 1을 더하면, 다음 `int`가 있는 주소로 이동합니다.
배열과 포인터의 연결 관계 이미지 예시

▲ 그림 2: 배열 이름이 첫 번째 요소의 주소를 가리키는 모습

왜 이렇게 연결될까요?

배열의 요소들이 메모리에 **연속적으로 저장**되기 때문입니다. 배열의 시작 주소만 알면, 각 요소의 자료형 크기만큼 주소를 더해가면서 다른 요소들에 쉽게 접근할 수 있습니다. 이것이 포인터 연산의 핵심 원리입니다.

이러한 특성 덕분에 C언어에서는 **배열을 다룰 때 포인터를 활용하면 더 효율적이고 유연한 코드 작성**이 가능해집니다.

---

🚀 배열과 포인터, 언제 어떻게 활용할까?

배열과 포인터는 C언어에서 다양한 방식으로 활용됩니다.

  • **함수에 배열 전달:** 함수에 배열을 인자로 전달할 때, 실제 배열 전체가 복사되는 것이 아니라 배열의 시작 주소(포인터)가 전달됩니다. 이는 메모리 효율성을 높입니다.
    void printArray(int *arr, int size) { // 배열의 시작 주소와 크기를 받음
        for (int i = 0; i < size; i++) {
            printf("%d ", *(arr + i)); // 포인터 연산으로 요소 접근
        }
        printf("\n");
    }
    
    int main() {
        int myNumbers[] = {10, 20, 30, 40, 50};
        printArray(myNumbers, 5); // 배열 이름을 넘기면 시작 주소가 전달됨
        return 0;
    }
                        
  • **동적 메모리 할당:** 프로그램 실행 중 필요한 만큼 메모리를 할당받아 사용할 때 포인터를 사용합니다. (malloc, calloc, realloc, free 함수)
  • **문자열 처리:** C언어에서 문자열은 사실상 '문자 배열'이므로, 포인터를 이용한 문자열 처리가 빈번하게 사용됩니다.
  • **연결 리스트, 트리 등 복잡한 자료구조 구현:** 메모리 상에 연속적이지 않은 데이터들을 서로 연결할 때 포인터가 필수적으로 사용됩니다.

마무리하며: 배열과 포인터, C언어의 핵심 역량! 🔑

C언어의 배열과 포인터는 처음에는 다소 어렵게 느껴질 수 있지만, 이 두 개념을 확실히 이해하면 C언어를 능숙하게 다룰 수 있는 강력한 기반을 마련하게 됩니다. **배열은 연속된 데이터 묶음이고, 포인터는 그 데이터 묶음의 '위치(주소)'를 가리키는 역할**을 한다고 기억하시면 됩니다.

특히 **배열 이름이 첫 번째 요소의 주소와 같다는 점**과 **포인터 연산을 통해 배열 요소를 효율적으로 접근할 수 있다는 점**을 명심하고, 다양한 예제 코드를 직접 작성하며 연습해 보는 것이 가장 중요합니다. 꾸준히 연습하다 보면 어느새 배열과 포인터가 친숙한 도구로 느껴질 것입니다. C언어 학습에 궁금한 점이 있다면 언제든지 댓글로 질문해주세요! 👇

#C언어 #C언어배열 #C언어포인터 #배열과포인터 #프로그래밍기초 #메모리주소 #C언어학습 #컴퓨터과학 #코딩 #개발

320x100
반응형

'IT > c언어' 카테고리의 다른 글

C 언어 기초 강의  (13) 2023.12.11
C 언어의 if문: 설명과 사용예시  (5) 2023.11.12
C 프로그램 작성과 컴파일 과정  (47) 2023.11.03
C 언어의 개요와 특징  (50) 2023.11.01