Quick Reference
명령행 인자는 문자열 배열입니다. argv[0]는 프로그램 이름, 실제 인자는 argv[1]부터이며 모든 접근 전에 argc를 검사합니다.
int main(int argc, char *argv[]) {
if (argc < 2) {
fprintf(stderr, "usage: %s <input>\n", argv[0]);
return 1;
}
errno = 0;
char *end;
long repeat = strtol(argv[1], &end, 10);
if (errno == ERANGE || end == argv[1] || *end != '\0') {
fprintf(stderr, "invalid repeat count: %s\n", argv[1]);
return 1;
}
return 0;
}필수 위치 인자는 먼저 argc로 존재를 확인하고, 숫자 문자열은 strtol의 end pointer와 범위를 함께 검사합니다. 옵션을 받는 프로그램은 unknown·중복·-- 뒤 인자 정책을 명시합니다.
문법
argc는 인자 개수, argv는 문자열 포인터 배열입니다. 보통 argv[0]는 프로그램 이름이고, 실제 인자는 argv[1]부터 시작합니다.
int main(int argc, char *argv[]) {
for (int i = 0; i < argc; i++) {
printf("argv[%d] = %s\n", i, argv[i]);
}
return 0;
}즉 명령행 인자는 타입이 정해진 값 목록이 아니라 문자열 목록이므로, 필요한 타입으로 변환하는 단계가 꼭 필요합니다.
입력 해석
숫자 변환은 atoi보다 strtol이 더 낫습니다.
char *end = NULL;
long value = strtol(argv[1], &end, 10);
if (end == argv[1] || *end != '\0') {
fprintf(stderr, "invalid number: %s\n", argv[1]);
return 1;
}argv[1]에 접근하기 전에 argc >= 2를 확인합니다. strtol 전에는 errno = 0으로 두고, end == argv[1], *end != '\0', errno == ERANGE, 목적 타입 범위를 모두 검사해야 합니다.
옵션이 적으면 strcmp로 직접 파싱해도 충분합니다.
int verbose = 0;
const char *path = NULL;
for (int i = 1; i < argc; i++) {
if (strcmp(argv[i], "-v") == 0) {
verbose = 1;
} else {
path = argv[i];
}
}중요한 건 코드를 짜기 전에 usage 규칙을 먼저 정하는 것입니다. 어떤 것은 플래그인지, 어떤 것은 값이 붙는 옵션인지, 어떤 것은 위치 인자인지 구조를 먼저 정해야 합니다.
unknown option, 중복 옵션, 값이 필요한 옵션의 누락, -- 뒤 인자를 option으로 해석할지에 대한 정책도 usage에 맞춰 고정합니다. 작은 도구라도 마지막 위치 인자를 계속 덮어쓰는 식으로 두면 호출 실수를 조용히 받아들이게 됩니다.
빠른 점검
argv[0]는 보통 프로그램 이름입니다.argv[1]부터 실제 인자가 시작됩니다.- 필수 인자는 개수 검사부터 해야 합니다.
- 숫자 변환은
atoi보다strtol이 안전합니다. - 옵션과 위치 인자는 규칙을 먼저 정하고 파싱해야 합니다.
- usage와 에러 메시지는
stderr에 출력하는 편이 보통 맞습니다.
주의할 점
명령행 인자 처리 코드는 입력 검증 코드입니다. "내가 실행하니까 대충 받아도 된다"는 식으로 짜면, 잘못된 문자열 하나가 범위 오류, 잘못된 파일 경로 처리, 옵션 해석 오류로 그대로 이어질 수 있습니다.
참고 링크
2 sources