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

C 프로그래밍 기초: 명령어 제어 구조 이해하기

devgodmj 2024. 10. 21. 21:17

1. 순차 제어 (Sequential Control)

순차 제어는 프로그램이 위에서 아래로 순차적으로 명령어를 실행하는 구조입니다. 대부분의 프로그래밍에서 기본적으로 사용되는 구조로, 특별한 조건이나 반복 없이 작성된 순서대로 명령이 실행됩니다.

int main() {
    int a = 5;
    int b = 10;
    int sum = a + b;
    printf("합계는 %d입니다.\n", sum);
    return 0;
}

 

위 코드에서는 a와 b를 더한 값을 출력하는데, 프로그램은 위에서 아래로 차례대로 실행됩니다.


2. 선택 제어 (Selection Control)

선택 제어는 특정 조건에 따라 실행되는 코드 블록을 선택하는 방식입니다. 조건문은 프로그램이 다양한 경로 중 하나를 선택할 수 있도록 도와줍니다.

조건문 사용 예시:

  • if-else 문: 주어진 조건이 참일 때와 거짓일 때 다른 명령어를 실행합니다.
if (a > b) {
    printf("a는 b보다 큽니다.\n");
} else {
    printf("a는 b보다 크지 않습니다.\n");
}
  • switch 문: 변수가 특정 값에 따라 실행되는 코드 블록을 선택합니다.
switch (op) {
    case '+':
        c = a + b;
        break;
    case '-':
        c = a - b;
        break;
    default:
        printf("잘못된 연산자입니다.\n");
        break;
}

3. 반복 제어 (Repetition Control)

반복 제어는 특정 조건이 만족될 때까지 코드 블록을 반복 실행하는 구조입니다. 반복문을 사용하면 동일한 작업을 효율적으로 처리할 수 있습니다.

반복문 사용 예시:

  • for 문: 반복 횟수가 정해진 경우 주로 사용됩니다.
for (int i = 0; i < 10; i++) {
    printf("%d\n", i);
}

 

  • while 문: 조건이 참일 동안 계속 실행됩니다.
while (n > 0) {
    printf("%d\n", n);
    n--;
}​
  • do-while 문: 일단 한 번은 실행되고, 그 후 조건을 검사합니다.
do {
    printf("%d\n", n);
    n--;
} while (n > 0);

4. 함수 호출과 복귀

C 프로그래밍에서 함수는 코드를 모듈화하고 재사용성을 높이는 중요한 도구입니다. 함수는 특정 작업을 수행하고 결과를 반환하는 블록으로, 필요할 때마다 호출하여 사용할 수 있습니다.

함수 선언 및 정의 예시:

int add(int x, int y) {
    return x + y;
}

int main() {
    int result = add(3, 4);
    printf("결과는 %d입니다.\n", result);
    return 0;
}

 

위 예시에서 add 함수는 두 수를 더한 값을 반환하며, main 함수는 이를 호출하여 결과를 출력합니다.

 

- 순차 제어: 코드가 작성된 순서대로 실행된다.
- 선택 제어: 조건에 따라 실행 경로를 선택한다.
- 반복 제어: 조건이 만족되는 동안 코드를 반복 실행한다.
- 함수 호출과 복귀: 함수를 통해 코드를 모듈화하고 재사용한다.

5. C 프로그램 기본 구조

C 프로그램은 입력과 출력을 다루기 위해 scanf와 printf 같은 함수들을 사용합니다. 특히 scanf 함수는 사용자의 입력을 변수에 저장할 때 메모리 주소를 직접 전달해야 하므로, 주소 연산자인 &를 사용합니다.

C 프로그램 예시:

#include <stdio.h>

int main() {
    int a, b, c;
    char op;

    printf("두 숫자를 입력하세요: ");
    scanf("%d %d", &a, &b);

    printf("연산자를 입력하세요: ");
    scanf(" %c", &op);

    if (op == '+') { c = a + b; }
    else if (op == '-') { c = a - b; }
    else if (op == '*') { c = a * b; }
    else if (op == '/') { c = a / b; }

    printf("%d %c %d = %d\n", a, op, b, c);
    return 0;
}

 

  • 선언문
    • 변수는 사용되기 전에 선언되어야 함.
    • 형(type)과 그 형을 갖는 변수들의 나열로 표현
  •  C언어의 자료형
    • char: 한 개의 문자를 저장할 수 있는 character 형
    • int: 정수 하나를 저장할 수 있는 정수형 
      int를 2byte, 즉 16비트로 저장하는 경우 32768에서 32767 사이의 값 저장 가능
    • float: 부동소수점 숫자를 저장할 수 있는 실수형
    • double: float보다 2배정도의 부동소수점 숫자를 저장할 수 있는 실수형

