C 프로그래밍 치트 시트

실습 랩을 통한 C 프로그래밍 학습

실습 랩과 실제 시나리오를 통해 C 프로그래밍을 학습하세요. LabEx 는 필수 구문, 메모리 관리, 포인터, 데이터 구조 및 고급 기술을 다루는 포괄적인 C 과정을 제공합니다. C 의 강력한 기능을 마스터하여 효율적인 시스템 수준 애플리케이션을 구축하고 저수준 프로그래밍 개념을 이해하십시오.

기본 구문 및 구조

Hello World 프로그램

C 프로그램의 기본 구조.

#include <stdio.h>
int main() {
    printf("Hello, World!\n");
    return 0;
}

헤더 및 전처리기

라이브러리 포함 및 전처리기 지시문 사용.

#include <stdio.h>    // 표준 입출력
#include <stdlib.h>   // 표준 라이브러리
#include <string.h>   // 문자열 함수
#include <math.h>     // 수학 함수
#define PI 3.14159
#define MAX_SIZE 100

주석

단일 행 및 다중 행 주석.

// 단일 행 주석
/*
다중 행 주석
여러 줄에 걸쳐 있음
*/
// TODO: 기능 구현
/* FIXME: 이 섹션의 버그 */

Main 함수

반환 값이 있는 프로그램 진입점.

int main() {
    // 여기에 프로그램 코드
    return 0;  // 성공
}
int main(int argc, char *argv[]) {
    // argc: 인자 개수
    // argv: 인자 값 (명령줄)
    return 0;
}
퀴즈

로그인하여 이 퀴즈에 답하고 학습 진행 상황을 추적하세요

main 함수에서 return 0은 무엇을 나타냅니까?
프로그램이 실패함
프로그램이 계속 실행 중임
프로그램이 성공적으로 실행됨
프로그램이 값을 반환하지 않음

기본 출력

콘솔에 텍스트 및 변수 표시.

printf("Hello\n");
printf("Value: %d\n", 42);
// 한 줄에 여러 값
printf("Name: %s, Age: %d\n", name, age);

기본 입력

콘솔에서 사용자 입력 읽기.

int age;
char name[50];
scanf("%d", &age);
scanf("%s", name);
// 공백 포함 전체 줄 읽기
fgets(name, sizeof(name), stdin);

데이터 타입 및 변수

기본 타입

다양한 종류의 값을 저장하기 위한 기본 데이터 타입.

// 정수 타입
int age = 25;
short small_num = 100;
long large_num = 1000000L;
long long huge_num = 9223372036854775807LL;
// 부동 소수점 타입
float price = 19.99f;
double precise = 3.14159265359;
// 문자 및 부울 (int 사용)
char grade = 'A';
int is_valid = 1;  // 1 은 참, 0 은 거짓

배열 및 문자열

C 에서의 배열 및 문자열 처리.

// 배열
int numbers[5] = {1, 2, 3, 4, 5};
int matrix[3][3] = {{1,2,3}, {4,5,6}, {7,8,9}};
// 문자열 (문자 배열)
char name[50] = "John Doe";
char greeting[] = "Hello";
char buffer[100];  // 초기화되지 않음
// 문자열 길이 및 크기
int len = strlen(name);
int size = sizeof(buffer);
퀴즈

로그인하여 이 퀴즈에 답하고 학습 진행 상황을 추적하세요

C 에서 문자열은 어떻게 표현됩니까?
특수 문자열 타입으로
정수로
문자 배열로
포인터로만

상수 및 한정자

불변 값 및 저장 한정자.

// 상수
const int MAX_SIZE = 100;
const double PI = 3.14159;
// 전처리기 상수
#define BUFFER_SIZE 512
#define TRUE 1
#define FALSE 0
// 저장 한정자
static int count = 0;     // 정적 변수
extern int global_var;    // 외부 변수
register int fast_var;    // 레지스터 힌트

제어 흐름 구조

조건문

조건에 따른 결정.

