我正在使用junghans的链表示例,并尝试使其发挥作用带有一些服务器代码。在char数组中,我可以插入一个主机(从inet_ntoa)并更新它的年龄。所以我可以向守护进程发送一个数据包,但随后它崩溃了。我尝试设置next_pointer=start_pointer;
,因为从我读到的内容来看循环列表。然而,在接收到第二个数据包后,strcpy崩溃。。
问题:
- 如果next_pointer=start_pointer不起作用,我该如何指向开头
- 在覆盖char数组的一个成员之前,我需要释放吗
struct x {
char name[20];
int age;
struct x *next_rec;
};
struct x *start_pointer;
struct x *next_pointer; // starting hosts, will be overwritten
char *names[] = {
"127.0.0.1",
"evil666",
"192.168.56.101",
""
};
int ages[] = {0,20,30,0};
// some other code
while (1) {
sleep(1);
us=time(NULL);
printf("%ld, Sleep a secondn", us);
buf[0] = 0x0;
current_host = 0x0;
memset (buf,0,sizeof buf);
if(recvfrom(s, buf, BUFLEN, 0, (struct sockaddr*)&si_other, &slen)==-1)
diep("recvfrom()");
current_host = inet_ntoa(si_other.sin_addr);
if(!current_host)
diep("inet_ntoa()");
/* linked list initialization */
/* Initalise 'start_pointer' by reserving
* memory and pointing to it
*/
start_pointer=(struct x *) malloc (sizeof (struct x));
if(!start_pointer)
diep("start pointer on holiday");
/* Initalise 'next_pointer' to point
* to the same location.
*/
next_pointer=start_pointer;
/* Put some data into the reserved
* memory.
*/
strcpy(next_pointer->name,current_host);
next_pointer->age = ages[count];
/* Loop until all data has been read */
while ( ages[++count] != 0 )
{
/* Reserve more memory and point to it */
next_pointer->next_rec=(struct x *) malloc (sizeof (struct x));
if(!next_pointer)
diep("next pointer on holiday");
strcpy(next_pointer->name, names[count]);
next_pointer->age = ages[count];
}
next_pointer->next_rec=NULL;
next_pointer=start_pointer;
/* insert new record, update age */
while (next_pointer != NULL)
{
printf("%s ", next_pointer->name);
if(strstr(next_pointer->name,current_host)) {
printf("%d n", next_pointer->age+1);
}
if(!strstr(next_pointer->name,current_host)) {
printf("%d n", next_pointer->age);
}
next_pointer=next_pointer->next_rec;
}
next_pointer=start_pointer; // XXX
您的代码的问题是您在init循环中从链接的C文件中错过了这一部分:next_pointer=next_pointer->next_rec
。结果,在第一次迭代中,您分配了新的列表节点,但随后修改了第一个节点的内容。然后在随后的迭代中,您可以分配更多的节点,但仍然只修改第一个节点。
然后在循环之后立即终止列表,但由于在此期间没有更新next_pointer,所以列表现在只有一个节点。(你在那里泄露了一些内存,用下一次分配和NULL覆盖了地址,所以现在你无法释放它。)
关于您更具体的问题:
问题1:next_pointer只是一个遍历列表的辅助变量。如果您想要一个循环列表,您应该将某个next_rec指针设置为start_pointer。你可以这样做:
for (next_pointer = start_pointer;
next_pointer->next_rec != NULL; /* This is not the last node. */
next_pointer = next_pointer->next_rec /* Move to the next node. */)
;
/* At this moment next_pointer points to the last node of the list. */
next_pointer->next_rec = start_pointer; /* And a cycle is there. */
然而,您实际上可以在初始化循环时执行此操作。当您退出init循环时,next_pointer确实指向最后一个节点。因此,与其用next_pointer->next_rec=NULL
来终止列表,不如用next_pointer->next_rec = start_pointer
来进行循环。
UPDATE也就是说,如果您确实想要一个循环列表。因为实际上next_pointer=start_pointer
会使next_pointer指向开头。所以我假设您希望列表的末尾指向开头(正如您提到的循环列表)。
问题2:如果你没有显式(使用malloc)或隐式(例如使用strdup)分配字符串,你就不需要释放它
- next_pointer->name是结构中的一个数组。您将它与结构(列表节点)一起分配,它将与节点一起释放
- name是指向常量字符串的指针数组。它们没有被分配,而是将成为应用程序编译代码的数据部分的一部分,因此无法释放
最后但同样重要的是:注意strcpy
。如果你只在那里复制IP,20个字符就足够了,但当你第一次尝试复制更大的东西时,缓冲区溢出,可能会覆盖其他一些数据。您应该使用strncpy
,n=19,后面跟next_pointer->name[19] = 0
,以确保null终止。
并且您不需要在那里运行strstr()
两次。它要么返回NULL指针,要么不返回,所以您可以运行
if (strstr( /* .. the arguments .. */ )) {
/* ... */
} else {
/* what if the call did return NULL. */
}