为什么K&R“反向波兰语计算器”中的“ NUMBER”在gdb中显示为无效?

时间:2019-02-07 12:30:45

标签: c linux debugging gdb

根据K&R,反向波兰语计算器减少了主要功能,以便更好地理解:

#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'是否创建与例如Linux上的$ PATH变量相同的外部变量NUMBER

  2. 为什么p $NUMBER命令显示外部变量void的{​​{1}}值?

  3. 为什么NUMBER命令显示p NUMBER?这是否意味着gdb的外部变量被阻止了?

3 个答案:

答案 0 :(得分:1)

  

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

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

  

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

     

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

NUMBER 是一个预处理器符号,在预处理阶段它会消失,因为它已被其值替换,编译器本身在编译源中看不到该符号,因此无法将其放入调试数据中有关它的信息(例如 tags ),因此对于调试器

未知

因此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,MAXVAL BUFSIZE 替换为它们的值

答案 1 :(得分:0)

我可以看到您对C语言语法有一些非常可怕的误解。并不是要rate视您,但是您是否尝试过从其他来源学习C? K&R是一本很棒的书,但是众所周知,它简明扼要,并假定您已经了解编程。尝试浏览以下列表:The Definitive C Book Guide and List

======

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

编写#define NUMBER '0'时,它指示编译器将源代码中NUMBER的每个实例替换为'0'。这是简单的搜索并替换为原始源代码。它不会创建变量,并且您不能为其分配值。因此,要求遵循#define的值是没有道理的。它始终将与源代码中写入的值相同。

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

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

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

答案 2 :(得分:0)

C的#define语句不会创建外部变量。它创建一个称为 macro 的东西。

在编译之前或编译初期,宏会在程序翻译过程中被替换。例如,对于#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