// If-else 문
if (age >= 18) {
    printf("Adult\n");
} else if (age >= 13) {
    printf("Teenager\n");
} else {
    printf("Child\n");
}
// 삼항 연산자
char* status = (age >= 18) ? "Adult" : "Minor";
// Switch 문
switch (grade) {
    case 'A':
        printf("Excellent!\n");
        break;
    case 'B':
        printf("Good job!\n");
        break;
    default:
        printf("Keep trying!\n");
}

For 루프

카운터 기반 루프로 반복.

// 전통적인 for 루프
for (int i = 0; i < 10; i++) {
    printf("%d ", i);
}
// 배열 반복
int numbers[] = {1, 2, 3, 4, 5};
int size = sizeof(numbers) / sizeof(numbers[0]);
for (int i = 0; i < size; i++) {
    printf("%d ", numbers[i]);
}
// 중첩 루프
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        printf("%d,%d ", i, j);
    }
}
퀴즈

로그인하여 이 퀴즈에 답하고 학습 진행 상황을 추적하세요

sizeof(numbers) / sizeof(numbers[0])은 무엇을 계산합니까?
배열의 요소 개수
배열의 총 메모리 크기
마지막 요소의 인덱스
요소 하나의 크기

While 루프

조건 기반 반복.

// While 루프
int count = 0;
while (count < 5) {
    printf("%d\n", count);
    count++;
}
// Do-while 루프 (최소 한 번 실행)
int input;
do {
    printf("Enter a number (0 to quit): ");
    scanf("%d", &input);
} while (input != 0);

루프 제어

Break 및 continue 문.

for (int i = 0; i < 10; i++) {
    if (i == 3) {
        continue; // 반복 건너뛰기
    }
    if (i == 7) {
        break;    // 루프 종료
    }
    printf("%d ", i);
}
// 중첩 루프와 break
for (int i = 0; i < 3; i++) {
    for (int j = 0; j < 3; j++) {
        if (i == j) break; // 내부 루프만 종료
        printf("%d,%d ", i, j);
    }
}

함수

함수 선언 및 정의

재사용 가능한 코드 블록 생성.

// 함수 선언 (프로토타입)
int add(int a, int b);
void printMessage(char* msg);
// 함수 정의
int add(int a, int b) {
    return a + b;
}
void printMessage(char* msg) {
    printf("%s\n", msg);
}
// 함수 호출
int result = add(5, 3);
printMessage("Hello, functions!");

함수에 배열 전달

배열과 함께 작동하는 함수.

// 매개변수로서의 배열 (포인터)
void printArray(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        printf("%d ", arr[i]);
    }
    printf("\n");
}
// 배열 요소 수정
void doubleValues(int arr[], int size) {
    for (int i = 0; i < size; i++) {
        arr[i] *= 2;
    }
}

재귀 함수

자신을 호출하는 함수.

// 팩토리얼 계산
int factorial(int n) {
    if (n <= 1) {
        return 1;  // 기본 사례
    }
    return n * factorial(n - 1);
}
// 피보나치 수열
int fibonacci(int n) {
    if (n <= 1) {
        return n;
    }
    return fibonacci(n-1) + fibonacci(n-2);
}

함수 포인터

동적 동작을 위한 함수 포인터.

// 함수 포인터 선언
int (*operation)(int, int);
// 포인터에 함수 할당
operation = add;
int result = operation(5, 3);
// 함수 포인터 배열
int (*operations[])(int, int) = {add, subtract, multiply};
result = operations[0](10, 5);

포인터 및 메모리 관리

포인터 기본 사항

메모리 주소에 접근하기 위한 포인터 선언 및 사용.

int x = 10;
int *ptr = &x;  // x 에 대한 포인터
printf("Value of x: %d\n", x);
printf("Address of x: %p\n", &x);
printf("Value of ptr: %p\n", ptr);
printf("Value pointed by ptr: %d\n", *ptr);
// 포인터를 통해 값 수정
*ptr = 20;
printf("New value of x: %d\n", x);
// 널 포인터
int *null_ptr = NULL;

배열과 포인터

배열과 포인터 간의 관계.

