В строго типизированных языках, таких как Java и C #, void
(или Void
) тип возвращаемого значения для метода, по-видимому, означает:
Этот метод ничего не возвращает. Ничего такого. Без возврата. Вы не получите ничего от этого метода.
Что действительно странно, так это то, что в C void
как тип возвращаемого значения или даже как тип параметра метода означает:
Это может быть что угодно. Вы должны прочитать исходный код, чтобы узнать. Удачи. Если это указатель, вы должны действительно знать, что вы делаете.
Рассмотрим следующие примеры в C:
void describe(void *thing)
{
Object *obj = thing;
printf("%s.\n", obj->description);
}
void *move(void *location, Direction direction)
{
void *next = NULL;
// logic!
return next;
}
Очевидно, что второй метод возвращает указатель, который по определению может быть чем угодно.
Поскольку C старше Java и C #, почему эти языки приняли void
значение «ничего», в то время как C использовал его как «ничего или что-либо (когда указатель)»?
void
том, что в примере кода используетсяvoid*
нечто совершенно иное.Object
в одном случае для устранения неоднозначности.dynamic
тип, который используется редко?Ответы:
Ключевое слово
void
(не указатель) означает «ничего» на этих языках. Это соответствует.Как вы заметили,
void*
означает «указатель на что-либо» в языках, которые поддерживают необработанные указатели (C и C ++). Это неудачное решение, потому что, как вы упомянули, оноvoid
означает две разные вещи.Я не смог найти историческую причину повторного использования
void
для обозначения «ничего» и «что-нибудь» в разных контекстах, однако С делает это в нескольких других местах. Например,static
имеет разные цели в разных контекстах. Очевидно, что в языке Си есть прецедент повторного использования ключевых слов, независимо от того, что вы думаете о практике.Java и C # достаточно различны, чтобы сделать чистый разрыв, чтобы исправить некоторые из этих проблем. Java и «безопасный» C # также не позволяют сырые указатели и не нужно легко совместимость C (небезопасный C # делает позволяют указатели , но подавляющее большинство C # код не попадает в эту категорию). Это позволяет им немного изменить ситуацию, не беспокоясь о обратной совместимости. Один из способов сделать это - ввести класс
Object
в корне иерархии, от которой наследуются все классы, поэтомуObject
ссылка выполняет ту же функциюvoid*
без проблем с типами и необработанного управления памятью.источник
They also do not allow raw pointers and do not need easy C compatibility.
Ложь. C # имеет указатели. Они обычно (и обычно должны быть) выключены, но они там.void
: очевидной причиной было бы избежать введения нового ключевого слова (и, возможно, взлома существующих программ). Если бы они ввели специальное ключевое слово (напримерunknown
), тогдаvoid*
это была бы бессмысленная (и, возможно, незаконная) конструкция, иunknown
была бы легальной только в формеunknown*
.void *
,void
означает «ничего», а не «ничего». Вы не можете разыменовать avoid *
, вам нужно сначала привести его к другому типу указателя.void
иvoid*
различные типы ( на самом деле,void
«нет тип»). Это делает столько же смысла , как говорить «int
вint*
то значит.» Нет, когда вы объявляете переменную-указатель, вы говорите: «Это адрес памяти, который может содержать что-то». В случаеvoid*
, если вы говорите «этот адрес содержит что-то, но что-то не может быть выведено из типа указателя».void
иvoid*
две разные вещи.void
в C означает то же самое, что и в Java, отсутствие возвращаемого значения. Avoid*
- указатель с отсутствием типа.Все указатели в C должны иметь возможность разыменования. Если вы разыменовываете a
void*
, какой тип вы ожидаете получить? Помните, что указатели C не содержат никакой информации о типе среды выполнения, поэтому тип должен быть известен во время компиляции.Учитывая этот контекст, единственное, что вы можете логически сделать с разыменованным,
void*
- это игнорировать его, что в точности соответствуетvoid
типу поведения .источник
gcc
не согласна с тобой. Если вы попытаетесь использовать это значение,gcc
выдайте сообщение об ошибкеerror: void value not ignored as it ought to be
.Возможно, было бы более полезно думать о
void
качестве возвращаемого типа. Тогда ваш второй метод будет читать «метод, который возвращает нетипизированный указатель».источник
void
он примерно эквивалентен*
языкам, подобным ActionScript 3, что просто означает «любой тип возврата».void
это не подстановочный знак. Указатель - это просто адрес памяти. Помещение типа перед*
надписью «вот тип данных, который вы можете ожидать найти по адресу памяти, указанному этим указателем». Помещениеvoid
перед*
компилятором говорит: «Я не знаю тип; просто верните мне необработанный указатель, и я выясню, что делать с данными, на которые он указывает».void*
указывает на «пустоту». Пустой указатель, на который он не указывает. Тем не менее, вы можете разыграть его как любой другой указатель.Давайте немного ужесточим терминологию.
Из онлайн-стандарта C 2011 :
void
Выражение не имеет никакого значения (хотя она может иметь побочные эффекты). Если у меня есть функция, определенная для возвратаvoid
, вот так:тогда звонок
не оценивает значение; Я не могу присвоить результат чему-либо, потому что нет результата.
Указатель на
void
это по существу «общий» тип указателя; Вы можете присвоить значениеvoid *
любому другому типу указателя объекта без необходимости явного приведения (именно поэтому все программисты на C будут кричать на вас для приведения результатаmalloc
).Вы не можете напрямую разыменовывать a
void *
; Вы должны сначала назначить его другому типу указателя объекта, прежде чем сможете получить доступ к указанному объекту.void
указатели используются для реализации (более или менее) универсальных интерфейсов; каноническим примером являетсяqsort
библиотечная функция, которая может сортировать массивы любого типа, если вы предоставляете функцию сравнения с учетом типов.Да, использование одного и того же ключевого слова для двух разных концепций (без значения и общего указателя) сбивает с толку, но это не так, как будто нет прецедента;
static
имеет несколько различных значений как в C, так и в C ++.источник
Это утверждение верно. Это верно и для C и C ++.
Это утверждение неверно.
void
возвращаемый тип в C или C ++ означает то же самое, что и в C # и Java. Вы путаетеvoid
сvoid*
. Они совершенно разные.Ага.
Указатель является значением , которое может быть разыменовываются . Разыменовывая действительный указатель дает место хранения из указываемых печатают . Недействительный указатель является указателем , который не имеет какой- либо особого указываемый тип; он должен быть преобразован в более конкретный тип указателя, прежде чем значение будет разыменовано для создания места хранения .
Java не имеет пустых указателей; C # делает. Они такие же, как в C и C ++ - значение указателя, которое не имеет какого-либо определенного типа, связанного с ним, которое должно быть преобразовано в более конкретный тип, прежде чем разыменовывать его для создания места хранения.
Вопрос бессвязный, поскольку он предполагает ложь. Давайте зададим несколько лучших вопросов.
Быть знакомым программистам, приходящим на Java или C # с языков, где
void
есть тип возврата.Быть знакомым программистам, приходящим на C # с языков, где
void*
указатель типа.источник
Первая функция ничего не возвращает. Вторая функция возвращает пустой указатель. Я бы объявил эту вторую функцию как
Если может помочь, если вы думаете, что «пустота» означает «без значения». Возвращаемое значение
describe
- «без значения». Возврат значения из такой функции недопустим, потому что вы сказали компилятору, что возвращаемое значение не имеет смысла. Вы не можете получить возвращаемое значение из этой функции, потому что оно не имеет смысла. Вы не можете объявить переменную типа,void
потому что она не имеет смысла. Напримерvoid nonsense;
, чепуха и фактически незаконна. Также обратите внимание, что массив voidvoid void_array[42];
- это тоже нонсенс.Указатель на void (
void*
) - это нечто иное. Это указатель, а не массив. Читайтеvoid*
как означающий указатель, который указывает на что-то "без значения". Указатель «без значения», что означает, что нет смысла разыменовывать такой указатель. Попытка сделать это фактически незаконна. Код, разыменовывающий пустой указатель, не будет компилироваться.Так что, если вы не можете разыменовать пустой указатель, и вы не можете создать массив пустот, как вы можете их использовать? Ответ в том, что указатель на любой тип может быть приведен к и от
void*
. Приведение некоторого указателя наvoid*
и возвращение указателя на исходный тип приведет к значению, равному исходному указателю. Стандарт C гарантирует такое поведение. Основная причина, по которой вы видите так много пустых указателей в C, заключается в том, что эта возможность обеспечивает способ реализации скрытия информации и объектно-ориентированного программирования на языке, который в значительной степени не является объектно-ориентированным языком.Наконец, причина, по которой вы часто видите
move
объявленныйvoid *move(...)
скорее, чем тотvoid* move(...)
, что в том, что размещение звездочки рядом с именем, а не с типом, является очень распространенной практикой в C. Причина в том, что следующее объявление доставит вам большие неприятности:int* iptr, jptr;
Это сделает вас Я думаю, что вы объявляете два указателя наint
. Ты не. Это заявление делает , а не указатель на . Правильное объявление: Вы можете делать вид, что звездочка принадлежит типу, если придерживаетесь практики объявления только одной переменной на оператор объявления. Но имейте в виду, что вы притворяетесь.jptr
int
int
int *iptr, *jptr;
источник
void* null_ptr = 0;
).Проще говоря, нет никакой разницы . Позвольте привести несколько примеров.
Скажем, у меня есть класс под названием Автомобиль.
Я полагаю, что здесь путаница основана на том, как вы определяете
void
противvoid*
. Возвращаемый типvoid
означает «я ничего не возвращаю», а возвращаемый типvoid*
означает «я возвращаю указатель типа ничего».Это связано с тем, что указатель является частным случаем. Объект-указатель всегда будет указывать на местоположение в памяти, независимо от того, существует ли допустимый объект или нет. Неважно,
void* car
ссылаются ли на меня байты, слова, машины или велосипеды, потому что яvoid*
указываю на что-то без определенного типа. Это до нас, чтобы позже определить это.В таких языках, как C # и Java, память управляется, а понятие указателей переносится в класс Object. Вместо ссылки на объект типа «ничего» мы знаем, что по крайней мере наш объект имеет тип «Объект». Позвольте мне объяснить, почему.
В обычном C / C ++, если вы создаете объект и удаляете его указатель, получить доступ к этому объекту невозможно. Это то, что известно как утечка памяти .
В C # / Java все является объектом, и это позволяет среде выполнения отслеживать каждый объект. Это не обязательно должно знать, что мой объект - Автомобиль, ему просто нужно знать некоторую базовую информацию, которая уже позаботилась о классе Object.
Для нас, программистов, это означает, что большая часть информации, которую мы должны были бы обрабатывать вручную в C / C ++, заботится о нас для класса Object, устраняя необходимость в специальном случае, который является указателем.
источник
Я попытаюсь сказать, как можно думать об этих вещах на C. Официальный язык в стандарте C не совсем удобен
void
, и я не буду пытаться полностью соответствовать этому.Тип
void
не является первоклассным гражданином среди типов. Хотя это тип объекта, он не может быть типом любого объекта (или значения), поля или параметра функции; однако это может быть тип возвращаемого значения функции и, следовательно, тип выражения (в основном это вызовы таких функций, но выражения, сформированные с помощью условного оператора, также могут иметь тип void). Но даже минимальное использованиеvoid
в качестве типа значения, для которого вышеизложенное оставляет дверь открытой, а именно завершение функции, возвращаемойvoid
с помощью оператора формы,return E;
гдеE
есть выражение типаvoid
, явно запрещено в C (хотя это разрешено в C ++, но по причинам, не применимым к C).Если бы объекты типа
void
были разрешены, они имели бы 0 битов (это количество информации в значении выраженияvoid
типа); это не будет большой проблемой, если разрешить такие объекты, но они будут довольно бесполезными (объекты размера 0 затруднят определение арифметики указателей, что, возможно, лучше просто запретить). Множество разных значений такого объекта будет иметь один (тривиальный) элемент; его значение можно было бы принять тривиально, потому что оно не имеет какого-либо вещества, но его нельзя изменить (не имея другого значения). Поэтому стандарт вводит в заблуждение, говоря, что онvoid
содержит пустой набор значений; такой тип может быть полезен для описания выражений, которые не могутоцениваться (например, переходы или не завершающиеся вызовы), хотя язык Си на самом деле не использует такой тип.Будучи запрещенным как тип значения,
void
использовался по крайней мере двумя не связанными напрямую способами для обозначения «запрещено»: указание в(void)
качестве спецификации параметра в типах функций означает, что предоставление любых аргументов им запрещено (в то время как «()» будет означать все разрешено, и да, даже предоставлениеvoid
выражения в качестве аргумента функциям со(void)
спецификацией параметра запрещено), а объявлениеvoid *p
означает, что выражение разыменования*p
запрещено (но не то, что оно является допустимым выражением типаvoid
). Таким образом, вы правы в том, что такое использованиеvoid
не соответствуетvoid
действительному типу выражений. Однако объект типаvoid*
на самом деле не является указателем на значения любого типа, это означает, что значение рассматривается как указатель, хотя на него нельзя ссылаться, и арифметика указателя с ним запрещена. «Обработанный как указатель» в действительности означает только то, что он может быть приведен к любому типу указателя без потери информации. Однако его также можно использовать для приведения целочисленных значений к и от него, поэтому он вообще не должен указывать на что-либо.источник
void*
к другому типу указателя может привести к потере информации (или даже к UB, я не могу вспомнить это от руки), но если значениеvoid*
пришло от приведения указателя того же типа, к которому вы сейчас приводите, то это не так.В C # и Java каждый класс является производным от
Object
класса. Следовательно, всякий раз, когда мы хотим передать ссылку на «что-то», мы можем использовать ссылку типаObject
.Поскольку для этих целей нам не нужно использовать указатели void на этих языках,
void
здесь не обязательно означает «что-то или ничего».источник
В C возможно иметь указатель на вещи любого типа [указатели ведут себя как тип ссылки]. Указатель может идентифицировать объект известного типа или он может идентифицировать объект произвольного (неизвестного) типа. Создатели C решили использовать тот же общий синтаксис для «указателя на предмет произвольного типа» и для «указателя на предмет определенного типа». Поскольку синтаксис в последнем случае требует, чтобы токен указывал, что это за тип, первый синтаксис требует размещения чего-то, где этот токен принадлежал бы, если бы тип был известен. C решил использовать ключевое слово «void» для этой цели.
В Паскале, как и в С, можно иметь указатели на вещи любого типа; более популярные диалекты языка Паскаль также допускают «указатель на вещь произвольного типа», но вместо того, чтобы использовать тот же синтаксис, который они используют для «указателя на вещь определенного типа», они просто ссылаются на первый как
Pointer
.В Java нет пользовательских типов переменных; все это либо примитив, либо ссылка на объект кучи. Можно определить вещи типа «ссылка на объект кучи определенного типа» или «ссылка на объект кучи произвольного типа». Первый просто использует имя типа без какой-либо специальной пунктуации, чтобы указать, что это ссылка (поскольку это не может быть что-либо еще); последний использует имя типа
Object
.Использование в
void*
качестве номенклатуры для указателя на что-то произвольного типа, насколько я знаю, уникально для C и прямых производных, таких как C ++; другие языки, которые я знаю, обрабатывают эту концепцию, просто используя четко названный тип.источник
Вы можете думать о ключевом слове
void
как о пустомstruct
( в C99 пустые структуры, по-видимому, запрещены , в .NET и C ++ они занимают более 0 памяти, и последнее, что я помню, все в Java - это класс):... что означает, что это тип с длиной 0. Вот как это работает, когда вы выполняете вызов функции, которая возвращает
void
... вместо того, чтобы выделить 4 байта или около того в стеке для целочисленного возвращаемого значения, он вообще не меняет указатель стека (увеличивает его на длину 0 ).Итак, если вы объявили некоторые переменные, такие как:
... а затем вы взяли адрес этих переменных, тогда, возможно,
b
иc
могли бы быть расположены в том же месте в памяти, потому чтоb
имеет нулевую длину. Таким образом, avoid*
является указателем в памяти на встроенный тип с нулевой длиной. Тот факт, что вы, возможно, знаете, что сразу после этого есть что-то, что может быть полезно для вас, - это другой вопрос, требующий от вас действительно знать, что вы делаете (и это опасно).источник