在函数调用中被单指针和双指针参数混淆

我试图更深入地理解C函数中的指针参数。我已经编写了一个测试程序,试图看到将单个指针传递给一个函数的双指针然后修改它之间的区别。

我有一个有两个function的程序。 第一个函数modifyMe1将单个指针作为参数,并将a属性更改为7.第二个函数modifyMe2将双指针作为参数,并将a属性更改为7。

我预计第一个函数modifyMe1将是“按值传递”,即如果我传入了我的结构指针,C将创建它指向的数据的副本。 对于后者,我正在做一个“传递参考”,它应该修改到位的结构。

但是,当我测试这个程序时,两个函数似乎都修改了结构。 我知道对于指针的性质我有一个误解是肯定的。 有人可以帮我解决这个问题吗?

谢谢!

这是我有的:

 #include  #include  struct myStructure { int a; int b; }; void modifyMe1(struct myStructure *param1) { param1->a = 7; } void modifyMe2(struct myStructure **param1) { (*param1)->a = 7; } int main(int argc, char *argv[]) { struct myStructure *test1; test1 = malloc(sizeof(test1)); test1->a = 5; test1->b = 6; modifyMe1(test1); printf("a: %d, b: %d\n", test1->a, test1->b); // set it back to 5 test1->a = 5; printf("reset. a: %d, b: %d\n", test1->a, test1->b); modifyMe2(&test1); printf("a: %d, b: %d\n", test1->a, test1->b); free(test1); return 0; } 

我的输出是:

 $ ./a a: 7, b: 6 reset. a: 5, b: 6 a: 7, b: 6 

您可以在C中以不同的方式传递参数(Captain Obvious,yes)。

  1. 按价值。 然后将其复制到堆栈。 因此函数在函数框架中具有变量的局部副本。 对参数的任何更改都不会更改传递的值。 它就像“只读”模式

     void fooByValue(myStructure_t arg) { printf("passed by value %d %d\n", arg.a, arg.b); arg.a = 0; } 
  2. 通过指针。 然后传递此变量的地址副本(所以是的,它仍然通过值传递,但是传递地址的值,而不是整个参数的值)。 所以这就像“读写”模式。 由于可以通过其地址访问传递的变量,因此可以在函数外部更改此变量的值。

     void fooByPtr(myStructure_t *arg) { printf("passed by pointer %d %d\n", arg->a, arg->b); arg->a = 0; } 

    但! 你仍然无法修改指针。

  3. 因此,如果要修改指针,则应将指针传递给指针。 这就像“读写修改”模式:

     void fooByDblPtr(myStructure_t **arg) { *arg = (myStructure_t*) malloc(sizeof(myStructure_t)); (*arg)->a = 10; (*arg)->b = 20; } 

    如果那只是指针那么就会出现内存泄漏:

     void fooByDblPtr(myStructure_t *arg) { arg = (myStructure_t*) malloc(sizeof(myStructure_t)); (arg)->a = 10; (arg)->b = 20; } 

    因为在这里你为地址的本地副本分配新的地址,这个参数将在函数完成后被销毁。

    UPD。 例如,我们有

     void fooByPtr(myStructure_t *arg) { printf("addr inside foo before %p\n", arg); arg = (myStructure_t*) malloc(sizeof(myStructure_t)); (arg)->a = 10; (arg)->b = 20; printf("addr inside foo after %p\n", arg); } void main() { myStructure_t *x = NULL; x = malloc(sizeof(myStructure_t)); x->a = 10; x->b = 20; printf("x addr before = %p\n", x); fooByPtr(x); printf("x addr after = %p\n", x); free(x); } 

    分配内部函数内存并将指针分配给局部变量。 来电者仍然保持旧价值。 函数调用后我们丢失了内存地址,因此无法释放。

    简短的结论:有一个简单的规则 – 如果需要更改参数,则将指针传递给它。 所以,如果想要改变指针,则将指针传递给指针。 如果想要更改双指针,则将指针传递给指针指针。

  4. 通过指针传递参数也要快得多,因为您不需要复制堆栈上的所有值(当然,如果值大于指向此值的指针,否则通过指针传递给只读是没有意义的)。 但它很危险,因为它可以在function内部进行修改。 因此,您可以使用const关键字来保护定义它的参数

     void constFoo(const myStructure_t *arg) { arg->a = 10; //compilation error arg->b = 20; //compilation error } 

    当您使用第三方库时,这非常有用:函数签名告诉您函数是否可以修改您的参数。 尽管const是可选的,但每次可能时都要编写const关键字

  5. 传递数组。 通常,还会将数组大小(因此,size_t)作为参数发送。 您将数组作为指针传递。

     void foo (int *buf, size_t nbuf) { .... } 

    例如,有时您可以找到开发人员发送指向对象而不是数组的指针的代码

     void foo (int *buf, size_t size) { size_t i; for (i = 0; i < size; i++) { printf("%d ", buf[i]); } } int main(int argc, char **argv) { int a = 10; int buf[1] = { 10 }; foo(buf, 1); foo(&a, 1); } 

    在这种情况下,一个元素的数组和指向元素的指针的行为相似(但它们不相同)。

使用常规参数,例如int您将获得本地副本
使用指针参数,比如说int*你可以修改它指向的内容
使用双指针参数,比如说int**你可以修改指针本身,即“重新指定”它。

添加另一个function:

 void modifyMe0(struct myStructure param1) { param1.a = 7; } 

这会按值传递结构。 函数中的修改不会反映在传递给modifyMe0()的参数中。

像这样添加调用代码:

 printf("Before 0: a = %d, b = %d\n", test1->a, test1->b); modifyMe0(*test1); printf("After 0: a = %d, b = %d\n", test1->a, test1->b); 

请注意,调用代码中的before和after值是相同的。 您还可以向modifyMeN()函数添加打印,以certificate其中的值已被修改。

将指向结构的指针传递给被调用函数时,可以修改调用函数中结构的值。 当您通过值将结构传递给被调用的函数时,不会修改调用函数中的结构的值。

你可以创建另一个函数:

 void modifyMe3(struct myStructure **p1) { free(*p1); *p1 = malloc(sizeof(*p1)); (*p1)->a = -3; (*p1)->b = -6; } 

像这样添加调用代码:

 printf("Before 3: address = %p, a = %d, b = %d\n", (void *)test1, test1->a, test1->b); modifyMe0(*test1); printf("After 3: address = %p, a = %d, b = %d\n", (void *)test1, test1->a, test1->b); 

请注意,在调用modifyMe3()之后,结构的地址已更改。