2010-02-01 17 views
7

Solicité question earlier para definir una estructura con malloc. Esta fue la respuesta que dieron por la mayoría:Definir una estructura en C con Malloc

struct retValue* st = malloc(sizeof(*st)); 

me estaba mostrando un amigo mi código, y llegamos a una piedra de tropiezo. ¿Podría alguien explicar por qué funciona este código? Desde mi punto de vista, * st no se ha definido cuando lo malloc, por lo que podría haber cualquier tipo de basura allí. Debe ser malloc(sizeof(struct retValue))

Gracias por cualquier ayuda

+0

Respondió su propia pregunta. sizeof (struct retValue) es correcto –

+1

Lo siento, la pregunta no era '¿Es correcto o qué es correcto?' era '¿Por qué funciona esto?' – Blackbinary

+2

Podría ayudar entender si cambia la terminología. No estás * definiendo * una estructura usando malloc, estás * asignando * una estructura usando malloc. Está definiendo 'st', que es un puntero (no una estructura). Ese puntero ya está definido y disponible para su uso en la expresión del inicializador (en el RHS del signo igual), simplemente no tiene un valor, por lo que la mayoría de los usos no serían válidos. Este está bien, sin embargo, porque sizeof no usa el valor. –

Respuesta

19

Sizeof mira el tipo de expresión que se le da, no evalúa la expresión. Por lo tanto, solo necesita asegurarse de que las variables utilizadas en la expresión estén declaradas para que el compilador pueda deducir su tipo.

En su ejemplo, st ya está declarado como pointer-to-struct-retValue. En consecuencia, el compilador puede deducir el tipo de la expresión "* st".

Aunque no parezca que ya está declarada en el código, el compilador ya se ha hecho cargo de él para usted. Todas las declaraciones en su código se mueven al comienzo del bloque en el que ocurren por el compilador. Supongamos que escribe

Una forma de ilustrar el conocimiento que está disponible para el compilador es mirar a la salida intermedia que genera. Considere este ejemplo de código ...

struct retValue {long int a, long int b}; 
... 
printf("Hello World!\n"); 
struct retValue* st = malloc(sizeof(*st)); 

con gcc como un ejemplo y teh código anterior en el principal() función de test.c, vamos a ver la salida intermedia mediante la ejecución ...

gcc -fdump-tree-cfg test.c 

el compilador generará el archivo test.c.022t.cfg - vistazo y verá

[ ... removed internal stuff ...] 
;; Function main (main) 

Merging blocks 2 and 3 
main (argc, argv) 
{ 
    struct retValue * st; 
    int D.3097; 
    void * D.3096; 

    # BLOCK 2 
    # PRED: ENTRY (fallthru) 
    __builtin_puts (&"Hello World!"[0]); 
    D.3096 = malloc (16); 
    st = (struct retValue *) D.3096; 
    D.3097 = 0; 
    return D.3097; 
    # SUCC: EXIT 

} 

Observe cómo la declaración se movió al principio del bloque y el argumento a malloc ya se reemplazó con el valor real que denota el tamaño del tipo en que se evalúa la expresión. Como se señaló en los comentarios, el hecho de que la declaración se movió a la parte superior del bloque es un detalle de implementación del compilador. Sin embargo, el hecho de que el compilador sea capaz de hacer esto y de insertar el tamaño correcto en el malloc muestra que el compilador pudo deducir la información necesaria de la entrada.

yo personalmente prefiero dar el nombre de tipo real como un parámetro a sizeof, pero eso es probablemente una cuestión de estilo de codificación, donde yo diría que la consistencia triunfos personales-preferencia.

+1

Su pregunta fue sobre '* st' haciendo" cosas malas ". '* st' no es válido si' st' no apunta a datos válidos. Pero como 'sizeof' no evalúa su argumento, está bien usar' * st' como un operando para 'sizeof', incluso si' st' es 'NULL' por ejemplo. –

+0

En realidad, la pregunta era "cómo/por qué funciona a pesar de que st no se puede desreferenciar en el momento en que malloc necesita saber cuánto espacio asignar" ... – VoidPointer

+1

Poniendo una declaración al principio del bloque en el análisis la versión del código es un detalle de implementación. No es necesario que el compilador lo haga, aunque le ocurra lo mismo a usted, y el código del cuestionario funciona independientemente de que el compilador lo haga o no. El siguiente código no se compila, mostrando que la declaración no se mueve "realmente": 'sizeof (* st); char * st = 0; '. –

19

El operador sizeof en realidad no evaluar su operando - que sólo se ve en su tipo. Esto se hace en tiempo de compilación en lugar de en tiempo de ejecución. Por lo tanto, puede realizarse con seguridad antes de que se haya asignado la variable.

+0

Creo que entiendo. Entonces tu diciendo 'sizeof' mira a * st y dice 'Oh, eso es un puntero!' y luego asigna suficiente memoria para un puntero. No le importa lo que * st realmente tenga. ¿Derecha? – Blackbinary

+5

No, es al revés. '* st' significa" lo que apunta a st ", por lo que el compilador devuelve el tamaño de la estructura, no el tamaño del puntero. –

+3

@Blackbinary: Cerrar: 'st' es un puntero, pero' * st' es una estructura. Entonces mira '* st' y dice" Oh, eso es 'struct retValue'!" y luego asigna suficiente memoria para una estructura retValue. El contenido real de '* st' no importa. – interjay

1

Lo que importa es la declaración/definición del tipo de estructura y no la definición de un objeto de dicha clase. En el momento en que llegue al malloc, el compilador habrá encontrado una declaración o una definición, de lo contrario, se golpearía un error del compilador.

El hecho de que sizeof no evalúe sus operandos es un problema lateral.

Una nit de menor importancia: recuerde que necesitamos paréntesis, cuando suministramos los nombres de tipos de sizeof como en:

sizeof(struct retValue); 

y no en el caso de los objetos, simplemente hacemos:

sizeof *st; 

Consulte el estándar:

6.5 .3 operadores unarios sintaxis

unary-expression: 
[...] 
sizeof unary-expression 
sizeof (type-name) 
0

En C, sizeof es un operador y no evalúa su argumento. Eso puede llevar a efectos "interesantes", que alguien nuevo en C no anticipa necesariamente. Mencioné eso con más detalle en my answer a la pregunta "Característica de lenguaje más extraño".

Cuestiones relacionadas