【问题标题】:Passing pointers/references to structs into functions将结构的指针/引用传递给函数
【发布时间】:2010-09-20 08:19:23
【问题描述】:

这听起来像是一个愚蠢的问题,但我还在学习 C,所以请多多包涵。 :)

我正在编写 K&R(结构)的第 6 章,到目前为止,这本书已经取得了巨大的成功。我决定大量使用结构体,因此在本章的开头用 point 和 rect 示例做了很多工作。我想尝试的一件事是通过指针更改 canonrect 函数(第 2 版,第 131 页)工作,从而返回 void

我有这个工作,但遇到了一个小问题,我希望你们能帮助我。我希望canonRect 创建一个临时矩形对象,执行它的更改,然后将它传递给临时矩形的指针重新分配,从而简化代码。

但是,如果我这样做,矩形不会改变。相反,我发现自己手动重新填充了我传入的 rect 的字段,这确实有效。

代码如下:

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

以下是问题:

  1. 为什么r = &amp;temp; 不起作用? (我认为这是因为我传入的是引用而不是指针;我认为引用不可修改但指针可以修改是否正确?)
  2. 如果我尝试传递指向canonRect 的指针,为什么会收到非法间接编译时错误? (即,如果我在 main() 中有 canonRect(*r);。)

我怀疑我已经知道#1 的答案,但#2 让我感到困惑——我认为传递指针是合法的。

无论如何...请原谅C新手。

【问题讨论】:

    标签: c pointers struct reference


    【解决方案1】:

    我想你想做的是:

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

    在上面的代码中,您将 rect 类型的 *r 设置为 rect 类型的 temp。

    Re 1:如果要更改 r 指向的内容,则需要使用指向指针的指针。如果这确实是您想要的(见上文,它并不是您真正想要的),那么您必须确保将其指向堆上的某个东西。如果你将它指向不是用 'new' 或 malloc 创建的东西,那么它将超出范围,你将指向不再用于该变量的内存。

    为什么你的代码不能使用 r = &temp?

    因为 r 是 rect* 类型。这意味着 r 是一个变量,它保存一个内存地址,该内存地址包含一个矩形。如果您更改 r 指向的内容,那很好,但这不会更改传入的变量。

    Re 2: * 在类型声明中不使用时是取消引用一元运算符。这意味着它将查找指针地址内的内容。所以通过 *r 你根本没有传递指针。面对面,因为 r 不是指针,所以这是无效的语法。

    【讨论】:

    • 呃……我今天一定是脑子有问题。将指针传递给指针将是 canonRect(**r);,不是吗?同样的非法间接错误。
    • 刚刚测试过;这是一个。感谢您非常详尽的解释。
    【解决方案2】:

    还值得注意的是,一旦该方法 canonRect 结束,您的变量 'struct rec temp' 将超出范围,并且您将指向无效内存。

    【讨论】:

      【解决方案3】:

      听起来您将“取消引用”运算符 (*) 与“地址”运算符 (&amp;) 混淆了。

      当您编写&amp;r 时,它会获取 r 的地址并返回指向 r 的指针(指针只是变量的内存地址)。所以你确实是在向函数传递一个指针。

      当你写*r 时,你试图取消对 r 的引用。如果 r 是一个指针,那将返回 r 指向的值。但是 r 不是指针,它是一个矩形,所以你会得到一个错误。

      为了让事情更混乱,* 字符也用于声明指针变量时。在这个函数声明中:

      void canonRect(struct rect *r) {
      

      r 被声明为指向struct rect 的指针。这与像这样使用* 完全不同:

      canonRect(*r); 
      

      在这两种情况下,* 字符的含义完全不同。

      【讨论】:

      • 调用者负责释放所有动态定位的内存
      【解决方案4】:

      首先,K&R c 没有“引用”的概念,只有指针。 &amp; 运算符的意思是“获取地址”。


      其次,cannonRect() 中的r 是一个局部变量,不是main() 中的r。更改本地r 指向的位置不会影响调用例程中的r


      最后,如前所述,本地 struct rect 分配在堆栈上,并在右括号处超出范围,

      【讨论】:

      • 没错,但更大的问题是他需要分配给 *r。
      【解决方案5】:

      您可能想了解参数可以(从概念上)传递给函数的不同方式。 C 是call-by-value,因此当您将指向 rect 的指针传递给函数时,您将传递指针的副本。函数对值 r 所做的任何直接(非间接)更改对调用者都是不可见的。

      如果你想让函数为调用者提供一个新的结构,那么有两种方法: 1.你可以返回一个矩形: 2.你可以传递一个指向一个rect的指针:

      第一种方式会更自然:

      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 为堆上的新结构分配内存。您不能仅通过声明一个结构来分配堆栈上的空间,因为当函数返回时,该内存将变得无效。

      【讨论】:

      • Rob,我认为您已经找到了问题的根源,但是如果您包含一个示例,您的答案可能会更好,原始 r 变量是如何发生泄漏的。这似乎是一个介绍性问题,因此没有经验的 C 编码人员可能无法理解细节。
      【解决方案6】:

      2.如果我尝试传递指向 canonRect 的指针,为什么会出现非法间接编译时错误? (IE,如果我有 canonRect(*r); 在 main()。)

      因为,它不是指针的目的。

      【讨论】:

        猜你喜欢
        • 2020-05-15
        • 1970-01-01
        • 2021-07-17
        • 1970-01-01
        • 1970-01-01
        • 2021-11-02
        • 2019-05-03
        • 1970-01-01
        相关资源
        最近更新 更多