Skip to content

Instantly share code, notes, and snippets.

@YangSiJun528
Created December 28, 2024 05:24
Show Gist options
  • Select an option

  • Save YangSiJun528/3c5fee7f26f3197e2755e0b8e773451a to your computer and use it in GitHub Desktop.

Select an option

Save YangSiJun528/3c5fee7f26f3197e2755e0b8e773451a to your computer and use it in GitHub Desktop.
C printf2 해석.md
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>

void print_number(int value) {
	char num_str[10];
	int i = 0;
	bool is_negative = false;

	if (value == 0) {
		putchar('0');
		return;
	}

	if (value < 0) {
		is_negative = true;
		value = -value;
	}

	while (value != 0) {
		num_str[i++] = (value % 10) + '0';
		value /= 10;
	}

	if (is_negative) {
		putchar('-');
	}

	while (i--) {
		putchar(num_str[i]);
	}
}

void printf2(const char *format, ...) {
	va_list args;
	va_start(args, format);

	while (*format != '\0') {
		if (*format == '%') {
			format++;
			switch (*format) {
				case 's': {
						  char *str = va_arg(args, char*);
						  while (*str) {
							  putchar(*str++);
						  }
						  break;
					  }
				case 'd': {
						  int d = va_arg(args, int);
						  print_number(d);
						  break;
					  }
			}
		} else {
			putchar(*format);
		}
		format++;
	}

	va_end(args);
}

int main() {
	printf2("String: %s, Integer: %d\n", "Hello World!@#!@#", -4200);
	return 0;
}

이 코드는 C 언어에서 가변 인자를 처리하고 출력하는 사용자 정의 함수 printf2를 구현한 예제입니다. 주요 기능은 표준 라이브러리 함수 printf의 기본 동작 일부를 구현하는 것입니다. 각 부분을 분석해 보겠습니다.


1. print_number 함수

이 함수는 정수를 문자로 변환해 출력합니다.

기능:

  • 숫자를 문자열 형식으로 변환하여 putchar로 하나씩 출력합니다.
  • 음수 처리와 0 처리에 대한 예외도 포함되어 있습니다.

동작:

  1. 초기화:

    • char num_str[10]: 숫자를 거꾸로 저장할 버퍼.
    • int i = 0: 현재 버퍼 인덱스.
    • bool is_negative = false: 음수인지 여부 체크.
  2. 예외 처리:

    • 입력값이 0이면 바로 '0'을 출력하고 종료.
  3. 음수 처리:

    • 음수일 경우 is_negativetrue로 설정하고 값을 양수로 변환.
  4. 숫자 변환:

    • value % 10을 통해 일의 자리 숫자를 추출하고, 문자로 변환해 num_str에 저장.
    • value /= 10으로 다음 자리수를 처리.
    • 숫자가 끝날 때까지 반복.
  5. 출력:

    • 음수일 경우 '-' 출력.
    • num_str을 역순으로 읽어 출력.

2. printf2 함수

이 함수는 printf와 비슷한 방식으로 문자열을 처리하며, 가변 인자를 지원합니다.

가변 인자 처리:

  1. va_list args: 가변 인자 목록을 관리하는 객체.
  2. va_start(args, format): 가변 인자 목록 초기화. format 이후의 인자를 처리할 준비.
  3. va_arg(args, type): 가변 인자 목록에서 지정된 type의 인자를 하나씩 꺼냄.
  4. va_end(args): 가변 인자 목록 처리를 종료.

문자열 처리:

  1. 문자열을 순회하며 format에 따라 문자 하나씩 처리.
  2. if (*format == '%'): 포맷 문자인 %를 만나면 다음 문자를 검사.
  3. 포맷 처리:
    • %s: 문자열. va_arg로 문자열 인자를 가져와 출력.
    • %d: 정수. va_arg로 정수 인자를 가져와 print_number 함수로 출력.
  4. 일반 문자는 그대로 출력.

3. main 함수

이 함수는 printf2를 호출하여 가변 인자의 동작을 테스트합니다.

코드:

printf2("String: %s, Integer: %d\n", "Hello World!@#!@#", -4200);

동작:

  1. 포맷 문자열 "String: %s, Integer: %d\n"과 두 인자 "Hello World!@#!@#"-4200을 전달.
  2. printf2 함수는 다음을 수행:
    • "String: ": 일반 문자열 출력.
    • %s: 문자열 "Hello World!@#!@#" 출력.
    • ", Integer: ": 일반 문자열 출력.
    • %d: 정수 -4200print_number로 출력.
    • '\n': 줄바꿈.

출력 결과:

String: Hello World!@#!@#, Integer: -4200