6. 함수와 재귀 호출

함수의 원형 (Function Prototype)

 

함수의 원형이란, 함수의 반환 타입, 이름, 매개변수의 타입 및 개수를 선언하는 부분입니다. 함수의 존재와 사용법을 컴파일러에게 미리 알려주는 역할을 하며, 함수가 실제로 정의되기 전에 호출되더라도 컴파일 에러를 방지할 수 있습니다.

type function_name (type1 parameter1, type2 parameter, ... typeN parameter)

 

예시

int my_pow(int x, int y); // 함수 원형 선언

 

  • 함수 원형에는 반환값의 형(type)과 함수 이름, 매개변수들의 형과 이름이 포함됩니다.

함수의 정의 (Function Definition)

 

함수의 정의는 함수의 실제 구현 부분을 명시합니다. 함수의 원형과 함께 함수의 동작을 포함하며, 본체(body)는 중괄호 {} 안에 작성됩니다.

예시

int add(int a, int b) {
    return a + b;
}

 

  • 실제 함수 프로그램은 원형 다음에 프로그램 코드에 의하여 정의됩니다.
  • main() 이외의 함수는 호출되어야 실행됩니다.
  • 함수가 호출되면 매개변수에 실제 값이 전달되고, 함수 정의의 프로그램 문장을 실행한 후 그 결과 값을 반환합니다.
int my_pow(int x, int y); //함수 원형 선언

main()
{
	int k;
    for(k=2; k < 6; k++)
    	printf("%d ** %d = %d\n", k, k+1, my_pow(k, k+1)); //함수 호출
}

imt my_pow(int x, int y)
{
	int i, ans = 1;
    for(i = 0; i < y; i++)
    	ans = ans * x;
    return ans;
}

매개변수와 값에 의한 호출 (Call by Value)

 

함수를 호출할 때 매개변수로 전달되는 값들은 원본 값을 변경하지 않고, 함수 내부에서만 복사본이 생성되어 사용됩니다. 이를 값에 의한 호출이라고 합니다.


재귀 호출 (Recursive Call) 함수

 

재귀란, 함수가 자기 자신을 다시 호출하는 프로그래밍 기법을 의미하며, 문제를 더 작은 단위로 분할하여 해결할 때 유용합니다. 재귀 함수를 작성할 때는 반드시 종료 조건(base case)을 명시해 무한 루프에 빠지지 않도록 해야 합니다.

예시: 팩토리얼 계산

int factorial(int n) {
    if (n <= 1) 
        return 1;
    else 
        return n * factorial(n - 1);
}

 

위 코드에서 factorial 함수는 n 값이 1 이하일 때 1을 반환하며, 그렇지 않으면 자기 자신을 호출하여 n * factorial(n - 1)의 결과를 반환합니다.

피보나치 수열과 재귀 함수

피보나치 수열은 각 항이 앞의 두 항의 합인 수열입니다. 이를 재귀 함수로 계산할 수 있습니다.

예시

int fibo(int n) {
    if (n == 0 || n == 1) 
        return 1;
    else 
        return fibo(n - 2) + fibo(n - 1);
}

 

재귀 호출의 구조

피보나치 수열을 구하는 재귀 함수는 여러 번 중복된 호출이 발생하여 비효율적일 수 있으며, 큰 n 값에서는 스택 메모리 사용이 급증하여 오버플로우 위험이 있습니다. 따라서 메모이제이션 등을 통해 개선할 수 있습니다.

참고사항

  • 효율성 문제: 재귀 구현은 동일한 값을 여러 번 계산하므로 비효율적입니다.
  • 메모리 사용량: 재귀 깊이가 깊어질수록 스택 메모리 사용량이 증가합니다.

이와 같은 재귀 호출 기법을 적절히 이해하고 활용하면, 복잡한 문제를 단순한 논리로 해결할 수 있습니다.