[절차 지향 프로그래밍] 혼자 공부하는 C언어

'혼자 공부하는 C언어' 내용 정리 + '절차 지향 프로그래밍' 영상처리 프로젝트
조수환's avatar
Apr 21, 2024
[절차 지향 프로그래밍] 혼자 
 공부하는 C언어
Contents
1. 프로그램 만들기2. 상수와 데이터 출력3. 변수와 데이터 입력4. 연산자7. 함수8. 배열9-1. 포인터9-2. 포인터 완전 정복을 위한 포인터 이해하기10. 배열과 포인터11. 문자12. 문자열13. 변수 사용 영역14. 응용 포인터15. 메모리 동적 할당16. 사용자 정의 자료형17. 파일 입출력18. 전처리와 분할 컴파일

1. 프로그램 만들기

  • 컴파일 과정 3단계
      1. 전처리(Preprocessing)
          • 사용자가 작성한 소스 파일을 컴퓨터에 적합한 형태로 편집하는 과정입니다.
            • #include 전처리 지시자를 통해 외부 헤더 파일(예: <stdio.h>)을 불러와 필요한 함수를 사용할 수 있습니다.
            • 전처리 과정에서는 소스 파일의 형태에는 변화가 없습니다.
      1. 컴파일(Compilation)
          • 전처리된 소스 파일을 기계어로 변환하는 과정입니다.
          • 변환된 결과물을 개체 파일(Object file)이라고 하며, CPU가 이해할 수 있는 기계어 명령어들로 구성되어 있습니다.
          • 그러나 개체 파일은 바로 실행할 수 없으며, 운영체제에서 인식할 수 있는 형태로 변환되어야 합니다.
      1. 링크(Linking)
          • 개체 파일을 운영체제에서 실행할 수 있는 실행 파일로 만드는 과정입니다.
          • 프로그램을 실행하기 전에 필요한 준비 작업을 수행하는 startup code가 결합됩니다.
          • startup code는 main 함수를 호출하여 사용자가 작성한 프로그램 코드가 실행되도록 합니다.
          • 따라서 프로그램에는 항상 main 함수가 존재해야 합니다.
    • 이처럼 컴파일 과정은 전처리, 컴파일, 링크의 3단계로 진행되며, 각 단계에서 소스 파일이 컴퓨터에서 실행 가능한 형태로 변환됩니다.

2. 상수와 데이터 출력

2.1 main 함수 구조

  • main 함수는 함수 원형과 함수 몸체로 구성됩니다.
  • 함수 원형은 함수의 이름, 매개변수, 반환형이 포함되어 있습니다.
  • 함수 몸체에는 실제 프로그램 코드가 작성됩니다.

2.2 개행문자 \n

  • \n은 커서의 현재 위치와 상관없이 다음 줄로 이동하게 합니다.
  • printf() 함수를 사용할 때 문자열의 끝에 \n을 붙이는 것이 일반적인 관례입니다.
  • 이를 통해 출력 결과가 깔끔하게 정렬됩니다.

2.3 제어 문자의 종류

  • \n: 개행, 커서를 다음 줄로 이동시킵니다.
  • \t: 탭, 커서를 다음 탭 위치로 이동시킵니다. 탭 위치는 8 단위로 정해져 있습니다.
  • \r: 캐리지 리턴, 커서를 현재 줄의 맨 앞으로 이동시킵니다.
  • \b: 백스페이스, 커서를 한 칸 왼쪽으로 이동시킵니다.
  • \a: 벨 소리, 경보음을 냅니다.

2.4 아스키(ASCII) 코드

  • 아스키 코드는 컴퓨터에서 사용되는 128개의 문자를 나타내는 코드 체계입니다.
  • 영문 대/소문자, 숫자, 특수문자, 제어문자 등이 모두 아스키 코드로 표현됩니다.
  • 각 문자는 0부터 127 사이의 고유한 아스키 코드 값을 가집니다.

2.5 상수의 비트 형태

  • 정수: 4바이트(32비트)로 구성된 2진수 형태
  • 실수: 8바이트(64비트)로 IEEE 754 표준 double 형식
  • 문자: 4바이트(32비트)로 아스키 코드 값과 같은 2진수 형태

2.6 음수의 변환

  1. 양수의 절대값 구하기
  1. 구한 절대값을 2진수로 변환하기
  1. 0과 1을 바꾸어 1의 보수 만들기
  1. 1을 더하여 2의 보수 만들기
      • ex) 10 + (-10) = 0 (자리올림으로 인해 0이 됨)

2.7 진법 변환

  1. 10진수 → 2진수: 10진수를 2로 나누며 나머지를 마지막부터 나열
  1. 2진수 → 8진수: 2진수를 3비트씩 묶어 각 묶음의 값을 더함
  1. 2진수 → 16진수: 2진수를 4비트씩 묶어 각 묶음의 값을 더함

3. 변수와 데이터 입력

3.1 정수 자료형

  • char: 1바이트
  • short: 2바이트
  • int: 4바이트
  • long: 4바이트
  • long long: 8바이트

3.2 정수 자료형 특징

  1. 일반적으로 int 자료형을 사용하는 것이 좋습니다.
      • shortint보다 메모리 크기가 작지만, 연산 시 int로 변환되어 속도가 느려질 수 있습니다.
  1. longint가 2바이트로 구현된 컴파일러에서만 사용합니다.
      • 컴파일러에 따라 intlong의 크기가 같다면 long을 사용할 필요가 없습니다.
      • sizeof(int) 함수를 통해 int의 크기를 확인할 수 있습니다.

3.3 unsigned 정수 자료형

  • 음수를 저장하지 않고 양수만 저장하므로 더 큰 범위의 값을 표현할 수 있습니다.
  • 나이, 몸무게 등 음수가 필요 없는 경우에 사용합니다.
  • unsigned를 명시하지 않으면 자동으로 signed(부호 있는) 정수형으로 선언됩니다.
  • 큰 양수를 %d로 출력하면 음수가 출력될 수 있고, 음수를 %u로 출력하면 양수가 출력될 수 있습니다.

3.4 실수 자료형 특징

  1. 정수형을 기본으로 사용하고, 실수가 필요한 경우에만 실수형을 사용합니다.
  1. 실수형은 double형을 기본으로 사용합니다.

3.5 문자열 저장

  • 문자열 배열의 크기는 문자열 길이보다 1 더 크게 선언해야 합니다.
  • 컴파일러가 문자열 끝에 NULL 문자(\0)를 자동으로 추가하기 때문입니다.

