C语言 指针作为 C 中的函数参数

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/18698317/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 07:24:21  来源:igfitidea点击:

Pointers as function arguments in C

cpointersparametersparameter-passing

提问by sherrellbc

If I were to have this code, for example:

如果我有这个代码,例如:

int num = 5;
int *ptr = #

What is the difference between the following two functions?

以下两个函数有什么区别?

void func(int **foo);
void func(int *foo); 

Where I call the function:

我在哪里调用函数:

func(&ptr); 


I realize that the former of the two takes a pointer to a pointer as a parameter, while the second takes only a pointer.

我意识到两者中的前者将指向指针的指针作为参数,而第二个仅接受一个指针。

If I pass in func(&ptr), I am effectively passing in a pointer. What difference does it make that the pointer points to another pointer?

如果我传入func(&ptr),我实际上是传入了一个指针。指针指向另一个指针有什么区别?

I believe the latter will give an incompatibility warning, but it seems that the details do not matter so long as you know what you are doing. It seems that perhaps for the sake of readability and understanding the former is a better option (2-star pointer), but from a logical standpoint, what is the difference?

我相信后者会给出不兼容警告,但似乎细节并不重要,只要你知道自己在做什么。看起来也许是为了可读性和理解前者是更好的选择(2星指针),但从逻辑的角度来看,有什么区别?

回答by atk

A reasonable rule of thumb is that you can't exactly change the exact thing that is passed is such a way that the caller sees the change. Passing pointers is the workaround.

一个合理的经验法则是,您不能完全更改传递的确切内容,因为调用者会看到更改。传递指针是解决方法。

Pass By Value: void fcn(int foo)

按值传递: void fcn(int foo)

When passing by value, you get a copy of the value. If you change the value in your function, the caller still sees the original value regardless of your changes.

按值传递时,您将获得该值的副本。如果您更改函数中的值,无论您如何更改,调用者仍会看到原始值。

Pass By Pointer to Value: void fcn(int* foo)

通过指向值的指针: void fcn(int* foo)

Passing by pointer gives you a copy of the pointer - it points to the same memory location as the original. This memory location is where the original is stored. This lets you change the pointed-to value. However, you can't change the actual pointer to the data since you only received a copy of the pointer.

通过指针传递给你一个指针的副本——它指向与原始指针相同的内存位置。该内存位置是存储原件的位置。这使您可以更改指向的值。但是,您无法更改指向数据的实际指针,因为您只收到了该指针的副本。

Pass Pointer to Pointer to Value: void fcn(int** foo)

将指针传递给值的指针: void fcn(int** foo)

You get around the above by passing a pointer to a pointer to a value. As above, you can change the value so that the caller will see the change because it's the same memory location as the caller code is using. For the same reason, you can change the pointer to the value. This lets you do such things as allocate memory within the function and return it; &arg2 = calloc(len);. You still can't change the pointer to the pointer, since that's the thing you recieve a copy of.

您可以通过将指针传递给指向值的指针来解决上述问题。如上所述,您可以更改该值,以便调用者看到更改,因为它与调用者代码使用的内存位置相同。出于同样的原因,您可以更改指向该值的指针。这让你可以在函数内分配内存并返回它;&arg2 = calloc(len);. 您仍然无法更改指向指针的指针,因为这是您收到的副本。

回答by dhein

The difference is simply said in the operations the processor will handle the code with. the value itself is just a adress in both cases, thats true. But as the address gets dereferenced, it's important for the processor and so also for the compiler, to know after dereferencing, what it will be handling with.

区别在于处理器将处理代码的操作。在这两种情况下,值本身只是一个地址,这是真的。但是当地址被取消引用时,对于处理器和编译器来说很重要,在取消引用后知道它将处理什么。

回答by glglgl

If I were to have this code, for example:

int num = 5;
int *ptr = #

What is the difference between the following two functions?:

void func(int **foo);
void func(int *foo);

如果我有这个代码,例如:

int num = 5;
int *ptr = #

以下两个函数有什么区别?:

void func(int **foo);
void func(int *foo);

The first one wants a pointer to a pointer to an int, the second one wants a pointer which directly points to an int.

第一个想要一个指向 int 的指针的指针,第二个想要一个直接指向 int 的指针。

Where I call the function:

func(&ptr);

我在哪里调用函数:

func(&ptr);

As ptris a pointer to an int, &ptris an address, compatible with a int **.

Asptr是指向 int 的指针,&ptr是地址,与int **.

The function taking a int *will do somethin different as with int **. The result of the conversation will be completely different, leading to undefined behaviour, maybe causing a crash.

使用 a 的函数int *会做一些与int **. 对话的结果将完全不同,导致未定义的行为,可能导致崩溃。

If I pass in func(&ptr) I am effectively passing in a pointer. What difference does it make that the pointer points to another pointer?

如果我传入 func(&ptr) 我实际上是在传入一个指针。指针指向另一个指针有什么区别?

               +++++++++++++++++++
adr1 (ptr):    +  adr2           +
               +++++++++++++++++++

               +++++++++++++++++++
adr2 (num):    +  42             +
               +++++++++++++++++++

At adr2, we have an int value, 42.

adr2,我们有一个 int 值,42。

At adr1, we have the address adr2, having the size of a pointer.

adr1,我们有地址adr2,具有指针大小。

&ptrgives us adr1, ptr, holds the value of &num, which is adr2.

&ptr给我们 adr1, ptr, 持有 的值&num,即 adr2。

If I use adr1as an int *, adr2will be mis-treated as an integer, leading to a (possibly quite big) number.

如果我adr1用作int *,adr2将被误认为是一个整数,从而导致一个(可能非常大)的数字。

If I use adr2as an int **, the first dereference leads to 42, which will be mis-interpreted as an address and possibly make the program crash.

如果我使用adr2as an int **,则第一个取消引用会导致 42,这将被错误解释为地址并可能使程序崩溃。

It is more than just optics to have a difference between int *and int **.

int *和之间的区别不仅仅是光学int **

I believe the latter will give an incompatibility warning,

我相信后者会给出不兼容警告,

... which has a meaning ...

......有一个意思......

but it seems that the details do not matter so long as you know what you are doing.

但似乎细节并不重要,只要你知道自己在做什么。

Do you?

你?

It seems that perhaps for the sake of readability and understanding the former is a better option (2-star pointer), but from a logical standpoint, what is the difference?

看起来也许是为了可读性和理解前者是更好的选择(2星指针),但从逻辑的角度来看,有什么区别?

It depends on what the function does with the pointer.

这取决于函数对指针的作用。

回答by Crowman

There are two main practical differences:

有两个主要的实际差异:

  1. Passing a pointer to a pointer allows the function to modify the contents of that pointer in a way that the caller can see. A classic example is the second argument to strtol(). Following a call to strtol(), the contents of that pointer should point to the first character in the string that was not parsed to compute the longvalue. If you just passed the pointer to strtol(), then any changes it made would be local, and it would be impossible to inform the caller what the location was. By passing the address of that pointer, strtol()can modify it in a way that the caller can see. It's just like passing the address of any other variable.

  2. More fundamentally, the compiler needs to know the type that is being pointed to in order to dereference. For instance, when dereferencing a double *, the compiler will interpret (on an implementation where doubleconsumes 8 bytes) the 8 bytes starting at the memory location as the value of the double. But, on a 32 bit implementation, when dereferencing a double **, the compiler will interpret the 4 bytes starting at that location as the address of another double. When dereferencing a pointer, the type being pointed to is the only information the compiler has about how to interpret the data at that address, so knowing the exact type is critical, and this is why it would be an error to think "they're all just pointers, so what's the difference"?

  1. 将指针传递给指针允许函数以调用者可以看到的方式修改该指针的内容。一个经典的例子是 的第二个参数strtol()。在调用 之后strtol(),该指针的内容应指向字符串中未解析以计算long值的第一个字符。如果您只是将指针传递给strtol(),那么它所做的任何更改都将是本地的,并且不可能通知调用者该位置是什么。通过传递该指针的地址,strtol()可以以调用者可以看到的方式修改它。这就像传递任何其他变量的地址一样。

  2. 更根本的是,编译器需要知道所指向的类型才能取消引用。例如,当取消引用 a 时double *,编译器会将(在double消耗 8 个字节的实现上)从内存位置开始的 8 个字节解释为 double 的值。但是,在 32 位实现中,当取消引用 a 时double **,编译器会将从该位置开始的 4 个字节解释为另一个双精度值的地址。取消引用指针时,所指向的类型是编译器关于如何解释该地址处数据的唯一信息,因此知道确切的类型至关重要,这就是为什么认为“它们是所有只是指针,所以有什么区别”?

