如何在 运行 时间内在 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。我还没有时间实际编译这些例子,希望我没有犯太多错误。

  1. 解析器。该模块接受用户输入,对其进行标记化并确保输入的表达式符合语法。
  2. 口译员。该模块获取解析器的输出并执行计算。
  3. 环境。该模块管理计算状态。

让我们从环境开始,这是我们需要的(或者至少我会实现的)。首先是我将用于环境的设计注意事项:

  1. 我们只会处理变量
  2. 我们只允许 300 个变量
  3. 所有变量都将具有全局作用域
  4. 重新绑定变量将覆盖旧绑定

现在,让我们定义以下结构:

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.