int arr[5] = {1, 2, 3, 4, 5};
int *p = arr;  // 첫 번째 요소 가리킴
// 배열 표기법 대 포인터 연산
printf("%d\n", arr[2]);   // 배열 표기법
printf("%d\n", *(p + 2)); // 포인터 연산
printf("%d\n", p[2]);     // 배열처럼 사용되는 포인터
// 포인터를 사용하여 반복
for (int i = 0; i < 5; i++) {
    printf("%d ", *(p + i));
}

동적 메모리 할당

런타임에 메모리 할당 및 해제.

#include <stdlib.h>
// 단일 정수를 위한 메모리 할당
int *ptr = (int*)malloc(sizeof(int));
if (ptr != NULL) {
    *ptr = 42;
    printf("Value: %d\n", *ptr);
    free(ptr);  // 할당된 메모리는 항상 해제
}
// 배열 동적 할당
int *arr = (int*)malloc(10 * sizeof(int));
if (arr != NULL) {
    for (int i = 0; i < 10; i++) {
        arr[i] = i * i;
    }
    free(arr);
}

문자열 포인터

문자열 및 문자 포인터 작업.

// 문자열 리터럴 및 포인터
char *str1 = "Hello";           // 문자열 리터럴
char str2[] = "World";          // 문자 배열
char *str3 = (char*)malloc(20); // 동적 문자열
// 문자열 함수
strcpy(str3, "Dynamic");
printf("Length: %lu\n", strlen(str1));
printf("Compare: %d\n", strcmp(str1, str2));
strcat(str2, "!");
// 동적 문자열은 항상 해제
free(str3);

구조체 및 사용자 정의 타입

구조체 정의

여러 필드를 가진 사용자 정의 타입 정의.

// 구조체 정의
struct Rectangle {
    double width;
    double height;
};
// typedef 를 사용한 구조체
typedef struct {
    char name[50];
    int age;
    double gpa;
} Student;
// 구조체 생성 및 초기화
struct Rectangle rect1 = {5.0, 3.0};
Student student1 = {"Alice", 20, 3.75};
// 구조체 멤버 접근
printf("Area: %.2f\n", rect1.width * rect1.height);
printf("Student: %s, Age: %d\n", student1.name, student1.age);

중첩 구조체

다른 구조체를 포함하는 구조체.

typedef struct {
    int day, month, year;
} Date;
typedef struct {
    char name[50];
    Date birthdate;
    double salary;
} Employee;
Employee emp = {
    "John Smith",
    {15, 6, 1985},
    50000.0
};
printf("Born: %d/%d/%d\n",
       emp.birthdate.day,
       emp.birthdate.month,
       emp.birthdate.year);

구조체 포인터

구조체 접근 및 수정을 위해 포인터 사용.

Student *student_ptr = &student1;
// 포인터를 통한 접근 (두 가지 방법)
printf("Name: %s\n", (*student_ptr).name);
printf("Age: %d\n", student_ptr->age);
// 포인터를 통해 수정
student_ptr->age = 21;
strcpy(student_ptr->name, "Alice Johnson");
// 동적 구조체 할당
Student *new_student = (Student*)malloc(sizeof(Student));
if (new_student != NULL) {
    strcpy(new_student->name, "Bob");
    new_student->age = 19;
    new_student->gpa = 3.2;
    free(new_student);
}

공용체 및 열거형

대안적인 데이터 구성 방법.

// 공용체 - 메모리 공간 공유
union Data {
    int integer;
    float floating;
    char character;
};
union Data data;
data.integer = 42;
printf("Integer: %d\n", data.integer);
// 열거형
enum Weekday {
    MONDAY, TUESDAY, WEDNESDAY,
    THURSDAY, FRIDAY, SATURDAY, SUNDAY
};
enum Weekday today = FRIDAY;
printf("Today is day %d\n", today);

파일 입출력 작업

파일 읽기

텍스트 파일에서 데이터 읽기.

#include <stdio.h>
// 파일 전체를 문자 단위로 읽기
FILE *file = fopen("data.txt", "r");
if (file != NULL) {
    int ch;
    while ((ch = fgetc(file)) != EOF) {
        putchar(ch);
    }
    fclose(file);
}
// 줄 단위로 읽기
FILE *file2 = fopen("lines.txt", "r");
char buffer[256];
while (fgets(buffer, sizeof(buffer), file2) != NULL) {
    printf("Line: %s", buffer);
}
fclose(file2);
// 형식화된 데이터 읽기
FILE *numbers = fopen("numbers.txt", "r");
int num;
while (fscanf(numbers, "%d", &num) == 1) {
    printf("Number: %d\n", num);
}
fclose(numbers);

