在构建一个小型Scheme解释器的早期阶段,我是C的新手。对于项目的这一部分,我正在尝试构建一个简单的cons单元数据结构。它应该采用像
这样的列表 (a b c)
并在内部表示如此:
[ ][ ] -> [ ][ ] -> [ ][/]
| | |
A B C
为了测试它是否正常工作,我有一个打印功能来回显输入。以下是无效的代码:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include "lexer.h"
#include "parse.h"
char token[20];
struct conscell {
char *data;
struct conscell *first, *rest;
};
void S_Expression ()
{
/* function from lexer to receive input a split into tokens no greater than 20 */
startTokens(20);
/* gets the next token */
strcpy(token, getToken());
/* List is a typedef for the struct conscell */
List tree = createList ();
tree = nextNode (tree);
printList(tree);
}
List createList ()
{
List node = malloc(sizeof (List));
if (node == NULL) {
printf("Out of memory!\n");
exit(1);
}
node->data = NULL;
node->first = NULL;
node->rest = NULL;
return node;
}
/* Recursive function to build cons cell structure */
List nextNode (List node)
{
node = createList ();
if (token[0] == '(')
{
strcpy(token, getToken());
node->first = nextNode(node->first);
node->rest = nextNode(node->rest);
}
else
{
if (token[0] == ')')
{
node = NULL;
}
else
{
List temp = createList();
temp->data = token;
temp->first = NULL;
temp->rest = NULL;
node->first = temp;
strcpy(token, getToken());
node->rest = nextNode(node->rest);
}
}
return node;
}
/* Prints output. So far, just trying to print symbols */
void printList(List node)
{
if (node != NULL)
{
if (node->data != NULL)
{
printf("%s", node->data);
}
}
}
到目前为止还无法打印出任何内容。我几乎肯定它的指针问题。如果有人能指出我(没有双关语意)朝着正确的方向发展,那将非常感激。
谢谢
答案 0 :(得分:2)
首先,我假设List
是struct conscell*
的typedef。如果不是,它应该是,否则你的代码将在没有大量警告的情况下编译。
方案缺点单元格应该是一个简单的单链表,而不是双向链表。所以你的个体细胞应该更像:
typedef conscell
{
unsigned char *data; //<== use unsigned char for a memory buffer
struct conscell* next; //<== only a "next" pointer needed
} conscell;
我看到你现在只是试图打印符号,所以使用char
而不是unsigned char
可以用于此目的,但是当你使用更通用的数据结构如lambdas时,等等,你将不得不切换到unsigned char*
或void*
来引用内存缓冲区来保存那些更复杂的数据结构。
另一个看起来有点令人困惑的问题是你正在使你的cons细胞的每个细胞成为另一个cons细胞,例如,这些代码行,
if (token[0] == '(')
{
strcpy(token, getToken());
node->first = nextNode(node->first);
node->rest = nextNode(node->rest);
}
以递归方式添加cons单元格作为“第一个”和“休息”......但这不是链接列表的样子。它应该有一个指向列表节点的指针作为列表的“头部”(而不是像你在这里看到的那样的另一个cons-cell),然后列表中的每个节点指向一些数据和下一个节点清单。
接下来,当您使用createList()
函数分配内存时,您的内存遍布整个内存,但之后永远不会删除该内存(即,您有node = NULL
之类的代码实际上是内存泄漏,因为您丢失了对node
最初指向的已分配内存位置的内存引用。在为其分配free()
之前,您必须在节点指针上调用NULL
。
最后,printList()
除了打印传递它的列表的第一个元素之外什么都不做...没有递归调用或循环来循环到链表中的下一个节点。因此,您不会使用该功能进行多次打印。它看起来应该更像:
void printList(List node)
{
List current = node;
while (current != NULL) //<== guard for the end-of-list
{
if (node->data != NULL)
{
printf("%s", node->data);
}
current = current->next; //cycle to the next node in the linked list
}
}
总而言之,1)您的cons数据结构应该表示由具有数据元素和指向下一个节点的指针的结构数据类型组成的单链表。通过指向第一个节点的头指针访问cons'ed列表。 2)在解析输入时,应该将节点添加到链表的前面,因为Scheme的cons
操作,以及方案中的所有操作,都是递归的,并且“向右折叠”,这意味着它们可以工作从基础案例(即两个元素的缺点),然后扩展该基础案例。因此,如果你有类似(cons 'd (cons 'c (cons 'b (cons 'a '()))))
的内容,则打印列表(d c b a)
。如果你愿意,也可以帮助将令牌放入堆栈,因为你递归地解析输入,然后从堆栈输入到你的链表(有点像RPN计算器的工作方式)。
答案 1 :(得分:0)
同时将\ n添加到printf以确保将其刷新到stdout:
printf("%s\n", node->data);