C:为啥指针地址在函数中设置为空,而在main中有一个非空地址? [复制]
Posted
技术标签:
【中文标题】C:为啥指针地址在函数中设置为空,而在main中有一个非空地址? [复制]【英文标题】:C: why does pointer address set to null in function, have a non-null address in main? [duplicate]C:为什么指针地址在函数中设置为空,而在main中有一个非空地址? [复制] 【发布时间】:2018-04-25 16:58:36 【问题描述】:我是 C 的初学者。我正在做一个创建树形数据结构的任务。我在main
中有一个if
,我在执行某些操作之前检查top
是否为NULL
。在我创建top pointer
的函数中,它在创建后立即返回NULL
(正如预期的那样,我还没有指出任何东西)。但是,当执行返回到main
时,它会返回一个地址,即使我没有为其分配一个地址。所以,我的 if 不像我想要的那样工作。
我注意到,如果我从 main
中将顶部设置为 NULL
,那么它会粘住。
我的问题是,当执行返回到main
时,为什么top
的地址会从NULL
变为其他地址?我是否在我的代码中做了一些事情,在不知不觉中将top
指向一个非预期的地址?
//struct for org chart tree
typedef struct org_tree
struct employee *top;
org_tree;
//create org tree
void CreateTree(org_tree *t)
org_tree *temp;
temp = malloc(sizeof(org_tree));
t = temp;
printf("%p\n", t->top); **here returns (nil) as expected
//main program
int main(void)
FILE *file;
file = fopen("employee.list", "r");
printf("file opened\n");
org_tree t;
CreateTree(&t);
printf("%p\n", t.top) **here returns a memory location
t.top = NULL **if I add this, then if below works.
char mgr[20], st1[20], st2[20], st3[20], st4[20];
while(fscanf(file, "%s %s %s %s %s\n", mgr, st1, st2, st3, st4)!=EOF)
employee *m;
if (t.top !=NULL) **this if not working like I want it to because NULL doesn't "stick" unless set in main.
///remaining code not relevant to question//
...
【问题讨论】:
难道你需要一个指向指针的指针?也许您只访问指针的本地副本......这意味着您可以获得它所指向的内容 - 但您将无法更改它所指向的内容?我不知道...CreateTree
未按预期设置org_tree t
。 C 中的所有内容都是按值传递的。这意味着org_tree t
的本地副本是在CreateTree
内部制作的。您分配了t = temp
,但是一旦CreateTree
返回,这两个都超出了范围,实际上会造成内存泄漏,因为没有任何东西指向您malloc
ed 的内存。如果要保存指向此malloc
ed 内存的指针,则必须从函数中返回它,或者传入org_tree**
类型并执行*t = temp;
@zappy t
的地址是一个指针。在该函数中,创建了一个新的t
,它采用传递的t
的值。与将 int 传递给函数完全相同。您在函数中对t
所做的任何更改都不会保留在函数之外。有 2 个不同的t
s,但它们指向同一个东西。所以要改变那个东西,你必须取消引用指针。 CreateTree
的任何地方都没有发生这种情况
t
按值传递给CreateTree()
。因此,调用者看不到t = temp
的分配。 t
是一个指针这一事实并没有改变它(传递的指针)是按值传递的事实。
但更重要的是,您甚至不需要CreateTree
函数!只要您执行org_tree t;
,您就会在自动存储中拥有一个org_tree
。无需尝试为它分配内存,实际上尝试甚至是逻辑谬误。如果你为另一个org_tree
分配内存,那就是另一个org_tree
,而不是原来的org_tree t
。 @zappy 没问题,但我什至不知道这是否回答了问题,呵呵,我只是跳到代码,这是我看到的第一个问题。此外,我现在正在打电话,不是一个理想的接听平台。
【参考方案1】:
问题是,当您将某些内容传递给函数时,会生成它的本地副本。现在您正在向函数发送地址。就是CreateTree()
中t
的内容。
被调用函数中的t
是什么? t
是 CreateTree
函数的局部变量。 t
的类型是 org_tree*
,这意味着它包含 org_tree
变量的地址。
您正在分配CreateTree()
中的一块内存。您正在将该块的地址分配给 t
变量。但这对被调用的函数没有任何意义。
一旦调用函数的被调用,局部变量
t
的生命就结束了。
所以回到main()
,它仍然是旧的t
变量。没有任何改变(它的价值)。
现在你想在这里实现什么?也许你想动态分配一个节点。
现在让我们看一个有效的例子:-
org_tree* t;
CreateTree(&t);
void CreateTree(org_tree **t)
org_tree *temp;
temp = malloc(sizeof(org_tree));
(*t) = temp;
现在你能说出被调用函数中t
的内容吗?
它仍然是一个指针变量,内容为地址。谁的地址?
指针变量的地址。更准确地说是org_tree*
变量的地址。
这是什么意思? (*t)
基本上是被调用函数的原始t
。这意味着我们现在可以改变它。这就是正在做的事情。
同样,如果我们在被调用函数中更改 t
的值,则该更改不会保留在 callee
函数中。只不过是本地副本而已。
没有**
,有没有一种简单的方法可以做到这一点?
有。
org_tree *t = CreateTree();
org_tree * CreateTree()
org_tree *temp;
temp = malloc(sizeof(org_tree));
return temp;
这是如何工作的? temp
不是局部变量吗?
是的。而在函数结束后temp
就没有生命了。但是它指向的内存地址不是这样的。该值被返回。当函数CreateTree
结束时,动态分配的内存仍然存在。并且当函数执行完成时,该内存不会被释放。
内存泄漏
现在看看这个例子(你写的第一个例子)。
void CreateTree(org_tree *t)
org_tree *temp;
temp = malloc(sizeof(org_tree));
t = temp;
printf("%p\n", t->top); **here returns (nil) as expected
在这里,您已将分配的块的内存地址分配给CreateTree()
中的局部变量t
。
CreateTree()
结束,现在无论如何您都可以访问分配的内存吗?
没有。
这就是为什么前面的函数被称为内存泄漏的原因。你调用这 10000 次你会泄漏大量的内存。
传递指针没什么特别的。它们的特别之处在于,我们引用了它包含的地址,并且对该地址中的内容进行了更改。我们开始认为这是指针魔术。这是一个副作用,您可以利用它来保持函数之间的值变化。
这里不要忘记检查malloc
的返回类型,并在完成处理后释放内存。
【讨论】:
非常有帮助,谢谢!!【参考方案2】:这是因为你在函数中的top变量是一个局部变量,它的作用域只在函数中有效,对其他函数不可用。
变量 top 被重新声明 n main 函数是一个单独的变量,与函数中的变量无关。它具有非空值,因为它包含垃圾值。
要解决您的问题,您可以将 top 变量设为 global 。 那就是在函数之前声明一次。 您不必在函数和主函数中再次声明它。现在这个变量将被所有函数共享。
【讨论】:
【参考方案3】:我想添加一个简单的测试用例来演示其他人的说法。
#include <stdio.h>
void foo(int *p)
printf("pointer: %ld\n", (long)&p);
printf("address: %ld\n", (long)p);
int main()
int i = 0;
int* p = &i;
printf("pointer: %ld\n", (long)&p);
printf("address: %ld\n", (long)p);
foo(p);
return 0;
结果:
pointer: 140736715097888
address: 140736715097884
pointer: 140736715097848
address: 140736715097884
上面的代码打印了一个指针的地址(它被分配的地方)和那个指针的内容(它指向的地址),你可以看到内容是一样的,但地址是不同的。这就是为什么如果你在f
中更改p
,这对外部p
没有影响。指针已被复制到f
的堆栈中。
【讨论】:
【参考方案4】:大多数 cmets 都发现了各种问题。请参阅我的 cmets 与您的原始代码内联,以及下面的重写示例(为简单起见,使用列表而不是树):
//struct for org chart tree
typedef struct org_tree
struct employee *top;
org_tree;
//create org tree
void CreateTree(org_tree *t)
org_tree *temp;
temp = malloc(sizeof(org_tree));
t = temp;
printf("%p\n", t->top); //here returns (nil) as expected
// Its not actually expected - the compiler happens to
// set it to null but it is actually undefined - you
// have not yet set t->top to any address (through malloc)
// or explicitly assigned it as NULL
//main program
int main(void)
FILE *file;
file = fopen("employee.list", "r");
printf("file opened\n");
org_tree t; //This line creates the tree
// as a locally allocated variable
// of type org_tree. The declaration
// allocates it though it is yet
// to be assigned.
//CreateTree(&t); //this is not required.
// It would be if your tree head
// was a pointer but it is not
printf("%p\n", t.top); //**here returns a memory location
// Through undefined behaviour. Even when you called
// CreateTree, you reassigned it (t=temp) which
// creates a memory hole (the original address
// assigned by declaration is lost)
t.top = NULL; //**if I add this, then if below works.
// Because it is now assigned.
char mgr[20], st1[20], st2[20], st3[20], st4[20];
while(fscanf(file, "%s %s %s %s %s\n", mgr, st1, st2, st3, st4)!=EOF)
employee *m;
if (t.top !=NULL) **this if not working like I want it to because NULL doesn't "stick" unless set in main.
///remaining code not relevant to question//
这是一个重写的版本,它通过示例解决了指针问题(尽管注意到它不是处理列表和树的最优雅的方式 - 但希望对解释有用)。显而易见的机会是将 CreateEmployee() 和 AddNodeToList() 结合起来:
#include <stdio.h>
#include <stdlib.h>
//There's many more elegant ways to do this, though this
//version extends your approach to provide understanding
// This is also simplified as a linked list, unsorted
// rather than a tree - again just to look at the pointer
// concepts;
// To use a tree a recursive walk is best to avoid complex
// procedural structures plus logic to determine left / right
// branches etc.
typedef struct employee
char mgr[20], st1[20], st2[20], st3[20], st4[20];
struct employee *next_node;
employee;
//struct for org chart list
typedef struct org_list
struct employee *top;
org_list;
//create org list
org_list *CreateList(void) // must return it
org_list *temp;
temp = malloc(sizeof(org_list));
//t = temp;
temp->top = NULL; //needs to be explicit
printf("%p\n", temp->top); //should be NULL
return temp; //send back the address in the heap
//create employee
employee *CreateEmployee(employee *emp)
emp = malloc(sizeof(employee));
emp->next_node = NULL;
return emp;
int AddNodeToList(org_list* list_head, employee* e)
if (list_head->top == NULL) //special case - empty list
list_head->top = e;
return 1; //all done
else
employee* temp_ptr; //temporary pointer to walk the list
temp_ptr = list_head->top;
while (temp_ptr->next_node != NULL)
temp_ptr = temp_ptr->next_node;
// temp_ptr now points to the last node in the list
// add the employee
temp_ptr->next_node = e;
return 1;
//main program
int main(void)
FILE *file;
file = fopen("employee.list", "r");
printf("file opened\n"); //not necessarily - check (file != NULL)
org_list *t; //This line creates _a pointer to_ the list
t = CreateList(); //you need an address to come back
// The other way is to use a pointer to a pointer but this
// gets confusing
printf("%p\n", t->top); // This is now NULL
// Note you haven't yet added an employee - just the head
//t.top = NULL; //already done
char mgr[20], st1[20], st2[20], st3[20], st4[20];
while (fscanf(file, "%s %s %s %s %s\n", mgr, st1, st2, st3, st4) != EOF)
employee *m; // this doesn't malloc or assign the underlying struct
// this does
m = CreateEmployee(m);
if (m != NULL)
// copy the scanned strings into m->mgr m->st1 etc using strncpy
// but m is still not added to the list yet!
AddNodeToList(t, m);
【讨论】:
非常有帮助,谢谢!以上是关于C:为啥指针地址在函数中设置为空,而在main中有一个非空地址? [复制]的主要内容,如果未能解决你的问题,请参考以下文章
作为 DataTable 的 gridView.DataSource 在 asp.net 中设置为空
在@NamedQuery (JPA QL 1.0) 中设置为空的参数
shared:ERROR: BINARYEN_ROOT 在 /root/.emscripten 中设置为空值
C语言基础:指针相关概念(指针的算术运算 指针数组指向指针的指针 传递指针给函数 从函数返回指针 )为啥C 语言不支持在调用函数时返回局部变量的地址?