Pregunta

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

Estoy trabajando en el capítulo 6 de K & amp; R (structs), y hasta ahora todo el libro ha tenido un gran éxito. Decidí trabajar con estructuras bastante, y por lo tanto hice mucho trabajo al principio del capítulo con los ejemplos de puntos y rectos. Una de las cosas que quería probar era cambiar el trabajo de canonrect (2ª edición, p. 131) a través de los punteros y, por lo tanto, devolver void .

Tengo este trabajo, pero tuve un problema con el que esperaba que ustedes pudieran ayudarme. Quería que canonRect creara un objeto de rectángulo temporal, realizara sus cambios, luego reasignara el puntero que se pasó al rectángulo temporal, simplificando así el código.

Sin embargo, si hago eso, el rect no cambia. En lugar de eso, me encuentro repulando manualmente los campos de la rect a la que me pasa, lo que sí funciona.

El código siguiente:

#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 = & amp; temp; no funciona? (Creo que esto se debe a que paso una referencia en lugar de un puntero; ¿tengo razón al pensar que las referencias no son modificables, pero los punteros son?)
  2. ¿Por qué puedo obtener un error ilegal de compilación indirecta en tiempo de compilación si intento pasar un puntero a canonRect ? (IE, si tuviera canonRect (* r); en main () .)

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

De todos modos ... perdona la C newb.

¿Fue útil?

Solución

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 desea cambiar lo que r apunta, debe utilizar un puntero a un puntero. Si eso es realmente lo que quieres (ver arriba, no es realmente lo que quieres) entonces deberías asegurarte de apuntarlo a algo en el montón. Si lo apunta a algo que no fue creado con 'nuevo' o malloc, quedará fuera de alcance y estará apuntando a la memoria que ya no se usa para esa variable.

¿Por qué tu código no funciona con r = & amp; temp?

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

Re 2: * cuando no se usa en una declaración de tipo es el operador unario de referencia. Esto significa que buscará lo que está dentro de la dirección del puntero. Así que al pasar * r no estás pasando ningún puntero. En la cara, ya que r no es un puntero, es una sintaxis no válida.

Otros consejos

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 apunte a una memoria no válida.

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

Cuando escribe & amp; 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). Así que realmente estás pasando un puntero a la función.

Cuando escribes * r , estás intentando desreferenciar r. Si r es un puntero, devolverá el valor al que apunta r. Pero r no es un puntero, es un rect, por lo que obtendrás 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 * de esta manera:

canonRect(*r); 

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

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


En segundo lugar, el r en cannonRect () es una variable local, y es no el r en main () . Cambiar la ubicación de los puntos r locales no afecta al r en la rutina de llamada.


Finalmente, como ya se señaló, la struct rect local está asignada en la pila y queda fuera del alcance en el corchete de cierre,

Es posible que desee leer sobre las diferentes formas en que los parámetros pueden pasarse (conceptualmente) a las funciones. C es call-by-value , así que cuando pasas tu puntero a un rectángulo en La función que 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. Usted puede devolver un rect:   2. puede pasar un puntero a un puntero a un 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 deberías tener cuidado de no perder estructuras.

Cualquiera que sea la técnica que use, la función necesitará asignar memoria para la nueva estructura en el montón usando malloc. No puede asignar el espacio en la pila simplemente declarando una estructura porque esa memoria se vuelve inválida cuando la función regresa.

  

2.¿Por qué puedo obtener un error en el tiempo de compilación indireccional ilegal si intento pasar un puntero a canonRect? (IE, si tuviera canonRect (* r); en main ().)

Porque, no es el propósito de puntero .

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top