我正在用'c'写单链列表的程序,有两种方式(它们在结构内存分配方式上有所不同)。
1.
struct SingleLinkedList
{
int data;
struct SingleLinkedList* next;
};
typedef struct SingleLinkedList sll;
sll* createNode()
{
sll* node = (sll*) malloc(sizeof(sll));
node -> next = NULL;
return node;
}
2.
struct SingleLinkedList
{
int data;
struct SingleLinkedList* next;
};
typedef struct SingleLinkedList sll;
sll createNode()
{
sll node;
node.next = NULL;
return node;
}
我想知道第二种程序的写法是否正确? 如果不正确,为什么会这样? 如果正确,为什么我在网上找不到这样的程序?
你的第一个程序返回一个 指针 到(分配的)结构中。的调用者 createNode
现在的责任是 free()
在指针超出作用域之前,它的内存就已经存在了,而节点存在的好处是,直到它被删除为止。free()
d.
你的第二个程序在没有 "分配 "内存的情况下,按值返回一个结构体。事实上,这个结构体 node
内创建的 createNode
函数返回时,函数不再存在;函数的调用者得到的是一个 拷贝 的(局部)结构。(尽管大多数编译器会将其优化为零。)
你不常看到第二种类型,因为。
1) 它实际上是一个多余的函数调用; 而不是... ...
ssl node = createNode();
...只是调用...
ssl node = { 0, NULL };
2)那个节点又只会存在到当前作用域结束。如果你在函数中建立一个这样的链接列表。initList()
,例如,一旦出现了 initList()
返回的所有节点都将离开范围,而你的指针将什么都不指向。好吧,无论如何,不是分配的节点结构。 ;-) 如果你在循环中初始化这些节点,每个节点都会在循环迭代结束时退出作用域......所有这些都很可能不是你想要的。 ;-)
首先,这两个函数都应该用一个参数来声明,这个参数的值是用来初始化数据成员的。data
的节点。
例如,可以用以下方式定义函数
sll * createNode( int data )
{
sll *node = malloc( sizeof( sll ) );
if ( node != NULL )
{
node->next = NULL;
node->data = data;
}
return node;
}
和
sll createNode( int data )
{
sll node = { data, NULL };
return node;
}
这两段代码都是正确的。你的程序是否会不正确,取决于你是否会正确使用这些函数。
例如第二个函数可以在下面的上下文中使用(将一个新节点追加到列表的尾部)。
int append( sll **head, int data )
{
while ( *head != NULL )
{
head = &( *head )->next;
}
*head = malloc( sizeof( sll ) );
if ( *head ) **head = createNode( data );
return *head != NULL;
}
第二段代码的唯一缺点是,在大多数情况下,最好将节点分配和初始化结合在一个函数中,而不是像第二段代码那样将这两个操作分开。
然而,有时将初始化本身放在一个单独的函数中是有意义的,因为初始化可以是足够复杂的。这将使整个代码更具可读性。
顺便说一下,在C++中,由于使用构造函数,正是这样做的。也就是由构造函数(独立函数)来负责初始化。
所以你可以考虑这个函数
sll createNode( int data )
{
sll node = { data, NULL };
return node;
}
类型的对象的构造函数。sll
.
所以,从介绍的两个代码片段中,函数的目的是不同的。第一个函数是动态分配一个节点。而第二个函数是用来初始化一个已经存在的节点。