ex250602 공부노트 - Hwanghyewon06/c- GitHub Wiki


🧠 C 언어 핵심 문법 요소 분석 중심: 전체 코드 정밀 해석


🧾 공통 포함 헤더들

#pragma warning(disable:4996)
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

🔹 #pragma warning(disable:4996)

  • Visual Studio 컴파일러 전용 지시문.
  • gets(), strcpy() 같은 보안 위험 함수 사용 시 발생하는 경고 C4996을 무시합니다.
  • 실제 프로덕션에서는 사용 권장 ❌ → fgets(), strncpy() 사용 권장.

🔹 #include <stdio.h>

  • 표준 입출력 함수 (printf(), scanf() 등) 사용을 위한 헤더.

🔹 #include <string.h>

  • 문자열 관련 함수 (strcmp(), strcpy(), strlen() 등) 사용 가능.

🔹 #include <stdlib.h>

  • 메모리 동적 할당 함수 (malloc(), free() 등) 사용 가능.

📦 주요 구조 분석

모든 코드의 핵심은 다음 흐름을 따릅니다:

  1. 문자열 입력 받기 →
  2. 조건 검사 (종료 여부 등) →
  3. 메모리 복사 or 주소 저장 →
  4. 문자열 출력 →
  5. 동적 메모리 할당 해제 (해당하는 경우)

💡 버전 1~3: 동적 메모리 할당 사용

char* sarr[1000] = { 0 }; // 문자열 포인터 배열
  • sarrchar* 타입을 1000개 저장하는 배열 → 문자열 1000개 저장 가능.
  • 각 요소는 char* → 문자열 시작 주소(포인터)를 저장하는 변수.
char buf[1000];
gets_s(buf, 1000);
  • buf는 지역 배열: 문자열을 입력 받는 임시 저장소.
  • gets_s()는 입력을 받아 buf에 저장.

📌 문자열 저장 처리 방식

char* s = (char*)malloc(strlen(buf) + 1);
strcpy(s, buf);
sarr[count++] = s;
  • malloc(strlen(buf) + 1): 문자열 길이 + 널 문자 공간만큼 동적 메모리 할당.
  • strcpy(): 입력 받은 문자열을 새로 할당한 메모리에 복사.
  • sarr[]: 복사된 문자열의 주소를 저장함.
  • 따라서 buf가 사라져도 문자열은 유지됨.

📌 종료 조건

if (strcmp("exit", buf) == 0)
    break; // 또는 run = 0;
  • 문자열 비교 (strcmp): 리터럴 "exit"과 사용자가 입력한 buf 내용이 동일하면 종료.

📌 메모리 해제 실수

for (int i = 0; i < count; ++i)
    free(sarr[0]); // ❌ 버그
  • 항상 sarr[0]만 해제 → 메모리 누수 발생
  • 정답은:
for (int i = 0; i < count; ++i)
    free(sarr[i]); // ✅ 각 문자열에 대해 메모리 해제

🧪 버전 4: 포인터 배열 사용, 복사 ❌

sarr[count++] = buf;
  • 문제점: buf는 지역변수로 매번 새로 선언됨 → 반복 시 덮어씌워짐.
  • 따라서 모든 sarr[i]마지막 입력 문자열만 가리킴.
  • 이건 포인터 주소를 공유하는 문제로, 문자열 복사를 생략했기 때문.

🧪 버전 5: 입력만 받고 출력

char buf[1000];
gets_s(buf, 1000);
printf("string : %s\n", buf);
  • 단순한 입력-출력 구조.
  • 포인터 없음, 메모리 할당 없음, 문자열 저장 없음.
  • 반복 구조 속에서 매 입력마다 바로 출력 후 버림.

🧪 버전 6: strcmp() 대신 문자 단위 비교

if (buf[0] == 'e' &&
    buf[1] == 'x' &&
    buf[2] == 'i' &&
    buf[3] == 't' &&
    buf[4] == '\0') // null 문자 포함 필수
    break;
  • "exit"을 문자열이 아닌 문자 배열 비교로 처리함.
  • 문자열 라이브러리 없이 직접 비교하는 방식.
  • '\0' 체크는 문자열 종료 확인의 핵심 (없으면 "exit123"도 통과함)

📊 개념 중심 요약