回答by delicateLatticeworkFever

Generally the difference indicates that the function will be assigning to the pointer, and that this assignment should not just be local to the function. For example (and keep in mind these examples are for the purpose of examining the nature of fooand not complete functions, any more than the code in your original post is supposed to be real working code):

通常,差异表明函数将分配给指针,并且此分配不应该只是函数的局部。例如(请记住,这些示例是为了检查 foo 的性质而不是完整的函数,就像您原始帖子中的代码应该是真正的工作代码一样):

void func1 (int *foo) {
    foo = malloc (sizeof (int));
}

int a = 5;
func1 (&a);

Is similar to

类似于

void func2 (int foo) {
    foo = 12;
}

int b = 5;
func2 (b);

In the sense that foomay equal 12 in func2(), but when func2() returns, bwill still equal 5. In func1(), foopoints to a new int, but ais still awhen func1() returns.

foofunc2()中可能等于 12,但当 func2() 返回时,b仍将等于 5。在 func1() 中,foo指向一个新的 int,但在 func1() 返回时a仍然是a

What if we wanted to change the value of aor b? WRT b, a normal int:

如果我们想改变aor的值b怎么办?WRT b,一个普通的 int:

void func3 (int *foo) {
    *foo = 12;
}    

int b = 5;
func2 (&b);

Will work -- notice we needed a pointer to an int. To change the value ina pointer (ie. the address of the int it points to, and not just the value in the int it points to):

会起作用——注意我们需要一个指向 int 的指针。要改变值指针(即它指向INT的地址,而不是仅仅在INT值它指向的。):

void func4 (int **foo) {
    *foo = malloc (sizeof (int));
}

int *a;
foo (&a);

'a' now points to the memory returned by malloc in func4(). The address &ais the address of a, a pointer to an int. An int pointer contains the address of an int. func4()takes the address of an int pointer so that it can put the address of an int into this address, just as func3() takes the address of an int so that it can put a new int value into it.

'a' 现在指向 func4() 中 malloc 返回的内存。地址&a是 的地址a,一个指向 int指针。int 指针包含一个 int 的地址。 func4()获取 int 指针的地址,以便将 int 的地址放入该地址中,就像 func3() 获取 int 的地址以便将新的 int 值放入其中一样。

That's how the different argument styles are used.

这就是使用不同参数样式的方式。

回答by adi

It has been a while since this has been asked, but here is my take on this. I am now trying to learn C and pointers are endlessly confusing... So I am taking this time to clarify pointers on pointers, for me at least. Here is how I think about it.
I have taken an example from here:

自从被问到这个问题已经有一段时间了,但这是我对此的看法。我现在正在尝试学习 C 并且指针无休止地令人困惑......所以我正在花时间澄清指针上的指针,至少对我来说。这是我的想法。
我从这里举了一个例子:

#include <stdlib.h>
#include <string.h>

int allocstr(int len, char **retptr)
{
    char *p = malloc(len + 1);  /* +1 for 
typedef struct {
    char *data;    /* item data */
    struct item *next;    /* point to successor */
} item;
*/ if(p == NULL) return 0; *retptr = p; return 1; } int main() { char *string = "Hello, world!"; char *copystr; if(allocstr(strlen(string), &copystr)) strcpy(copystr, string); else fprintf(stderr, "out of memory\n"); return 0; }

I was wondering why allocstrneeds a double pointer. If it is a pointer it means that you can pass it and it will be changed after return...
If you do this example, it works fine. But if you change the allocstrto have just *pointer instead of **pointer (and copystrinstead of &copystrin main) you get segmentation fault. Why? I put some printfs in the code and it works fine until the line with strcpy. So I am guessing that it did not allocate memory for copystr. Again, why?
Let's go back to what it means to pass by pointer. It means you pass the memory location and you can write directly there the value you want. You can modify the value because you have access to the memory location of your value.
Similarly, when you pass a pointer to a pointer, you pass the memory location of your pointer - in other words, the memory location of your memory location. And now (pointer to pointer) you can change the memory location as you could change the value when you were using just a pointer.
The reason the code works is that you pass the address of a memory location. The function allocstrchanges the size of that memory location so it can hold "Hello world!" and it returns a pointer to that memory location.
It's really the same as passing a pointer but instead of a value we have a memory location.

我想知道为什么allocstr需要一个双指针。如果它是一个指针,则意味着您可以传递它并且返回后它将被更改...
如果您执行此示例,则可以正常工作。但是,如果您将allocstr更改为只有 *pointer 而不是 **pointer (并且在 main 中使用copystr而不是©str),则会出现分段错误。为什么?我在代码中加入了一些 printfs,它工作正常,直到strcpy行。所以我猜测它没有为copystr分配内存。再说一遍,为什么?
让我们回到通过指针传递的含义。这意味着您传递了内存位置,您可以直接在那里写入您想要的值。您可以修改该值,因为您可以访问您的值的内存位置。
类似地,当您将指针传递给指针时,您传递的是指针的内存位置——换言之,您的内存位置的内存位置。现在(指向指针的指针)您可以更改内存位置,因为您可以在仅使用指针时更改值。
代码起作用的原因是您传递了内存位置的地址。函数allocstr更改该内存位置的大小,以便它可以容纳“Hello world!” 它返回一个指向该内存位置的指针。
这实际上与传递指针相同,但我们有一个内存位置而不是值。

回答by Mouhammed Elshaaer

When using linked structures in C, for example, a simple linked list. Consider you have some items in your list and you want to add a new item, one way, easiest let's say, is to insert at the beginning when items order does not matter. So, here is our simple item structure,

在 C 中使用链接结构时,例如,一个简单的链表。考虑到您的列表中有一些项目并且您想要添加一个新项目,一种最简单的方法是在项目顺序无关紧要时插入到开头。所以,这是我们简单的项目结构,

void insert_item( item **head, char *data) {

    item *new_item;    /* temporary pointer */

    new_item = malloc( sizeof(item) );
    new_item->data = data;

    /* This is how we would set the next item if the parameter was item* head */
    //new_item->next = head;

    /* till this line, nothing useful for passing item** head */
    new_item->next = *head;

    /*
     * Here is where we needed to update the head to point to the newly inserted item at
     * the beginning. which wouldn't be possible if the head parameter was item* head.
     */
    *head = new_item;
}

And to insert at the beginning,

并在开头插入,

item *head;
head = malloc( sizeof(item) );
head->data = "head item data";

printf("before inserting: %s \n", head->data); //before inserting: head item data

insert_item(&head, "new item data");

printf("after inserting: %s \n", head->data); //after inserting: new item data

You can test this like so,

你可以这样测试,

##代码##