如何在 运行 时间内在 c 中声明变量
how to declare variables during run time in c
我即将开始一个完全用 c 编写的简单计算器之类的项目,我想知道如何允许用户在程序的 运行 时间内创建变量,这个变量可以是数字或复数甚至矩阵或
一种方法是将变量的类型、名称及其大小和值存储在一个临时文本文件中,并在需要时检索它,有没有更好的方法。我希望我能在 运行 时间里在 c
中声明真正的变量
您不能在运行时在 C 中声明新变量。
对于你想要的,你可以为你想要支持的每种类型制作一个结构列表。然后每个结构包含变量的名称及其值。声明一个变量将向列表中添加一个新结构。
我假设您正在考虑与您的计算器进行交互,可能看起来像这样:
> myCalc
mc> x=5
mc> 5
mc> 3*x
mc> 15
mc> quit
>
其中 myCalc
是您的程序名称,mc>
提示显示与计算器的交互,其中用户输入语句,计算器显示语句的结果。
现在,考虑第一个语句 x=5
,我们需要解析它并根据您使用的语法确定它是否有效。假设是,那么您需要评估该陈述,为了便于讨论,该陈述有
抽象语法树 (AST) ASMT(x,VAL(5))
。 ASMT 和 VAL 是名义运算符。
现在,我认为这是为 x 添加一个新的绑定到当前环境中。这个环境究竟是什么样子取决于你愿意允许什么,所以现在让我们假设你只允许变量赋值。一个简单的关联数组可以在这里工作,其中键是变量名,数据是值。
现在考虑下一条语句 3*x
,在解析之后我们可以假设表达式的 AST 是 TIMES(3, ID(x))
。现在对此进行评估,或者解释器首先需要处理 ID(x)
部分,该部分将在环境中查找 x
的值,即 5.
在上面之后,AST 看起来像 TIMES(3,5)
将直接评估为 15。
N.B。我对 AST 的表示方式以及解释器如何评估它的看法相当宽松。我试图给出要做什么的味道,而不是完整的低级实现细节。
希望这有帮助(一点点),
T
好的,您将需要三个基本模块:
N.B。我还没有时间实际编译这些例子,希望我没有犯太多错误。
- 解析器。该模块接受用户输入,对其进行标记化并确保输入的表达式符合语法。
- 口译员。该模块获取解析器的输出并执行计算。
- 环境。该模块管理计算状态。
让我们从环境开始,这是我们需要的(或者至少我会实现的)。首先是我将用于环境的设计注意事项:
- 我们只会处理变量
- 我们只允许 300 个变量
- 所有变量都将具有全局作用域
- 重新绑定变量将覆盖旧绑定
现在,让我们定义以下结构:
typedef struct st
{
char* tokenName;
int type;
union
{
int iVal;
float fVal;
} val;
} tableEntry, * ptableEntry;
tableEntry symbolTable[300];
现在一些功能:
一个。 init(tableEntry*) -- 这个函数初始化了环境,即将符号 table 中的所有值设置为某个预定义的空状态。
b。 addValue(tableEntry*, name, value) -- 这个函数接受一个指向环境的指针并向环境添加一个新条目。
c。 int lookupValue(tableEntry*, name)——这个函数接受一个指向环境的指针,并查看是否已经在其中定义了令牌名称。我们已经看到一个问题,我们允许整数和浮点数,但想要一个单一的查找函数,所以我们可能需要某种变体类型或想出一些方法来 return 不同的类型。
d. updateValue(tableEntry*, name, value)——这个函数接受一个指向环境的指针并更新一个现有的值。这引发了一个未解决的规范,如果找不到令牌,updateValue 应该做什么?就我个人而言,我只会添加该值,但作为计算器的设计者,您可以决定如何操作。
这应该是环保的开始。
现在让我们谈谈解释器。为此,假设解析器以前缀形式发出抽象语法树。例如:
语句 x=3 将作为 = x 3
发出
语句 z = 4 + 5 将作为 = z + 4 5
发出
好吧,这里的诀窍是我们并没有真正发出 3
,而是一个令牌,其中包含有关正在传递的内容的更多信息。
令牌的可能实现可能是:
typedef struct tok
{
int tokType;
char* tokVal;
} token, * ptoken;
还让我们进行以下枚举:
enum {EMPTY=0, ID, VAL, EQ, PLUS, SUB, MULT, DIV, LPAREN, RPAREN};
所以,有了这个简化的语句 = x 3
,实际上就是下面这样
结构:
{EQ, null} {ID, "x"} {VAL, "3"}
好的,所以伪代码中的解释器看起来像(假设上面的内容作为列表呈现给解释器)。
while list not empty
token <-- head(list) /* this returns the first token as well as removing it from the list */
switch (token.tokType)
{
....
case EQ: /* handling assignment */
token <--- head(list)
name = token.name
token <--- head(list)
val = atoi(token.tokVal)
addValue(env*, name, val);
break;
case ID:
name = token.name
val = lookupValue(env*, name)
....
}
请注意,上述代码的实际格式很可能需要修改以处理其他构造,这只是一个概念性示例!
现在轮到你了 - 尝试一下,向我们展示你的想法。
稍后
T.
我即将开始一个完全用 c 编写的简单计算器之类的项目,我想知道如何允许用户在程序的 运行 时间内创建变量,这个变量可以是数字或复数甚至矩阵或 一种方法是将变量的类型、名称及其大小和值存储在一个临时文本文件中,并在需要时检索它,有没有更好的方法。我希望我能在 运行 时间里在 c
中声明真正的变量您不能在运行时在 C 中声明新变量。
对于你想要的,你可以为你想要支持的每种类型制作一个结构列表。然后每个结构包含变量的名称及其值。声明一个变量将向列表中添加一个新结构。
我假设您正在考虑与您的计算器进行交互,可能看起来像这样:
> myCalc
mc> x=5
mc> 5
mc> 3*x
mc> 15
mc> quit
>
其中 myCalc
是您的程序名称,mc>
提示显示与计算器的交互,其中用户输入语句,计算器显示语句的结果。
现在,考虑第一个语句 x=5
,我们需要解析它并根据您使用的语法确定它是否有效。假设是,那么您需要评估该陈述,为了便于讨论,该陈述有
抽象语法树 (AST) ASMT(x,VAL(5))
。 ASMT 和 VAL 是名义运算符。
现在,我认为这是为 x 添加一个新的绑定到当前环境中。这个环境究竟是什么样子取决于你愿意允许什么,所以现在让我们假设你只允许变量赋值。一个简单的关联数组可以在这里工作,其中键是变量名,数据是值。
现在考虑下一条语句 3*x
,在解析之后我们可以假设表达式的 AST 是 TIMES(3, ID(x))
。现在对此进行评估,或者解释器首先需要处理 ID(x)
部分,该部分将在环境中查找 x
的值,即 5.
在上面之后,AST 看起来像 TIMES(3,5)
将直接评估为 15。
N.B。我对 AST 的表示方式以及解释器如何评估它的看法相当宽松。我试图给出要做什么的味道,而不是完整的低级实现细节。
希望这有帮助(一点点), T
好的,您将需要三个基本模块:
N.B。我还没有时间实际编译这些例子,希望我没有犯太多错误。
- 解析器。该模块接受用户输入,对其进行标记化并确保输入的表达式符合语法。
- 口译员。该模块获取解析器的输出并执行计算。
- 环境。该模块管理计算状态。
让我们从环境开始,这是我们需要的(或者至少我会实现的)。首先是我将用于环境的设计注意事项:
- 我们只会处理变量
- 我们只允许 300 个变量
- 所有变量都将具有全局作用域
- 重新绑定变量将覆盖旧绑定
现在,让我们定义以下结构:
typedef struct st
{
char* tokenName;
int type;
union
{
int iVal;
float fVal;
} val;
} tableEntry, * ptableEntry;
tableEntry symbolTable[300];
现在一些功能:
一个。 init(tableEntry*) -- 这个函数初始化了环境,即将符号 table 中的所有值设置为某个预定义的空状态。
b。 addValue(tableEntry*, name, value) -- 这个函数接受一个指向环境的指针并向环境添加一个新条目。
c。 int lookupValue(tableEntry*, name)——这个函数接受一个指向环境的指针,并查看是否已经在其中定义了令牌名称。我们已经看到一个问题,我们允许整数和浮点数,但想要一个单一的查找函数,所以我们可能需要某种变体类型或想出一些方法来 return 不同的类型。
d. updateValue(tableEntry*, name, value)——这个函数接受一个指向环境的指针并更新一个现有的值。这引发了一个未解决的规范,如果找不到令牌,updateValue 应该做什么?就我个人而言,我只会添加该值,但作为计算器的设计者,您可以决定如何操作。
这应该是环保的开始。
现在让我们谈谈解释器。为此,假设解析器以前缀形式发出抽象语法树。例如:
语句 x=3 将作为 = x 3
发出
语句 z = 4 + 5 将作为 = z + 4 5
好吧,这里的诀窍是我们并没有真正发出 3
,而是一个令牌,其中包含有关正在传递的内容的更多信息。
令牌的可能实现可能是:
typedef struct tok
{
int tokType;
char* tokVal;
} token, * ptoken;
还让我们进行以下枚举:
enum {EMPTY=0, ID, VAL, EQ, PLUS, SUB, MULT, DIV, LPAREN, RPAREN};
所以,有了这个简化的语句 = x 3
,实际上就是下面这样
结构:
{EQ, null} {ID, "x"} {VAL, "3"}
好的,所以伪代码中的解释器看起来像(假设上面的内容作为列表呈现给解释器)。
while list not empty
token <-- head(list) /* this returns the first token as well as removing it from the list */
switch (token.tokType)
{
....
case EQ: /* handling assignment */
token <--- head(list)
name = token.name
token <--- head(list)
val = atoi(token.tokVal)
addValue(env*, name, val);
break;
case ID:
name = token.name
val = lookupValue(env*, name)
....
}
请注意,上述代码的实际格式很可能需要修改以处理其他构造,这只是一个概念性示例!
现在轮到你了 - 尝试一下,向我们展示你的想法。
稍后 T.