3.6 배열에는 대입 연산자(=)를 사용할 수 없습니다.

  • 배열의 크기가 고정되어 있어 대입 연산이 불가능합니다.
  • 문자열을 배열에 저장하려면 strcpy() 함수를 사용해야 합니다.
  • 보안 문제가 있는 함수(gets, strcpy, scanf 등)는 _CRT_SECURE_NO_WARNINGS 전처리기를 사용하여 해결할 수 있습니다.

3.7 const를 사용한 변수

  • 변수를 선언할 때 그 앞에 const를 붙이면 초기화된 값을 바꿀 수 없습니다. 따라서 값이 바뀌지 않음을 보장받을 수 있지만 엄연한 변수입니다.

3.8 예약어와 식별자

  • 예약어: 컴파일러와 사용 방법이 약속된 단어
  • 식별자: 사용자가 만들어 사용하는 단어
  • 식별자 규칙
      1. 알파벳 대/소문자, 숫자, 밑줄(_)로 구성
      1. 숫자로 시작할 수 없음
      1. 대/소문자는 구분됨
      1. 예약어는 식별자로 사용할 수 없음

3.9 scanf 함수를 이용한 데이터 입력

  1. 변환 문자를 통해 입력 값의 자료형을 결정합니다.
  1. 여러 개의 값을 한 번에 입력받으려면 변환 문자를 나열하고 변수 사이를 콤마(,)로 구분합니다.
  1. 문자열 입력 시 배열명 앞에 & 기호를 붙이지 않습니다.
  1. 한글은 2바이트, 영문은 1바이트 문자입니다.
  1. char 형 변수 입력 시 %c 변환 문자를 사용해야 합니다.

4. 연산자

4.1 산술 연산자와 대입 연산자

  • 산술 연산자: +, -, *, /, %, 피연산자가 하나인 경우는 부호를 바꾸는 역할을 합니다.
  • 대입 연산자: 우선순위에 따라 다른 연산을 먼저 수행하고 그 결과를 왼쪽 변수에 저장합니다.
  • 실수 연산에는 나머지의 개념이 없으므로 나머지 연산자의 피연산자로는 반드시 정수만을 사용해야 합니다.

4.2 증감 연산자

  • 상수에 직접 증감 연산자를 사용할 수 없습니다.
  • 후위 표기 a++는 다른 연산자와 함께 사용될 때 가장 나중에 연산됩니다.
  • Undefined behavior 예시: (++a) + a + (++a) - 하나의 수식에서 같은 변수를 두 번 이상 사용할 때는 그 변수에 증감 연산자를 사용하면 안 됩니다.

4.3 관계 연산자

  • 대입 연산자와 동등함을 나타내는 관계 연산자를 혼동하지 말아야 합니다.
  • 같지 않음을 나타내는 관계 연산자는 !=입니다.

4.4 논리 연산자

  • short-circuit rule
    • && - 좌항이 거짓이면 우항은 볼 것 없이 거짓
    • || - 좌항이 참이면 우항은 볼 것 없이 참
    • 따라서 예상치 못한 결과가 나올 수 있으니 주의해서 코딩해야 합니다.

4.5 형 변환 연산자

  • 피연산자의 값을 복사해 일시적으로 형태를 바꾸므로, 연산 후 메모리에 남아 있는 피연산자의 형태나 값은 변하지 않습니다.
  • int형을 기본적으로 사용하고 실수 연산 결과가 필요할 때만 형 변환해서 사용하는 것이 좋습니다.
  • 자동 형 변환 (암시적 혹은 묵시적 형 변환) - 기본 규칙은 데이터 크기가 작은 값이 크기가 큰 값으로 바뀌는 것이지만, 예상치 못한 값의 변형이 생길 수 있으므로 가능하면 피연산자의 형태를 같게 맞춰 사용하는 편이 좋습니다.

4.6 sizeof 연산자

  • warning C4477 - sizeof 연산의 결과 값 형태와 변환 문자가 일치하지 않아 나오는 경고로, 불편하면 변환 문자를 %zd로 바꾸거나, #pragma warning(disable:4477)을 소스 코드 첫 줄에 추가할 수 있습니다.
  • sizeof 연산자에 괄호를 쓰지 않아도 되지만, 피연산자에 괄호로 묶어 주는 것이 편리합니다. 하지만 함수가 아닙니다.

4.7 복합대입 연산자

  • 왼쪽 피연산자는 반드시 변수가 와야 합니다.
  • 가장 마지막에 복합대입 연산자를 계산합니다.

4.8 콤마 연산자

  • 한 번에 여러 개의 수식을 차례로 나열해야 할 때 사용합니다.
  • 왼쪽부터 오른쪽으로 차례로 연산을 수행하며, 가장 오른쪽의 피연산자가 최종 결과값이 됩니다.
  • 제어문에서 조건식을 나열하는 괄호 안과 같이 세미콜론 사용이 불가능한 구조에서 사용합니다.

4.9 조건 연산자(삼항 연산자)

  • (a < b) ? a : b;
    • 코드를 간략히 만들어 주는 효과가 있지만, 가독성이 떨어질 수 있으므로 필요한 곳에서만 사용하는 것이 좋습니다.

4.10 비트 연산자

  • & - 두 비트가 모두 1인 경우에만 1로 계산
  • ^ - 두 비트가 서로 다른 경우만 1로 계산
  • | - 두 비트 중 하나라도 1이면 1로 계산
  • ~ - 1을 0으로 바꾸고 0을 1로 바꿈
  • << - 비트를 왼쪽으로
  • >> - 비트를 오른쪽으로

4.11 연산자 우선순위와 연산 방향

  • 큰 흐름으로 보면 다음과 같습니다:
    • 단항 연산자 > 이항 연산자 > 삼항 연산자 순으로 연산
    • 산술 연산자 > (비트 이동 연산자) > 관계 연산자 > 논리 연산자 순으로 연산
  • 여러 연산자를 함께 사용할 때는 주저 없이 괄호를 사용해서 표현을 명확하게 하는 것이 좋습니다.

7. 함수

7.1 재귀함수와 반복문의 차이

  • 재귀호출은 하나의 함수에서 코드를 반복 실행하는 듯하지만, 실제로는 새로운 함수를 실행하는 것과 같습니다.
  • 함수는 호출만으로 일정 크기의 메모리를 사용하므로 무한 호출하면 스택 메모리를 모두 사용하게 되어 메모리 부족으로 강제 종료됩니다. 따라서, 반복 고리를 끊을 수 있는 조건식을 반드시 포함해야 합니다.
    • 코드 읽기가 쉽지 않고 반복 호출되면서 메모리를 사용하므로 제한적으로 사용합니다.
  • 처리할 데이터를 스스로 입력하는 함수에는 매개변수가 없어도 됩니다.
  • 전달받은 데이터를 화면에 출력하는 함수는 반환형을 쓰지 않아도 됩니다.
  • 같은 내용을 단지 화면에 출력하는 함수는 매개변수와 반환값을 둘 다 쓰지 않아도 됩니다.