개념 설명
char* sarr[1000] 문자열 포인터 배열. 각 요소는 문자열의 시작 주소를 저장
buf[1000] 입력을 임시 저장하는 지역 배열
malloc() 문자열을 위한 동적 메모리 할당. +1\0 포함 때문
strcpy() 문자열 내용을 새로운 메모리로 복사
free() malloc()으로 할당한 메모리를 해제. 반드시 반복문 사용 필요
gets_s() 보안 강화된 문자열 입력 함수. 크기 제한 필수
strcmp() 문자열 내용 비교. 동일하면 0 반환
포인터 주소 저장 문제 sarr[i] = buf;는 같은 주소 복사 → 내용 덮어쓰기 발생

🚫 주요 실수 사례

  1. free(sarr[0])만 호출 → 나머지 메모리 누수
  2. sarr[i] = buf;모든 포인터가 같은 주소 가리킴 (복사 안됨)
  3. buf[4] == 't'까지만 비교 → 문자열 종료 문자 체크 누락 시 오류

✅ 최종 정리

✔ 반드시 알아야 할 핵심 사항

항목 필수 개념
문자열은 배열 or 포인터로 처리된다 둘은 메모리 관리 방식이 다름
포인터 배열은 주소만 저장 내용을 복사하려면 mallocstrcpy 필요
동적 메모리는 해제 필수 반복문으로 free() 처리해야 메모리 누수 방지
지역 배열은 반복 시 덮어써짐 주소만 저장하면 마지막 내용만 남게 됨
문자열 비교는 strcmp() 또는 문자 직접 비교 가능 단, 정확한 길이와 \0 체크 필요

#include <stdio.h>
struct Person
{
	char name[20]; // 포인터와 배열
	int age;

};
void PrintPerson(Person* p)
{
	printf("name: %s, age: %d\n", p ->name, p-> age);
}
int main()
{
	Person p1 = { "홍길동", 30 };
	Person p2 = { "Hong Gil- Dong", 30 };

	PrintPerson(&p1);
	PrintPerson(&p2);
}

#include <stdio.h>
//struct Person
//{
//	char name[20]; // 포인터와 배열
//	int age;
//
//};
//int main()
//{
//	Person p1 = { "홍길동", 30 };
//	Person p2 = { "Hong Gil- Dong", 30 };
//
//	printf("name: %s, age: %d\n", p1.name, p1.age);
//	printf("name: %s, age: %d\n", p2.name, p2.age);
//	printf("%c\n", p2.name[3]);
//}

//#include <stdio.h>
//struct Person
//{
//	char name[20]; // 포인터와 배열
//	int age;
//
//};
//int main()
//{
//	Person p1 = { "홍길동", 30 };
//
//	printf("name: %s, age: %d\n", p1.name, p1.age);
//} 야 제대로 해 이 코드 설명을 작성하라는 거야

✅ 전체 코드 정밀 분석


📌 1. 헤더파일 포함

#include <stdio.h>
  • stdio.hStandard Input Output Header입니다.
  • 이 파일을 포함시켜야 printf() 함수 같은 표준 입출력 함수들을 사용할 수 있습니다.
  • 만약 이 줄이 없으면 printf()를 사용할 수 없고, 컴파일 에러가 발생합니다.

📌 2. 구조체 정의

struct Person
{
    char name[20]; // 포인터와 배열
    int age;
};

🔹 구조체란?

  • struct는 관련된 변수들을 하나의 사용자 정의 자료형으로 묶는 키워드입니다.
  • 여기서는 사람(Person)이라는 구조체를 정의하고 있음.

🔹 내부 멤버

char name[20];

  • 문자형 배열이며, 최대 19글자의 문자열을 저장할 수 있음 (+1은 문자열 끝에 붙는 널 문자 '\0' 때문).
  • 즉, "홍길동" 또는 "Hong Gil- Dong" 같은 문자열을 저장하는 공간입니다.
  • 포인터가 아님: 이 배열은 실제 문자열 데이터를 구조체 내부에 저장함.

int age;

  • 사람의 나이를 저장하는 정수형 변수입니다.

📌 3. 함수 정의

void PrintPerson(Person* p)
{
    printf("name: %s, age: %d\n", p->name, p->age);
}

🔹 void

  • 이 함수는 아무 값도 반환하지 않음을 의미합니다.
  • 단지 구조체 정보를 출력만 하고 끝남.

