21.3.2.1.2. optarg 전역 변수 메모리 안전성 확보 및 정수/실수형 변환(strtol, strtof) 에러 핸들링
우리는 지역 변수 포인터(myoptarg)를 활용해 px4_getopt 파서루프를 스레드 관점에서 완벽하게 안전하게 구성해 내었다.
// 파싱 루프 내부
case 'v':
// myoptarg 변수 안에는 사용자가 타자 친 '5'라는 글자가 들어있다.
my_custom_value = atoi(myoptarg);
break;
이제 파서는 -v 깃발 뒤에 따라온 문자열 텍스트("5")가 적힌 메모리 주소를 myoptarg 포인터에 걸어서 우리에게 건네주었다. 우리는 이 텍스트 덩어리를 잘 씹어서 컴퓨터가 인식할 수 있는 순수한 숫자(Integer나 Float) 변수로 캐스팅(Casting)해야 한다.
초보 개발자들은 여기서 십중팔구 C 언어의 국민 함수인 atoi() 나 atof()를 들이밀곤 한다. 하지만 항공 우주 소프트웨어(Flight Stack)인 PX4의 엄격한 코드 컨벤션에서는, 의존성이 강한 메인 파라미터 파싱에 저런 구시대적이고 맹목적인 캐스팅 함수의 사용을 절대 승인해주지 않는다.
1. atoi() / atof()의 함정과 불친절함
왜 atoi를 쓰면 안 될까? 사용자가 -v 5 대신 오타를 내서 -v 5abc라고 쳤거나, 아예 숫자가 아니라 -v hello라고 문자를 입력했다고 가정해 보자.
atoi 함수는 매우 멍청하고 무책임하다. 이 함수는:
- 앞쪽에 아라비아 숫자가 있으면 읽을 수 있는 데까지만 대충 읽고 뒤의 에러 문자열(
abc)은 무시해 버린다. (Silent Error) - 아예 숫자가 아닌 문자열(
hello)이 들어오면 에러를 뿜는 대신 조용히 숫자0을 반환해 버린다!
만약 저 -v 옵션이 기체의 초기 추력(Thrust) 퍼센티지나, 모터의 PWM 주파수를 결정하는 파라미터였다면 как(어떻게) 될까? 사용자는 hello라고 잘못 쳤을 뿐인데, 펌웨어는 아무런 경고도 없이 추력을 0으로 세팅하고 하늘로 날아오르려(혹은 추락하리라) 할 것이다.
2. 철통 방어 무기: strtol()과 strtof()의 2단 포인터 검증
PX4 코어 메인테이너들은 이러한 잠재적 재앙을 막기 위해, 문자열 파싱 시 반드시 C 표준 라이브러리의 좀 더 진보된 함수인 **strtol(String to Long)**과 **strtof(String to Float)**를 사용하도록 강제한다.
이 함수들의 가장 강력한 무기는 바로 **‘두 번째 포인터 인자(End Pointer)’**에 있다.
#include <cstdlib> // strtol, strtof가 포함된 헤더
// ... 파싱 루프 내부
case 'v': {
char *endptr = nullptr;
// 10은 10진법을 의미함
long val = strtol(myoptarg, &endptr, 10);
// 유효성 검사 1: 아무 숫자도 읽지 못했거나, 쓰레기 문자가 뒤에 섞여있는가?
if (myoptarg == endptr || *endptr != '\0') {
PX4_ERR("Invalid integer value for -v: %s", myoptarg);
return 1; // 즉시 스레드 종료
}
// 유효성 검사 2: 자료형(int)의 표현 범위를 초과하는가? (옵션)
if (val < 0 || val > 100) {
PX4_ERR("-v value out of range (0-100)");
return 1;
}
my_custom_value = (int)val;
break;
}
2.1 방어벽의 작동 원리 (End Pointer)
strtol 함수는 숫자를 읽어나가다가 숫자가 아닌 글자(a, b, ., 등)를 만나는 순간 파싱을 멈춘다. 그리고 멈춰버린 바로 그 불법 글자의 메모리 주소를 endptr이라는 포인터에 쾅 하고 찍어준다.
우리는 이 endptr의 상태만 검사하면 입력값의 무결성을 100% 장담할 수 있다.
myoptarg == endptr: 애초에 첫 글자부터 문자가 들어와서 숫자를 단 한 글자도 못 읽었다는 뜻이다. 에러!*endptr != '\0': 숫자를 좀 읽다가 마지막에 이상한 문자(예:5abc)가 걸려서 널 포인터(\0)까지 깔끔하게 도달하지 못했다는 뜻이다. 에러!
이처럼 strtol과 strtof의 포인터 추적(Pointer Tracking) 기법을 사용하면, 사용자가 타이핑한 텍스트 쓰레기가 컨트롤러의 중요한 수학 공식 안으로 흘러 들어오는 것을 원천 차단할 수 있다.
이제 커스텀 모듈을 NSH 콘솔에 무사히 띄우고 옵션을 예쁘게 깎는 방법까지 모두 마스터했다. 다음 장(21.3.3)에서는 실제 드론 하드웨어에 이 코드를 올리기 전, 내 노트북 모니터 안에서 안전하게 날려볼 수 있는 시뮬레이션(SITL) 컴파일 방법에 대해 알아보도록 하겠다.