IT개발및프로그래밍/자료구조

C 언어의 배열과 구조체: 데이터 관리의 효율성

devgodmj 2024. 10. 21. 21:29

1. 배열

 

배열은 같은 데이터 타입을 가지는 여러 개의 값을 한데 모아 관리할 수 있는 구조입니다.  C언어에서 자주 사용되는 기본 자료 구조로, 각 값들은 배열 내의 인덱스로 접근할 수 있습니다.

 

배열의 개념

  • 배열은 각 원소의 위치 정보를 나타내는 인덱스와 그 인덱스에 해당하는 데이터 값의 쌍으로 이루어져 있습니다.
    • 동일한 자료형을 갖는 여러 데이터의 집합으로, 같은 이름으로 참조됩니다. 
    • 각 데이터는 메모리 내의 연속적인 위치에 할당됩니다.
    • 배열 내의 각 원소를 구분하기 위해 첨자(subscript)를 사용합니다.

데이터 타입(Data Type)

  • 기본 자료 타입
    • int, float, double, char 등 단일 값의 데이터 타입을 의미합니다.
  • 군집 자료 타입
    • 배열(Array): 기본 데이터 타입의 여러 개의 데이터를 묶어서 하나의 군집 형태로 사용하는 자료형입니다.
  • 사용자 정의 자료 타입(구조체)
    • C언어에서 제공하는 사용자 정의 자료형으로 서로 다른 데이터 형의 데이터를 묶어서 사용할 수 있습니다.

배열의 선언

type var_name[size];
int a[10];

 

위와 같이 선언하면 int형 변수 10개를 묶어서 배열 a로 사용합니다. 각 요소는 첨자를 사용해 접근하며, 예를 들어 a[0], a[1], ..., a[9] 등으로 사용합니다.

 

배열의 대표적인 정보

  • 배열의 이름배열의 첫 데이터의 주소를 나타냅니다.
a = &a[0];   // 배열 a의 이름은 첫 번째 데이터의 주소를 가리킴
a + i = &a[i];  // 배열의 i번째 요소의 주소
*(a + i) = a[i];  // 배열의 i번째 요소에 접근하는 방법

 

배열 변수의 사용

  • 반복문을 이용하여 배열의 각 변수를 처리할 수 있습니다.
sum = 0;
for (i = 0; i < n; i++) {
    sum = sum + a[i];
}

위 예시는 배열의 모든 요소의 합을 계산하는 코드입니다.  for문을 통해 각 요소에 접근하여 처리합니다.

 


2. 배열의 접근과 포인터

배열 개념

  • 배열은 같은 자료형을 가진 연속적인 데이터 항목들의 집합입니다.
    • 배열은 같은 타입의 여러 데이터들을 하나의 변수로 관리합니다.
    • 예를 들어 int a[10];은 int 타입의 값을 10개 저장할 수 있는 배열입니다.

배열의 정보

  • 배열을 선언할 때는 배열 이름과 크기, 그리고 각 항목의 자료형을 정의합니다.
    • 1차원 배열: int a[10];
      - 배열의 이름: a
      - 배열의 크기: 10
      - 배열 항목의 자료형: 정수 (int)
    • 2차원 배열: int b[7][9];
      - 배열의 이름: b
      - 배열의 크기: 7 x 9 = 63
      - 배열 항목의 자료형: 정수(int)
    • n차원 배열: int a[d1][d2]...[dn];
      - 배열의 이름: a
      - 배열의 크기: d1 x d2 x ... 2 dn
      - 배열 항목의 자료형: 정수(int)

