#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의 기본 동작 일부를 구현하는 것입니다. 각 부분을 분석해 보겠습니다.
이 함수는 정수를 문자로 변환해 출력합니다.
- 숫자를 문자열 형식으로 변환하여
putchar로 하나씩 출력합니다. - 음수 처리와
0처리에 대한 예외도 포함되어 있습니다.
-
초기화:
char num_str[10]: 숫자를 거꾸로 저장할 버퍼.int i = 0: 현재 버퍼 인덱스.bool is_negative = false: 음수인지 여부 체크.
-
예외 처리:
- 입력값이
0이면 바로'0'을 출력하고 종료.
- 입력값이
-
음수 처리:
- 음수일 경우
is_negative를true로 설정하고 값을 양수로 변환.
- 음수일 경우
-
숫자 변환:
value % 10을 통해 일의 자리 숫자를 추출하고, 문자로 변환해num_str에 저장.value /= 10으로 다음 자리수를 처리.- 숫자가 끝날 때까지 반복.
-
출력:
- 음수일 경우
'-'출력. num_str을 역순으로 읽어 출력.
- 음수일 경우
이 함수는 printf와 비슷한 방식으로 문자열을 처리하며, 가변 인자를 지원합니다.
va_list args: 가변 인자 목록을 관리하는 객체.va_start(args, format): 가변 인자 목록 초기화.format이후의 인자를 처리할 준비.va_arg(args, type): 가변 인자 목록에서 지정된type의 인자를 하나씩 꺼냄.va_end(args): 가변 인자 목록 처리를 종료.
- 문자열을 순회하며
format에 따라 문자 하나씩 처리. if (*format == '%'): 포맷 문자인%를 만나면 다음 문자를 검사.- 포맷 처리:
%s: 문자열.va_arg로 문자열 인자를 가져와 출력.%d: 정수.va_arg로 정수 인자를 가져와print_number함수로 출력.
- 일반 문자는 그대로 출력.
이 함수는 printf2를 호출하여 가변 인자의 동작을 테스트합니다.
printf2("String: %s, Integer: %d\n", "Hello World!@#!@#", -4200);- 포맷 문자열
"String: %s, Integer: %d\n"과 두 인자"Hello World!@#!@#"와-4200을 전달. printf2함수는 다음을 수행:"String: ": 일반 문자열 출력.%s: 문자열"Hello World!@#!@#"출력.", Integer: ": 일반 문자열 출력.%d: 정수-4200을print_number로 출력.'\n': 줄바꿈.
String: Hello World!@#!@#, Integer: -4200
...(가변 인자)는 다양한 타입과 개수의 인자를 처리하는 데 유용.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는 항상 전달되는 고정 인자이고, ... 이후에는 가변적인 수와 타입의 인자를 받을 수 있습니다.
가변 인자를 처리하기 위해 다음과 같은 매크로와 함수가 필요합니다:
va_list: 가변 인자 목록을 저장하는 변수.va_start(va_list, last_fixed_arg): 가변 인자 목록을 초기화. 고정된 마지막 인자를 기준으로 가변 인자의 시작점을 지정.va_arg(va_list, type): 가변 인자 목록에서 지정된 타입의 다음 인자를 가져옴.va_end(va_list): 가변 인자 처리가 끝난 후 정리.
- 컴파일러는 함수 호출 시 전달된 모든 인자를 스택에 저장합니다.
va_start는 가변 인자가 시작하는 위치를 설정합니다.va_arg를 호출할 때마다 해당 타입 크기만큼 스택에서 값을 읽고 포인터를 다음 인자로 이동합니다.
va_list args;
va_start(args, format);- 가변 인자 목록
args를 선언하고,format이후의 인자를 처리할 준비를 합니다.
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로 정수를 읽습니다.
va_end(args);- 가변 인자 처리 종료.
va_end는 내부적으로 가변 인자 포인터를 무효화하여 안전성을 보장합니다.
- 유연성: 다양한 개수와 타입의 인자를 처리할 수 있음.
- 표준화된 방식: 표준 라이브러리 함수(
printf,scanf등)처럼 포맷 기반 입력/출력이 가능.
- 타입 안전성 부족: 인자의 타입이 명시적이지 않아, 호출자가 잘못된 타입을 전달하면 예측할 수 없는 동작이 발생.
- 오류 검출 어려움: 전달된 인자의 수와 타입이 일치하지 않아도 컴파일러가 오류를 감지하지 못함.
- 복잡성: 가변 인자 목록을 처리할 때 정확한 관리가 필요하며, 실수로 인한 오류 가능성 증가.
- 가변 인자의 첫 번째 고정 인자는 반드시 포함하고, 가변 인자의 시작 위치를 알리는 데 사용해야 합니다.
va_arg로 꺼내는 타입이 실제 전달된 인자의 타입과 일치해야 합니다.- 가변 인자 목록을 종료할 때 반드시
va_end를 호출해야 합니다.
...(가변 인자)는 C 언어에서 높은 유연성을 제공하지만, 신중하게 사용해야 합니다. 타입 안전성을 보장하지 않으므로 잘못된 사용은 프로그램 동작을 예측 불가능하게 만들 수 있습니다. printf2 함수는 이를 간단히 활용하여 문자열과 정수를 처리하는 방법을 보여주는 좋은 예입니다.