C Programming Study_4

김현우·2025년 5월 1일
0

멘토링

목록 보기
5/7

📌 포인터(Pointer) 완전 정복

1. 포인터란?

포인터란 다른 변수의 주소를 저장하는 변수입니다. 즉, 포인터는 "값"이 아닌 "주소"를 가리킵니다.

int a = 10;
int *p = &a; // p는 변수 a의 주소를 저장함

🔍 설명

a는 정수형 변수
&a는 변수 a의 주소를 의미
*p는 포인터가 가리키는 주소에 저장된 값을 의미 (역참조)

2. 포인터 선언과 초기화

int num = 100;
int *ptr = #

printf("num의 값: %d\n", num);      // 100
printf("ptr이 가리키는 값: %d\n", *ptr); // 100
printf("ptr의 주소값: %p\n", ptr);   // num의 주소

3. 포인터와 배열

포인터는 배열과 밀접한 관계가 있습니다.

int arr[3] = {1, 2, 3};
int *p = arr;

printf("%d\n", *p);     // 1
printf("%d\n", *(p+1)); // 2

🔍 핵심 포인트

배열 이름 arr 자체가 첫 번째 요소의 주소 (&arr[0])로 작동

*(p + i)는 arr[i]와 동일

4. 포인터와 함수 (Call by Reference)

void swap(int *x, int *y) {
    int temp = *x;
    *x = *y;
    *y = temp;
}

int main() {
    int a = 5, b = 10;
    swap(&a, &b);
    printf("a = %d, b = %d\n", a, b); // a = 10, b = 5
}

🔄 왜 이렇게 사용하나요?

포인터를 통해 함수 내에서 원본 값을 바꿀 수 있음 (참조에 의한 호출)

5. 이중 포인터 (Double Pointer)

int val = 20;
int *p = &val;
int **pp = &p;

printf("%d\n", **pp); // 20

🎯 사용 예시

동적 메모리 할당 시 메모리의 주소를 함수에서 반환하려면 이중 포인터가 필요함

#include <stdio.h>
#include <stdlib.h>

void allocateMemory(int **ptr) {
    *ptr = (int *)malloc(sizeof(int)); // 메모리 할당
    if (*ptr != NULL) {
        **ptr = 1234; // 값 저장
    }
}

int main() {
    int *p = NULL;
    allocateMemory(&p); // 포인터의 주소 전달 (이중 포인터 사용)
    
    if (p != NULL) {
        printf("할당된 메모리의 값: %d\n", *p); // 1234 출력
        free(p); // 메모리 해제
    }
    
    return 0;
}

6. 동적 메모리 할당과 포인터 (malloc, free)

#include <stdio.h>
#include <stdlib.h>

int main() {
    int *p = (int *)malloc(sizeof(int));
    *p = 42;
    printf("%d\n", *p);
    free(p);
}

⚠️ 주의

malloc은 힙 영역에 메모리 공간을 동적으로 할당

사용이 끝난 후 반드시 free로 해제해야 메모리 누수 방지 가능

7. 포인터와 문자열

char *str = "Hello";
printf("%c\n", *str);     // 'H'
printf("%s\n", str);      // "Hello"

8. 포인터 배열 vs 배열 포인터

char *ptrs[3]; // 포인터들의 배열
int (*ptr)[3]; // 정수 배열을 가리키는 포인터

int p[3] ==> int를 가리키는 포인터 3개
int (
p)[3] ==> int 3개짜리 배열 1개를 가리키는 포인터

int arr[5];
int *p = arr;

sizeof(arr); // 20 (5 * sizeof(int))
sizeof(p);   // 4 or 8 (포인터 크기, 시스템 의존)

배열은 고정 크기를 가지지만 포인터는 주소만 참조

배열의 이름은 포인터처럼 쓰일 수 있으나 완전히 동일하지는 않음

9. 💡 구조체와 포인터

struct Point {
    int x, y;
};

struct Point p = {1, 2};
struct Point *ptr = &p;

printf("%d\n", ptr->x); // 1

구조체 사용이 좋은 예시

#include <stdio.h>
#include <string.h>

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

void printStudent(struct Student s) {
    printf("학번: %d\n", s.id);
    printf("이름: %s\n", s.name);
    printf("성적: %.2f\n", s.grade);
}

int main() {
    struct Student s1;

    s1.id = 2023202042;
    strcpy(s1.name, "김현우");
    s1.grade = 93.5;

    printStudent(s1);

    return 0;
}

확장 가능성 예시

struct Student students[3]; // 여러 학생 저장 가능
struct Student *sp = &s1;   // 구조체 포인터로 접근
printf("%s\n", sp->name);   // -> 연산자 사용

10. 🧨 Dangling Pointer (댕글링 포인터)

int *p;

{
    int a = 10;
    p = &a;
} // a는 여기서 소멸됨

printf("%d\n", *p); // 잘못된 접근 → 정의되지 않은 동작

스코프 밖으로 나간 지역변수의 주소를 사용하면 댕글링 포인터

free() 후에도 해당 포인터를 계속 사용하면 동일한 문제가 발생

실습 코드