8. 배열

8.1 배열과 반복문

  • sizeof 연산자를 활용하여 배열 요소의 개수를 계산하실 수 있습니다.
  • 배열 요소의 개수 = sizeof(배열명) / sizeof(배열 요소)

8.2 char형 배열의 선언과 초기화

  • 일반 배열 선언과 초기화와 동일하지만, NULL문자 \0를 저장하기 위해 최소 여분의 1바이트 공간이 필요합니다.

8.3 널 문자의 용도

  • 남는 배열 요소에는 자동으로 아스키 코드 값이 0인 문자 \0가 채워지며, 이는 문자열의 끝을 표시하는 용도로 사용됩니다.

8.4 문자열 대입

  • 일반 변수처럼 대입 연산자를 사용하여 새로운 문자열을 저장할 수 없습니다.
  • strcpy()함수를 사용하여 문자열을 복사하셔야 합니다.

8.5 문자열 전용 입출력 함수: gets()puts()

  • gets()는 scanf와 달리 빈칸을 포함해 한 줄 전체를 문자열로 입력할 수 있습니다.
    •  gets()배열의 크기를 검사하지 않으므로 항상 배열의 크기를 생각해서 사용해야 합니다.
  • puts()는 문자열을 출력하는 함수입니다.

9-1. 포인터

9.1 메모리의 주소

  • 프로그램이 사용하는 메모리의 위치를 주소 값으로 식별할 수 있습니다.
  • 변수명은 이러한 메모리 주소에 임의의 이름을 붙인 것입니다.

9.2 주소 연산자 &

  • 변수의 시작 주소를 구해줍니다.
  • 전용 변환 문자는 %p이며, 주소값의 데이터 크기에 따라 자릿수를 맞춰 16진수 대문자로 출력됩니다.

9.3 포인터와 간접 참조 연산자

  • 포인터는 변수의 메모리 주소를 저장하는 변수입니다.
  • 포인터 변수 선언 시 변수의 자료형을 적습니다.
  • 포인터가 가리키는 변수를 사용할 때 간접 참조 연산자 *을 사용합니다.

9.4 const를 사용한 포인터

  • 가리키는 변수의 값을 간접 참조해서 바꿀 수 없습니다.
  • 그러나 직접 변수에 값을 대입하는 방식으로는 바꿀 수 있습니다.
int x = 10; int* ptr = &x; *ptr = 20; // 가능: 포인터 변수를 통해 간접적으로 x의 값을 20으로 변경 printf("x = %d\n", x); // 출력: x = 20 int y = 30; **ptr = y; // 컴파일 에러: 포인터로 간접 참조하여 값을 바꿀 수 없음 ptr = &y; // 가능: 포인터 변수 자체에 y의 주소를 대입하여 ptr이 y를 가리키도록 함 printf("*ptr = %d\n", *ptr); // 출력: *ptr = 30

9-2. 포인터 완전 정복을 위한 포인터 이해하기

9.5 주소와 포인터의 차이

  • 포인터에 다른 주소를 대입하여 값을 바꿀 수 있습니다.
  • 하나의 변수를 동시에 가리키는 것도 가능합니다.
  • 주소는 상수처럼 정해진 값만 사용할 수 있지만, 포인터는 변수처럼 공간으로 사용할 수 있습니다.
  • 주소는 L-value로 사용할 수 없지만, 포인터 변수는 L-value로 사용할 수 있습니다.
    • int x = 10; int* ptr = &x; // 포인터 변수 ptr는 x의 주소를 가지고 있음 *ptr = 20; // 포인터 변수 ptr를 통해 x의 값을 20으로 변경 (L-value 사용) printf("x = %d\n", x); // 출력: x = 20
  • 둘 다 산술 연산자를 사용할 수 있습니다.

9.6 주소와 포인터의 크기

  • sizeof 연산자로 주소와 포인터의 크기를 확인할 수 있습니다.
  • 포인터 변수의 크기는 주소의 크기와 같습니다.
  • 포인터 변수가 가리키는 변수의 크기는 달라질 수 있습니다.

9.7 포인터의 대입 규칙

  1. 포인터는 가리키는 변수의 형태가 같을 때만 대입해야 합니다.
  1. 형 변환을 사용한 포인터의 대입은 언제나 가능합니다.

9.8 함수의 반환

  • 함수는 오직 한 개의 값만 반환할 수 있습니다.
  • 두 개 이상의 변수를 반환 받으려면 포인터를 사용해야 합니다.

10. 배열과 포인터

10.1 배열명

  • 배열명은 첫 번째 배열 요소의 주소 값을 나타냅니다.
  • 배열명은 첫 번째 배열 요소를 가리킵니다.

10.2 배열 요소 사용

  • 주소 + 정수 → 주소 + (정수 * 주소를 구한 변수의 크기)
    • (ary + 0) == ary[0]

10.3 배열명 역할을 하는 포인터

  • 포인터 변수로 배열 요소에 접근할 수 있습니다.
    • pa = 10;(pa + 1) = 20;pa[2] = pa[0] + pa[1];

10.4 배열명과 포인터의 차이

  1. sizeof 연산 결과가 다릅니다.
      • sizeof(배열명): 배열 전체 크기 = arr[10] = 40B
      • sizeof(포인터): 포인터 크기 = *arr = 4B
  1. 변수와 상수의 차이
      • 포인터: 값 변경 가능
      • 배열명: 상수이므로 값 변경 불가능
      int arr[5] = {1, 2, 3, 4, 5}; int* ptr = arr; printf("arr = %p\n", arr); // arr = 메모리 주소 (예: 0x7ffee9c04a00) printf("ptr = %p\n", ptr); // ptr = 메모리 주소 (예: 0x7ffee9c04a00) arr++; // 컴파일 에러: 배열명은 상수이므로 값을 변경할 수 없음 ptr++; // 가능: 포인터 변수이므로 값(주소)을 변경할 수 있음 printf("arr = %p\n", arr); // arr = 이전 메모리 주소 (예: 0x7ffee9c04a00) printf("ptr = %p\n", ptr); // ptr = 이전 메모리 주소 + 4 (예: 0x7ffee9c04a04)

