Void в C: четыре значения ключевого слова
Кажется, void — это просто «пустота». На деле в C и C++ оно играет четыре разные роли: от функций без возвращаемого значения до указателей на что угодно. Разбираем историю слова, предложенного создателем Bourne Shell, и ловушки, в которые до сих пор попадают новички.

В C всего 32 ключевых слова, и void кажется одним из самых простых: оно обозначает «ничего». Но если присмотреться, это «ничего» играет в языке как минимум четыре разные роли. Более того, именно void, а не печально известный static, можно считать самым «перегруженным» ключевым словом в C: у static три смысла, а у void — четыре.
Разбираем, откуда взялось это слово — его придумал создатель Bourne Shell Стивен Борн — и зачем оно нужно в современном C и C++.
Ключевые выводы
— В C и C++ ключевое слово void используется четырьмя способами: возвращаемый тип, отсутствие параметров, указатель на неизвестный тип и явное приведение к «ничему».
— Слово void предложил Стивен Борн, автор Bourne Shell; до этого функция без возвращаемого значения просто не указывала тип.
— f(void) и f() — разные вещи в C: первая форма явно говорит «параметров нет».
— void* — универсальный указатель, но разыменовывать его нельзя.
— Приведение к void помогает подавить предупреждения о неиспользуемом возвращаемом значении или параметре.
Краткая история: как появилось void
В самой ранней версии C, так называемом K&R-диалекте, функция без возвращаемого значения писалась без типа. Если возвращаемый тип не указан, компилятор считал, что функция возвращает int. Параметры тоже можно было описывать отдельно от списка аргументов — прямо как в старых учебниках:
Такой код сегодня выглядит экзотично, но именно так писали до появления прототипов функций. При этом компилятор всё равно генерировал машинную команду возврата и передавал вызывающей стороне какое-то мусорное значение. Хотя вызывающий не ждал результата, это было неэффективно и неочевидно.
Это раздражало Стивена Борна — да, того самого, который создал Bourne Shell. Он пожаловался Деннису Ритчи, и Ритчи увидел выгоду: если явно сказать, что функция ничего не возвращает, можно сэкономить одну машинную инструкцию. Так в C появилось слово void.
Позже void обросло новыми смыслами и перекочевало в C++, C#, Java, D и Swift — хотя Swift предпочитает писать его с заглавной буквы: Void.
Четыре смысла void
1. Функция не возвращает значение
Самый известный смысл. Вместо того чтобы молча возвращать int, функция явно объявляет: «я работаю ради побочного эффекта».
В современном C и C++ большинство функций без возвращаемого значения пишутся именно так. Это делает намерения автора очевидными и помогает компилятору ловить ошибки: если вы случайно попытаетесь использовать результат void-функции, получите ошибку компиляции.
2. Явное отсутствие параметров: f(void)
В C++ пустые скобки f() означают «функция не принимает параметров». В K&R C те же пустые скобки означали лишь «это функция», без всякой информации о параметрах. Чтобы сохранить совместимость со старым кодом и одновременно ввести прототипы, в C придумали запись f(void):
Разница тонкая, но важная. Если в C объявить void foo();, а потом вызвать foo(42), компилятор не проверит количество и типы аргументов — это старая K&R-форма. Запись void foo(void); защищает от таких ошибок.
Совет:
Всегда пишитеint main(void)в C. Формаvoid main()не соответствует стандарту и может ломать работу runtime.
3. Указатель на неизвестный тип: void*
Объект типа void создать нельзя, но указатель на void — можно. void* — это универсальный указатель: он может хранить адрес данных любого типа. В отличие от char*, который в K&R C выполнял ту же роль, void* чётко говорит: «тип здесь не важен».
Практически все функции динамического выделения памяти — malloc, calloc, realloc — возвращают void*. В C это удобно: компилятор неявно приводит void* к целевому типу. В C++ такой неявный каст запрещён, поэтому там пишут int* arr = (int*)malloc(...) или, что лучше, используют new.
4. Приведение к void
Последний смысл — самый неочевидный. Приведение выражения к void означает: «я намеренно игнорирую результат». Это полезно в двух случаях.
Первый: отбрасывание возвращаемого значения функции. Например, printf возвращает число напечатанных символов, но обычно оно не нужно:
Такая запись читается как «вызываем функцию только ради побочного эффекта». Большинство компиляторов и так не ругаются на неиспользованный результат printf, но для функций с атрибутом [[nodiscard]] приведение к void — единственный легальный способ проигнорировать результат.
Второй случай — неиспользуемый параметр функции. В callback-интерфейсах часто приходится принимать аргументы, которые конкретная реализация не использует:
В C23 можно просто опустить имя параметра, а в C++ — всегда можно было. Но приведение к void работает в любом стандарте и явно показывает намерение.
Практика: что проверить в своём коде
- В C пишите
int main(void), а неvoid main(). - Для функций без параметров используйте
f(void), особенно в заголовочных файлах. - Не разыменовывайте
void*— сначала приведите указатель к конкретному типу. - Если игнорируете возвращаемое значение, добавьте
(void)— это помогает при включённых предупреждениях. - Не пытайтесь объявить переменную типа
void: объект «ничего» в C не существует.
All uses of void are valid in both C and C++. If that weren't the case, I would have said so.
Часто задаваемые вопросы
Сколько значений у void в C и C++?
Как минимум четыре: возвращаемый тип функции, явное отсутствие параметров в C, универсальный указатель void* и приведение к void для игнорирования значения.
Можно ли создать переменную типа void?
Нет. void — неполный тип, объект этого типа не может существовать. Но указатель на void (void*) — вполне реальная и полезная сущность.
Чем f(void) отличается от f() в C?
f(void) — современный прототип: функция не принимает аргументов. f() — старая K&R-форма, не несущая информации о параметрах; компилятор не проверит аргументы вызова.
Зачем писать (void) перед вызовом функции?
Это сигнал «я намеренно игнорирую возвращаемое значение». Особенно полезно для функций с атрибутом [[nodiscard]] и при строгих настройках компилятора.
Почему void main() считается ошибкой?
Стандарт C требует, чтобы main возвращала int. Форма void main() негарантирована: на некоторых системах она может ломать код завершения программы и инициализации runtime.
Выводы
void — не просто синтаксический сахар для «пустоты». Это ключевое слово, которое помогает компилятору различать четыре разных ситуации: отсутствие результата, отсутствие параметров, неизвестный тип данных и намеренное игнорирование значения.
Исторически void появилось из инженерной прагматики: сэкономить одну инструкцию возврата и сделать код понятнее. Сегодня оно остаётся незаменимым инструментом — особенно если вы пишете на C, работаете с системными библиотеками или объясняете студентам, почему void main() — не лучшая идея.
Источник: Paul J. Lucas, «Into the Void», Dev.to. Дополнительный справочник по void — cppreference.com.