✅ 예제 1: 포인터를 이용한 값 변경

정수형 변수 하나를 선언하고, 해당 변수의 주소를 포인터에 저장합니다. 이후 포인터를 통해 변수의 값을 변경합니다. 포인터를 통해 변수의 값을 읽고 쓸 수 있다는 개념을 익히는 데 초점을 둡니다.

요구 사항:

변수 a에 값 10을 저장
포인터 p에 a의 주소 저장
*p를 통해 a의 값을 20으로 변경
변경 전후의 a 값 출력

✅ 예제 2: 배열과 포인터 연산

정수형 배열을 선언한 뒤, 배열 이름을 포인터처럼 사용해 배열 요소에 접근합니다. 포인터 산술 연산(p + i)을 사용하여 각 요소에 접근하며 출력합니다.

요구 사항:

정수 배열 arr에 1, 2, 3 저장
포인터 p를 arr로 초기화
반복문을 사용하여 *(p + i)로 배열 요소 출력

✅ 예제 3 (개선): 포인터를 이용한 최대값 교환 함수

세 개의 정수 중 가장 큰 값을 첫 번째 변수에 저장되도록 포인터를 활용하여 reorder_max_first() 함수를 작성합니다. 단순한 swap보다 논리적 분기가 포함되며, 포인터로 다수의 값을 비교/수정하는 패턴을 학습합니다.

요구 사항:

int a, b, c를 인자로 받아
세 변수 중 가장 큰 값을
a에 위치시키고 나머지 두 값은 그대로 둠
예: a=3, b=7, c=5 → a=7, b=3, c=5
main() 함수에서 세 변수의 주소를 전달
함수 호출 전/후 세 값 출력

✅ 예제 4 (개선): 구조체 배열과 포인터로 학생 검색하기

학생 5명의 정보를 구조체 배열로 저장하고, 학생의 ID로 검색했을 때 해당 학생의 이름과 점수를 출력하는 프로그램입니다. 포인터 연산을 통해 구조체 배열을 탐색하며, 포인터와 배열, 구조체의 결합을 학습합니다.

요구 사항:

struct Student { int id; char name[20]; float grade; }
5명의 정보를 구조체 배열에 저장
ID를 입력받고, 포인터를 사용해 배열을 선형 탐색
일치하는 학생이 있으면 이름과 성적 출력, 없으면 "찾을 수 없음"

✅ 예제 5 (개선): 동적 2차원 배열 생성 및 평균 계산

사용자에게 행과 열의 개수를 입력받아, malloc()을 사용해 동적으로 2차원 배열을 생성합니다. 각 원소를 사용자에게 입력받고, 평균을 계산한 뒤, 메모리를 해제합니다. 중첩 포인터 int **matrix를 이용한 메모리 할당/접근 패턴을 학습합니다.

요구 사항:

사용자로부터 행(row), 열(col) 수 입력받기
int **matrix를 사용해 2차원 배열 동적 생성
각 원소 입력받아 저장
전체 평균 계산 및 출력
free()로 모든 동적 메모리 해제

✅ 예제 6 (개선): 이중 포인터로 문자열 배열 관리

char **words를 사용해 문자열 3개를 저장하는 프로그램을 작성합니다. 각 문자열을 malloc()으로 동적 할당한 뒤 내용을 복사하고, 모든 문자열을 출력합니다. 이중 포인터를 통해 다수의 문자열을 동적으로 처리하는 패턴을 학습합니다.

요구 사항:

char **words를 선언하여 문자열 3개 저장
각 문자열 공간을 malloc()으로 확보하고, strcpy()로 값 복사
반복문으로 문자열 전체 출력
free()로 각 문자열 및 words 자체 해제

과제

✅ [1] 과제 설명: 포스기 프로그램 만들기

📝 과제 목적

구조체, 포인터, 배열, 동적 메모리 할당, 함수 호출 시 포인터 전달 등의 개념을 실전 문제에 종합적으로 적용할 수 있는지 평가합니다.

📋 문제 설명

당신은 편의점 포스기 소프트웨어를 개발 중입니다. 사용자는 여러 상품을 입력하고 결제를 진행할 수 있어야 합니다. 아래의 조건을 만족하는 프로그램을 작성하세요.

📌 요구 사항

상품 구조체 정의
struct Item 구조체는 다음 멤버를 포함합니다:

char name[30] : 상품 이름
int price : 가격
int quantity : 수량

동적 메모리 할당

사용자에게 구매할 상품 개수(N)를 입력받고, 동적으로 구조체 배열을 할당하여 상품 정보를 입력받습니다.

총액 계산 함수

int calculateTotal(struct Item *items, int count) 함수를 작성하여, 총 결제 금액을 계산합니다.

출력 함수

void printReceipt(struct Item *items, int count) 함수를 작성하여 아래와 같은 형태로 영수증을 출력합니다.

--- 영수증 ---
1. 콜라 x2 = 3000원
2. 과자 x1 = 1500원
총액: 4500원

메모리 해제 필수

malloc을 사용한 만큼 free()를 사용해 동적 메모리를 모두 해제하세요.

profile
학생

0개의 댓글