我有这样的结构
struct node
{
int data;
struct node* next;
};
我用它来创建单链表。
我创建了其他功能,如
int push(struct node* head,int element);
将数据推送到使用节点结构创建的堆栈。
然后该函数尝试使用代码更新传递给它的struct node* head
(它还做其他事情)
head=(struct node*)malloc(sizeof(struct node));
这样的电话是
struct node* stack;
push(stack,number);
看起来这段代码创建了传递给它的指针的副本。所以我不得不将功能改为
int push(struct node** head,int element)
和
*head=(struct node*)malloc(sizeof(struct node));
这样的电话是
struct node* stack;
push(&stack,number);
所以我的问题是,早期的功能是做什么的?如果我想更新指针的原始值或者我的方法是错误的,是否有必要将struct node**
传递给函数?
抱歉,我无法提供完整的代码,因为这是一项任务。
答案 0 :(得分:6)
C总是按值传递。要更改传递给函数的变量,而不是传递变量本身,请传递引用(其地址)。
假设您使用旧签名
调用您的函数int push(struct node* head,int element);
struct node *actual_head = NULL;
push(actual_head, 3);
现在,在调用push之前,您的变量actual_head
的值为NULL
。
在push函数内部,新变量head
将被推送到堆栈。它将与传递给它的值相同,即NULL
。
然后当您致电head = malloc(...)
时,您的变量head
将获得一个新值,而不是您想要的actual_head
。
要缓解上述情况,您必须将功能的签名更改为
int push(struct node** head,int element);
struct node *actual_head = NULL
push(&actual_head, 3);
现在,如果您仔细注意,actual_head
的值为NULL
,但此指针也存储在某处,某处是其地址&actual_head
。我们将此地址视为1234
。
现在在push函数内部,你的变量head
可以保存指针的地址(注意两个*),其值为1234
现在当你执行*head = malloc(...)
时,实际上是在更改位置1234处的对象值,即actual_head
对象。
答案 1 :(得分:4)
C 始终按值传递参数(即通过复制它)。这甚至适用于指针,但在这种情况下,它是指针本身被复制。大多数情况下你使用指针,这很好,因为你有兴趣操纵指针指向的数据。但是,在您的情况下,您希望修改指针本身,因此您确实必须使用指向指针的指针。
答案 2 :(得分:2)
代码没有按照您的想法进行,但不是因为它创建了节点的副本,而是创建了指针的副本。
尝试打印
fprintf(stdout, "Address of head: %p\n", (void *) head);
两者,在push()
内和调用者函数中。
你传入的指针和参数在内存中有不同的地址,虽然它们都指向同一个地址,但在funcion返回后,malloc()
的结果不存在。
你需要像这样传递指针指针
int push(struct node **head, int element)
{
/* Ideally, check if `head' is `NULL' and find the tail otherwise */
*head = malloc(sizeof(**head));
if (*node == NULL)
return SOME_ERROR_VALUE;
/* Do the rest here */
return SOME_SUCCESS_VALUE_LIKE_0;
}
要打电话,只需
struct node *head;
head = NULL;
push(&head, value);
/* ^ take the address of head and pass a pointer with it */
当然,push()
实施应该是非常不同的,但我认为你会明白这一点。
答案 3 :(得分:2)
是。
程序的第一个版本是按值传递指针。虽然它传递了一个地址(由pointer to struct
持有)但它没有通过pointer's address
- 更新值所必需的。
每当您想更新变量的值时,您必须传递变量的地址。要传递指针地址,您需要一个参数pointer to pointer to type
。
在您的情况下,pointer to pointer to struct node
。
答案 4 :(得分:1)
每个人所说的一切都是完全正确的。但是,我认为你也应该考虑设计。您的部分问题是您正在将堆栈本身与在其上存储数据所需的内部结构混淆。您应该有一个堆栈对象和一个节点对象。即。
struct Node
{
int data;
struct Node* next;
}
struct Stack
{
struct Node* head;
}
然后,您的推送功能可以在没有任何双重间接的情况下获取指向堆栈的指针。此外,没有将某些东西推到堆叠中间的节点上的危险。
void push(struct Stack* stack, int value)
{
struct Node* node = malloc(sizeof node);
node->data = value;
node->next = stack->head;
stack->head = node;
}
答案 5 :(得分:0)
功能
int push(struct node* head,int element) {
head=(struct node*)malloc(sizeof(struct node));
}
分配一些内存并将其丢弃(导致内存泄漏)。
将“指向结构的指针”传递给函数会创建它的本地副本。
如果要更新指针的原始值,则必须将struct node**
传递给函数。 (使用全局变量通常被认为是一个坏主意)
答案 6 :(得分:0)
将stack
传递给您的函数push(struct node* head,int element)
并做
head=(struct node*)malloc(sizeof(struct node));
指针head
将更新为由malloc()
分配的内存,而stack
在您刚刚传递值时不知道此内存。(这里未初始化)
当您传递地址时,您有一个指向指针的指针,该指针使push()
内的更改反映在stack
上
答案 7 :(得分:0)
所以我的问题是,早期的功能在做什么?
您的早期函数被定义为接收指向对象的指针。您向函数传递了一个未初始化的struct node指针。函数不能对表示未初始化指针的值执行任何操作。所以你的函数被传递了垃圾,但是没有造成任何伤害,因为你的函数会立即通过用指向已分配内存的指针来忽略它。您的函数现在没有使用您传递给除临时本地存储之外的任何值。从函数返回后,函数的参数将被丢弃(它们只是副本),并且堆栈变量的值与以前一样,仍然未初始化。编译器通常会在初始化之前警告您使用变量。
顺便说一下,函数返回时,指向已分配内存的指针值也被抛弃/丢失。所以现在内存中没有引用的位置,因此没有办法释放它,即你有内存泄漏。
如果我想更新指针的原始值或者我的方法错了,是否有必要将struct node **传递给函数?
是的,有必要传递要调用的函数填充的变量的地址。必须编写它以接受指向它将提供的数据类型的指针。由于您使用指针引用对象,并且由于您的函数正在生成指向对象的指针,因此必须将指针传递给指向对象的指针。
或者,您可以将指针作为函数的值返回,例如
struct node * Function() { return (struct node *)malloc(sizeof(struct node)); }
电话会是......
struct node *stack;
stack = Function();
if(stack == NULL) { /* handle failure */ }
因此,您的方法没有错,只需要您的实施(和理解)工作。