10.5 포인터와 배열 요소

  • 출력 방법
      1. pa를 배열명처럼 사용: printf("%d", pa[0]);
      1. (pa + 0)printf("%d", *(pa + 0));
      1. paprintf("%d", *pa);
  • 주의점
      1. 포인터의 값이 변할 수 있으므로 유효한 값인지 확인해야 합니다.
      1. 포인터에 증가 연산자와 간접 참조 연산자를 함께 사용할 때는 전위 표현이 불가능합니다.

10.6 포인터의 뺄셈과 관계 연산

  • 포인터 - 포인터값의 차 / 가리키는 자료형의 크기

10.7 배열 출력 함수

  • 함수 호출 시 배열명을 전달합니다.
  • 함수 내에서 포인터를 배열명처럼 사용합니다.
#include <stdio.h> void printArray(int* arr, int size) { printf("[ "); for (int i = 0; i < size; i++) { printf("%d ", arr[i]); // arr[i]처럼 배열 표기법 사용 가능 } printf("]\n"); } int main() { int arr[] = {1, 2, 3, 4, 5}; int size = sizeof(arr) / sizeof(int); printArray(arr, size); // 배열명 arr을 전달 return 0; }

10.8 배열 입력 함수

  • 배열 출력 함수와 구현 방법이 동일합니다.
  • 함수 내에서 포인터를 직접 사용하여 데이터를 저장할 배열의 위치를 나타냅니다.

11. 문자

11.1 아스키 코드

  • 128개의 문자를 0~127 숫자로 표현한 것입니다.
  • 숫자, 대/소문자, 특수 문자, 제어 문자로 구성됩니다.
  • 대/소문자는 아스키 코드 값의 차이가 32입니다.
    • 종류
      문자 상수
      아스키 코드 값
      출력할 때
      숫자 문자 (10개)
      '0' ~ '9'
      48 ~ 57
      문자 출력
      대문자 (26개)
      'A' ~ 'Z'
      65 ~ 90
      문자 출력
      소문자 (26개)
      'a' ~ 'z'
      97 ~ 122
      문자 출력
      특수 문자 (33개)
      ' '(공백), '$', '&' ...
      32, 36, 38 ...
      문자 출력
      제어 문자 (33개)
      '\0''\t''\n''\r' ...
      0, 9, 10, 13 ...
      제어 기능 수행

11.2 scanf를 사용한 문자 입력

  • scanf("%c", &ch)화이트 스페이스까지 입력 받습니다.
    • %cscanf 함수에서 사용될 때 알파벳이나 숫자 모양의 문자(단순 정수가 아님) 등 형태가 있는 문자를 입력하지만 공백이나 탭 문자, 개행 문자와 같은 제어 문자도 입력하므로 주의해야 합니다.
  • getchar()개행 문자를 제외한 문자 입력입니다.

11.3 getchar 함수를 사용한 문자열 입력

// 예제 11-6 #include <stdio.h>void my_gets(char* str, int size); int main(void) { char str[7]; my_gets(str, sizeof(str)); printf("입력한 문자열 : %s\n", str); return 0; } void my_gets(char* str, int size) { int ch; int i = 0; ch = getchar(); while ((ch != '\n') && (i < size - 1)) { str[i] = ch; i++; ch = getchar(); } str[i] = '\0'; }
  • 입력 버퍼를 지워야 합니다.
  • stdin, stdout
    • fgetc(stdin), fputc(ch, stdout)으로 문자 입출력이 가능합니다.
    • 표준 입력/출력 버퍼를 직접 사용합니다.

11.4 버퍼를 사용하는 입력 함수

  • 버퍼를 활용하여 데이터를 안정적으로 입력 받을 수 있습니다.
    • 입출력 함수가 버퍼를 사용하면 좋은 점
        1. 안정적인 데이터 입력
            • 버퍼는 일정 크기의 연속된 저장 공간으로, 입력된 데이터를 일시적으로 보관합니다.
            • 키보드로 입력된 데이터는 우선 버퍼에 저장되므로, 프로그램이 다른 작업을 수행하더라도 데이터가 소실되지 않고 안정적으로 입력 받을 수 있습니다.
        1. 독립적인 입력 장치 사용
            • scanf 함수는 입력 장치와 직접 연결되지 않고, 정해진 크기와 형태를 가진 버퍼에서 입력을 처리합니다.
            • 이는 입력 장치가 변경되더라도 함수를 수정하지 않고도 동일한 방식으로 사용할 수 있음을 의미합니다.
            • 입력 장치와 버퍼 간의 연결은 운영체제가 담당하므로 프로그램은 입력 장치의 변경에 독립적으로 동작할 수 있습니다.

12. 문자열

12.1 문자열의 기본 구조와 특징

  • 문자열은 문자 배열로 표현됩니다.
  • 문자열의 첫 번째 문자 주소를 사용하여 문자열을 나타냅니다.
  • 문자열은 상수로 구현할 수 있습니다.
  • 주소를 통해 문자열을 직접 수정하는 것은 바람직하지 않습니다.

12.2 문자열 출력 함수

  • printf("%s", 문자열)로 문자열을 출력할 수 있습니다.
  • while() 문을 이용해 문자열의 각 문자를 하나씩 출력할 수 있습니다.

12.3 문자열 입력 함수

  • scanf("%s", 문자열)로 공백이 없는 문자열을 입력받을 수 있습니다.
  • gets() 함수는 공백이 포함된 문자열을 입력 받을 수 있지만, 버퍼 문제가 발생할 수 있습니다.
    • char str[80]; printf("문자열 입력: "); gets(str); printf("입력한 문자열: %s\n", str);
    • gets() 함수 사용 시 중요
        1. 버퍼에는 개행 문자 그대로, 배열에는 널 문자로 바뀌어 저장
        1. gets() 함수는 enter만 눌러도 입력을 끝냄
  • fgets() 함수는 버퍼 크기를 고려하여 문자열을 입력 받습니다.
    • char str[80]; printf("문자열 입력: "); fgets(str, sizeof(str), stdin); printf("입력한 문자열: %s\n", str);