배열의 주소 계산

 

  • 1차원 배열의 주소 계산: 배열의 데이터가 메모리에 연속적으로 저장되기 때문에 각 항목의 주소는 쉽게 계산됩니다.
    • 예를 들어 int a[5] = {10, 20, 30, 40, 50}이 있고, a[0]의 주소가 1000번지이고 sizeof(int) = 4일 때:
      • a[1]의 주소는 1000 + 1 * 4 = 1004 번지
      • a[3]의 주소는 1000 + 3 * 4 = 1012 번지
  • 다차원 배열의 주소 계산:
    • 행 우선 저장 방식: 2차원 배열 int b[5][3];의 경우:
      • b[i][j]의 주소는 b[0][0] + (i * d2 + j) * sizeof(int)로 계산됩니다.
      • 예를 들어 b[1][2]의 주소를 계산할 때, b[0][0]의 주소가 100이고 sizeof(int) = 4라면:
        b[1][2]의 주소 = 100 + (1 * 3 + 2) * 4 = 120
    • 3차원 배열의 주소 계산:
      • 배열 int ex[5][8][10];에서 ex[i][j][k]의 주소는 다음과 같이 계산됩니다:
        ex[0][0][0]의 주소 + (i * d2 * d3 + j * d3 + k) * sizeof(int)
      • 예를 들어 ex[1][3][8]의 주소를 계산할 때, ex[0][0][0]의 주소가 100이고 sizeof(int) = 4라면:
        주소 = 100 + (1 * 8 * 10 + 3 * 10 + 8) * 4 = 572

 

배열과 포인터의 관계

 

  • 배열의 이름은 포인터:
    • 배열의 이름 자체는 첫 데이터의 주소로서 포인터 타입의 변수입니다.
    • 배열의 요소에 접근할 때, 배열 이름을 사용하여 첫 주소에서 오프셋을 더하는 방식으로 접근합니다.
  • 포인터와 배열의 연산:
    • a + i는 배열 a에서 i번째 요소의 주소를 나타냅니다.
    • *(a + i)는 배열의 i번째 요소의 값을 의미합니다.

 

배열과 함수

 

  • 배열을 함수의 매개변수로 넘길 때, 배열의 이름을 전달합니다.
  • 배열을 함수에 전달하면, 실제 배열 데이터가 아닌 배열의 첫 번째 요소의 주소가 전달됩니다.
  • 예시
float trunc_sum(float data[]);

int main() {
    float xarray[10], fsum = 0.0;
    int i;
    printf("Enter 10 reals: ");
    for (i = 0; i < 10; i++) {
        scanf("%f", &xarray[i]);
        fsum += xarray[i];
    }
    printf("Sum = %.2f\n", fsum);
    printf("Truncation Value = %.2f\n", trunc_sum(xarray));
}

float trunc_sum(float data[]) {
    float sum = 0.0;
    int i, ivalue;
    for (i = 0; i < 10; i++) {
        ivalue = (int)data[i];
        sum += (data[i] - ivalue);
    }
    return sum;
}

 

 

 

 

  • 위 코드에서 xarray는 trunc_sum 함수에 배열의 첫 번째 요소의 주소를 전달합니다.
  • 함수에서는 배열의 요소에 접근할 때 포인터처럼 동작합니다.

3. 배열의 연산

데이터 저장 및 접근 

  • 배열을 선언하고 데이터를 입력 받은 후, 특정 인덱스에 접근하여 값을 출력할 수 있습니다.
  • 예시 코드:
int exarray[100], k;
for (k = 0; k < 10; k++) {
    scanf("%d", &exarray[k]);
}
for (k = 9; k >= 0; k--) {
    printf("%d ", exarray[k]);
}

 

위의 코드는 10개의 데이터를 입력 받고, 거꾸로 출력하는 예제입니다.

 

배열의 항목 삽입

 

  • 주어진 배열에 데이터를 삽입하는 과정을 보여줍니다.
  • 새로운 데이터를 삽입하기 위해 뒤의 항목들을 한 칸씩 밀어야 하며, 이를 통해 빈 자리에 새로운 값을 저장할 수 있습니다.

 

데이터 검색

 

  • 특정 값을 배열에서 검색하는 알고리즘을 구현합니다.
  • 선형 검색 방법으로, 배열을 순차적으로 탐색하여 원하는 값이 있는지 확인하고, 있다면 1을 반환하고, 없다면 0을 반환합니다.

 


 

