Да пожалуйста! /*
* Функция безопасного сложения для uint8_t.
*/
bool safe_add_u8(uint8_t a, uint8_t b, uint8_t* result) {
// ПРЕДУСЛОВИЕ (REQUIRE):
// Проверяем, что сложение не вызовет переполнения.
// Второй аргумент (ID) НЕ НУЖЕН. Макрос сам возьмет __LINE__ как ID.
REQUIRE(b <= (UINT8_MAX - a));
// Если мы здесь, значит условие выполнено.
*result = a + b;
// ПОСТУСЛОВИЕ (ENSURE):
// Гарантируем корректность результата.
ENSURE(*result >= a);
ENSURE(*result >= b);
ENSURE(*result == (a + b));
return true;
}
int main( int argc, char **argv )
{
uint8_t current_val = 200;
uint8_t add_val = 60;
uint8_t result;
printf("First call...\n");
if (safe_add_u8(current_val, add_val, &result)) {
// Поймали
}
printf("Done\n");
// Корректный вызов
printf("Second call...\n");
add_val = 50;
if (safe_add_u8(current_val, add_val, &result)) {
// result == 250
}
printf("Done\n");
return 0;
}
Вывод этого пробничка:
./safe_example
First call
Fail check in ./safe_example.c at 18 line: 'b <= ((255) - a)'
Fail check in ./safe_example.c at 25 line: '*result >= a'
Fail check in ./safe_example.c at 26 line: '*result >= b'
Fail check in ./safe_example.c at 27 line: '*result == (a + b)'
Done
Second call
First call
Погонять на столе, что да как. В некритичных местах можно и оставить включенным потом. Понятно, что в большинстве случаев здешние доны граничные ситуации в голове прокручивают. А вдруг пропустишь?
В окончательной версии изменением дефайна всё отключается, остаётся ((void)0)