12.4 문자열 조작 함수

  • strcpy() 함수로 문자열을 복사할 수 있습니다.
    • strcpy(복사 받을 배열명, 복사할 문자열)
      • 첫 번째 인수 → char 배열이나 배열명을 저장한 포인터만 사용 가능합니다.
      • 두 번째 인수 → 문자열의 시작 위치를 알 수 있다면 어떤 것이든 사용 가능합니다.
      char str1[80] = "apple"; char str2[80]; strcpy(str2, str1); printf("복사된 문자열: %s\n", str2);
  • strncpy() 함수로 지정한 길이만큼 문자열을 복사할 수 있습니다.
    • char str[20] = "mango tree"; strncpy(str, "apple-pie", 5); printf("수정된 문자열: %s\n", str);
  • strcat(), strncat() 함수로 문자열을 이어 붙일 수 있습니다.
    • char str[80] = "straw"; strcat(str, "berry"); printf("문자열 결합: %s\n", str); strncat(str, "piece", 3); printf("문자열 일부 결합: %s\n", str);
    • strcat() 함수 사용 시 주의점
        1. 메모리 침범 가능성이 있습니다.
        1. 사용할 때 배열을 초기화 해야 합니다.
  • strlen() 함수로 문자열의 길이를 구할 수 있습니다.
    • char str1[80] = "banana"; char str2[80] = "strawberry"; if (strlen(str1) > strlen(str2)) printf("str1이 더 깁니다.\n"); else printf("str2가 더 깁니다.\n");
  • strcmp(), strncmp() 함수로 문자열을 비교할 수 있습니다.
    • char str1[80] = "pear"; char str2[80] = "peach"; if (strcmp(str1, str2) > 0) // str1 - str2 printf("사전 순으로 str1이 나중에 나옵니다.\n"); else printf("사전 순으로 str2가 나중에 나옵니다.\n"); if (strncmp(str1, str2, 3) == 0) printf("앞 3글자가 같습니다.\n"); else printf("앞 3글자가 다릅니다.\n");

13. 변수 사용 영역

13.1 지역 변수

  • 함수 내부에서만 사용할 수 있는 변수입니다.
  • auto 예약어를 사용하거나 생략할 수 있습니다.
  • 다른 함수에서 사용할 수 없습니다.
  • 메모리 관리와 디버깅에 유리합니다.

13.2 전역 변수

  • 함수 밖에서 선언된 변수입니다.
  • 프로그램 전체에서 사용 가능합니다.
  • 0으로 자동 초기화됩니다.
  • 전역 변수와 지역 변수 이름이 같으면 지역 변수가 우선입니다.

13.3 정적 지역 변수

  • static 예약어를 사용하여 선언합니다.
  • 함수 호출이 끝나도 변수의 값이 유지됩니다.
  • 0으로 자동 초기화됩니다.

13.4 레지스터 변수

  • register 예약어를 사용하여 변수를 선언합니다.
  • CPU의 레지스터에 저장되어 빠른 처리가 가능합니다.
  • 전역 변수, 주소 연산이 불가능합니다.

13.5 데이터 공유 방법

  • 값 복사: 함수 호출 시 변수의 값을 전달합니다.
  • 주소 전달: 함수 호출 시 변수의 주소를 전달하여 값을 변경할 수 있습니다.

13.6 주소 반환 함수

  • 정적 지역 변수나 전역 변수의 주소를 반환할 수 있습니다.
  • 지역 변수의 주소는 반환할 수 없습니다.
  • 반환값의 자료형은 반환값을 저장할 포인터의 자료형입니다.
#include <stdio.h> int* getStaticAddress() { static int x = 0; // 정적 지역 변수 return &x; } int* getGlobalAddress() { int global = 10; // 전역 변수 return &global; } int* getLocalAddress() { int local = 20; // 지역 변수 return &local; // 컴파일 경고: 주소가 참조 범위를 벗어남 } int main() { int* ptr1 = getStaticAddress(); int* ptr2 = getGlobalAddress(); int* ptr3 = getLocalAddress(); // 문제 발생 가능 printf("*ptr1 = %d\n", *ptr1); // 0 printf("*ptr2 = %d\n", *ptr2); // 10 printf("*ptr3 = %d\n", *ptr3); // 쓰레기 값 or 런타임 에러 가능 return 0; }

14. 응용 포인터

14.1 이중 포인터

  • 포인터의 주소를 저장하는 포인터입니다.
  • 첫 번째 *은 가리키는 변수의 자료형, 두 번째 *은 자신의 자료형입니다.
규칙
1. 포인터를 변수명으로 쓰면 그 안의 값이 됩니다.
2. 포인터에 & 연산을 하면 포인터 변수의 주소가 됩니다.
3. 포인터의 * 연산은 화살표를 따라갑니다.
notion image
  • 활용
      1. 포인터 을 바꾸는 함수의 매개변수
      1. 포인터 배열을 매개변수로 받는 함수
      #include <stdio.h> int main() { int x = 10; int* ptr = &x; // 단일 포인터 int** dptr = &ptr; // 이중 포인터 printf("x의 값: %d\n", x); // 출력: 10 printf("x의 주소: %p\n", &x); // 출력: x의 주소 (예: 0x7ffee4c04a1c) printf("ptr의 값: %p\n", ptr); // 출력: x의 주소 (예: 0x7ffee4c04a1c) printf("ptr의 주소: %p\n", &ptr); // 출력: ptr의 주소 (예: 0x7ffee4c04a20) printf("dptr의 값: %p\n", dptr); // 출력: ptr의 주소 (예: 0x7ffee4c04a20) printf("dptr의 주소: %p\n", &dptr); // 출력: dptr의 주소 (예: 0x7ffee4c04a28) // 이중 포인터를 통해 x의 값 변경 **dptr = 20; // *(ptr)에 해당하는 값인 x를 20으로 변경 printf("x의 새로운 값: %d\n", x); // 출력: 20 return 0; }

14.2 배열과 포인터

  • 배열 이름은 1차원 배열의 주소입니다.
  • 2차원 배열 요소 참조 시 포인터 연산을 활용합니다.
  • int ary[3][4];
      1. 2차원 배열 ary의 논리적 배열 요소는 3개 입니다.
      1. 2차원 배열 ary의 물리적 배열 요소는 12개 입니다.
  • 2차원 배열 요소 참조 원리
    • 2차원 배열은 1차원 배열과 같이 모든 저장 공간이 메모리에 연속으로 할당됩니다.
    • 배열명 = 1차원 배열의 주소 = 1차원 배열 전체를 가리킴
      • 배열 포인터를 쓰면 1차원의 물리적 공간을 2차원의 논리적 구조로 사용이 가능합니다.
      notion image
      • sizeof(ary) → 배열 전체의 크기 (48바이트)
      • sizeof(&ary[0]) → 주소의 크기 (4바이트)
      • sizeof(ary[0]) → 부분배열 전체의 크기 (16바이트)
      • sizeof(&ary[0][0]) → 주소의 크기 (4바이트)

14.3 함수 포인터

  • 함수 이름은 함수의 시작 주소입니다.
  • 함수 포인터를 사용하여 다양한 기능의 함수를 선택적으로 호출할 수 있습니다.
    • 함수의 형태만 같으면 기능과 상관없이 모든 함수에 함수 포인터 사용이 가능합니다.
      • 형태가 같은 다양한 기능의 함수를 선택적으로 호출할 때 주로 사용합니다.
