C 返回本地指针
我的问题是这个问题的延伸: 我编写了以下代码来创建一个空列表:C 返回本地指针,c,pointers,C,Pointers,我的问题是这个问题的延伸: 我编写了以下代码来创建一个空列表: struct node* create_empty_list(void) { struct node *head = NULL; return head; } 我刚刚读到,返回局部变量的指针是无用的,因为当函数退出时,变量将被销毁。我相信上面的代码返回了一个NULL指针,所以我不认为它是指向局部变量的指针。 这里是分配给指针的内存。我没有在堆上分配任何内存,它应该作为一个自动变量在堆栈上。但是当代码退出(指向指针)
struct node* create_empty_list(void)
{
struct node *head = NULL;
return head;
}
我刚刚读到,返回局部变量的指针是无用的,因为当函数退出时,变量将被销毁。我相信上面的代码返回了一个NULL
指针,所以我不认为它是指向局部变量的指针。这里是分配给指针的内存。我没有在堆上分配任何内存,它应该作为一个自动变量在堆栈上。但是当代码退出(指向指针)时,如果我尝试在程序中使用它,通过给这个指针分配一些指针对象/取消引用等等,会发生什么呢
struct node* create_empty_list(void)
{
struct node *head = NULL;
return head;
}
相当于:
struct node* create_empty_list(void)
{
return NULL;
}
这很好
如果您有以下情况,就会出现问题:
struct node head;
return &head; // BAD, returning a pointer to an automatic object
在这里,您将返回一个局部变量的值,这是正常的:
struct node* create_empty_list()
{
struct node* head = NULL;
return head;
}
head
的值恰好是NULL
(0),在函数create\u empty\u list
返回之前,该值被复制到堆栈中。调用函数通常会将该值复制到其他变量中
例如:
void some_func()
{
struct node* some_var = create_empty_list();
...
}
在下面的每个示例中,您都将返回局部变量的地址,这是不正确的:
struct node* create_empty_list()
{
struct node head = ...;
return &head;
}
struct node** create_empty_list()
{
struct node* head = ...;
return &head;
}
返回head
的地址,该地址可能是每次调用函数create_empty_list
时的不同地址(取决于此时堆栈的状态)。此地址通常为4字节值或8字节值(取决于系统的地址空间),在函数返回之前复制到堆栈中。您可以“以您喜欢的任何方式”使用此值,但不应依赖于它表示有效变量的内存地址这一事实
关于变量的一些基本事实,对于您理解这些事实很重要:
- 每个变量都有一个地址和一个值
- 变量的地址是常量(即,在声明变量后它不能更改)
- 变量的值不是常量(除非明确声明它为
变量)const
- 使用单词指针时,意味着变量的值本身就是其他变量的地址。尽管如此,指针仍然有自己的地址(与它的值无关)
请注意,上面的描述不适用于数组。正如其他人所提到的,您正在返回值,这是完全正确的 但是,如果您已将主体功能更改为:
struct node head;
return &head;
您将返回地址(指针指向)局部变量,这可能是潜在的危险,因为它在堆栈上分配,并在离开函数体后立即释放
如果您将代码更改为:
struct node * head = (struct node *) malloc( sizeof( struct node ) );;
return head;
然后返回本地值的值,即指向堆分配内存的指针,该指针将保持有效,直到您对其调用free
。
这里是分配给指针的内存。我没有
在堆上分配任何内存,它应该作为一个
自动变量。但是当代码退出(到
指针),如果我试图在程序中使用它,则通过分配此指针
一些指向对象/取消引用等
struct node* create_empty_list(void)
{
struct node *head = NULL;
return head;
}
您的案例中没有为指针分配内存。有分配给的内存包含堆栈上的指针,但由于指针指向NULL
,因此它不指向任何可用内存。此外,您不必担心指针是否在堆栈上,因为返回它将创建指针的副本
(如其他人所提到的)当您在函数体中声明对象时,内存会隐式地分配到堆栈上。您可能知道(根据您的问题判断),内存是通过显式地请求在堆上分配的(在C中使用malloc
)
如果你试图取消对指针的引用,你将会得到一个分段错误。您可以分配给它,因为这只会覆盖NULL
值。为了确保没有出现分段错误,需要检查所使用的列表是否不是NULL
指针。例如,这里有一个附加函数:
struct node
{
int elem;
struct node* next;
};
struct node* append(struct node* list, int el) {
// save the head of the list, as we would be modifying the "list" var
struct node* res = list;
// create a single element (could be a separate function)
struct node* nn = (struct node*)malloc(sizeof(struct node));
nn->elem = el;
nn->next = NULL;
// if the given list is not empty
if (NULL != list) {
// find the end of the list
while (NULL != list->next) list = list->next;
// append the new element
list->next = nn;
} else {
// if the given list is empty, just return the new element
res = nn;
}
return res;
}
关键部分是
if(NULL!=list)
检查。如果没有它,您将尝试取消引用列表,从而导致分段错误。在这种情况下,用于存储指针的内存分配在堆栈上。您返回的是局部变量的值,这很好。您返回的是局部变量,而不是指向它的指针@flipper:是的,但是你不应该去引用它,当一个空指针被声明时,它是否真的存储了一个null
而不是内存地址,直到它被分配了一个指针对象?@flipper:我已经添加了一些关于变量的基本事实,这对你理解很重要…@barakmanos:While&x是一个l值,并非每个l值都是&x。看见换句话说,我不会说变量的地址是“aka l-value”。否则-很好的解释。@stanm:我同意,这不是一个很好的相关性,谢谢你指出这一点。答案相应更新(删除l-value和r-value两个词)。快速离题:我们应该在列表的前面或末尾添加新元素(如上所述)?我看不到定义此属性的链表,而且似乎每个人都可以随意添加。什么是标准的链表行为,如果有的话?你可以两者都做——它们是不同的操作。根据您需要列表的目的,您可以避免编写其中一个。