C 컴파일과 함수
120분 안팎
학습 목표
gcc -std=c11 -Wall -Wextra -Werror로 작은 센서 변환 프로그램을 빌드합니다.
개념
장치 연결 전에 실행 경계를 만듭니다
센서 드라이버를 처음 맡으면 거리 숫자가 맞는지만 보고 넘어가기 쉽습니다. 하지만 1250mm가 1m로 잘리는 오류는 장애물 위치와 정지 판단까지 바꿉니다. 이번 레슨은 입력 정수를 m 단위 실수로 변환하는 작은 C 함수를 작성합니다. 정상 입력 하나를 눈으로 보는 데서 끝내지 않고 소수 부분이 필요한 값과 0을 테스트합니다. 로봇 프로그램의 계산과 빌드가 각각 어떤 실패를 만드는지도 구분합니다.
이 모듈의 작업 환경
Python 3, bash, C11을 지원하는 gcc 명령을 준비합니다. macOS의 gcc 명령은 Apple Clang을 호출할 수 있으므로 명령 이름과 실제 컴파일러 제품을 구분합니다. 네 레슨은 ZIP을 푼 starter 폴더에서 hal_sim.c의 TODO를 고치고 bash check.sh로 확인합니다. solution ZIP은 비교용이며 따라하기의 성공 출력은 수정이 끝난 구현에서 나온 값입니다. ROS 2, 보드, 외부 C 패키지는 사용하지 않습니다.
각 실습의 check.sh와 demo.sh는 자신의 폴더로 이동하고 mktemp로 만든 임시 디렉터리에 실행 파일을 빌드합니다. EXIT 처리로 산출물을 지워 원본 폴더에 바이너리를 남기지 않습니다. 프로젝트 미션은 앞 모듈의 Python 코드와 회귀 테스트를 포함합니다. C 코드를 바꾸는 동안 기존 프레임 계산을 삭제하거나 새 계산으로 대체하지 않습니다. 실패 출력과 수정 후 출력은 같은 테스트 조건으로 비교합니다.
Python 함수와 C 함수의 차이를 확인합니다
C 함수는 반환형과 매개변수형을 소스에 적습니다. double mm_to_m(int mm)는 정수 mm를 받아 double 결과를 반환하는 계약입니다. 함수 본체의 중괄호는 실행할 문장 범위를 묶고 return은 값을 호출자에게 돌려줍니다. 문장 끝에는 세미콜론이 필요합니다. Python처럼 들여쓰기만으로 블록을 정하지 않으므로 보기 좋게 들여쓰더라도 중괄호 위치를 함께 검토합니다.
변환 함수는 값을 계산하는 역할만 맡습니다. printf를 함수 안에 넣으면 단위 검사와 화면 표시가 섞이고, 다음 모듈에서 숫자를 재사용하기가 어려워집니다. 반환값은 double로 유지하고 sim_main.c가 마지막 표시 단계에서 소수점 세 자리로 출력합니다. 1.250이라는 문자열을 계산 함수의 결과로 만들지 않습니다. 같은 계산을 테스트 실행 파일과 관찰 실행 파일에서 호출해도 함수의 의미는 같습니다.
정수 나눗셈이 먼저 실행됩니다
1250 / 1000의 두 피연산자는 정수입니다. C의 정수 나눗셈 결과 1이 나온 뒤 double로 반환되어 1.0이 됩니다. 반환형만 실수로 바꿔서는 이미 사라진 소수 부분을 복구하지 못합니다. 1250 / 1000.0으로 작성하면 한쪽이 실수이므로 실수 나눗셈으로 1.25를 얻습니다. 정수 입력은 유지하고 나누는 상수에서 연산 종류를 명확하게 정합니다.
mm_to_m은 단위 변환만 담당하며 센서 유효 범위를 검사하지 않습니다. 따라서 -250은 -0.25로 변환됩니다. 이 값이 실제 거리로 허용되는지는 read_range의 별도 계약에서 판단합니다. 변환과 센서 검증을 합치면 부호 처리 오류인지 장치 범위 오류인지 찾기 어렵습니다. 입력의 정수형은 이 과제의 계약이며 문자 파싱과 임의로 큰 외부 숫자 처리까지 확장하지 않습니다.
헤더는 호출 계약입니다
hal.h에는 함수 선언과 이후 레슨에서 사용할 구조체 선언을 둡니다. hal_sim.c는 그 계약을 구현하고 test_hal.c는 공개 함수를 호출합니다. 세 파일이 같은 함수 모양을 이해하도록 구현 파일도 헤더를 포함합니다. 선언은 본체를 만들지 않으므로 헤더에 선언이 있다고 실행 파일에 함수가 들어가는 것은 아닙니다. 정의가 담긴 소스가 빌드 명령에 참여해야 합니다.
헤더의 인클루드 가드는 같은 번역 단위 안에서 헤더가 반복 포함되는 문제를 막습니다. 여러 소스 파일에 같은 외부 함수 정의를 복사하는 문제까지 막지는 않습니다. 테스트 파일에 변환 함수 본체를 붙여 넣으면 실제 HAL 구현 대신 테스트 자체를 시험하게 됩니다. 함수 정의를 한 곳에 두고 두 실행 파일이 같은 정의와 링크되도록 유지합니다. 선언과 정의의 반환형이나 인자 개수가 다르면 계약부터 맞춥니다.
경고를 빌드 결과의 일부로 봅니다
gcc -std=c11 -Wall -Wextra -Werror hal_sim.c test_hal.c -lm -o 실행파일 형식으로 빌드합니다. -std=c11은 언어 기준, -Wall과 -Wextra는 여러 경고 그룹, -Werror는 발생한 경고를 오류로 취급하는 설정입니다. 이 옵션이 모든 잘못된 메모리 사용이나 단위 오류를 찾아 주지는 않습니다. 정수 나눗셈처럼 문법상 합법인 잘못된 계산은 실행 테스트가 찾아야 합니다.
헤더를 찾지 못하는 file not found는 경로나 파일명부터 확인합니다. undeclared function처럼 선언이 없다는 진단은 호출 파일의 include와 함수 철자를 봅니다. undefined reference 또는 Undefined symbols는 링크에 구현 소스가 빠졌는지 확인합니다. 컴파일러와 운영체제에 따라 문구가 달라지므로 모든 환경에서 동일한 진단 문자열을 기대하지 않습니다. 단계와 함수 이름을 근거로 원인을 좁힙니다.
실행 성공과 계산 성공은 다릅니다
main의 반환값 0은 테스트 실행이 성공했다는 종료 상태로 사용합니다. 검사 불일치가 있으면 test_hal.c는 FAIL 항목을 stderr에 적고 1로 종료합니다. 출력에 숫자가 나타났다는 사실만으로 성공을 판단하지 않습니다. 셸 스크립트는 실패한 종료 상태에서 멈추므로 미션에서 C 검사 실패를 숨기고 Python 검사만 통과시키는 방식은 허용되지 않습니다.
starter의 정수 나눗셈은 빌드는 통과하지만 fraction, minimum mm, signed conversion 검사가 실패합니다. 테스트 이름은 어떤 기대가 깨졌는지 알려 줍니다. fraction은 1250mm의 0.25m가 보존되지 않았다는 뜻입니다. zero와 maximum mm가 통과하더라도 구현 전체가 맞는 것은 아닙니다. 1000의 배수만 시험하면 결함이 드러나지 않으므로 경계와 소수 사례를 함께 선택합니다.
한 줄 수정의 근거를 남깁니다
변환 상수를 1000.0으로 고친 뒤 bash check.sh를 다시 실행합니다. PASS unit checks=5가 나와야 하고 demo에서 1250mm=1.250m, 50mm=0.050m를 관찰합니다. printf의 %.3f는 소수점 아래 세 자리를 보여 주며 계산값 자체를 세 자리로 저장하는 기능은 아닙니다. 출력 자리수를 바꿔 오류를 감추기보다 변환식과 기대 수치를 먼저 확인합니다.
완료 기록에는 사용한 빌드 옵션, 실패한 테스트 이름, 수정 이유, 수정 후 종료 상태를 적습니다. 실행 파일이 없다는 오류가 나면 계산부터 수정하지 말고 빌드 성공과 실행 경로를 확인합니다. m 단위 숫자를 확보한 다음 레슨에서는 이 숫자를 가상 장치가 소유한 배열에서 꺼내는 계약을 만듭니다. 전처리 세부 흐름과 기호표 분석은 더 읽기로 넘기고 여기서는 작은 함수의 재현 가능한 실행을 끝냅니다.
호출 경계를 코드로 확인합니다
변환 결과를 double로 받아 표시 코드에 넘기는 조각입니다. stdio.h의 선언과 main 본체는 다운로드한 sim_main.c에서 확인합니다.
double range_m = mm_to_m(1250);
printf("range=%.3f\n", range_m);따라하기
시작 코드의 실패를 확인합니다
starter ZIP을 푼 폴더에서 실행합니다. stdout 요약과 별도로 stderr의 FAIL 항목이 어느 계약을 가리키는지 읽습니다. 종료 상태 1은 이번 TODO에서 기대한 실패입니다.
bash check.sh실행 결과
FAIL unit checks=5 failures=3
계약에 맞는 대입을 작성합니다
hal_sim.c의 해당 TODO를 다음 코드로 고칩니다. 아래는 편집할 C 조각이며 셸에서 직접 실행하는 명령이 아닙니다. 헤더와 테스트는 유지합니다.
double mm_to_m(int mm) {
return mm / 1000.0;
}같은 검사를 다시 실행합니다
수정한 starter 폴더에서 다시 검사합니다. 성공 요약과 종료 상태 0을 확인합니다. 이 출력은 solution 구현에서 실행해 확인한 값입니다.
bash check.sh실행 결과
PASS unit checks=5
저장된 결과를 관찰합니다
수정 후 관찰 실행 파일로 값과 상태를 확인합니다. 아래 명령은 임시 디렉터리에서 빌드하고 정리합니다.
bash demo.sh실행 결과
1250mm=1.250m 50mm=0.050m
확인 문제
실습
hal_sim.c의 mm_to_m을 수정해 정수 mm의 소수 m를 보존합니다. 0, 1250, 50, 8000, -250mm 변환 검사 5개를 통과합니다. 센서 유효 범위 검사는 이 함수에 추가하지 않습니다.
실행 명령
bash check.sh
기대 결과
PASS unit checks=5
모범 답안
모범 답안 내려받기더 읽기
면접 질문
- C 센서 변환 함수의 빌드 성공과 계산 정확성을 어떻게 구분해 검증합니까?
- 실험 기록을 재생할 때 함께 남겨야 할 정보를 설명해 주시면 됩니다.