根据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
问题:
在编译并运行上述程序之后,能否从linux的shell中访问NUMBER
的值?换句话说,预处理指令#define NUMBER '0'
是否创建与例如Linux上的$ PATH变量相同的外部变量NUMBER
?
为什么p $NUMBER
命令显示外部变量void
的{{1}}值?
为什么NUMBER
命令显示p NUMBER
?这是否意味着gdb的外部变量被阻止了?
答案 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
======
NUMBER
,MAXOP
和MAXVAL
是常量。它们是通过预处理程序指令定义的,并且是 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'
替换一样。
关于您的具体问题:
这些宏定义通常不会在编译器生成的调试信息中进行跟踪(尽管这种跟踪可能作为功能提供),并且它们对于命令Shell或调试器也不可见。
在GDB中,$foo
是指名为foo
的GDB变量,而不是名为foo
的程序变量。为了方便调试,GDB提供了单独的变量。它们是用于与GDB进行交互的,并非来自该程序。因此,命令p $NUMBER
要求GDB打印其名为NUMBER
的变量的值。没有此类变量,因此GDB将其报告为void
。
p NUMBER
显示“在当前上下文中没有符号“ NUMBER””,因为没有GDB已知的符号NUMBER
。