En lenguajes fuertemente tipados como Java y C #, void(o Void) como tipo de retorno para un método parecen significar:
Este método no devuelve nada. Nada. Sin retorno. No recibirá nada de este método.
Lo realmente extraño es que en C, voidcomo un tipo de retorno o incluso como un tipo de parámetro de método significa:
Realmente podría ser cualquier cosa. Tendrías que leer el código fuente para averiguarlo. Buena suerte. Si es un puntero, realmente deberías saber lo que estás haciendo.
Considere los siguientes ejemplos en 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;
}
Obviamente, el segundo método devuelve un puntero, que por definición podría ser cualquier cosa.
Dado que C es más antiguo que Java y C #, ¿por qué estos lenguajes adoptaron voidcomo "nada" mientras que C lo usó como "nada o nada (cuando un puntero)"?

voidmientras que el ejemplo de código usavoid*algo completamente diferente.Objecten un caso para desambiguar.dynamictipo que rara vez se usa?Respuestas:
La palabra clave
void(no un puntero) significa "nada" en esos idiomas. Esto es consistenteComo notó,
void*significa "puntero a cualquier cosa" en lenguajes que admiten punteros sin formato (C y C ++). Esta es una decisión desafortunada porque, como mencionaste,voidsignifica dos cosas diferentes.No he podido encontrar la razón histórica detrás de la reutilización
voidpara significar "nada" y "nada" en diferentes contextos, sin embargo, C lo hace en varios otros lugares. Por ejemplo,statictiene diferentes propósitos en diferentes contextos. Obviamente, hay un precedente en el lenguaje C para reutilizar palabras clave de esta manera, independientemente de lo que uno pueda pensar de la práctica.Java y C # son lo suficientemente diferentes como para hacer un corte limpio para corregir algunos de estos problemas. Java y C # "seguro" tampoco permiten punteros sin procesar y no necesitan compatibilidad con C fácil (C # inseguro sí permite punteros, pero la gran mayoría del código de C # no entra en esta categoría). Esto les permite cambiar un poco las cosas sin preocuparse por la compatibilidad con versiones anteriores. Una forma de hacerlo es introducir una clase
Objecten la raíz de la jerarquía de la que heredan todas las clases, por lo que unaObjectreferencia cumple la misma funciónvoid*sin la maldad de los problemas de tipo y la administración de memoria sin procesar.fuente
They also do not allow raw pointers and do not need easy C compatibility.Falso. C # tiene punteros. Usualmente están (y usualmente deberían estar) apagados, pero están ahí.void: una razón obvia sería evitar introducir una nueva palabra clave (y potencialmente romper los programas existentes). Si hubieran introducido una palabra clave especial (punknown. Ej. ),void*Sería una construcción sin sentido (y posiblemente ilegal), yunknownsería legal solo en forma deunknown*.void *,voidsignifica "nada", no "nada". No puede desreferenciar avoid *, primero debe convertirlo a otro tipo de puntero.voidyvoid*son diferentes tipos (en realidad,voides "sin tipo"). Tiene tanto sentido como decir "lointinint*significa algo". No, una vez que declaras una variable de puntero estás diciendo "esta es una dirección de memoria capaz de contener algo". En el caso devoid*, está diciendo que "esta dirección contiene algo pero que algo no puede inferirse del tipo de puntero".voidyvoid*son dos cosas diferentes.voiden C significa exactamente lo mismo que en Java, la ausencia de un valor de retorno. Avoid*es un puntero con ausencia de un tipo.Todos los punteros en C necesitan poder ser desreferenciados. Si desreferenciara a
void*, ¿qué tipo esperaría obtener? Recuerde que los punteros en C no llevan ninguna información de tipo de tiempo de ejecución, por lo que el tipo debe conocerse en el momento de la compilación.Dado ese contexto, lo único que puede hacer lógicamente con un desreferenciado
void*es ignorarlo, que es exactamente el comportamiento quevoiddenota el tipo.fuente
gccno está de acuerdo contigo. Si intenta usar el valor,gccgenere un mensaje de error que digaerror: void value not ignored as it ought to be.Quizás sería más útil pensar
voiden el tipo de retorno. Luego, su segundo método sería "un método que devuelve un puntero sin tipo".fuente
voides más o menos equivalente a*lenguajes como ActionScript 3, que simplemente significa "cualquier tipo de retorno".voidno es un comodín. Un puntero es solo una dirección de memoria. Poner un tipo antes del*dice "aquí está el tipo de datos que puede esperar encontrar en la dirección de memoria a la que hace referencia este puntero". Poniendovoidantes el dicho*al compilador "No sé el tipo; solo devuélveme el puntero sin procesar y descubriré qué hacer con los datos a los que apunta".void*apunta a un "vacío". Un puntero desnudo sin nada a lo que apunta. Aún así puedes lanzarlo como cualquier otro puntero.Vamos a ajustar un poco la terminología.
Del estándar C 2011 en línea :
Una
voidexpresión no tiene valor (aunque puede tener efectos secundarios). Si tengo una función definida para devolvervoid, así:entonces la llamada
no evalúa a un valor; No puedo asignar el resultado a nada, porque no hay resultado.
Un puntero a
voides esencialmente un tipo de puntero "genérico"; puede asignar un valorvoid *a cualquier otro tipo de puntero de objeto sin necesidad de una conversión explícita (razón por la cual todos los programadores de C le gritarán por emitir el resultado demalloc).No puede desreferenciar directamente un
void *; primero debe asignarlo a un tipo de puntero de objeto diferente antes de poder acceder al objeto señalado.voidlos punteros se utilizan para implementar (más o menos) interfaces genéricas; El ejemplo canónico es laqsortfunción de biblioteca, que puede ordenar matrices de cualquier tipo, siempre que proporcione una función de comparación con reconocimiento de tipo.Sí, usar la misma palabra clave para dos conceptos diferentes (sin valor versus puntero genérico) es confuso, pero no es que no haya precedentes;
statictiene múltiples significados distintos en C y C ++.fuente
Esa afirmación es correcta. También es correcto para C y C ++.
Esa afirmación es incorrecta.
voidcomo tipo de retorno en C o C ++ significa lo mismo que hace en C # y Java. Estás confundiendovoidconvoid*. Son completamente diferentesSí.
Un puntero es un valor que puede ser desreferenciado . Anular la referencia a un puntero válido proporciona una ubicación de almacenamiento del tipo apuntado . Un puntero vacío es un puntero que no tiene ningún tipo de apuntamiento particular; debe convertirse a un tipo de puntero más específico antes de desreferenciar el valor para producir una ubicación de almacenamiento .
Java no tiene punteros vacíos; C # hace. Son los mismos que en C y C ++: un valor de puntero que no tiene ningún tipo específico asociado, que debe convertirse a un tipo más específico antes de desreferenciarlo para producir una ubicación de almacenamiento.
La pregunta es incoherente porque presupone falsedades. Hagamos algunas mejores preguntas.
Estar familiarizado con los programadores que vienen a Java o C # desde lenguajes donde
voides un tipo de retorno.Conocer a los programadores que llegan a C # desde lenguajes donde
void*hay un tipo de puntero.fuente
La primera función no devuelve nada. La segunda función devuelve un puntero vacío. Hubiera declarado esa segunda función como
Podría ayudar si piensa en "vacío" como que significa "sin significado". El valor de retorno de
describees "sin significado". Devolver un valor de dicha función es ilegal porque le dijo al compilador que el valor devuelto no tiene sentido. No puede capturar el valor de retorno de esa función porque no tiene sentido. No puede declarar una variable de tipovoidporque no tiene sentido. Por ejemplo, novoid nonsense;tiene sentido y de hecho es ilegal. También tenga en cuenta que una matriz de vacíovoid void_array[42];también es una tontería.Un puntero a void (
void*) es algo diferente. Es un puntero, no una matriz. Leervoid*como significado un puntero que apunta a algo "sin significado". El puntero es "sin significado", lo que significa que no tiene sentido desreferenciar dicho puntero. Intentar hacerlo es ilegal. El código que desreferencia un puntero vacío no se compilará.Entonces, si no puede desreferenciar un puntero vacío, y no puede hacer una serie de vacíos, ¿cómo puede usarlos? La respuesta es que un puntero a cualquier tipo se puede lanzar hacia y desde
void*. Alvoid*lanzar un puntero y volver a un puntero al tipo original, se obtendrá un valor igual al puntero original. El estándar C garantiza este comportamiento. La razón principal por la que ve tantos punteros vacíos en C es que esta capacidad proporciona una forma de implementar la ocultación de información y la programación basada en objetos en un lenguaje muy orientado a objetos.Finalmente, la razón por la que a menudo se ve
movedeclarado como envoid *move(...)lugar devoid* move(...)porque es poner el asterisco al lado del nombre en lugar del tipo es una práctica muy común en C. La razón es que la siguiente declaración te meterá en un gran problema:int* iptr, jptr;esto te haría cree que está declarando dos punteros a unoint. Usted no. Esta declaración hacejptrunintmás que un puntero a unint. La declaración adecuada esint *iptr, *jptr;: puede fingir que el asterisco pertenece al tipo si se apega a la práctica de declarar solo una variable por declaración de declaración. Pero tenga en cuenta que está fingiendo.fuente
void* null_ptr = 0;).En pocas palabras, no hay diferencia . Deja que te dé algunos ejemplos.
Digamos que tengo una clase llamada Car.
Creo que aquí la confusión en este punto se basa apagado cómo definiría
voidvsvoid*. Un tipo de retornovoidsignifica "no devuelvo nada", mientras que un tipo de retornovoid*significa "devuelvo un puntero de tipo nada".Esto se debe al hecho de que un puntero es un caso especial. Un objeto puntero siempre apuntará a una ubicación en la memoria, ya sea que haya un objeto válido allí o no. Si mis
void* carreferencias a un byte, una palabra, un automóvil o una bicicleta no importan porque misvoid*puntos apuntan a algo sin un tipo definido. Depende de nosotros definirlo más adelante.En lenguajes como C # y Java, la memoria se gestiona y el concepto de punteros se transfiere a la clase Object. En lugar de tener una referencia a un objeto de tipo "nada", sabemos que al menos nuestro objeto es de tipo "Objeto". Déjame explicarte por qué.
En C / C ++ convencional, si crea un objeto y elimina su puntero, entonces es imposible obtener acceso a ese objeto. Esto es lo que se conoce como pérdida de memoria .
En C # / Java, todo es un objeto y esto permite que el tiempo de ejecución realice un seguimiento de cada objeto. No necesariamente necesita saber que mi objeto es un automóvil, simplemente necesita conocer información básica que ya está a cargo de la clase Object.
Para nosotros, los programadores, eso significa que gran parte de la información que tendríamos que cuidar manualmente en C / C ++ nos ocupa la clase Object, eliminando la necesidad del caso especial que es el puntero.
fuente
Trataré de decir cómo se podría pensar en estas cosas en C. El lenguaje oficial en el estándar C no está realmente a gusto
void, y no trataré de ser completamente consistente con él.El tipo
voidno es un ciudadano de primera clase entre los tipos. Aunque es un tipo de objeto, no puede ser el tipo de ningún objeto (o valor), de un campo o de un parámetro de función; sin embargo, puede ser el tipo de retorno de una función y, por lo tanto, puede ser el tipo de una expresión (básicamente de llamadas de tales funciones, pero las expresiones formadas con el operador condicional también pueden tener un tipo de vacío). Pero incluso el uso mínimo devoidun tipo de valor para el que lo anterior deja la puerta abierta, es decir, finalizar una función que regresavoidcon una declaración de la formareturn E;dondeEes una expresión de tipovoid, está explícitamente prohibido en C (aunque está permitido en C ++, pero por razones no aplicables a C).Si
voidse permitieran objetos de tipo , tendrían 0 bits (es decir, la cantidad de información en el valor de una expresión devoidtipo); no sería un problema importante permitir tales objetos, pero serían bastante inútiles (los objetos de tamaño 0 darían cierta dificultad para definir la aritmética del puntero, lo que quizás sería mejor prohibir). El conjunto de valores diferentes de dicho objeto tendría un elemento (trivial); su valor podría tomarse, trivialmente porque no tiene ninguna sustancia, pero no puede modificarse (sin ningún valor diferente ). Por lo tanto, el estándar se confunde al decir quevoidcomprende un conjunto vacío de valores; dicho tipo podría ser útil para describir expresiones que no puedenser evaluado (como saltos o llamadas que no terminan) aunque el lenguaje C no emplea tal tipo.Al ser rechazado como tipo de valor,
voidse ha utilizado al menos en dos formas no directamente relacionadas para designar "prohibido": especificar(void)como especificación de parámetros en tipos de función significa proporcionar cualquier argumento para ellos (mientras que '()' significaría todo lo contrario está permitido; y sí, incluso proporcionar unavoidexpresión como argumento para funciones con(void)especificación de parámetros está prohibido), y declararvoid *psignifica que la expresión de desreferenciación*pestá prohibida (pero no es que sea una expresión de tipo válidavoid). Entonces tiene razón en que estos usos devoidno son realmente consistentesvoidcomo un tipo válido de expresiones. Sin embargo, un objeto de tipovoid*no es realmente un puntero a valores de ningún tipo, significa un valor que se trata como un puntero, aunque no se puede desreferenciar y se prohíbe la aritmética del puntero. El "tratado como un puntero" en realidad solo significa que puede emitirse desde y hacia cualquier tipo de puntero sin pérdida de información. Sin embargo, también se puede usar para emitir valores enteros hacia y desde, por lo que no tiene que apuntar a nada en absoluto.fuente
void*otro tipo de puntero puede perder información (o incluso tener UB, no puedo recordarlo de la mano), pero si el valor del resultadovoid*proviene de lanzar un puntero del mismo tipo al que ahora está lanzando, entonces no lo haceEn C # y Java, cada clase se deriva de una
Objectclase. Por lo tanto, cada vez que deseamos pasar una referencia a "algo", podemos usar una referencia de tipoObject.Dado que no necesitamos usar punteros vacíos para ese propósito en estos idiomas,
voidno tiene que significar "algo o nada" aquí.fuente
En C, es posible tener un puntero a cosas de cualquier tipo [los punteros se comportan como un tipo de referencia]. Un puntero puede identificar un objeto de un tipo conocido, o puede identificar un objeto de tipo arbitrario (desconocido). Los creadores de C eligieron usar la misma sintaxis general para "puntero a cosa de tipo arbitrario" que para "puntero a cosa de algún tipo particular". Dado que la sintaxis en el último caso requiere un token para especificar cuál es el tipo, la sintaxis anterior requiere poner algo donde pertenecería ese token si se conociera el tipo. C eligió usar la palabra clave "void" para ese propósito.
En Pascal, como con C, es posible tener punteros a cosas de cualquier tipo; los dialectos más populares de Pascal también permiten "puntero a cosa de tipo arbitrario", pero en lugar de usar la misma sintaxis que usan para "puntero a cosa de algún tipo particular", simplemente se refieren al primero como
Pointer.En Java, no hay tipos de variables definidas por el usuario; todo es una referencia de objeto primitivo o de montón. Se pueden definir cosas de tipo "referencia a un objeto de montón de un tipo particular" o "referencia a un objeto de montón de tipo arbitrario". El primero simplemente usa el nombre del tipo sin puntuación especial para indicar que es una referencia (ya que no puede ser otra cosa); este último usa el nombre del tipo
Object.El uso de
void*como nomenclatura para un puntero a algo de tipo arbitrario es, hasta donde yo sé, exclusivo de C y derivados directos como C ++; otros idiomas que conozco manejan el concepto simplemente usando un tipo con un nombre distintivo.fuente
Puede pensar en la palabra clave
voidcomo un vacíostruct( en C99 las estructuras vacías aparentemente no están permitidas , en .NET y C ++ ocupan más de 0 memoria, y por último recuerdo que todo en Java es una clase):... lo que significa que es un tipo con longitud 0. Así es como funciona cuando realiza una llamada de función que devuelve
void... en lugar de asignar 4 bytes más o menos en la pila para un valor de retorno entero, no cambia el puntero de la pila (lo incrementa en una longitud de 0 )Entonces, si declaró algunas variables como:
... y luego tomaste la dirección de esas variables, entonces quizás
bycpodría ubicarse en el mismo lugar en la memoria, porquebtiene longitud cero. Entonces avoid*es un puntero en memoria al tipo incorporado con longitud cero. El hecho de que puedas saber que hay algo inmediatamente después que puede ser útil para ti es otro asunto y requiere que realmente sepas lo que estás haciendo (y es peligroso).fuente