0 программистов ругал сердитый шeф,
Потом уволил одного, и стало их FF.
– Юрий Нестеренко, "0A программистов"
Сейчас мы немного рассмотрим теорию, чтобы заполнить пространство под вопросом о false – будет много букв – сложнее случайно увидеть "ответ".
Но эта теория тут не просто так: наша функция main
– хоть и необычная, но
функция. Опять же, мы объявили саму функцию main
как функцию,
возвращающую int
– тип данных. Очень хитрый тип данных.
Немного о функциях
Итак, в общем виде функцию можно описать следующим шаблоном:
<возвращаемый тип> <имя функции>(<тип1> <арг1>, <тип1> <арг2>, ...) {
<тело функции>
}
Функция имеет свою локальную область видимости – переменные, определённые внутри неё недоступны снаружи. По факту они создаются на стеке и после выхода из функции стек откатится к состоянию до попадания в функцию – данные станут недоступны (разве что не найдётся какой-то обходной путь – хак).
Параметры функции передаются по значению и их изменения не будут видны снаружи функции (они также объявляются на стеке). С другой стороны, параметром может быть адрес в памяти – тогда можно модифицировать ту память.
Возврат значений производится, как и во многих языках с помошью return
.
Стоит заметить, что в отличии от всяких Go и Python, мы не можем вернуть сразу несколько значений. Это ограничение, естественно, можно обойти, но оно же во многом определило, как создаются API многих функций. Так, на будущее.
Что такое int?
Вернёмся к нашему "хитрому" типу данных. Типы данных во многих существующих языках – набор функций и ограничений на хранимую в переменной информацию. В Си же это размер байт, выделенных на переменную (этакое ограничение) + его представление (удобство, "синтаксический сахар" для работы с байтами).
В чём же хитрость?
Начнём с того, что int – знаковое целое – то есть туда можно положить целое число: хоть отрицательное, хоть положительное. Но не очень большое.
Размер int зависит то платформы. Посмотреть размер типа или переменной можно функцией sizeof:
int main() {
return sizeof(int);
}
Собираем:
clang int_size.c -o ./int_size
Запускаем:
→ false
→ echo $?
4
– видим, что в нашем случае int занимает 4 байта.
А что, если мы туда положим что-то большее?
int main(void) {
int num = 0xffffffff;
return num + 12;
}
Переполнение:
→ echo $?
11
На самом деле 0xffffffff – не самый большой int, самый большой – 0x7FFFFFFF из-за устройства представления байтов знаковым целым числом. В диапозоне [8000000, 0xffffffff] - отрицательные числа.
То есть первый бит – бит знака. Так что, если вы выйдите за самое большое число int, вы внезапно получите самое маленькое число... Вот такой он, хитрый int!
Однако, 0xffffffff прекрасно справляется с демонстрацией переполнения 0xffffffff + 12 = 11.
Ответ на загадку
Так что, если взять 0xffffffff и прибавить к нему 1... И вернуть это... В общем, это явно не было нулём, но стало! Более того: код возврата вообще ограничен беззнаковым байтом, так что каждое число, кратное 256 – также окажется истиной! И да, это уже вопросы к самому коду.