这听起来像是一个愚蠢的问题,但我还在学习C,所以请耐心等待。 :)
我正在研究K& R(结构)的第6章,到目前为止,这本书已经取得了巨大的成功。我决定使用相当多的结构,因此在本章的早期就使用了point和rect示例做了很多工作。我想尝试的其中一件事是通过指针更改canonrect
函数(第2版,第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 = &temp;
不起作用? (我认为这是因为我传入了一个引用而不是一个指针;我认为引用不可修改但是指针是正确的吗?)canonRect
的指针,为什么会出现非法的间接编译时错误? (IE,如果canonRect(*r);
中有main()
。)我怀疑我已经知道#1的答案了,但#2让我感到困惑 - 我认为传递指针是合法的。
无论如何......请原谅C newb。
答案 0 :(得分:18)
我认为您想要做的是:
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,类型为rect。
Re 1:如果你想改变r指向的东西,你需要使用指向指针的指针。如果这真的是你想要的(见上文,它不是你想要的那样)那么你必须确保将它指向堆上的东西。如果你将它指向不是用'new'或malloc创建的东西,那么它将超出范围,你将指向不再用于该变量的内存。
为什么你的代码不适用于r =&amp; temp?
因为r是rect *类型。这意味着r是一个变量,它保存一个内存包含一个rect的内存地址。如果你改变r指向的东西,那很好,但这不会改变传入的变量。
Re 2:*未在类型声明中使用时是解引用一元运算符。这意味着它将查找指针地址内的内容。所以通过传递* r你根本就没有传递指针。因为r不是指针,所以语法无效。
答案 1 :(得分:12)
值得注意的是,只要该方法canonRect结束并且您将指向无效的内存,您的变量'struct rec temp'将超出范围。
答案 2 :(得分:4)
听起来你将“解除引用”运算符(*
)与'address of'运算符(&
)混淆。
当您编写&r
时,它获取r的地址并返回指向r的指针(指针只是变量的内存地址)。所以你真的把指针传递给函数。
当你写*r
时,你试图取消引用r。如果r是一个指针,那将返回r指向的值。但是r不是指针,它是一个矩形,所以你会得到一个错误。
为了使事情更加混乱,在声明指针变量时也使用*
字符。在这个函数声明中:
void canonRect(struct rect *r) {
r
被声明为指向struct rect
的指针。这与使用*
完全不同:
canonRect(*r);
在这两种情况下,*字符意味着完全不同的东西。
答案 3 :(得分:2)
您可能希望了解参数可以(概念上)传递给函数的不同方法。 C是call-by-value,所以当你将指针传递给函数的rect时,你传递的是指针的副本。函数直接(非间接)对值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为堆上的新结构分配内存。您只能通过声明一个结构来分配堆栈上的空间,因为当函数返回时该内存将变为无效。
答案 4 :(得分:2)
首先,K&amp; R c没有“引用”的概念,只是指针。 &
运算符的意思是“取地址”。
其次,r
中的cannonRect()
是一个局部变量,不 r
中的main()
。更改本地r
点的位置不会影响调用例程中的r
。
最后,正如已经指出的那样,本地struct rect
在堆栈上分配,并且在近大括号中超出范围,
答案 5 :(得分:-1)
2.如果我尝试传入指向canonRect的指针,为什么会出现非法的间接编译时错误? (IE,如果我有canonRect(* r);在main()中。)
因为,它不是指针的目的。