我正在使用链接列表示例from junghans并尝试使其工作
一些服务器代码。在char数组中,我可以插入一个主机(来自inet_ntoa)
并更新它的年龄。所以我可以向守护进程发送一个数据包,但随后它崩溃了。
我尝试设置next_pointer=start_pointer;
,因为根据我的阅读,它会是
圆形清单。但是,收到第二个数据包后,strcpy崩溃..
问题:
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 second\n", 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
答案 0 :(得分:0)
您的代码存在的问题是您从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)分配字符串,则不需要释放它。特别是在该代码中,没有可用的字符串:
最后但并非最不重要:了解strcpy
。如果你只复制那里的IP,那么20个字符总是足够的,但是你第一次尝试复制更大的东西,你就会有缓冲区溢出并且可以覆盖其他一些数据。您应该使用strncpy
,其中n = 19,然后是next_pointer->name[19] = 0
以确保空终止。
您不需要在那里运行strstr()
两次。它要么返回NULL指针,要么可以运行
if (strstr( /* .. the arguments .. */ )) {
/* ... */
} else {
/* what if the call did return NULL. */
}