오류 확인

파일 작업을 안전하게 처리.

FILE *file = fopen("data.txt", "r");
if (file == NULL) {
    printf("Error opening file!\n");
    perror("fopen");  // 시스템 오류 메시지 출력
    return 1;
}
// 읽기 오류 확인
if (ferror(file)) {
    printf("Error reading file!\n");
}
// 파일 끝 확인
if (feof(file)) {
    printf("Reached end of file\n");
}
fclose(file);

파일 쓰기

텍스트 파일에 데이터 쓰기.

// 파일에 쓰기
FILE *outfile = fopen("output.txt", "w");
if (outfile != NULL) {
    fprintf(outfile, "Hello, file!\n");
    fprintf(outfile, "Number: %d\n", 42);
    fclose(outfile);
}
// 기존 파일에 추가
FILE *appendfile = fopen("log.txt", "a");
if (appendfile != NULL) {
    fprintf(appendfile, "New log entry\n");
    fclose(appendfile);
}
// 파일에 배열 쓰기
int numbers[] = {1, 2, 3, 4, 5};
FILE *numfile = fopen("numbers.txt", "w");
for (int i = 0; i < 5; i++) {
    fprintf(numfile, "%d ", numbers[i]);
}
fclose(numfile);

바이너리 파일 작업

바이너리 데이터 효율적으로 읽고 쓰기.

// 바이너리 데이터 쓰기
Student students[3] = {
    {"Alice", 20, 3.75},
    {"Bob", 21, 3.2},
    {"Charlie", 19, 3.9}
};
FILE *binfile = fopen("students.bin", "wb");
fwrite(students, sizeof(Student), 3, binfile);
fclose(binfile);
// 바이너리 데이터 읽기
Student loaded_students[3];
FILE *readbin = fopen("students.bin", "rb");
fread(loaded_students, sizeof(Student), 3, readbin);
fclose(readbin);

문자열 조작

문자열 함수

string.h 라이브러리의 일반적인 문자열 작업.

#include <string.h>
char str1[50] = "Hello";
char str2[] = "World";
char dest[100];
// 문자열 길이
int len = strlen(str1);
printf("Length: %d\n", len);
// 문자열 복사
strcpy(dest, str1);
strncpy(dest, str1, 10); // 처음 10 자 복사
// 문자열 연결
strcat(dest, " ");
strcat(dest, str2);
strncat(dest, "!", 1);   // 문자 1 개 추가
// 문자열 비교
int result = strcmp(str1, str2);
if (result == 0) {
    printf("Strings are equal\n");
}

문자열 검색

문자열 내에서 부분 문자열 및 문자 찾기.

char text[] = "The quick brown fox";
char *ptr;
// 문자 첫 번째 발생 찾기
ptr = strchr(text, 'q');
if (ptr != NULL) {
    printf("Found 'q' at position: %ld\n", ptr - text);
}
// 마지막 발생 찾기
ptr = strrchr(text, 'o');
printf("Last 'o' at position: %ld\n", ptr - text);
// 부분 문자열 찾기
ptr = strstr(text, "brown");
if (ptr != NULL) {
    printf("Found 'brown' at: %s\n", ptr);
}

문자열 변환

문자열을 숫자로, 그 반대로 변환.

#include <stdlib.h>
// 문자열을 숫자로 변환
char num_str[] = "12345";
char float_str[] = "3.14159";
int num = atoi(num_str);
long long_num = atol(num_str);
double float_num = atof(float_str);
printf("Integer: %d\n", num);
printf("Long: %ld\n", long_num);
printf("Double: %.2f\n", float_num);
// 숫자를 문자열로 변환 (sprintf 사용)
char buffer[50];
sprintf(buffer, "%d", 42);
sprintf(buffer, "%.2f", 3.14159);
printf("String: %s\n", buffer);

사용자 정의 문자열 처리