#include <stdio.h> int add(int a, int b) { return a + b; } double divide(double a, double b) { return a / b; } void printResult(void (*func)(void), int a, int b) { printf("Result: %d\n", func(a, b)); } int main() { void (*ptr)(void) = NULL; ptr = add; printResult(ptr, 5, 3); // 출력: Result: 8 ptr = divide; printResult(ptr, 10, 2); // 출력: Result: 5 return 0; }

14.4 void 포인터

  • 가리키는 자료형이 정해지지 않은 포인터입니다.
  • 다른 자료형의 주소를 저장할 수 있습니다.

15. 메모리 동적 할당

15.1 동적 할당의 개념

  • 프로그램 실행 중에 메모리 공간을 할당하는 것 입니다.
  • mallocfree 함수를 사용하여 동적 할당과 반환을 수행합니다.

15.2 malloc 함수

  • 지정된 크기만큼의 메모리 공간을 할당하고 그 주소를 반환합니다.
  • 반환값이 NULL인지 확인해야 합니다.
  • 사용이 끝나면 free 함수로 메모리를 반환해야 합니다.
ptr = (int*)malloc(size * sizeof(int)); // 2차원 배열 동적 할당 int row = 512; int col = 512; unsigned char** p; p = (unsigned char**)malloc(sizeof(unsigned char*) * row); // 3개 할당 (행) for (int i = 0; i < row; i++) { if(p[i]=!NULL) p[i] = (unsigned char*)malloc(sizeof(unsigned char) * col); // 5개 할당 (열) } // 2차원 배열 반환 for (int i = 0; i < inH; i++) { free(inImage[i]); } free(inImage);

15.3 배열처럼 사용하기

  • 한 번에 메모리 공간을 할당하고 포인터로 관리합니다.
  • 포인터를 배열처럼 사용할 수 있습니다.

15.4 기타 동적 할당 함수

  • calloc: 할당한 공간을 0으로 초기화합니다.
    • ptr = (int*)calloc(size, sizeof(int));
  • realloc: 할당된 공간의 크기를 변경합니다.
    • newptr = (int*)realloc(ptr, newSize * sizeof(int));

15.5 동적 할당을 활용한 문자열 처리

  • 입력 문자열의 길이를 모를 때 동적 할당으로 유연하게 대응이 가능합니다.

15.6 함수에서의 동적 할당

  • 함수로 동적 할당된 메모리를 관리할 때는 구조를 잘 살펴야 합니다.

15.7 명령행 인수 사용

  • 명령행 인수
    • 명령행에서 프로그램을 실행시킬 때 프로그램 이름 외에도 줄 수 있는 프로그램에 필요한 정보입니다.
    • int main(int argc, char *argv[])
    • argc (Argument Count): 명령행에 입력된 문자열의 개수를 나타냅니다. 프로그램 이름 자체도 하나의 문자열로 간주되므로 최소 1 이상의 값을 가집니다.
    • argv (Argument Vector): 명령행에 입력된 문자열들의 배열입니다. argv[0]은 프로그램 이름, argv[1]부터 명령행 인수가 저장됩니다.

16. 사용자 정의 자료형

16.1 구조체(Struct)

  • 사용자가 정의하는 복합 자료형입니다.
  • 다양한 자료형을 하나로 묶을 수 있습니다.
  • 구조체 변수의 멤버에 접근할 때는 구조체명.멤버명 형식을 사용합니다.
#include <stdio.h> #include <string.h> struct Person { char name[20]; int age; char job[20]; }; int main() { struct Person p1; strcpy(p1.name, "John Doe"); p1.age = 35; strcpy(p1.job, "Software Engineer"); printf("Name: %s\n", p1.name); printf("Age: %d\n", p1.age); printf("Job: %s\n", p1.job); return 0; }

16.2 구조체 선언의 위치

  • main 함수 앞 - 프로그램 전체에서 사용 가능합니다.
  • 함수 안 - 그 함수 내에서만 사용 가능합니다.

16.3 구조체 변수의 크기

  • 멤버 사이에 "패딩 바이트"가 삽입될 수 있습니다.
  • #pragma pack(1)을 사용하면 바이트 정렬을 최소화할 수 있습니다.

16.4 다양한 구조체 멤버

  • 문자열, 포인터 등 다양한 형태의 멤버를 가질 수 있습니다.

16.5 구조체 내 구조체 사용

  • 구조체의 멤버로 다른 구조체를 가질 수 있습니다.

16.6 구조체 변수의 초기화 및 대입

  • 중괄호로 묶어 초기화할 수 있습니다.
  • 대입 연산이 가능하여 함수 인수로도 사용 가능합니다.

16.7 구조체와 함수

  • 구조체 변수를 함수 인수로 전달할 수 있습니다.
  • 함수에서 구조체 변수를 반환할 수 있습니다.

16.8 구조체 포인터와 화살표 연산자

  • 구조체 변수의 주소를 가리키는 포인터를 사용 가능합니다.
  • 포인터로 구조체 멤버에 접근할 때는 화살표 연산자(->)를 사용합니다.

16.9 구조체 배열

  • 구조체 변수들의 배열을 선언할 수 있습니다.
  • 배열 처리 함수를 만들어 사용할 수 있습니다.

16.10 자기 참조 구조체와 연결 리스트

  • 구조체 내에 자기 자신을 가리키는 포인터 멤버를 가질 수 있습니다.
  • 이를 통해 동적으로 관련 데이터를 연결할 수 있습니다.

16.11 공용체(Union)

  • 모든 멤버가 하나의 저장 공간을 공유하는 복합 자료형입니다.
  • 메모리 절약과 동시에 같은 공간의 값을 다양한 형태로 사용할 수 있습니다.
  • 공용체 변수의 크기는 가장 큰 멤버의 크기로 결정합니다.
  • 공용체 변수의 초기화는 첫 번째 멤버만 초기화가 가능합니다.
#include <stdio.h> union Data { int i; float f; }; int main() { union Data data; data.i = 10; printf("Integer value = %d\n", data.i); data.f = 220.5; printf("Float value = %f\n", data.f); printf("Integer value = %d\n", data.i); return 0; }

16.12 열거형(Enum)

  • 정수 값을 기호화하여 나열할 수 있는 자료형입니다.
  • enum 열거형명 { 기호1, 기호2, ... } 형식으로 선언합니다.
  • 기호화된 정수 값을 변수에 저장하여 사용합니다.
