punteros puntero parametros funciones estructuras como cadenas aritmetica apuntadores c pointers struct reference

estructuras - punteros como parametros de funciones en c



Pasar punteros/referencias a estructuras en funciones (6)

Esto va a sonar como una pregunta tonta, pero todavía estoy aprendiendo C, así que por favor tengan paciencia conmigo. :)

Estoy trabajando en el capítulo 6 de K & R (structs), y hasta ahora todo el libro ha tenido un gran éxito. Decidí trabajar con estructuras bastante pesadas, y por lo tanto trabajé mucho al comienzo del capítulo con los ejemplos de punto y rect. Una de las cosas que quería probar era cambiar la función canonrect (2nd Edition, p 131) a través de punteros y, por lo tanto, devolver el void .

Tengo esto funcionando, pero me encontré con un problema con el que esperaba que ustedes pudieran ayudarme. Quería canonRect para crear un objeto rectángulo temporal, realizar sus cambios, luego reasignar el puntero que se pasa al rectángulo temporal, simplificando así el código.

Sin embargo, si hago eso, el rect no cambia. En cambio, me encuentro repoblando manualmente los campos de rect que me dieron, lo cual funciona.

El código sigue:

#include <stdio.h> #define min(a, b) ((a) < (b) ? (a) : (b)) #define max(a, b) ((a) > (b) ? (a) : (b)) struct point { int x; int y; }; struct rect { struct point lowerLeft; struct point upperRight; }; // canonicalize coordinates of rectangle void canonRect(struct rect *r); int main(void) { struct point p1, p2; struct rect r; p1.x = 10; p1.y = 10; p2.x = 20; p2.y = 40; r.lowerLeft = p2; // note that I''m inverting my points intentionally r.upperRight = p1; printf("Rectangle, lower left: %d, %d; upper right: %d %d/n/n", r.lowerLeft.x, r.lowerLeft.y, r.upperRight.x, r.upperRight.y); // can''t pass a pointer, only a reference. // (Passing pointers results in illegal indirection compile time errors) canonRect(&r); printf("Rectangle, lower left: %d, %d; upper right: %d %d/n/n", r.lowerLeft.x, r.lowerLeft.y, r.upperRight.x, r.upperRight.y); } void canonRect(struct rect *r) { struct rect temp; temp.lowerLeft.x = min(r->lowerLeft.x, r->upperRight.x); temp.lowerLeft.y = min(r->lowerLeft.y, r->upperRight.y); temp.upperRight.x = max(r->lowerLeft.x, r->upperRight.x); temp.upperRight.y = max(r->lowerLeft.y, r->upperRight.y); r = &temp; // doesn''t work; my passed-in rect remains the same // I wind up doing the following instead, to reassign all // the members of my passed-in rect //r->lowerLeft = temp.lowerLeft; //r->upperRight = temp.upperRight; }

Así que aquí están las preguntas:

  1. ¿Por qué r = &temp; ¿no trabajo? (Creo que esto se debe a que paso una referencia en lugar de un puntero, ¿estoy en lo cierto al pensar que las referencias no son modificables, pero los punteros sí?)
  2. ¿Por qué podría obtener un error de tiempo de compilación indirecto ilegal si intento pasar un puntero a canonRect ? (IE, si tuviera canonRect(*r); en main() )

Sospecho que ya sé la respuesta al # 1, pero el # 2 me deja perplejo: pensé que era legal pasar punteros.

De todos modos ... por favor, perdona el C newb.


Es posible que desee leer sobre las diferentes formas en que los parámetros se pueden pasar (conceptualmente) a las funciones. C es call-by-value , por lo que cuando pasa el puntero a un rect en la función, está pasando una copia del puntero. Cualquier cambio que la función realice en el valor r directamente (no indirectamente) no será visible para la persona que llama.

