遍历 C 指针列表:奇怪的 printf 行为
Posted
技术标签:
【中文标题】遍历 C 指针列表:奇怪的 printf 行为【英文标题】:Traversing C pointer list: weird printf behaviour 【发布时间】:2017-02-06 19:33:49 【问题描述】:我声明了一个用 C 实现的链表如下:
struct node_List
int i;
char * name;
struct node_List* next;
;
typedef struct node_List nodeList;
然后我将列表头全局声明为:
nodeList list; // head of the list - does not contain relevant data
最后,我有一个函数id(char * s)
,它的唯一参数是字符串s
。
nodeType id(char *s)
nodeType *p; // another List type
if ((p = malloc(sizeof(nodeType))) == NULL)
// error: out of memory;
nodeList * node = &list;
// printf(" ");
while (node->next != NULL)
node = node->next;
if (strcmp(node->name, s) == 0)
// printf(" ");
// assign node to an attribute in p
return p;
// error: not found;
问题是,当我运行这个程序并调用foo("somestring")
时,程序执行error: not found
部分并中止执行,尽管字符串somestring
在列表中。
我尝试通过插入一些printf()
来执行相同的程序以进行调试,它运行良好,除了它会在输出中打印额外的字符。
每次我添加一些打印行时都会发生这种情况,例如如果我取消注释我在上面的示例中编写的两个printf()
s(其中一个或两个,我得到相同的成功结果)。如果 printf 被调用时不带参数或使用空字符串 ""
,则它不起作用。
我不知道发生了什么,我仔细检查了列表创建和填充功能,我完全确定它们可以正常工作。我尝试更改 while
中断条件,但这也不起作用。我在 Linux(使用 gcc)和 Windows(使用 CodeBlocks 编辑器的集成编译器)上都观察到了类似的行为
printf 指令怎么会对程序产生如此大的影响?
编辑:此代码是用 Yacc 编写的语法分析器的一部分。整个代码可以在下面找到。读了很久,还没有完成,但是上面的代码已经过测试并且可以按照说明工作。
词法分析器:http://pastebin.com/1TEzzHie
解析器:http://pastebin.com/vwCtMhX4
【问题讨论】:
您似乎跳过了列表的第一个元素。在您使用的第一步中: node = node->next; 该行为可能还取决于您将list
初始化为什么,以及您如何构造链表的其余部分。你没有表现出来
我们无法调试部分代码。请提供minimal reproducible example。
只是澄清一下-您是说:使用发布的代码找不到字符串,但是如果未注释两个printf,则找到字符串?
您很可能遇到了内存损坏问题。或者严重误入歧途的事情。使用带有Valgrind 的机器并运行它。听起来,您已经完成了足够多的工作,知道这很奇怪,并且您当前尝试调试它的方式不起作用。因此,您需要进入不同的调试模式。使代码可测试;单独测试它。确保使用 Valgrind 隔离它是干净的。然后考虑将其重新插入到更复杂的场景中。未定义的行为包括“大部分情况下似乎可以工作”的选项。
【参考方案1】:
查看提供的源代码时,探索链表的算法有两种方法在while循环比较中错过节点。
方式 1 - 仅从列表的第二个节点开始。
在比较之前放置node = node->next;
将强制第一个比较为&(list)->next
而不是&(list)
。
要从第一个节点开始,只需将
node = node->next;
放在后面 比较。
方式 2 - 永远不会结束到列表的最后一个节点。
在while条件中使用(node->next != NULL)
会在比较最后一个节点之前强制退出循环=>node->next = NULL;
。
要以最后一个节点结束,只需将 while 条件更改为
(node != NULL)
。
解决方案:
while (node != NULL) // end from the last node
if (strcmp(node->name, s) == 0)
// printf(" ");
// assign node to an attribute in p
return p;
node = node->next; // explore link after comparison
【讨论】:
正如评论和声明中所写,list
是一个空头,其值应被忽略。通过将node = node->next;
放在while
之后,我获得了与您描述的相同的结果。这里的问题是,为什么单个非空 printf 语句会如此大地改变代码的行为,@JonathanLeffler 可能在上面的 cmets 中以最好的方式回答了这个问题【参考方案2】:
实际的错误是函数返回的变量的类型声明错误:
nodeType* createPoint(char* l)
nodeList* p;
if((p=malloc(sizeof(nodeList))) == NULL)
yyerror("out of memory");
else
// do stuff with p
return p;
函数返回值为nodeType*
,p 被实例化为nodeList*
。
这两种类型的声明非常简单,这就是程序可以运行的原因。
工作代码可以在here找到。
printf()
的奇怪行为可能是由 printf 的参数所需的堆空间引起的:因为这个函数接受任意数量的参数,所以它将它们保存在一个列表中。这个列表在堆中被实例化,在那里覆盖了由于 createPoint
的错误实现留下的旧数据。
【讨论】:
以上是关于遍历 C 指针列表:奇怪的 printf 行为的主要内容,如果未能解决你的问题,请参考以下文章