#include <stdio.h> enum WeekDay { Mon, Tue, Wed, Thu, Fri, Sat, Sun }; int main() { enum WeekDay today = Wed; switch (today) { case Mon: printf("Today is Monday.\n"); break; case Tue: printf("Today is Tuesday.\n"); break; case Wed: printf("Today is Wednesday.\n"); break; case Thu: printf("Today is Thursday.\n"); break; case Fri: printf("Today is Friday.\n"); break; case Sat: printf("Today is Saturday.\n"); break; case Sun: printf("Today is Sunday.\n"); break; } return 0; }

16.13 typedef를 이용한 새로운 자료형 정의

  • typedef를 사용하면 기존 자료형의 이름을 새로운 이름으로 정의할 수 있습니다.
  • 구조체 등의 복합 자료형에서 주로 사용합니다.
  • 예약어 사용이 불필요해져 코드가 간결해집니다.
#include <stdio.h> typedef struct { int id; char name[20]; double gpa; } Student; void printStudentInfo(Student s) { printf("ID: %d\n", s.id); printf("Name: %s\n", s.name); printf("GPA: %.2f\n", s.gpa); } int main() { Student s1 = {123, "John Doe", 3.8}; printStudentInfo(s1); return 0; }

17. 파일 입출력

17.1 파일 개방/폐쇄

  • fopen()함수로 파일을 열 수 있으며, 개방 모드(r, w, a-append)를 지정할 수 있습니다.
    • 함수 원형: FILE *fopen(const char*, const char*);
    • 개방할 파일을 찾는 기본 위치는 [현재 작업 디렉터리]
    • 다른 곳에 있는 파일을 개방하고 싶다면 경로를 함께 작성합니다.
    • 실제 파일이 있는 장치와 연결되는 스트림 파일을 메모리에 생성 → 스트림 파일에 접근할 수 있도록 파일 포인터를 반환합니다.
  • fclose()함수로 파일을 닫을 수 있습니다.
    • 함수 원형: int fclose(FILE*);
    • 닫을 파일의 파일 포인터를 제공합니다.
    • 파일을 성공적으로 닫았을 때는 0, 오류가 발생하면 EOF 를 반환합니다.
    • 사용이 끝난 파일은 즉시 닫아 스트림 파일의 데이터를 장치에 기록하는 것이 좋습니다.
#include <stdio.h> int main(void) { FILE* fp; fp = fopen("a.txt", "r"); if (fp == NULL) { printf("파일이 열리지 않았습니다.\n"); return 1; } printf("파일이 열렸습니다.\n"); fclose(fp); return 0; }

17.2 문자 입출력 함수

  • fgetc() 함수로 파일에서 문자를 입력 받을 수 있습니다.
    • 파일에서 하나의 문자를 입력해 반환합니다.
    • 파일의 데이터를 모두 읽으면 EOF 를 반환합니다.
    • 데이터 입력 과정
      1. 파일 포인터와 연결된 스트림 파일에서 데이터를 가져옵니다.
      1. 하드디스크에서 데이터를 가져올 때는 한 번에 버퍼의 크기만큼 가져와 저장합니다.
      1. 버퍼에서 첫 번째 문자를 가져와 반환합니다.
      1. 두 번째 호출될 때는 버퍼로부터 바로 문자를 읽습니다.
        1. notion image
      하드디스크에 더 이상 읽을 데이터가 없으면 EOF 를 반환합니다.
  • fputc() 함수로 파일에 문자를 출력할 수 있습니다.
    • 출력할 문자와 파일 포인터를 인수로 주면 파일로 문자를 출력합니다.
    • 반환값은 출력한 문자를 다시 반환합니다.
    • 에러가 발생하면 EOF 를 반환합니다.
    • 데이터 출력 과정
      1. 버퍼에 데이터를 모읍니다.
      1. 버퍼가 모두 채워지면 하드디스크에 출력합니다.
      1. 버퍼가 모두 채워지지 않더라도 개행 문자(\n)를 출력하거나 새로운 입력을 수행하는 경우 데이터를 장치로 출력합니다.
        1. notion image
      #include <stdio.h> int main(void) { // 파일 읽기 FILE* fp; int ch; fp = fopen("a.txt", "r"); if (fp == NULL) { printf("파일이 열리지 않았습니다.\n"); return 1; } while (1) { ch = fgetc(fp); if (ch == EOF) break; putchar(ch); } fclose(fp); // 파일 쓰기 fp = fopen("b.txt", "w"); if (fp == NULL) { printf("파일을 만들지 못했습니다.\n"); return 1; } char str[] = "banana"; int i = 0; while (str[i] != '\0') { fputc(str[i], fp); i++; } fputc('\n', fp); fclose(fp); return 0; }

17.3 표준 입출력 스트림

  • stdin, stdout, stderr와 같은 표준 입출력 스트림을 사용할 수 있습니다.
    • 스트림 파일명
      용도
      연결된 입출력 장치
      stdin
      표준 입력 스트림
      키보드
      stdout
      표준 출력 스트림
      모니터
      stderr
      표준 에러 스트림
      모니터

17.4 텍스트 파일과 바이너리 파일

  • 텍스트 파일은 아스키 코드로 저장되고, 바이너리 파일은 바이너리 형식으로 저장됩니다.
  • 파일 개방 모드
    • 파일 개방 모드는 기본적으로 읽기(r), 쓰기(w), 추가(a) 3가지가 있습니다.
    • '+' 를 사용하면 읽기와 쓰기를 동시에 할 수 있습니다.
    • 텍스트 파일의 경우: r+, w+, a+
    • 바이너리 파일의 경우: rb, wb, ab, r+b, w+b, a+b
  • 텍스트 파일 처리
    • char str[100]; // 읽어올 한줄 FILE* rfp; // 파일 처리 3단계 // (1) 파일 열기 rfp = fopen("c:/windows/win.ini", "rt"); // (2) 파일 읽거나 쓰기 int count = 1; while (1) { fgets(str, 100, rfp); if (feof(rfp)) break; printf("%2d행 : %s", count, str); count++; } // (3) 파일 닫기 fclose(rfp);
  • 바이너리 파일 처리
    • FILE* rfp; char fileName[200] = "D:/RAW/LENNA512.raw"; unsigned char image[height][width]; rfp = fopen(fileName, "rb"); fread(image, 1, height * width, rfp); printf("%d", image[511][511]); fclose(rfp);

