C로 옮기는 측정 함수
100분 안팎
학습 목표
조건문·반복문·함수로 센서 범위 검사를 구현합니다.
개념
표본 판정을 C 함수로 고정합니다
센서 기록기의 여러 경로에서 동일한 범위를 검사해야 한다면 판단을 함수 하나에 모읍니다. 호출하는 쪽이 출력이나 저장을 맡고 sample_valid는 raw가 정상인지 반환합니다. 규격이 바뀌었을 때 함수와 테스트 경계를 함께 수정하면 됩니다. 여기서는 Python에서 확인한 규칙을 C로 옮기며 조건, 함수 반환값, 테스트 반복이 어떻게 연결되는지 확인합니다. 입력 파싱과 하드웨어 접근은 이 함수의 책임이 아닙니다.
함수 계약은 int sample_valid(int raw)입니다. int raw는 이번 호출에서 받은 정수이고 반환형 int는 결과가 정수라는 뜻입니다. 정상 범위에서는 1, 범위 밖에서는 0을 돌려줍니다. 이 값은 센서 원시값 자체가 아니라 판정입니다. raw가 0이어도 반환은 1이어야 합니다. 함수 안에서 printf를 호출하면 다른 호출자가 사용할 때 출력이 섞이므로 판정만 반환하도록 작게 유지합니다.
Python 규칙을 문법만 번역하지 않습니다
정상 조건은 raw >= 0 && raw <= 4095입니다. C의 논리 AND는 &&이고 비교 결과는 참이면 1, 거짓이면 0입니다. Python의 연쇄 비교를 그대로 0 <= raw <= 4095로 쓰면 C에서는 왼쪽 비교 결과인 0 또는 1을 다시 4095와 비교합니다. 그래서 범위 밖 값도 참이 될 수 있습니다. 언어를 옮길 때는 수식 모양보다 각 연산이 평가하는 값을 따라갑니다.
함수 본체는 중괄호로 묶고 return 문장 뒤에 세미콜론을 붙입니다. if의 참 분기에서 return 1을 하고 나머지 경로에서 return 0을 해도 같은 계약입니다. 모든 입력 경로에 반환값이 있어야 합니다. 괄호와 세미콜론이 빠지면 컴파일이 멈추지만, 상한이 4096인 코드는 문법이 맞아서 빌드될 수 있습니다. 컴파일 검사는 문장 형태와 자료형의 일부 문제를, 테스트는 요구한 판단 결과를 확인합니다.
Python의 raw는 실행 중 자료형이 정해지는 이름이었지만 C의 raw는 int 객체입니다. C의 int 표현 범위는 구현에 따라 달라집니다. 이번 검사 함수는 이미 int로 전달된 값을 받으므로 문자열을 해석하거나 큰 정수를 int로 바꾸지 않습니다. 4095까지는 이 실습 환경에서 표현 가능합니다. 최종 CLI는 변환 전 범위 검사를 따로 제공합니다. 함수가 입력을 검사한다는 말을 원시값 범위 검사와 텍스트 변환 검사를 구별해 사용합니다.
#include "sensor.h"
int sample_valid(int raw) {
if (raw >= 0 && raw <= 4095) {
return 1;
}
return 0;
}위 코드를 sensor.c에 저장합니다. sensor.h는 함수 선언을 제공하고 다른 파일에서 함수 이름과 인자·반환형을 알게 합니다. 헤더에 함수 본체를 넣는 대신 sensor.c 한 곳에서 정의합니다. 이번 레슨은 이 함수를 직접 테스트하며 다음 레슨에서는 별도로 컴파일된 main.o가 정의를 어떻게 찾는지 살펴봅니다. 선언과 정의가 같은 계약을 가리키도록 sensor.c도 자기 헤더를 포함합니다.
반복 테스트가 조건의 양 끝을 감시합니다
로컬 실습의 test_sensor.c에는 입력 values와 기대값 expected가 같은 순서로 들어 있습니다. -1, 0, 1, 2048, 4094, 4095, 4096을 함수에 넘깁니다. i는 현재 검사할 위치이고 0에서 시작해 i < 7인 동안 반복합니다. ++i는 다음 위치로 이동합니다. 두 배열은 테스트 자료를 보관하기 위한 것이며 배열의 메모리와 포인터 관계는 다음 모듈에서 자세히 다룹니다.
각 반복에서는 actual에 반환값을 담고 expected[i]와 비교합니다. 다르면 원시값, 기대, 실제를 함께 출력하고 failures를 하나 늘립니다. 모든 사례가 끝난 뒤 검사 건수와 실패 건수를 출력합니다. 실패가 있으면 테스트 프로그램은 1, 없으면 0을 반환합니다. 화면에 PASS 같은 문자열만 쓴 뒤 실제로는 0을 반환하는 가짜 검증과 다르게 make가 종료 코드로 실패를 전달받게 합니다.
실습 starter는 raw <= 4095만 검사하므로 -1도 수락합니다. 처음 make test를 실행하면 정상값 사례들은 이미 맞고 음수 사례가 실패합니다. 실패 문장의 expected=0 actual=1은 테스트가 요구한 거부 대신 함수가 수락했다는 뜻입니다. 기대값을 1로 바꾸면 프로젝트 규격을 바꾸는 것이므로 sensor.c의 조건을 복구합니다. 테스트를 지우거나 실패 출력을 감추는 것도 해결이 아닙니다.
테스트 범위를 바꿀 때는 내부값만 늘리지 않습니다. 상한을 잘못 포함하는 버그는 4095와 4096을 나란히 검사해야 드러납니다. 하한을 잘못 쓰는 버그는 -1과 0으로 드러납니다. 1과 4094는 경계 바로 안쪽을 확인합니다. 모든 사례를 한 번 실행하는 반복문이 있어도 자료가 편향되면 잘못된 판단이 통과할 수 있으므로 입력 선정과 실행 코드를 함께 검토합니다.
경고와 실패를 서로 다른 근거로 읽습니다
gcc -std=c11 -Wall -Wextra -Werror는 C11 규칙과 경고 옵션을 사용하고 경고도 오류로 처리합니다. gcc라는 명령 이름은 실제 구현 이름을 보장하지 않으므로 설치한 컴파일러 정보를 기록합니다. -g -O0는 디버깅 정보를 포함하고 최적화를 끈 설정이며 이 레슨의 동작 비교를 단순하게 합니다. 컴파일러에 따라 진단 문구는 달라질 수 있지만 사용하지 않은 변수나 반환 누락은 코드를 읽는 단서입니다.
error가 sensor.c의 줄 번호를 가리키면 먼저 해당 문장과 직전 문장을 확인합니다. return 0을 지우고 끝까지 도달하는 경로가 생기면 반환 관련 경고가 나올 수 있습니다. if 뒤에 세미콜론을 붙여 빈 조건 본문을 만들면 들여쓰기와 실제 제어가 달라집니다. =는 대입, ==는 동등 비교입니다. 경고를 끄기 전에 코드를 조건의 의미와 맞춰 고칩니다. 이번 함수에는 대입을 조건으로 사용할 필요가 없습니다.
컴파일이 성공한 뒤 FAIL raw=-1 같은 문장이 나오면 C 문법보다 판단식과 기대값을 봅니다. make의 Error 숫자만 보고 컴파일 오류라고 단정하지 않습니다. make는 실행한 하위 명령이 실패해도 오류를 보고합니다. 따라서 그 앞의 gcc 진단인지 테스트 실패 문장인지 구분합니다. 원시값, 기대값, 실제값이 없는 실패 기록은 문제를 다시 재현할 입력을 찾기 어렵게 만듭니다.
수정 후 make clean으로 이 실습의 빌드 산출물을 지우고 make test로 다시 만듭니다. 오래된 실행 파일을 실행하면 소스를 고친 효과를 확인하지 못할 수 있습니다. Makefile은 test_sensor.c, sensor.c, sensor.h가 바뀌면 build/test를 다시 만드는 의존성을 갖습니다. 파일이 실행에 어떤 영향을 주는지는 다음 레슨의 분리 빌드에서 확장합니다. 지금은 함수가 두 경계와 내부값에 같은 계약을 지키는지 확인합니다.
이 함수는 실물 센서의 전압 허용치를 보호하는 회로가 아닙니다. PC에서 int 값을 판정하는 소프트웨어입니다. 함수가 범위 밖을 거부한다고 잘못된 배선까지 안전해지는 것은 아닙니다. 신입이 결과를 인계할 때는 입력 자료형과 판정 범위를 적고, 아직 확인하지 않은 장치 특성을 따로 적습니다. 실습을 마친 뒤 raw=0의 반환값과 raw=-1의 반환값을 설명하고 출력 책임을 함수 밖에 둔 이유를 말해 봅니다.
따라하기
처음 실패를 읽기
범위 함수 starter를 풀어 그 폴더에서 실행합니다. make의 stderr 문구는 환경마다 달라 여기서는 테스트 표준 출력과 하위 종료 코드를 관찰합니다. 실패가 있는 상태가 출발점입니다.
make -s test 2>/dev/null
status=$?
printf "make exit=%s\n" "$status"실행 결과
FAIL raw=-1 expected=0 actual=1 range: 7 cases, 1 failures make exit=2
함수 계약 복구
본문의 함수를 sensor.c에 저장한 후 실행합니다. 파일을 바꾼 뒤에도 같은 입력 자료로 확인합니다.
make -s clean
make -s test실행 결과
range: 7 cases, 0 failures
값과 판정 구별
probe.c를 아래 내용으로 만든 뒤 이어지는 명령을 실행합니다. 원시값이 아니라 함수의 판정 두 개를 비교합니다.
#include <stdio.h>
#include "sensor.h"
int main(void) { printf("zero=%d outside=%d\n", sample_valid(0), sample_valid(4096)); return 0; }
gcc -std=c11 -Wall -Wextra -Werror -I. probe.c sensor.c -o build/probe
./build/probe실행 결과
zero=1 outside=0
확인 문제
실습
starter의 sensor.c에서 sample_valid를 완성합니다. int 원시값 0~4095에는 1, 그 밖에는 0을 반환합니다. 함수 내부에는 출력을 추가하지 않습니다. make test에서 음수·양쪽 경계·내부·상한 밖의 7개 검사가 통과해야 합니다. 테스트 기대값은 유지합니다.
실행 명령
make test
기대 결과
range: 7 cases, 0 failures / 종료 코드 0
모범 답안
모범 답안 내려받기더 읽기
면접 질문
- C의 범위 검사를 함수로 분리할 때 선언, 반환값, 경계 테스트를 어떻게 맞추나요?