4. 주요 포인트

  • ... (가변 인자)는 다양한 타입과 개수의 인자를 처리하는 데 유용.
  • va_list, va_start, va_arg, va_end는 가변 인자 처리의 필수 도구.
  • 가변 인자를 사용할 때는 반드시 처리 순서와 타입을 정확히 파악해야 합니다.
  • printf2는 최소한의 포맷 처리만 구현되었으며, 포맷 옵션(%f, %x 등)과 오류 처리가 없는 간단한 버전입니다.

확장 가능성

  • printf2에 추가 포맷 지원(%f, %x, %u)을 구현 가능.
  • 문자열 길이 제한, 유효성 검사 추가 가능.
  • 효율성을 위해 출력 버퍼를 도입해 한 번에 출력하도록 최적화 가능.

가변인자 설명 추가

C 언어에서 ...(가변 인자)는 함수 정의에서 특정 개수의 인자가 아닌, 가변적인 수와 타입의 인자를 전달받을 때 사용됩니다. 가변 인자를 처리하려면 C 표준 라이브러리에서 제공하는 <stdarg.h> 헤더의 기능을 활용해야 합니다.

가변 인자 함수의 정의

가변 인자를 사용하는 함수는 최소한 하나 이상의 고정된 인자를 가져야 하며, ...은 마지막에 위치해야 합니다. 예를 들어:

void example_function(const char *fixed_arg, ...);

위 코드에서 fixed_arg는 항상 전달되는 고정 인자이고, ... 이후에는 가변적인 수와 타입의 인자를 받을 수 있습니다.


가변 인자 처리 메커니즘

1. <stdarg.h> 헤더 사용

가변 인자를 처리하기 위해 다음과 같은 매크로와 함수가 필요합니다:

  • va_list: 가변 인자 목록을 저장하는 변수.
  • va_start(va_list, last_fixed_arg): 가변 인자 목록을 초기화. 고정된 마지막 인자를 기준으로 가변 인자의 시작점을 지정.
  • va_arg(va_list, type): 가변 인자 목록에서 지정된 타입의 다음 인자를 가져옴.
  • va_end(va_list): 가변 인자 처리가 끝난 후 정리.

2. 동작 원리

  • 컴파일러는 함수 호출 시 전달된 모든 인자를 스택에 저장합니다.
  • va_start는 가변 인자가 시작하는 위치를 설정합니다.
  • va_arg를 호출할 때마다 해당 타입 크기만큼 스택에서 값을 읽고 포인터를 다음 인자로 이동합니다.

코드에서의 가변 인자 사용 (printf2)

1. 초기화

va_list args;
va_start(args, format);
  • 가변 인자 목록 args를 선언하고, format 이후의 인자를 처리할 준비를 합니다.

2. 인자 읽기

switch (*format) {
    case 's': {
        char *str = va_arg(args, char*);
        // 문자열 출력
        break;
    }
    case 'd': {
        int d = va_arg(args, int);
        // 정수 출력
        break;
    }
}
  • va_arg(args, type)을 호출해 가변 인자 목록에서 다음 인자를 읽습니다. 인자의 타입(char*, int 등)을 정확히 지정해야 합니다.
  • 예제에서는 %s로 문자열을 읽고, %d로 정수를 읽습니다.

3. 종료

va_end(args);
  • 가변 인자 처리 종료. va_end는 내부적으로 가변 인자 포인터를 무효화하여 안전성을 보장합니다.

가변 인자의 장점과 한계

장점

  1. 유연성: 다양한 개수와 타입의 인자를 처리할 수 있음.
  2. 표준화된 방식: 표준 라이브러리 함수(printf, scanf 등)처럼 포맷 기반 입력/출력이 가능.

한계

  1. 타입 안전성 부족: 인자의 타입이 명시적이지 않아, 호출자가 잘못된 타입을 전달하면 예측할 수 없는 동작이 발생.
  2. 오류 검출 어려움: 전달된 인자의 수와 타입이 일치하지 않아도 컴파일러가 오류를 감지하지 못함.
  3. 복잡성: 가변 인자 목록을 처리할 때 정확한 관리가 필요하며, 실수로 인한 오류 가능성 증가.

가변 인자 사용 시 주의 사항

  1. 가변 인자의 첫 번째 고정 인자는 반드시 포함하고, 가변 인자의 시작 위치를 알리는 데 사용해야 합니다.
  2. va_arg로 꺼내는 타입이 실제 전달된 인자의 타입과 일치해야 합니다.
  3. 가변 인자 목록을 종료할 때 반드시 va_end를 호출해야 합니다.

결론

...(가변 인자)는 C 언어에서 높은 유연성을 제공하지만, 신중하게 사용해야 합니다. 타입 안전성을 보장하지 않으므로 잘못된 사용은 프로그램 동작을 예측 불가능하게 만들 수 있습니다. printf2 함수는 이를 간단히 활용하여 문자열과 정수를 처리하는 방법을 보여주는 좋은 예입니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment