¿Qué tienen de especial las estructuras?

81

Sé que en C no podemos devolver una matriz de una función, sino un puntero a una matriz. Pero quiero saber qué es lo especial structsque los hace retornables por funciones a pesar de que pueden contener matrices.

¿Por qué la structenvoltura hace válido el siguiente programa?

#include <stdio.h>

struct data {
    char buf[256];
};

struct data Foo(const char *buf);

int main(void)
{
    struct data obj;
    obj = Foo("This is a sentence.");
    printf("%s\n", obj.buf);
    return 0;
}

struct data Foo(const char *buf)
{
    struct data X;
    strcpy(X.buf, buf);
    return X;
}
Sourav Ghosh
fuente
1
Podría hacer lo mismo con un union. ¿Qué tienen de especial los sindicatos?
user253751
19
Debería preguntar por qué las matrices son tan raras en C.
CodesInChaos
al devolver una estructura, si la estructura no cabe en un par de registros, entonces el compilador asigna una memoria 'oculta', la estructura se copia (a través de memcpy ()) a la memoria oculta y luego se copia nuevamente (a través de memcpy ( )) a la variable de estructura de los llamadores. Esa memoria 'oculta' se pierde para todas las demás funciones. Las dos llamadas adicionales ay memcpy()la pérdida de la memoria 'oculta' es la razón principal por la que una estructura no debería ser passed toni returned fromuna función. La mejor política es pasar un puntero a la estructura.
user3629249
ninguna de las tres respuestas aborda el paso de una estructura (más bien, solo discute el paso de matrices), pero no responde a la pregunta.
user3629249
1
@ user3629249: es imposible responder la pregunta porque la pregunta se basa en una falta de comprensión. La única forma de responder a la pregunta es intentar explicar por qué no se puede formular la pregunta. Imagínese si le preguntara "¿Por qué el azul es del mismo color que el rojo?" Sería rápido para explicar por qué no puede responder la pregunta.
Hogan

Respuestas:

100

Una mejor manera de hacer la misma pregunta sería "¿qué tienen de especial las matrices?", Ya que son las matrices las que tienen un manejo especial adjunto, no structs.

El comportamiento de pasar y devolver matrices por puntero se remonta a la implementación original de C. Las matrices "decaen" a punteros, causando mucha confusión, especialmente entre personas nuevas en el lenguaje. Las estructuras, por otro lado, se comportan como tipos incorporados, como ints, doubles, etc. Esto incluye cualquier matriz incrustada en struct, excepto los miembros de matriz flexible , que no se copian.

Sergey Kalinichenko
fuente
4
'causando mucha confusión' de hecho. 'x' y '& x' tienen el mismo valor / dirección es una locura. No es sorprendente que los novatos se equivoquen indirectamente :(
Martin James
1
Puede que mi memoria me esté engañando, pero ¿no hubo momentos en los que pasar structs por valor no fue posible?
Alk
5
@alk Creo que, cuando las estructuras se agregaron por primera vez al lenguaje, inicialmente había algunas restricciones para pasarlas a / devolverlas desde funciones, pero estas se marcaron claramente como una deficiencia en el compilador que se corrigió lo suficientemente pronto, no una indicación que no había nada malo en querer pasarlos y devolverlos.
Steve Summit
8
@jamesqf: struct Point {short x, y, z;};. ¿Realmente quieres usar punteros para moverlos? Ciertamente no está ahorrando espacio de esta manera.
Mark VY
6
@jamesqf No estoy seguro de que esto merezca una respuesta. Si cree que C no es mucho más que un ensamblaje, si cree que nunca hay una razón para no usar un puntero, tal vez pueda ver cómo podría pensar que pasar estructuras es inútil. Pero para el resto de nosotros, que tratamos a C como un lenguaje de alto nivel (aunque sea de bajo nivel, como van los HLL), y que tratamos el sistema de tipos de C como general (con la excepción del estado de segunda clase de las matrices ), ¿por qué no querríamos pasar o devolver estructuras? (Por cierto, IIRC, K & R1 dijeron que el paso de estructura estaba en camino, y funcionó en V7 cc cuando salió el libro)
Steve Summit
38

En primer lugar, para citar C11, el capítulo §6.8.6.4, returndeclaración, (el énfasis es mío )

Si returnse ejecuta una instrucción con una expresión, el valor de la expresión se devuelve al llamador como el valor de la expresión de llamada a la función.

Devolver una variable de estructura es posible (y correcto) porque se devuelve el valor de la estructura . Esto es similar a devolver cualquier tipo de datos primitivo (devolver int, por ejemplo).

Por otro lado, si devuelve una matriz , al usar la return <array_name>, esencialmente devuelve la dirección del primer elemento de la matriz NOTA , que se vuelve inválida en el llamador si la matriz era local a las funciones llamadas. Entonces, devolver la matriz de esa manera no es posible.

Entonces, TL; DR , no hay nada especial con structs, la especialidad está en las matrices .


NOTA:

Citando de C11nuevo, el capítulo §6.3.2.1, ( énfasis mío )

Excepto cuando es el operando del sizeofoperador, el _Alignofoperador o el &operador unario , o es un literal de cadena utilizado para inicializar una matriz, una expresión que tiene el tipo '' matriz de tipo '' se convierte en una expresión con el tipo '' pointer to type '' que apunta al elemento inicial del objeto de matriz y no es un lvalue. [...]

Sourav Ghosh
fuente
¿Qué es exactamente OTOH?
1
@Sukl Es la abreviatura de "por otro lado" :)
Sourav Ghosh
1
@Sukl Creo que estas abreviaturas son aproximadamente tan antiguas como la propia Internet. Seguramente se usaron mucho durante los días de gloria de Usenet y aún sobreviven en la mayoría de los foros. Afortunadamente, incluso para los que no lo saben, Google puede decodificarlos ;-) Y solo hay unos pocos que se usan con frecuencia en la actualidad (los más comunes)
chi
@chi sí. Google es bastante brillante y puede decodificarlos.
1
@Sukl: Puede que AcronymFinder le resulte útil para localizar acrónimos.
Jonathan Leffler
11

No hay nada especial en los structtipos; es que hay algo especial en los tipos de matriz que evita que sean devueltos directamente desde una función.

Una structexpresión se trata como una expresión de cualquier otro tipo que no sea de matriz; evalúa el valor de struct. Entonces puedes hacer cosas como

struct foo { ... };

struct foo func( void )
{
  struct foo someFoo;
  ...
  return someFoo;
}

La expresión someFooevalúa el valor del struct fooobjeto; el contenido del objeto se devuelve desde la función (incluso si esos contenidos contienen matrices).

Una expresión de matriz se trata de manera diferente; si no es el operando de los operadores sizeofunarios o &, o si no es un literal de cadena que se usa para inicializar otra matriz en una declaración, la expresión se convierte ("decae") del tipo "matriz de T" a "puntero a T" y el valor de la expresión es la dirección del primer elemento.

Por lo tanto, no puede devolver una matriz por valor de una función, porque cualquier referencia a una expresión de matriz se convierte automáticamente en un valor de puntero.

John Bode
fuente
-5

Las estructuras tienen miembros de datos públicos de forma predeterminada, por lo que es posible, en el caso de una estructura, acceder a los datos en main pero no en el caso de la clase. Entonces, el ajuste de estructura es válido.

hoh
fuente
1
¿Has visto que la pregunta es sobre C? En C, no hay distinción public/ privatey no hay classes. La pregunta es por qué structse necesita a en C para devolver el valor de una matriz.
PJTraill