산업 제조
산업용 사물 인터넷 | 산업자재 | 장비 유지 보수 및 수리 | 산업 프로그래밍 |
home  MfgRobots >> 산업 제조 >  >> Industrial programming >> C 언어

C 배열 완전 정복: 선언, 초기화, 접근, 다차원, 함수 전달, 포인터 관계

배열은 동일 타입 요소의 연속된 메모리 집합입니다. C에서 배열의 선언, 초기화, 메모리 레이아웃, 함수 전달, 포인터와의 관계까지 실무 관점에서 정리합니다.

배열 선언

타입 이름[크기];           // 크기: 상수 표현식 (C99 이전)
타입 이름[상수표현식];     // C99: VLA(가변 길이 배열) 지원 - 런타임 크기 가능

예시

int scores[100];           // 고정 크기 100
double prices[50];
char name[32];
int n = 10;
int vla[n];                // C99 VLA (스택 할당, 큰 크기 주의)

배열 초기화

// 1. 선언 시 초기화 (중괄호)
int arr[5] = {10, 20, 30, 40, 50};

// 2. 크기 생략 → 초기값 개수만큼 자동 할당
int arr[] = {1, 2, 3, 4, 5};  // 크기 5

// 3. 부분 초기화 → 나머지는 0 초기화
int arr[10] = {1, 2, 3};  // arr[3]~arr[9] = 0

// 4. 지정 초기화자 (C99) - 순서 무관, 중복 시 나중 값
int arr[10] = {[0]=1, [5]=10, [9]=100};
int arr[] = {[2]=5, [0]=1};  // 크기 3: {1, 0, 5}

// 5. 문자열로 char 배열 초기화
char str[] = "Hello";  // {'H','e','l','l','o','\0'} 크기 6
char str[10] = "Hi";   // {'H','i','\0',0,0...} 크기 10

배열 요소 접근

int arr[5] = {10, 20, 30, 40, 50};
printf("%d\n", arr[0]);   // 10 (첫 요소)
printf("%d\n", arr[4]);   // 50 (마지막 요소)
arr[2] = 300;             // 수정
// arr[5] = 100;         // 위험: 버퍼 오버플로

배열과 포인터의 관계 (핵심)

차이점: 배열 vs 포인터

int arr[10];
int *ptr = arr;

sizeof(arr)   // 40 (배열 전체 크기: 10 * 4)
sizeof(ptr)   // 8  (포인터 크기, 64비트)

&arr          // int (*)[10] - 배열 타입 포인터
&ptr          // int ** - 포인터의 포인터

// arr = ptr;  // 에러: 배열은 lvalue가 아님 (수정 불가)
// ptr = arr;  // OK: 포인터에 배열 주소 대입

다차원 배열

2차원 배열

int matrix[3][4];  // 3행 4열, 총 12개 int

// 초기화
int mat[2][3] = {
    {1, 2, 3},
    {4, 5, 6}
};

// 부분 초기화
int mat[3][3] = {{1}, {2, 3}};  // {{1,0,0}, {2,3,0}, {0,0,0}}

// 접근
mat[row][col]  // 행 우선 저장 (Row-major)
*(mat[row] + col)
*(*(mat + row) + col)

메모리 레이아웃

연속된 1차원 배열로 저장: mat[0][0], mat[0][1], ..., mat[0][3], mat[1][0], ...

가변 길이 2차원 배열 (C99 VLA)

void process(int rows, int cols) {
    int matrix[rows][cols];  // 스택 할당, 큰 크기 시 스택 오버플로 주의
    // ...
}

배열과 함수

배열을 매개변수로 전달

  • 배열을 통째로 반환 불가 (포인터 반환은 가능: 정적/동적 할당/구조체 래핑)

  • 배열 대입 불가: arr1 = arr2 에러

  • 복사하려면 memcpy, 루프, struct 래핑 활용

  • 구조체로 배열 감싸기 (값 전달/반환 가능)

    typedef struct { int data[10]; size_t len; } IntArray;
    
    IntArray create_array() {
        IntArray a = {.len = 10};
        for (size_t i = 0; i < a.len; i++) a.data[i] = i * i;
        return a;  // 값 복사 반환 가능
    }

    배열 포인터 vs 포인터 배열

    int (*arr_ptr)[10];   // 10개 int 배열에 대한 포인터 (배열 포인터)
    int *ptr_arr[10];     // int 포인터 10개 배열 (포인터 배열)
    
    // 배열 포인터 활용: 2차원 배열 행 단위 접근
    int mat[3][4];
    int (*row_ptr)[4] = mat;  // mat 첫 행 주소
    row_ptr++;  // 다음 행으로 이동 (16바이트 이동)

    문자열과 배열

    • char str[] = "hello"; → 수정 가능 배열 (스택)

    • char *str = "hello"; → 읽기 전용 리터럴 가리킴 (수정 시 UB)

    • 문자열 함수: strlen, strcpy, strcat, strcmp, strchr, strstr (<string.h>)

    모범 사례

    • 크기 상수화: #define MAX 100 또는 enum { MAX = 100 };

    • 배열 크기 매크로: #define ARRAY_LEN(arr) (sizeof(arr)/sizeof((arr)[0])) (포인터엔 동작 안 함)

    • 경계 검사 루틴화: assert(i < n), 안전한 래퍼 함수

    • 큰 배열은 동적 할당(malloc) 또는 정적 저장 기간 사용 (스택 한계 회피)

    • VLA 사용 자제: 스택 오버플로 위험, C11에서 선택 사항으로 변경

    C 언어

    1. C# 배열 마스터하기: 선언, 초기화, 조작과 LINQ 연산
    2. C# 가변 배열(Jagged Array): 선언, 초기화, 접근 완전 가이드
    3. C++ 배열을 함수에 전달하는 방법: 실전 가이드
    4. C 언어에서 1차원 및 2차원 배열을 함수에 전달하는 방법 – 실전 가이드
    5. C 언어에서 배열과 포인터의 관계 이해하기
    6. Java 배열 복사 기법: 대입, 루프, System.arraycopy, Arrays.copyOfRange 총정리
    7. C++ 배열 완전 정복: 선언, 초기화, 포인터 기법
    8. C++ 동적 배열 할당 완벽 가이드: new, delete, 실전 코드 예제
    9. Java 배열 튜토리얼: 선언, 생성, 초기화와 예제 코드
    10. MATLAB: 다차원 배열과 특수 배열 마스터하기
    표현타입값주소 연산 &
    arrint * (첫 요소 주소로 자동 변환)&arr[0]&arr은 int (*)[5] (배열 전체 주소)
    &arr[0]int *첫 요소 주소-
    arr + iint *&arr[i]-
    *(arr + i)intarr[i]-