我是C的初学者。我正在创建一个树数据结构的作业。我在if
中有一个main
,在执行某些操作之前,我会检查top
是否为NULL
。在我创建top pointer
的函数中,它在创建后立即返回NULL
(正如预期的那样,我还没有将它指向任何东西)。但是,当执行回到main
时,它会返回一个地址,即使我还没有分配地址。所以,我的工作并不像我想要的那样。
我注意到,如果我在NULL
内设置为main
,那么它就会坚持下去。
我的问题是,为什么top
的地址在执行返回NULL
时会从main
变为其他内容?我是否在代码中做了一些事情,在不知不觉中将点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//
}
}
...
答案 0 :(得分:2)
事情就是当你将某些东西传递给一个函数时,就会制作一个本地副本。现在您要向该函数发送一个地址。它是t
中CreateTree()
的内容。
被叫函数中的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
}
您已将此分配的块内存地址分配给t
中的本地变量CreateTree()
。
CreateTree()
结束,现在你可以访问分配的内存吗?
不。
这就是为什么之前的功能据说是泄漏内存的原因。你调用这些10000次就会泄漏大量的内存。
传递指针没什么特别的。使它们与众不同的是,我们引用它包含的地址,并对该地址中的内容进行更改。我们开始认为它是指针魔术。这是一种副作用,您可以利用它来持续改变函数之间的值。
此外,请不要忘记检查malloc
的返回类型,并在完成操作后释放内存。
答案 1 :(得分:0)
It's because your top variable in the function is a local variable and it's scope is only valid in the function and is not available to other functions.
The variable top is redeclared n main function is a individual variable and is not related to the variable in the function. It has non null value cause it contains a garbage value.
To fix your problem you can make the top variable global . That is declare it once at before the functions . You can't have to declare it again in function and main function. Now this variable will shared by all the functions.
答案 2 :(得分:0)
I would like to add a simple test case to demonstrate what others say.
#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;
}
Results:
pointer: 140736715097888
address: 140736715097884
pointer: 140736715097848
address: 140736715097884
Above code prints the address of a pointer (where it is allocated) and contents of that pointer (the address where it points to) as you can see the content is the same but the addresses are different. That's why if you change p
inside f
that will have no effect on the outsider p
. The pointer has been copied into f
's stack.
答案 3 :(得分:0)
大多数评论都提到了各种问题。查看我的评论内联您的原始代码,以及下面的重写示例(为简单起见,使用列表而不是树):
//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);
}
}