17.5 다양한 파일 입출력 함수

  • fgets()
    • 함수 원형: char *fgets(char *s, int n, FILE *stream);
    • 파일에서 최대 n-1개의 문자를 읽어 s에 저장하고, 개행 문자를 포함한 문자열을 반환합니다.
  • fputs()
    • 함수 원형: int fputs(const char *s, FILE *stream);
    • 문자열 s를 파일에 출력하고 성공하면 0, 실패하면 EOF를 반환합니다.
  • fscanf()
    • 함수 원형: int fscanf(FILE *stream, const char *format, ...);
    • 파일에서 데이터를 읽어 지정된 형식으로 변환하여 변수에 저장합니다.
  • fprintf()
    • 함수 원형: int fprintf(FILE *stream, const char *format, ...);
    • 지정된 형식에 따라 데이터를 파일에 출력합니다.

17.6 기타 함수

  • fseek()
    • 함수 원형: int fseek(FILE *stream, long offset, int whence);
    • 파일 포인터의 위치를 변경합니다.
  • rewind()
    • 함수 원형: void rewind(FILE *stream);
    • 파일 포인터를 파일의 시작 위치로 이동합니다.
  • feof()
    • 함수 원형: int feof(FILE *stream);
    • 파일의 끝에 도달했는지 확인합니다.
  • fflush()
    • 함수 원형: int fflush(FILE *stream);
    • 스트림 버퍼를 비웁니다.

18. 전처리와 분할 컴파일

18.1 파일을 포함하는 #include

  • 전처리 과정에서 #include 지시자에 의해 해당 파일의 내용이 소스 코드에 포함됩니다.
  • <>""를 사용하여 파일 위치를 지정합니다.
  • 헤더 파일을 사용하면 효율성과 가독성 향상, 분할 컴파일이 가능합니다.

18.2 매크로명을 만드는 #define

  • 매크로 상수는 자주 사용하는 복잡한 값을 의미 있는 이름으로 정의합니다.
  • 간단한 표현이 가능하지만 디버깅 및 유지보수가 어렵습니다.

18.3 #define을 사용한 매크로 함수

  • 함수와 유사하게 동작하지만 실제 함수가 아닙니다.
  • 장점
    • 함수 호출에 필요한 오버헤드 없이 빠른 실행이 가능합니다.
  • 단점
    • 치환 후 발생할 수 있는 문제 예측이 어렵습니다.

18.4 이미 정의된 매크로

  • FILEFUNCTIONLINEDATETIME
    • 이미 정의된 매크로
      기능
      __FILE__
      전체 디렉토리 경로를 포함한 파일명
      __FUNCTION__
      매크로명이 사용된 함수 이름
      __LINE__
      매크로명이 사용된 행번호
      __DATE__
      컴파일을 시작한 날짜
      __TIME__
      컴파일을 시작한 시간
  • #line 지시자로 행번호와 파일명을 직접 설정하는 것이 가능합니다.

18.5 매크로 연산자 ###

  • #: 매크로 인수를 문자열로 변환합니다.
  • ##: 두 토큰을 붙여서 새로운 토큰을 생성합니다.

18.6 조건부 컴파일 지시자

  • #if, #else, #elif, #ifdef, #ifndef, #endif
  • 컴파일 시 조건에 따라 코드를 선택적으로 컴파일합니다.
    • #if 조건식 컴파일할 문장 #endif

18.7 #pragma 지시자

  • 컴파일러 동작 방식을 제어합니다.
  • #pragma pack으로 구조체 정렬을 제어합니다.
  • #pragma warning으로 경고 메시지를 제거합니다.

18.8 분할 컴파일 방법

  • 프로그램을 여러 파일로 나누어 작성합니다.
  • gcc main.c sub.c -o main 으로 컴파일합니다.

18.9 분할 컴파일에서 externstatic의 용도

  • extern
    • 다른 파일에 선언된 전역 변수를 사용합니다.
    • 변수가 다른 파일에 있음을 알리는 용도일 뿐, 새로운 변수를 선언하는 것은 아닙니다.
  • static
    • 다른 파일에서 전역 변수를 공유하지 못하도록 할 때 사용합니다.
    • static 변수
      • 다른 파일에서 같은 이름의 전역 변수를 선언할 수 있습니다.
      • 사용 범위를 하나의 파일로 제한하므로 데이터를 보호할 수 있습니다.
    • static 함수
      • 다른 파일에서 같은 이름의 함수를 정의할 수 있습니다.
      • 다른 파일에서 잘못 호출할 가능성을 차단합니다.

18.10 헤더 파일의 필요성과 중복 문제 해결 방법

  • 필요성
    • 공유 자원(함수, 구조체 등) 관리에 용이합니다.
  • 중복 문제 해결
    • #ifndef, #define으로 중복을 방지합니다.
 

소프트웨어 배포 생명 주기

💡
본 프로젝트는 아래의 소프트웨어 배포 생명 주기에 따라 진행 되었습니다.
notion image
  • 알파(Alpha)
    • 알파 단계는 초기 개발 단계로, 핵심 기능은 구현되어 있지만 많은 버그가 존재할 수 있습니다.
  • 베타(Beta)
    • 베타 단계는 알파 단계 이후로, 기능은 대부분 구현되어 있지만 여전히 버그가 있을 수 있습니다.
  • RC(Release Candidate)
    • RC 단계는 베타 단계 이후로, 대부분의 버그가 수정되어 출시에 가까운 상태입니다.
  • 프리뷰(Preview)
    • 프리뷰 단계는 RC 이후로, 최종 버전을 미리 공개하는 단계입니다.
  • 릴리즈(Release)
    • 릴리즈 단계는 정식 버전으로, 일반 사용자에게 배포되는 단계입니다.
    • RTM(Release to Manufacturing), GA(General Availability), 커뮤니티 버전 등이 있습니다.

  • 패치(Patch)
    • 패치는 릴리즈 버전에서 발견된 중요 버그를 수정하는 작은 업데이트입니다.
  • 업데이트(Update)는 새로운 기능 추가 및 버그 수정을 포함하는 중간 규모의 업데이트입니다.
  • 서비스 팩(Service Pack)은 대규모 업데이트로, 여러 개의 패치와 업데이트를 하나로 묶은 것입니다.
 

Window API 프로그래밍

  • Windows API는 Microsoft에서 제공하는 Windows 운영 체제의 기능을 사용할 수 있게 해주는 인터페이스입니다.
💡
이번 미니 프로젝트 Version 1에서는 간단한 Windows API 함수를 사용하여 DOS 창을 통해 이미지를 Windows 화면에 직접 출력하는 방식으로 구현하였습니다.
notion image
  • 반전 효과 적용 화면
notion image
 

강의 소스 코드 및 미니 프로젝트 Version 1 발표 자료

Share article
RSSPowered by inblog