【问题标题】:Why does the `NUMBER` in K&R "Reverse Polish Calculator" is showing as void in gdb?为什么 K&R“逆波兰计算器”中的“NUMBER”在 gdb 中显示为 void?
【发布时间】:2019-02-07 12:30:45
【问题描述】:

根据K&R, Reverse Polish Calculator, 减少了主要功能,以便更好地理解:

#include <stdio.h>
#include <stdlib.h>
#define NUMBER '0'
#define MAXOP 5

void push(double);
int pop(void);
int getop(char []);

int main(){
    int type;
    char s[MAXOP];
    double op2;
    while ((type=getop(s))!=EOF){
        switch(type):
            case NUMBER:
                push(atof(s));
                printf("\t%s\n",s);
    }

}



#define MAXVAL 100

char val[MAXVAL];
int sp;

void push(double f){
    if (sp<MAXVAL)
        val[sp++]=f;
}

int pop(void){
    if (sp>0)
        return val[--sp];
}

#include <ctype.h>

int getch(void);
void ungetch(int);

int getop(char s[]){
    int i,c;
    while (s[0]=c=getch())==' '||c=='\t')
        ;
    s[1]='\0';
    if (!isdigit(c)&&c!='.')
        return c;
    i=0;
    if (isdigit(c))
        while (isdigit(s[++i]=c=getch()))
            ;
    if (c=='.')
        while (isdigit(s[++i]=c=getch()))
            ;
    s[i]='\0';
    if (c!=EOF)
        ungetch(c);
    return NUMBER;
}


#define BUFSIZE 100

char buf[BUFSIZE];
int bufp=0;

int getch(void){
    return (bufp>0)?buf[--bufp]:getchar();
}

int ungetch(int c){
    if (bufp>=BUFSIZE)
        printf("ungetch: too many characters\n");
    else 
        buf[bufp++]=c;
}

我可以看到,MAXOP 5/* max size of operand or operator */,它被定义为外部变量,使用#define。我想不通的是,如何在程序运行的每个阶段使用 gdb 实际跟踪 MAXOP 的值?

在我将号码10 提供给getchar() 后,调试时:

14                      while ((type=getop(s))!=EOF){
(gdb) n

Breakpoint 14, getop (s=0x7efff5dc "\n") at t.c:47
47                      while ((s[0]=c=getch())==' '||c=='\t')
(gdb) p c
$22 = 10
(gdb) n

Breakpoint 31, getch () at t.c:72
72                      return (bufp>0)?buf[--bufp]:getchar();
(gdb) n
10

Breakpoint 34, getch () at t.c:73
73              }
(gdb) n 

在某个时刻,当到达getop 函数的末尾时:

Breakpoint 30, getop (s=0x7efff5dc "10") at t.c:62
62                      return NUMBER;
(gdb) p number
No symbol "number" in current context.
(gdb) p (NUMBER)
No symbol "NUMBER" in current context.
(gdb) p $NUMBER
$39 = void
(gdb) n
63              }
(gdb) n

Breakpoint 2, main () at t.c:15
15                              switch(type){
(gdb) p type
$40 = 48
(gdb) p NUMBER
No symbol "NUMBER" in current context.
(gdb) p /s NUMBER
No symbol "NUMBER" in current context.
(gdb) p /d $NUMBER
$41 = Value can't be converted to integer.
(gdb) p $NUMBER
$42 = void

问题:

  1. 上面的程序编译运行后,可以从linux的shell访问NUMBER的值吗?换句话说,预处理指令#define NUMBER '0' 是否创建了外部变量NUMBER,它与例如Linux 上的变量$PATH 相同?

  2. 为什么p $NUMBER 命令显示外部变量void 的值NUMBER

  3. 为什么p NUMBER 命令显示No symbol "NUMBER" in current context.?这是否意味着外部变量被 gdb 阻止?

【问题讨论】:

  • NUMBER 不是符号,而是预处理器定义。编译程序后,所有预处理器定义都消失了。
  • 预处理器在编译开始前将所有出现的NUMBER替换为'0',而'0'不是变量。
  • 如果你用 gcc 的 -g3 选项编译你的程序,那么在任何不太旧的 gdb 中你可以输入 macro expand NUMBERmacro expand MAXVAL 等来查看它的值,如果你'重新停止在宏具有值的范围内的某个点。

标签: c linux debugging gdb


【解决方案1】:

上面的程序编译运行后,能否从linux的shell访问NUMBER的值?换句话说,预处理指令#define NUMBER '0' 创建的外部变量NUMBER 是否与Linux 上的变量$PATH 相同?

不,幸运的是,当您执行程序时,预处理器符号和 C 符号没有映射到 shell 变量中。

为什么 p $NUMBER 命令显示外部变量 NUMBER 的值为 void?

为什么 p NUMBER 命令在当前上下文中显示无符号“NUMBER”。?这是否意味着外部变量被 gdb 阻止?

NUMBER 是一个预处理器符号,它在预处理阶段消失,因为它被它的值替换,编译器本身在它编译的源代码中看不到那个符号,所以它不能把调试数据中有关它的信息(例如标签),因此调试器不知道它

所以p $NUMBER 等价于p $KQHJDSFKJQHKJSDHKJHQSJHDKJHQKJHDSJHSQD 和值void

p NUMBER 等同于p KQHJDSFKJQHKJSDHKJHQSJHDKJHQKJHDSJHSQD 并表示该符号不存在


如果我只是在将您的 #include 放在评论中之后进行预处理阶段(以免从他们那里得到数千行):

/tmp % gcc -E c.c
# 1 "c.c"
# 1 "<built-in>"
# 1 "<command-line>"
# 1 "c.c"





void push(double);
int pop(void);
int getop(char []);

int main(){
    int type;
    char s[5];
    double op2;
    while ((type=getop(s))!=EOF){
        switch(type):
            case '0':
                push(atof(s));
                printf("\t%s\n",s);
    }

}





char val[100];
int sp;

void push(double f){
    if (sp<100)
        val[sp++]=f;
}

int pop(void){
    if (sp>0)
        return val[--sp];
}



int getch(void);
void ungetch(int);

int getop(char s[]){
    int i,c;
    while (s[0]=c=getch())==' '||c=='\t')
        ;
    s[1]='\0';
    if (!isdigit(c)&&c!='.')
        return c;
    i=0;
    if (isdigit(c))
        while (isdigit(s[++i]=c=getch()))
            ;
    if (c=='.')
        while (isdigit(s[++i]=c=getch()))
            ;
    s[i]='\0';
    if (c!=EOF)
        ungetch(c);
    return '0';
}