수동 문자열 조작 기술.

// 문자열에서 문자 개수 세기
int countChar(char *str, char target) {
    int count = 0;
    while (*str) {
        if (*str == target) count++;
        str++;
    }
    return count;
}
// 제자리에서 문자열 뒤집기
void reverseString(char *str) {
    int len = strlen(str);
    for (int i = 0; i < len/2; i++) {
        char temp = str[i];
        str[i] = str[len-1-i];
        str[len-1-i] = temp;
    }
}

컴파일 및 빌드 프로세스

GCC 컴파일

C 용 GNU 컴파일러 모음.

# 기본 컴파일
gcc -o program main.c
# 디버깅 정보 포함
gcc -g -o program main.c
# 최적화 수준
gcc -O2 -o program main.c
# 여러 소스 파일
gcc -o program main.c utils.c math.c
# 추가 디렉토리 포함
gcc -I/usr/local/include -o program main.c
# 라이브러리 링크
gcc -o program main.c -lm -lpthread

C 표준

특정 C 표준 버전으로 컴파일.

# C90/C89 표준 (ANSI C)
gcc -std=c89 -o program main.c
# C99 표준
gcc -std=c99 -o program main.c
# C11 표준 (권장)
gcc -std=c11 -o program main.c
# C18 표준 (최신)
gcc -std=c18 -o program main.c
# 모든 경고 활성화
gcc -Wall -Wextra -std=c11 -o program main.c

Makefile 기본 사항

make 유틸리티를 사용하여 컴파일 자동화.

# 간단한 Makefile
CC = gcc
CFLAGS = -std=c11 -Wall -g
TARGET = program
SOURCES = main.c utils.c
$(TARGET): $(SOURCES)
$(CC) $(CFLAGS) -o $(TARGET) $(SOURCES)
clean:
rm -f $(TARGET)
.PHONY: clean

모범 사례 및 팁

명명 규칙

일관된 명명은 코드 가독성을 높입니다.

// 변수 및 함수: snake_case
int student_count;
double calculate_average(int scores[], int size);
// 상수: UPPER_CASE
#define MAX_BUFFER_SIZE 1024
#define PI 3.14159
// 구조체: PascalCase 또는 snake_case
typedef struct {
    char name[50];
    int age;
} Student;
// 전역 변수: g_ 접두사 사용
int g_total_count = 0;
// 함수 매개변수: 명확한 이름
void process_data(int *input_array, int array_size);

메모리 안전성

일반적인 메모리 관련 버그 방지.

// 변수 항상 초기화
int count = 0;        // 좋음
int count;            // 위험 - 초기화되지 않음
// malloc 반환 값 확인
int *ptr = malloc(sizeof(int) * 10);
if (ptr == NULL) {
    printf("Memory allocation failed!\n");
    return -1;
}
// 할당된 메모리는 항상 해제
free(ptr);
ptr = NULL;  // 우발적인 재사용 방지
// 배열 경계 확인
for (int i = 0; i < array_size; i++) {
    // 안전한 배열 접근
    array[i] = i;
}

성능 팁

효율적인 C 코드 작성.

// 적절한 데이터 타입 사용
char small_num = 10;   // 작은 값의 경우
int normal_num = 1000; // 일반적인 정수의 경우
// 루프 내 함수 호출 최소화
int len = strlen(str); // 한 번 계산
for (int i = 0; i < len; i++) {
    // 문자열 처리
}
// 자주 접근하는 변수에 register 사용
register int counter;
// 크기를 알 때 동적 할당보다 배열 선호
int fixed_array[100];  // 스택 할당
// 대 동적 배열
int *dynamic_array = malloc(100 * sizeof(int));

코드 구성

유지 관리를 위해 코드 구조화.

// 헤더 파일 (utils.h)
#ifndef UTILS_H
#define UTILS_H
// 함수 프로토타입
double calculate_area(double radius);
int fibonacci(int n);
// 구조체 정의
typedef struct {
    int x, y;
} Point;
#endif // UTILS_H
// 구현 파일 (utils.c)
#include "utils.h"
#include <math.h>
double calculate_area(double radius) {
    return M_PI * radius * radius;
}

관련 링크