Думайте о пустоте как о "пустой структуре". Позвольте мне объяснить.
Каждая функция принимает последовательность параметров, где каждый параметр имеет тип. Фактически, мы могли бы упаковать параметры в структуру со слотами структуры, соответствующими параметрам. Это позволяет каждой функции иметь ровно один аргумент. Точно так же функции выдают результат, который имеет тип. Это может быть логическое значение, или это может быть float, или это может быть структура, содержащая произвольный набор других типизированных значений. Если нам нужен язык с несколькими возвращаемыми значениями, просто настаивать, чтобы они были упакованы в структуру. Фактически, мы всегда можем настаивать на том, чтобы функция возвращала структуру. Теперь каждая функция принимает ровно один аргумент и выдает ровно одно значение.
Теперь, что происходит, когда мне нужна функция, которая выдает "нет" значения?
Хорошо, рассмотрим, что я получу, когда сформирую структуру с 3 слотами:
содержит 3 значения. Когда у меня есть 2 слота, он содержит два значения. Когда это
имеет один слот, одно значение. И когда у него ноль слотов, он держит ... э-э,
нулевые значения, или «нет» значения. Таким образом, я могу думать о функции, возвращающей пустоту
как возвращение структуры, не содержащей значений. Вы даже можете решить, что "пустота"
это просто синоним для типа, представленного пустой структурой,
а не ключевое слово в языке (может быть, это просто предопределенный тип:)
Точно так же я могу думать о функции, не требующей значений, как о принятии пустой структуры, например, "void".
Я даже могу реализовать свой язык программирования таким образом. Передача пустого значения
занимает нулевые байты, поэтому передача пустых значений является лишь частным случаем передачи
другие значения произвольного размера. Это облегчает обработку компилятором
«пустой» результат или аргумент. Вы, вероятно, хотите функцию языка
это может отбросить результат функции; в C, если вы называете не пустым результатом
функция foo в следующем утверждении:
Foo (...);
компилятор знает, что foo выдает результат, и просто игнорирует его.
Если void является значением, это работает отлично, и теперь "процедуры" (которые
просто прилагательное для функции с пустым результатом) просто тривиально
случаи общих функций.
Void * немного смешнее. Я не думаю, что дизайнеры C думали о пустоте в
вышеуказанным способом; они просто создали ключевое слово. Это ключевое слово было доступно, когда кто-то
нужна точка на произвольный тип, поэтому void * как идиома в C.
Это на самом деле работает довольно хорошо, если вы интерпретируете пустоту как пустую структуру.
Указатель void * - это адрес места, где эта пустая структура имеет
был поставлен
Преобразование из void * в T * для других типов T также работает с этой точки зрения.
Приведения указателей представляют собой полный чит, который работает на большинстве распространенных архитектур, чтобы использовать тот факт, что если составной тип T имеет элемент с подтипом S, физически помещенный в начале T в его макете хранения, то приведение S * к T * наоборот, использование одного и того же физического адреса машины имеет тенденцию работать, так как большинство указателей машины имеют одно представление. Замена типа S на тип void дает точно такой же эффект, и, таким образом, приведение к / от void * работает.
Язык программирования PARLANSE реализует вышеприведенные идеи довольно близко.
Мы дурачились в его дизайне и не обращали пристального внимания на «пустоту» в качестве возврата
введите и, следовательно, есть ключевые слова языка для процедуры. В основном это просто
изменение синтаксиса, но это одна из вещей, вы не можете обойтись, как только вы получите
большой рабочий код на языке.