🔹 PrintPerson(Person* p)

  • PrintPerson은 함수 이름.

  • 매개변수로 Person 구조체의 **포인터(주소)**를 받습니다.

    • Person* p는 구조체 Person의 포인터형.
    • 즉, p는 구조체 변수를 가리키는 주소를 저장함.

🔹 printf("name: %s, age: %d\n", p->name, p->age);

  • p->name: 구조체 포인터 p가 가리키는 구조체의 name 멤버를 의미.
  • p->age: 마찬가지로 구조체 포인터의 age 멤버.
  • -> 연산자는 구조체 포인터에서 멤버에 접근할 때 사용합니다.

📌 4. 메인 함수

int main()
{
    Person p1 = { "홍길동", 30 };
    Person p2 = { "Hong Gil- Dong", 30 };

    PrintPerson(&p1);
    PrintPerson(&p2);
}

🔹 int main()

  • 프로그램의 시작 지점.
  • 운영체제가 처음으로 실행하는 함수이며, 반드시 있어야 합니다.

🔹 Person p1 = { "홍길동", 30 };

  • 구조체 Person의 변수 p1을 선언하고 초기화합니다.
  • "홍길동"name 배열에 저장, 30age에 저장됩니다.
  • 문자 배열에 한글을 넣을 수 있지만, 인코딩(UTF-8, EUC-KR 등)에 따라 배열 크기 초과 가능성 있음.

🔹 Person p2 = { "Hong Gil- Dong", 30 };

  • 두 번째 구조체 변수 선언.
  • name에 영문 이름이 들어가고, age는 30.

🔹 PrintPerson(&p1);

  • &p1은 구조체 변수 p1주소를 함수에 전달함.
  • 함수는 포인터를 통해 p1의 데이터를 참조하여 출력.

🔹 PrintPerson(&p2);

  • 위와 동일하게 p2의 주소를 전달하여 출력.

✅ 추가 주석 처리된 코드 분석

▶ 두 번째 버전 (포인터 없이 직접 접근)

//Person p1 = { "홍길동", 30 };
//Person p2 = { "Hong Gil- Dong", 30 };
//
//printf("name: %s, age: %d\n", p1.name, p1.age);
//printf("name: %s, age: %d\n", p2.name, p2.age);
//printf("%c\n", p2.name[3]);

🔹 p1.name, p1.age

  • . 연산자는 일반 구조체 변수에서 멤버에 접근할 때 사용합니다.
  • 포인터가 아닐 때는 ->가 아닌 . 사용.

🔹 p2.name[3]

  • p2의 이름 문자열에서 4번째 문자를 출력.
  • 인덱스는 0부터 시작하기 때문에 "Hong Gil- Dong"에서 name[3]'g'.

▶ 세 번째 버전 (가장 간단한 형태)

//Person p1 = { "홍길동", 30 };
//printf("name: %s, age: %d\n", p1.name, p1.age);
  • 구조체 하나만 생성하고 출력하는 코드.
  • 포인터, 함수 없이 구조체를 직접 다루는 가장 단순한 형태입니다.

🧠 정리 요약표

문법 요소 역할 및 설명
#include <stdio.h> 표준 입출력 함수 사용 (printf 등)
struct Person 사용자 정의 자료형: 사람 정보 묶기
char name[20] 이름 저장용 배열 (고정 크기 문자열 저장)
int age 나이 저장용 변수
void PrintPerson(Person* p) 구조체 포인터로 받은 데이터를 출력하는 함수
-> 구조체 포인터에서 멤버 접근할 때 사용
. 일반 구조체 변수에서 멤버 접근 시 사용
&p1, &p2 구조체의 주소를 전달 (포인터 매개변수 전달용)


✅ C 언어 주요 함수들의 개념도 및 역할 요약


1. gets_s()

문자열 입력 함수 (Visual Studio 전용 안전 함수)

┌────────────┐
│ 사용자 입력 │
└─────┬──────┘
      ↓
┌──────────────┐
│ gets_s(buf, size) │
└─────┬────────────┘
      ↓
┌────────────────────────┐
│ buf 배열에 문자열 저장 │
└────────────────────────┘
  • 기능: 사용자로부터 문자열을 한 줄 입력받아 buf에 저장.
  • 보호 기능: 두 번째 인자 size버퍼 오버플로우 방지.
  • 종료 조건: 사용자가 [Enter] 누르면 입력 종료됨.

