首页 C 外部变量

C 外部变量

举报
开通vip

C 外部变量C 外部变量 C语言程序可以看成由一系列的外部对象构成,这些外部对象可能是变量或函数。形容词external 与internal 相对的,internal 用于描述定义在函数内部的函数参数及变量。外部变量定 义在函数之外,因此可以在许多函数中使用。由于C语言不允许在一个函数中定义其它函数,因此函数本身是“外部的”。默认情况下,外部变量与函数具有下列性质:通过同一个名字对 外部变量的所有引用(即使这种引用来自于单独编译的不同函数)实际上都是引用同一个对象(标准中把这一性质称为外部链接)。在这个意义上,外部变量...

C  外部变量
C 外部变量 C语言程序可以看成由一系列的外部对象构成,这些外部对象可能是变量或函数。形容词external 与internal 相对的,internal 用于描述定义在函数内部的函数参数及变量。外部变量定 义在函数之外,因此可以在许多函数中使用。由于C语言不允许在一个函数中定义其它函数,因此函数本身是“外部的”。默认情况下,外部变量与函数具有下列性质:通过同一个名字对 外部变量的所有引用(即使这种引用来自于单独编译的不同函数)实际上都是引用同一个对象( 标准 excel标准偏差excel标准偏差函数exl标准差函数国标检验抽样标准表免费下载红头文件格式标准下载 中把这一性质称为外部链接)。在这个意义上,外部变量类似于Fortran 语言的COMMON块__________或Pascal 语言中在最外层程序块中声明的变量。我们将在后面介绍如何定义只能 在某一个源文件中使用的外部变量与函数。 因为外部变量可以在全局范围内访问,这就为函数之间的数据交换提供了一种可以代替 函数参数与返回值的方式。任何函数都可以通过名字访问一个外部变量,当然这个名字需要通过某种方式进行声明。 如果函数之间需要其享大量的变量,使用外部变量要比使用一个很长的参数 关于同志近三年现实表现材料材料类招标技术评分表图表与交易pdf视力表打印pdf用图表说话 pdf 更方便、 有效。但是,我们在第1 章中已经指出,这样做必须非常谨慎,因为这种方式可能对程序结构产生不良的影响,而且可能会导致程序中各个函数之间具有太多的数据联系。 外部变量的用途还表现在它们与内部变量相比具有更大的作用域和更长的生存期。自动 变量只能在函数内部使用,从其所在的函数被调用时变量开始存在,在函数退出时变量也将消失。而外部变量是永久存在的,它们的值在一次函数调用到下一次函数调用之间保持不变。因此,如果两个函数必须共享某些数据,而这两个函数互不调用对方,这种情况下最方便的方式便是把这些共享数据定义为外部变量,而不是作为函数参数传递。 下面我们通过一个更复杂的例子来说明这一点。我们的目标是编写一个具有加(+)、减(-)、乘(*)、除(/)四则运算功能的计算器程序。为了更容易实现,我们在计算器中使用 逆波兰表示法代替普通的中辍表示法(逆波兰表示法用在某些袖珍计算器中,Forth与Postscript 等语言也使用了逆波兰表示法)。 在逆波兰表示法中,所有运算符都跟在操作数的后面。比如,下列中缀表达式: (1 –2) * (4 + 5) 采用逆波兰表示法表示为: 1 2 - 4 5 + * 逆波兰表示法中不需要圆括号,只要知道每个运算符需要几个操作数就不会引起歧义。 计算器程序的实现很简单。每个操作数都被依次压入到栈中;当一个运算符到达时,从 栈中弹出相应数目的操作数(对二元运算符来说是两个操作数),把该运算符作用于弹出的操 作数,并把运算结果再压入到栈中。例如,对上面的逆波兰表达式来说,首先把1 和2 压入到栈中,再用两者之差-1 取代它们;然后,将4 和5压入到栈中,再用两者之和9取代它们。最后,从栈中取出栈顶的-1和9,并把它们的积-9 压入到栈顶。到达输入行的末尾时,把栈顶 的值弹出并打印。 这样,该程序的结构就构成一个循环,每次循环对一个运算符及相应的操作数执行一次 操作: while (下一个运算符或操作数不是文件结束指示符) if (是数) 将该数压入到栈中 else if (是运算符) 弹出所需数目的操作数 执行运算 将结果压入到栈中 else if (是换行符) 弹出并打印栈顶的值 else 出错 栈的压入与弹出操作比较简单,但是,如果把错误检测与恢复操作都加进来,该程序就 显得很长了,最好把它们设计成独立的函数,而不要把它们作为程序中重复的代码段使用。另外还需要一个单独的函数来取下一个输入运算符或操作数。 到目前为止,我们还没有讨论设计中的一个重要问题:把栈放在哪儿?也就是说,哪些 例程可以直接访问它?一种可能是把它放在主函数main中,把栈及其当前位置作为参数传递 给对它执行压入或弹出操作的函数。但是,main函数不需要了解控制栈的变量信息,它只进 行压入与弹出操作。因此,可以把栈及相关信息放在外部变量中,并只供push 与pop 函数 访问,而不能被main函数访问。 把上面这段话转换成代码很容易。如果把该程序放在一个源文件中,程序可能类似于下 列形式: #include... /* 一些包含头文件*/ #define... /* 一些define定义*/ main使用的函数声明 main() { ... } push与pop所使用的外部变量 void push( double f) { ... } double pop(void) { ... } int getop(char s[]) { ... } 被getop调用的函数 我们在后面部分将讨论如何把该程序分割成两个或多个源文件。 main函数包括一个很大的switch循环,该循环根据运算符或操作数的类型控制程序的 转移。这里的switch语句的用法比3.4 节中的例子更为典型。 #include #include /* for atof() */ #define MAXOP 100 /* max size of operand or operator */ #define NUMBER '0' /* signal that a number was found */ int getop(char []); void push(double); double pop(void); /* reverse Polish calculator */ main() { int type; double op2; char s[MAXOP]; while ((type = getop(s)) != EOF) { switch (type) { case NUMBER: push(atof(s)); break; case '+': push(pop() + pop()); break; case '*': push(pop() * pop()); break; case '-': op2 = pop(); push(pop() - op2); break; case '/': op2 = pop(); if (op2 != 0.0) push(pop() / op2); else printf("error: zero divisor\n"); break; case '\n': printf("\t%.8g\n", pop()); break; default: printf("error: unknown command %s\n", s); break; } } return 0; } 因为+与*两个运算符满足交换律,因此,操作数的弹出次序无关紧要。但是,-与/两个运算符的左右操作数必须加以区分。在函数调用 push(pop() - pop()); /* WRONG */ 中并没有定义两个pop 调用的求值次序。为了保证正确的次序,必须像main 函数中一样把 第一个值弹出到一个临时变量中。, #define MAXVAL 100 /* maximum depth of val stack */ int sp = 0; /* next free stack position */ double val[MAXVAL]; /* value stack */ /* push: push f onto value stack */ void push(double f) { if (sp < MAXVAL) val[sp++] = f; else printf("error: stack full, can't push %g\n", f); } /* pop: pop and return top value from stack */ double pop(void) { if (sp > 0) return val[--sp]; else { printf("error: stack empty\n"); return 0.0; } } 如果变量定义在任何函数的外部,则是外部变量。因此,我们把push 和pop 函数必须 共享的栈和栈顶指针定义在这两个函数的外部。但是,main函数本身并没有引用栈或栈顶指 针,因此,对main函数而言要将它们隐藏起来。 下面我们来看getop函数的实现。该函数获取下一个运算符或操作数。该任务实现起来 比较容易。它需要跳过空格与制表符。如果下一个字符不是数字或小数点,则返回;否则,把这些数字字符串收集起来(其中可能包含小数点),并返回NUMBER,以标识数已经收集起 来了。 #include int getch(void); void ungetch(int); /* getop: get next character or numeric operand */ int getop(char s[]) { int i, c; while ((s[0] = c = getch()) == ' ' || c == '\t') ; s[1] = '\0'; if (!isdigit(c) && c != '.') return c; /* not a number */ i = 0; if (isdigit(c)) /* collect integer part */ while (isdigit(s[++i] = c = getch())) ; if (c == '.') /* collect fraction part */ while (isdigit(s[++i] = c = getch())) ; s[i] = '\0'; if (c != EOF) ungetch(c); return NUMBER; } 这段程序中的getch 与ungetch 两个函数有什么用途呢?程序中经常会出现这样的情况:程序不能确定它已经读入的输入是否足够,除非超前多读入一些输入。读入一些字符以合成一个数字的情况便是一例:在看到第一个非数字字符之前,已经读入的数的完整性是不能确定的。由于程序要超前读入一个字符,这样就导致最后有__________一个字符不属于当前所要读入 的数。 如果能“反读”不需要的字符,该问题就可以得到解决。每当程序多读入一个字符时, 就把它压回到输入中,对代码其余部分而言就好像没有读入该字符一样。我们可以编写一对互相协作的函数来比较方便地模拟反取字符操作。getch函数用于读入下一个待处理的字符, 而ungetch 函数则用于把字符放回到输入中,这样,此后在调用getch 函数时,在读入新 的输入之前先返回ungetch函数放回的那个字符。 这两个函数之间的协同工作也很简单。ungetch 函数把要压回的字符放到一个共享缓冲区(字符数组)中,当该缓冲区不空时,getch 函数就从缓冲区中读取字符;当缓冲区为空 时,getch函数调用getchar函数直接从输入中读字符。这里还需要增加一个下标变量来记 住缓冲区中当前字符的位置。 由于缓冲区与下标变量是供getch 与ungetch 函数共享的,且在两次调用之间必须保 持值不变,因此它们必须是这两个函数的外部变量。可以按照下列方式编写getch、ungetch 函数及其共享变量: #define BUFSIZE 100 char buf[BUFSIZE]; /* buffer for ungetch */ int bufp = 0; /* next free position in buf */ int getch(void) /* get a (possibly pushed-back) character */ { return (bufp > 0) ? buf[--bufp] : getchar(); } void ungetch(int c) /* push character back on input */ { if (bufp >= BUFSIZE) printf("ungetch: too many characters\n"); else buf[bufp++] = c; } 标准库中提供了ungetc,它将一个字符压回到栈中,我们将在第7 章中讨论该函数。为了提供一种更通用的方法,我们在这里使用了一个数组而不是一个字符。 练习4-3 在有了基本框架后,对计算器程序进行扩充就比较简单了。在该程序中加入 取模(%)运算符,并注意考虑负数的情况。 练习4-4 在栈操作中添加几个命令,分别用于在不弹出元素的情况下打印栈顶元素; 复制栈顶元素;交换栈顶两个元素的值。另外增加一个命令用于清空栈。 练习4-5 给计算器程序增加访问sin、exp 与pow 等库函数的操作。有关这些库函 数的详细信息,参见附录B.4节中的头文件。 练习4-6 给计算器程序增加处理变量的命令(提供26 个具有单个英文字母变量名的 变量很容易)。增加一个变量存放最近打印的值。 练习4-7 编写一个函数ungets(s),将整个字符串s 压回到输入中。ungets 函数 需要使用buf和bufp吗?它能否仅使用ungetch 函数? 练习4-8 假定最多只压回一个字符。请相应地修改getch与ungetch这两个函数。 练习4-9 以上介绍的getch与ungetch函数不能正确地处理压回的EOF。考虑压回EOF时应该如何处理?请实现你的设计方案。 练习4-10 另一种方法是通过getline 函数读入整个输入行,这种情况下可以不使用getch与ungetch函数。请运用这一方法修改计算器程序。
本文档为【C 外部变量】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
该文档来自用户分享,如有侵权行为请发邮件ishare@vip.sina.com联系网站客服,我们会及时删除。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。
本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。
网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。
下载需要: 免费 已有0 人下载
最新资料
资料动态
专题动态
is_212655
暂无简介~
格式:doc
大小:31KB
软件:Word
页数:15
分类:互联网
上传时间:2019-01-12
浏览量:43