C 将空指针(结构的一部分)强制转换为另一个指针数据类型
我试图自己找出如何解析C中的S表达式,以便为我自己的初级Lisp(作为学习练习编写,而不是用于生产)存储数据和代码 在解释我的代码和我的推理之前,我应该解释一下,我所知道的关于S表达式的所有信息都是Wikipedia文章中关于它的介绍部分,偶尔也会浏览一下常见的Lisp代码,所以我的结构和变量的命名可能有点不对劲 我的实现语言是C,在定义任何函数之前,我创建了以下结构:C 将空指针(结构的一部分)强制转换为另一个指针数据类型,c,parsing,lisp,C,Parsing,Lisp,我试图自己找出如何解析C中的S表达式,以便为我自己的初级Lisp(作为学习练习编写,而不是用于生产)存储数据和代码 在解释我的代码和我的推理之前,我应该解释一下,我所知道的关于S表达式的所有信息都是Wikipedia文章中关于它的介绍部分,偶尔也会浏览一下常见的Lisp代码,所以我的结构和变量的命名可能有点不对劲 我的实现语言是C,在定义任何函数之前,我创建了以下结构: typedef enum { string, letter, integer, } atom_type
typedef enum {
string,
letter,
integer,
} atom_type;
typedef struct {
void* blob;
atom_type type;
} atom;
typedef struct expr {
atom* current;
struct expr* next;
} expr;
每个原子都存储在一个structatom
,其中包含一个enum实例(我不确定这个术语是否正确)和一个指向要存储的数据的空指针。每个S表达式“节点”由一个指向原子的指针和一个指向下一个S表达式节点的指针组成
我编写了一个基本函数,它接受字符串并将其解析为原子,如下所示:
atom* parse_term(char* str) {
size_t len = strlen(str);
atom* current = malloc(sizeof(atom));
if(str[0] == '\'') {
current->blob = (char*) &str[1];
current->type = letter;
} else if(str[0] == '\"') {
char temp[256];
int pos = 1;
while(str[pos] != '\"') {
temp[pos] = str[pos];
pos++;
}
current->blob = malloc(256 * sizeof(char));
current->blob = (char*) &temp;
current->type = string;
} else if(isdigit(str[0])){
char temp[256];
int pos = 0;
while(str[pos] != ' ') {
temp[pos] = str[pos];
pos++;
}
int tmp = atoi(temp);
current->blob = (int*) &tmp;
current->type = integer;
}
return current;
}
该功能似乎工作正常;至少,当我打印出数据类型时,它会正确地显示它。但除此之外,我不知道如何打印出实际的“blob”:我尝试使用%p格式代码以及switch语句:
void print_atom(atom* current) {
switch(current->type) {
case string:
printf("atom%s\ttype:%d", current->blob, current->type);
case letter:
printf("atom%c\ttype:%d", current->blob, current->type);
case integer:
printf("atom%c\ttype:%d", current->blob, current->type);
}
}
但这不起作用。在字符串的情况下,它返回乱码文本,而在其他情况下,它只是不打印原子信息应该存在的任何内容
我想这是我在结构中使用void*的结果;我该怎么补救呢?我认为我确实正确地进行了强制转换(尽管我很可能是错的,请告诉我),我唯一能想到的另一个选择是在“atom”结构中为每个受支持的数据类型存储一个硬编码变量,但这似乎浪费了资源。不要使用
void*
。使用接头
。这就是union
s的作用
在这个例子中,我使用了一个“匿名联合”,这意味着我可以引用它的字段,就好像它们直接在Atom结构中一样。(我根据自己的偏见更改了名称的拼写,因此类型大写,常量为ALLCAPS。我还将Atom的typedef和struct声明分开,以防Atom是自引用的
typedef枚举{
一串
信,
整数
}原子型;
类型定义结构原子;
结构原子{
联合{
char*str;
煤焦;
int-num;
};
原子型;
};
无效打印\u原子(原子*当前){
开关(当前->类型){
大小写字符串:
printf(“原子%s\t类型:%d”,当前->str,当前->类型);
案例信:
printf(“原子%c\t类型:%d”,当前->let,当前->tyoe);
大小写整数:
printf(“原子%d\t类型:%d”,当前->数量,当前->类型);
}
}
正如有人在评论中所说,Lisp对象实际上不是这样的。通常的实现是将cons单元格和atoms组合起来,类似这样(而不是AtomType
)。您还需要将CELL
添加到枚举中
typedef结构单元;
结构单元{
联合{
char*str;
煤焦;
int-num;
结构{
Cell*hd;//历史名称:car
Cell*tl;//历史名称:cdr
};
};
细胞型;
};
这里有一个匿名联合中的匿名结构。一些人说这令人困惑。其他人(不管怎样,我)说它的语法噪音更小。请使用您自己的判断
在Cell
的定义中使用Cell*
是typedef struct Cell Cell
的动机
你可以玩不完全便携但通常是ok的游戏来减少单元的内存消耗,而大多数实际实现都是这样。我没有,因为这是一种学习体验
还要注意的是,真正的Lisp(和许多玩具Lisp)有效地避免了大多数解析任务;该语言包括字符宏,可以有效地执行所需的解析(这并不多);在大多数情况下,它们可以在Lisp本身中实现(尽管您需要某种引导方式).基本的Lisp数据类型是cons单元格,用于表示列表。它包含两个对称字段:car和cdr,这两个字段都指向S表达式。cons单元格用于表示列表,但最基本的是点对。S表达式可以是cons单元格、符号、字符串、数字等。当前->blob=malloc(256*sizeof(char));当前->blob=(char*)&temp;
=即时内存泄漏,最终在temp
之后UB不再有效。事实上,所有存储自动本地变量(temp,tmp)地址的代码行,然后在对象生命周期之外使用这些地址是未定义行为的秘诀。请看更多详细信息/hintsBuildYourOwnLisp最初让我对Lisp感兴趣-我故意选择不遵循该教程,因为它依赖于解析器库,而解析器正是我期待编写的部分。