Si desea que la función proporcione a la persona que llama una nueva estructura, hay dos formas de hacerlo: 1. puede devolver una rect: 2. puede pasar un puntero a un puntero a una rect en:

La primera forma sería más natural:

struct rect* canonRect(struct rect* r) { struct rect* cr = (struct rect*) malloc(sizeof(struct rect)); ... return cr; }

La segunda forma sería:

void canonRect(struct rect** r) { *r = (struct rect*) malloc(sizeof(struct rect)); }

y la persona que llama usaría:

canonRect(&r);

Pero la persona que llama pierde el puntero original que tenía, y tendría que tener cuidado de no filtrar las estructuras.

Cualquiera que sea la técnica que use, la función necesitará asignar memoria para la nueva estructura en el montón utilizando malloc. No puede asignar el espacio en la pila simplemente declarando una estructura porque esa memoria no es válida cuando la función retorna.


Parece que estás confundiendo al operador de "desreferencia" ( * ) con el operador de "dirección de" ( & ).

Cuando escribe &r , obtiene la dirección de r y devuelve un puntero a r (un puntero es solo una dirección de memoria de una variable). Entonces realmente está pasando un puntero a la función.

Cuando escribe *r , está intentando desreferenciar r. Si r es un puntero, devolverá el valor al que r apunta. Pero r no es un puntero, es un rect, por lo que obtendrá un error.

Para hacer las cosas más confusas, el carácter * también se usa al declarar variables de puntero. En esta declaración de función:

void canonRect(struct rect *r) {

r se declara como un puntero a una struct rect . Esto es completamente diferente de usar * como este:

canonRect(*r);

En ambos casos, el carácter * significa algo completamente diferente.


Primero, K & R c no tiene un concepto de "referencias", solo punteros. El operador & significa "tomar la dirección de".

En segundo lugar, el r en cannonRect() es una variable local, y no es el r en main() . Cambiar donde los puntos r locales no afectan a la r en la rutina de llamada.

Finalmente, como ya se señaló, la struct rect local struct rect se asigna en la pila, y sale fuera del alcance en el corsé cerrado,


También vale la pena señalar que su variable ''struct rec temp'' va a quedar fuera del alcance tan pronto como el método canonRect termine y usted estará apuntando a la memoria no válida.


2. ¿Por qué podría obtener un error de tiempo de compilación indirecto ilegal si trato de pasar un puntero a canonRect? (IE, si tuviera canonRect (* r); en main ())

Porque, no es el propósito del puntero .


Creo que lo que quieres hacer es esto:

void canonRect(struct rect *r) { struct rect temp; temp.lowerLeft.x = min(r->lowerLeft.x, r->upperRight.x); temp.lowerLeft.y = min(r->lowerLeft.y, r->upperRight.y); temp.upperRight.x = max(r->lowerLeft.x, r->upperRight.x); temp.upperRight.y = max(r->lowerLeft.y, r->upperRight.y); *r = temp; }

En el código anterior está configurando * r que es de tipo rect a temp, que es de tipo rect.

Re 1: si quieres cambiar lo que está apuntando, necesitas usar un puntero a un puntero. Si eso es realmente lo que quieres (ver más arriba, no es realmente lo que quieres) entonces deberías asegurarte de apuntar a algo en el montón. Si lo apunta a algo que no se creó con ''nuevo'' o malloc, se saldrá del alcance y señalará la memoria que ya no se usa para esa variable.

¿Por qué su código no funciona con r = & temp?

Porque r es de rect * tipo. Eso significa que r es una variable que contiene una dirección de memoria cuya memoria contiene un rect. Si cambia lo que está señalando r, está bien, pero eso no cambia la variable pasada.

Re 2: * cuando no se utiliza en una declaración de tipo es el operador unario de desreferencia. Esto significa que buscará lo que está dentro de la dirección del puntero. Entonces al pasar * r no estás pasando un puntero en absoluto. En vista desde que r no es un puntero, esa es una sintaxis inválida.