Передача указателей / ссылок на структуры в функции

StackOverflow https://stackoverflow.com/questions/256148

  •  05-07-2019
  •  | 
  •  

Вопрос

Это будет звучать глупо, но я все еще изучаю C, поэтому, пожалуйста, потерпите меня. :)

Я работаю над 6-й главой K & amp; R (структура), и до сих пор в книге были замечены успехи. Я решил довольно много работать со структурами, поэтому в начале главы проделал большую работу с точными и прямыми примерами. Одна из вещей, которую я хотел попробовать, это изменить работу функции canonrect (2nd Edition, p 131) с помощью указателей и, следовательно, вернуть void .

У меня это работает, но я столкнулся с ошибкой, я надеялся, что вы, ребята, сможете мне помочь. Я хотел, чтобы canonRect создал объект временного прямоугольника, выполнил его изменения, а затем переназначил указатель, который передается во временный прямоугольник, упрощая таким образом код.

Однако, если я это сделаю, прямоугольник не изменится. Вместо этого я обнаруживаю, что заново заполняю поля прямоугольника, в который мне передается, и это работает.

Код следующий:

#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;
}

Итак, вот вопросы:

<Ол>
  • Почему r = & amp; temp; не работает? (Я думаю , это потому, что я передаю ссылку вместо указателя; правильно ли я считаю, что ссылки не модифицируются, а указатели есть?)
  • Почему я могу получить недопустимую ошибку во время компиляции косвенного обращения, если я попытаюсь передать указатель на canonRect ? (IE, если бы у меня было canonRect (* r); в main () .)
  • Я подозреваю, что уже знаю ответ на # 1, но # 2 сбивает меня с толку - я думал, что было законно передавать указатели.

    Во всяком случае ... пожалуйста, прости новичка C.

    Это было полезно?

    Решение

    Я думаю, что вы хотите сделать следующее:

    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; 
    }
    

    В приведенном выше коде вы устанавливаете * r, который имеет тип rect к temp, который имеет тип rect.

    Re 1: Если вы хотите изменить то, на что указывает r, вам нужно использовать указатель на указатель. Если это действительно то, что вы хотите (см. Выше, это не совсем то, что вы хотите), то вам нужно обязательно указать на что-то в куче. Если вы укажете на что-то, не созданное с помощью 'new' или malloc, это выпадет из области видимости, и вы будете указывать на память, которая больше не используется для этой переменной.

    Почему ваш код не работает с r = & amp; temp?

    Потому что r имеет тип * rect. Это означает, что r - это переменная, которая содержит адрес памяти, память которого содержит прямоугольник. Если вы измените то, на что указывает r, это нормально, но это не изменит переданную переменную.

    Re 2: * когда не используется в объявлении типа, является унарным оператором разыменования. Это означает, что он будет искать то, что находится внутри адреса указателя. Таким образом, передавая * r, вы вообще не передаете указатель. На лице, так как r не указатель, это неверный синтаксис.

    Другие советы

    Также стоит отметить, что ваша переменная struct rec temp выйдет из области видимости, как только этот метод canonRect завершится, и вы укажете на недопустимую память.

    Похоже, вы путаете оператор 'разыменования' ( * ) с оператором 'address of' ( & amp; ).

    Когда вы пишете & amp; r , он получает адрес r и возвращает указатель на r (указатель - это просто адрес памяти переменной). Таким образом, вы действительно передаете указатель на функцию.

    Когда вы пишете * r , вы пытаетесь разыменовать r. Если r является указателем, он вернет значение, на которое указывает r. Но r не указатель, это прямоугольник, поэтому вы получите ошибку.

    Чтобы сделать вещи более запутанными, символ * также используется при объявлении переменных указателя. В этом объявлении функции:

    void canonRect(struct rect *r) {
    

    r объявляется как указатель на struct rect . Это полностью отличается от использования * следующим образом:

    canonRect(*r); 
    

    В обоих случаях символ * означает что-то совершенно другое.

    Во-первых, K & amp; R c не имеет понятия "ссылки", просто указатели. Оператор & amp; означает "взять адрес".

    <Ч>

    Во-вторых, r в cannonRect () является локальной переменной и не является r в <код> Основной () . Изменение местоположения локальных точек r не влияет на r в вызывающей подпрограмме.

    <Ч>

    Наконец, как уже отмечалось, локальная struct rect размещается в стеке и выходит из области видимости при закрывающей скобке,

    Возможно, вы захотите ознакомиться с различными способами, которыми параметры (концептуально) могут передаваться в функции. C вызов по значению , поэтому, когда вы передаете указатель на прямоугольник в функция, которую вы передаете копию указателя. Любые изменения, внесенные функцией в значение r напрямую (не косвенно), не будут видны вызывающей стороне.

    Если вы хотите, чтобы функция предоставляла вызывающей стороне новую структуру, то есть два способа сделать это:   1. вы можете вернуть прямоугольник:   2. вы можете передать указатель на указатель на прямоугольник в:

    Первый способ будет более естественным:

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

    Второй способ будет следующим:

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

    и вызывающий затем будет использовать:

       canonRect(&r);
    

    Но вызывающая сторона теряет свой оригинальный указатель, и вам следует быть осторожным, чтобы не пропустить структуры.

    Какой бы метод вы ни использовали, функция должна будет выделять память для новой структуры в куче, используя malloc. Вы не можете выделить место в стеке, просто объявив структуру, потому что эта память становится недействительной, когда функция возвращает.

      

    2.Почему я могу получить недопустимую ошибку компиляции косвенного обращения, если я попытаюсь передать указатель на canonRect? (IE, если бы у меня было canonRect (* r); в main ().)

    Потому что это не цель указателя .

    Лицензировано под: CC-BY-SA с атрибуция
    Не связан с StackOverflow
    scroll top