指针作为C中的函数参数
如果我有这个代码,例如:
int num = 5; int *ptr = #
以下两个function有什么区别?
void func(int **foo); void func(int *foo);
我在哪里调用函数:
func(&ptr);
我意识到两者的前者将指针指向一个参数,而第二个只取一个指针。
如果我传入func(&ptr)
,我实际上是在传入一个指针。 指针指向另一个指针有什么区别?
我相信后者会给出一个不兼容的警告,但只要你知道你在做什么,似乎细节无关紧要。 似乎可能为了可读性和理解,前者是更好的选择(2星指针),但从逻辑的角度来看,有什么区别?
一个合理的经验法则是,您无法准确地更改传递的确切内容,以便调用者看到更改。 传递指针是一种解决方法。
传递值: void fcn(int foo)
通过值传递时,您将获得该值的副本。 如果更改函数中的值,则无论您的更改如何,调用者仍会看到原始值。
通过指针传递给值: void fcn(int* foo)
通过指针传递给你一个指针的副本 – 它指向与原始相同的内存位置。 该存储位置是存储原件的位置。 这使您可以更改指向的值。 但是,由于您只收到指针的副本,因此无法更改指向数据的实际指针。
将指针传递给指向值的指针: void fcn(int** foo)
通过将指针传递给指向值的指针,可以解决上述问题。 如上所述,您可以更改该值,以便调用者可以看到更改,因为它与调用者代码使用的内存位置相同。 出于同样的原因,您可以将指针更改为值。 这使你可以在函数中分配内存并返回它; &arg2 = calloc(len);
。 您仍然无法更改指针的指针,因为这是您收到的副本。
差别在处理器将处理代码的操作中简单地说。 在这两种情况下,价值本身只是一个地址,这是真的。 但是,当地址被取消引用时,对于处理器以及编译器来说,在解除引用之后知道它将处理什么是很重要的。
如果我有这个代码,例如:
int num = 5; int *ptr = #
以下两个函数有什么区别?:
void func(int **foo); void func(int *foo);
第一个想要一个指向int的指针,第二个想要一个直接指向int的指针。
我在哪里调用函数:
func(&ptr);
由于ptr
是指向int的指针,因此&ptr
是一个地址,与int **
兼容。
采用int *
的函数将与int **
有所不同。 会话的结果将完全不同,导致未定义的行为,可能导致崩溃。
如果我传入func(&ptr),我实际上是在传入一个指针。 指针指向另一个指针有什么区别?
+++++++++++++++++++ adr1 (ptr): + adr2 + +++++++++++++++++++ +++++++++++++++++++ adr2 (num): + 42 + +++++++++++++++++++
在adr2
,我们有一个int值,42。
在adr1
,我们有地址adr2
,具有指针的大小。
&ptr
给我们adr1, ptr
,保存&num
的值,即adr2。
如果我将adr1
用作int *
,则adr2
将被误认为是整数,从而导致(可能非常大)数字。
如果我使用adr2
作为int **
,第一个取消引用会导致42,这将被误解为一个地址并可能导致程序崩溃。
int *
和int **
之间的区别不仅仅是光学。
我相信后者会给出一个不兼容的警告,
……有意义……
但只要你知道自己在做什么,似乎细节无关紧要。
你呢?
似乎可能为了可读性和理解,前者是更好的选择(2星指针),但从逻辑的角度来看,有什么区别?
这取决于函数对指针的作用。
有两个主要的实际差异:
-
将指针传递给指针允许函数以调用者可以看到的方式修改该指针的内容。 一个典型的例子是
strtol()
的第二个参数。 在调用strtol()
,该指针的内容应该指向字符串中未被解析以计算long
值的第一个字符。 如果您只是将指针传递给strtol()
,那么它所做的任何更改都将是本地的,并且无法通知调用者该位置是什么。 通过传递该指针的地址,strtol()
可以以调用者可以看到的方式修改它。 这就像传递任何其他变量的地址一样。 -
更基本的是,编译器需要知道指向的类型才能取消引用。 例如,当解除引用
double *
,编译器将解释(在double
消耗8个字节的实现上)从内存位置开始的8个字节作为double的值。 但是,在32位实现中,当解除引用double **
,编译器会将从该位置开始的4个字节解释为另一个double的地址。 取消引用指针时,指向的类型是编译器有关如何解释该地址数据的唯一信息,因此了解确切类型至关重要,这就是为什么认为“它们是错误的”所有只是指针,所以有什么区别“?
通常,差异表示函数将分配给指针,并且此赋值不应仅仅是函数的本地。 例如(并记住这些示例是为了检查foo的性质而不是完整的函数,除了原始post中的代码应该是真正的工作代码):
void func1 (int *foo) { foo = malloc (sizeof (int)); } int a = 5; func1 (&a);
类似于
void func2 (int foo) { foo = 12; } int b = 5; func2 (b);
在func2()中foo
可能等于12,但是当func2()返回时, b
仍然等于5.在func1()中, foo
指向一个新的int,但是当func1()返回时仍然a
。
如果我们想要更改a
或b
的值, a
怎么办? WRT b
,正常的int:
void func3 (int *foo) { *foo = 12; } int b = 5; func2 (&b);
会工作 – 注意我们需要一个指向int的指针。 要更改指针中的值(即,它指向的int的地址,而不仅仅是它指向的int中的值):
void func4 (int **foo) { *foo = malloc (sizeof (int)); } int *a; foo (&a);
‘a’现在指向func4()中malloc返回的内存。 地址&a
是&a
的地址, 指向int的指针 。 int指针包含int的地址。 func4()
获取int指针的地址,以便它可以将int的地址放入此地址,就像func3()获取int的地址一样,以便它可以将新的int值放入其中。
这就是使用不同参数样式的方式。
已经有一段时间了,但是这是我对此的看法。 我现在正在努力学习C和指针无休止地混淆……所以我花时间来澄清关于指针的指针,至少对我来说。 以下是我的想法。
我从这里举了一个例子:
#include #include int allocstr(int len, char **retptr) { char *p = malloc(len + 1); /* +1 for \0 */ if(p == NULL) return 0; *retptr = p; return 1; } int main() { char *string = "Hello, world!"; char *copystr; if(allocstr(strlen(string), ©str)) strcpy(copystr, string); else fprintf(stderr, "out of memory\n"); return 0; }
我想知道为什么allocstr需要一个双指针。 如果它是指针,则意味着您可以传递它,并且在返回后它将被更改…
如果你做这个例子,它工作正常。 但是如果你将allocstr更改为只有*指针而不是**指针(而copystr而不是&copystr在main中)则会出现分段错误 。 为什么? 我在代码中放了一些printfs,它工作正常,直到带有strcpy的行。 所以我猜它没有为copystr分配内存。 再次,为什么?
让我们回到通过指针传递意味着什么。 这意味着你传递了内存位置,你可以直接在那里写下你想要的值。 您可以修改该值,因为您可以访问值的内存位置。
类似地,当您将指针传递给指针时,您将传递指针的内存位置 – 换句话说,就是内存位置的内存位置。 现在(指向指针的指针)可以更改内存位置,因为您可以在仅使用指针时更改值。
代码工作的原因是您传递了内存位置的地址。 函数allocstr改变了该内存位置的大小,因此它可以保存“Hello world!” 并返回指向该内存位置的指针。
它实际上与传递指针相同,但我们有一个内存位置而不是值。