2. strcmp(str1, str2)

문자열 비교 함수

┌────────────┐  ┌────────────┐
│  str1 ("abc")│  │  str2 ("abc")│
└────┬────────┘  └────┬────────┘
     ↓                 ↓
         비교 (문자열 내용 순서대로)
               ↓
     ┌────────────────────┐
     │ return 0 → 같음     │
     │ return <0 or >0 → 다름 │
     └────────────────────┘
  • 기능: 두 문자열을 내용 기준으로 비교.

  • 리턴값:

    • 0 → 완전히 같음
    • <0 → str1 < str2
    • >0 → str1 > str2
  • 종료 조건: 널 문자 '\0'까지 비교함.


3. strlen(str)

문자열 길이 계산 함수

┌───────────────┐
│ 문자열: "hello"│
└─────┬─────────┘
      ↓
     계산: h-e-l-l-o (5글자)
      ↓
┌────────────────────┐
│ 리턴: 5 (널 문자는 제외) │
└────────────────────┘
  • 기능: 문자열에서 '\0' 이전까지의 문자 개수 반환.
  • 주의: 배열 크기 아님, 문자열 실제 글자 수임.

4. strcpy(dest, src)

문자열 복사 함수

┌────────────┐     ┌────────────┐
│ src: "dog" │ ──▶ │ dest       │
└────────────┘     └────────────┘
                      ↓
              dest: "dog"
  • 기능: src 문자열을 dest에 복사 (\0 포함).
  • 조건: destsrc보다 큰 공간이어야 함.

5. malloc(size)

동적 메모리 할당 함수

┌─────────────┐
│ malloc(20)  │
└────┬────────┘
     ↓
┌───────────────────┐
│  힙 메모리에 20바이트 │
│  공간을 할당 후 주소 반환 │
└───────────────────┘
  • 기능: 요청한 크기만큼 힙 영역에 메모리 블록 할당.
  • 리턴값: 메모리 시작 주소 (void* → 타입 캐스팅 필요)
  • 주의: NULL 체크 필수 (할당 실패 시)

6. free(ptr)

동적 메모리 해제 함수

┌─────────────┐
│ free(ptr)   │
└────┬────────┘
     ↓
┌────────────────────┐
│  ptr가 가리키는 메모리 해제 │
└────────────────────┘
  • 기능: malloc()으로 할당한 메모리 반환.

  • 주의사항:

    • 해제 후 포인터를 NULL로 초기화하는 것이 좋음.
    • 같은 포인터를 두 번 해제하면 오류 발생.

7. printf()

출력 함수

┌──────────────┐
│ printf("%s", str) │
└───────┬──────────┘
        ↓
┌───────────────────┐
│ str의 문자열을 화면에 출력 │
└───────────────────┘
  • 기능: 서식 지정자(%d, %s, %c, %f)를 사용하여 다양한 데이터 형식 출력 가능.
  • 출력 장치: 콘솔(표준 출력)

📌 전체 흐름 예시 – 입력부터 저장까지 요약 흐름도

        [사용자 입력]
               ↓
         gets_s(buf)
               ↓
         strcmp(buf, "exit")로 종료 검사
               ↓
        ┌──────────────────────────────┐
        │  동적 메모리 할당 (malloc)    │
        │  문자열 복사 (strcpy)        │
        │  포인터 배열에 저장 (sarr[]) │
        └──────────────────────────────┘
               ↓
        [모든 문자열 출력 후]
               ↓
        free(sarr[i])로 메모리 해제

✅ 요약 표: 함수별 핵심 개념 정리

함수 기능 반환값 / 특징
gets_s() 사용자 문자열 입력 (보안 강화 버전) 입력된 문자열 (buf에 저장)
strcmp() 문자열 비교 0: 같음, 양수/음수: 다름
strlen() 문자열 길이 측정 \0 제외한 문자 수
strcpy() 문자열 복사 destsrc 복사
malloc() 동적 메모리 할당 메모리 주소 (void*)
free() 동적 메모리 해제 없음
printf() 출력 함수 화면에 출력. 반환값은 출력한 문자 수

⚠️ **GitHub.com Fallback** ⚠️