4. 구조체의 정의와 활용

구조체는 여러 가지 자료형의 데이터를 하나로 묶어 관리할 수 있는 사용자 정의 자료형입니다. 예를 들어, 학생의 이름, 나이, 성적 등 서로 다른 자료형의 데이터를 하나로 묶어 처리할 때 구조체가 유용합니다.

 

구조체의 정의 예시:

typedef struct {
    int id;
    char name[20];
    float score;
} Student;

 

위 코드에서는 Student라는 구조체가 정의되어 있으며, 학생의 학번(id), 이름(name), 성적(score)을 저장할 수 있습니다.


5. 구조체 배열의 활용

구조체 배열은 여러 개의 구조체를 배열로 관리할 수 있게 해줍니다. 예를 들어, 여러 명의 학생 정보를 관리하려면 Student 구조체 배열을 사용할 수 있습니다.

 

구조체 배열 선언 예시:

Student students[20];  // 20명의 학생 정보를 저장할 수 있는 구조체 배열 선언

 

이 배열을 사용하면 각 학생의 정보를 인덱스로 구분하여 저장하고, 데이터를 쉽게 접근할 수 있습니다.

 

데이터 입력 및 처리 예시:

for (int i = 0; i < 20; i++) {
    printf("학생 정보를 입력하세요 (ID, 이름, 성적): ");
    scanf("%d %s %f", &students[i].id, students[i].name, &students[i].score);
}

 

위 코드에서는 for 반복문을 사용하여 20명의 학생 정보를 입력받고 있습니다.


6. 구조체와 함수의 활용

구조체는 함수의 매개변수로 전달하여 데이터를 처리할 수 있습니다. 예를 들어, 학생 중에서 성적이 4.0 이상인 학생을 출력하는 함수를 작성할 수 있습니다.

 

성적이 4.0 이상인 학생 출력 함수:

void over40(Student students[], int n) {
    printf("성적이 4.0 이상인 학생 목록:\n");
    for (int i = 0; i < n; i++) {
        if (students[i].score >= 4.0) {
            printf("ID: %d, 이름: %s\n", students[i].id, students[i].name);
        }
    }
}

이 함수는 성적이 4.0 이상인 학생들의 정보를 출력합니다.


7. 최고 성적 학생 찾기

구조체와 배열을 사용하여 최고 성적을 받은 학생을 찾을 수도 있습니다.

 

최고 성적 학생을 반환하는 함수:

Student find_max(Student students[], int n) {
    int max_index = 0;
    for (int i = 1; i < n; i++) {
        if (students[i].score > students[max_index].score) {
            max_index = i;
        }
    }
    return students[max_index];
}

 

이 함수는 최고 성적을 가진 학생을 찾아 반환합니다.


8. 파일 처리와 구조체 배열

구조체 배열을 사용하여 파일에서 데이터를 읽고 처리할 수 있습니다. 예를 들어, 수강 신청 데이터를 저장한 파일을 읽어 강좌 정보 등을 처리할 수 있습니다.

 

파일에서 강좌 정보를 읽어오는 코드:

FILE *file = fopen("course_data.txt", "r");
Course courses[MAX_CNUM];  // Course 구조체 배열 선언
int i = 0;

while (fscanf(file, "%s %s %u %u", courses[i].courseid, courses[i].tname, &courses[i].snum, &courses[i].roomnum) != EOF) {
    i++;
}

fclose(file);

 

이 코드는 course_data.txt 파일에서 데이터를 읽어와 courses 구조체 배열에 저장합니다.


9. 구조체와 배열의 활용 예제

다음은 40명 이상의 수강생이 있는 강좌 코드를 출력하는 프로그램 예시입니다.

for (int i = 0; i < dnum; i++) {
    if (courses[i].snum >= 40) {
        printf("강좌 코드: %s\n", courses[i].courseid);
    }
}