char buf[100];
int bufp=0;

int getch(void){
    return (bufp>0)?buf[--bufp]:getchar();
}

int ungetch(int c){
    if (bufp>=100)
        printf("ungetch: too many characters\n");
    else
        buf[bufp++]=c;
}
/tmp % 

如您所见,NUMBER、MAXOP、MAXVALBUFSIZE 被它们的值替换

【讨论】:

    【解决方案2】:

    C 的#define 语句不会创建外部变量。它创建了所谓的

    宏在程序翻译期间被替换,在编译之前或早期。例如,使用#define NUMBER '0',结果就像源代码中NUMBER 的每个实例都被替换为'0'

    关于您的具体问题:

    1. 这些宏定义通常不会在编译器生成的调试信息中进行跟踪(尽管此类跟踪可能作为一项功能提供),并且它们对命令 shell 或调试器不可见。

    2. 在 GDB 中,$foo 指的是名为 foo 的 GDB 变量,而不是名为 foo 的程序变量。 GDB 提供单独的变量以方便在调试期间使用。它们用于与 GDB 交互,而不是来自程序。因此命令p $NUMBER 要求GDB 打印其名为NUMBER 的变量的值。没有这样的变量,因此 GDB 将其报告为 void

    3. p NUMBER 显示“当前上下文中没有符号“NUMBER””,因为 GDB 不知道符号 NUMBER

    【讨论】:

    • 知道了。最后一个问题,如果NUMBER 的每个实例都替换为'0',case NUMBER 如何返回1?换句话说,开关如何区分case NUMBER是否匹配??
    • @readonly: case NUMBER: 不返回任何内容。它是一个标签,而不是一个可执行语句。它是switch 语句的标签。在宏替换期间,它被替换为case '0':。然后,当switch (type) 执行时,如果type 等于'0',程序控制转到case '0': 标签。
    【解决方案3】:

    我可以看出您对 C 语言语法有一些非常严重的误解。不是要责备你,而是你是否尝试过从其他来源学习 C? K&R 是一本很棒的书,但它以简洁而著称,并假设您已经了解编程。尝试浏览此处的列表:The Definitive C Book Guide and List

    ======

    NUMBERMAXOPMAXVAL 是常量。它们是通过预处理器指令定义的,并且是 NOT 变量。绝对不是外部变量,这是一个截然不同的概念。

    当您编写#define NUMBER '0' 时,它会指示编译器将源代码中NUMBER 的每个实例替换为'0'。这是对原始源代码的简单搜索和替换。它不会创建变量,您也无法为其赋值。因此,要求遵循 #define'ed 值的值是没有意义的。它总是与源代码中写入的值相同。

    另外,请注意,您在程序中定义的变量与系统上的环境变量之间没有直接关系。

    关于接下来的两个问题,简短的回答是“因为 GDB 不知道它们存在”。 更长的答案:如前所述,您的预处理器指令只是对编译器的搜索和替换指令。一旦完成它们,就无需再保留它们,因此编译器将丢弃它们。 GDB 只知道编译器生成的最终二进制文件中可用的程序。如果编译器在二进制文件中没有提到任何关于NUMBER 的内容,GDB 甚至都不知道它曾经存在过。

    现在,这并不意味着无法在 GDB 中看到这些数据。编译时,可以将-ggdb3选项传递给GCC,使GCC能够生成特定于GDB的调试代码。这包括有关程序的详细信息,包括所有宏和预处理器指令。使用这个额外的标志,您可以看到 #define'ed 常量的值,但是请记住,它们永远不会改变。这通常仅对查看宏函数的执行有用,这是一个更高级的主题。

    【讨论】:

    • 书单已经完全废了。它不再是推荐书籍的列表。但显然,除了考古之外,还要远离 K&R。
    • 有吗?我真的不知道。我已经链接到它多年了。我听说国王是一本好书,但我没有读过,所以不能从经验中谈起。
    • The C book list has gone haywire. What to do with it? 帖子被删除和取消删除,18个月后仍然存在同样的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-06-14
    • 1970-01-01
    • 2021-11-29
    • 2014-12-07
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多