4.1函数的基本知识函数的定义形式如下:返回值类型函数明(参数声明表) { 声明和语句 } 函数定义中的各构成部分都可以省略。最简单的函数如下所示: dummy() {} 该函数不执行任何操作也不返回任何值。如果函数定义中省略了返回值类型,则默认为int类型。 cc main.c getline.c strindex.c 来编译这3 个文件,并把生成的目标代码分别存放在文件main.o、getline.o 与 strindex.o中,然后再把这3 个文件一起加载到可执行文件a.out中。
4.2返回非整型值函数
4.3 外部变量
文章中有下面一段描述和代码:
如果能“反读”不需要的字符,该问题就可以得到解决。每当程序多读入一个字符时,
就把它压回到输入中,对代码其余部分而言就好像没有读入该字符一样。我们可以编写一对
互相协作的函数来比较方便地模拟反取字符操作。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;
}
这段代码我有个疑问,当buf里面存储了多个数据的时候,getch读取的字符时倒序排列的;从文中无法看出外部数据时如何放入缓冲中的。不清楚是自己理解错了还是代码确实有问题 请高手解答。
4.4 作用域规则
对于在函数开头声明的自动变量来说,其作用域是声明该变量名的函数。
外部变量或函数的作用域从声明它的地方开始,到其所在的(待编译的)文件的末尾结束。
变量声明用于说明变量的属性 (主要是 变量的类型),而变量定义除此以外还将引起存储器的分配。
外部变量的初始化只能出现在其定义中。
4.7寄存器变量只有自动变量和函数形参变量能够设置为寄存器变量;寄存器变量的地址是不可以访问的;由于硬件的限制寄存器变量的数量是有限制的,但是多余的声明不会出错,编译器可以忽略过量的或不支持的寄存器变量声明。
4.8 程序块结构变量作用域.
4.9 初始化在不进行显式初始化的情况下,外部变量和静态变量都将被初始化为0,而自动变量和寄存器变量的初值则没有定义(即初值为无用的信息)。对于外部变量与静态变量来说,初始化表达式必须是常量表达式,且只初始化一次(从概念上讲是在程序开始执行前进行初始化)。对于自动变量与寄存器变量,则在每次进入函数或程序块时都将被初始化。对于自动变量与寄存器变量来说,初始化表达式可以不是常量表达式:表达式中可以包含任意在此表达式之前已经定义的值,包括函数调用。数组的初始化:int days[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 如果初始化表达式的个数比数组元索数少,则对外部变量、静态变量和自动变量来说,没有初始化表达式的元素将被初始化为0,如果初始化表达式的个数比数组元素数多,则是错误的。字符数组的初始化比较特殊:可以用一个字符串来代替用花括号括起来并用逗号分隔的初始化表达式序列。例如:char pattern[] = "ould "; 它同下面的声明是等价的:char pattern[] = { 'o', 'u', 'l', 'd'}; 这种情况下,数组的长度是5(4 个字符加上一个字符串结束符'/0')。
4.10 递归递归并不节省存储器的开销,因为递归调用过程中必须在某个地方维护一个存储处理值的栈。递归的执行速度并不快,但递归代码比较紧凑,并且比相应的非递归代码更易于编写与理解。在描述树等递归定义的数据结构时使用递归尤其方便。
4.11 C预处理器预处理器是编译过程中单独执行的第一个步骤。两个最常用的预处理器指令是:#include 指令(用于在编译期间把指定文件的内容包含进当前文件中)和#define指令(用任意字符序列替代一个标记)。
4.11.1 文件包含 #include "文件名" 在原文件所在目录搜索或 #include <文件名> 在指定目录搜索(环境变量). 如果某个包含文件的内容发生了变化,那么所有依赖于该包含文件的源文件都必须重新编译。
4.11.2 宏替换 #define 名字替换文本可以通过#undef指令取消名字的宏定义,如#undef getchar. 如果在替换文本中,参数名以#作为前缀则结果将被扩展为由实际参数替换该参数的带引号的字符串。如:#define dprint(expr) printf(#expr " = %g/n", expr)使用语句 dprint(x/y) 调用该宏时,该宏将被扩展为: printf("x/y" " = &g/n", x/y); 其中的字符串被连接起来了,这样,该宏调用的效果等价于 printf("x/y = &g/n", x/y); 预处理器运算符##为宏扩展提供了一种连接实际参数的手段。如果替换文本中的参数与 ##相邻,则该参数将被实际参数替换,##与前后的空白符将被删除,并对替换后的结果重新扫描。如:#define paste(front, back) front ## back 因此,宏调用paste(name, 1)的结果将建立记号name1。 条件包含实例,帮助处理文件依赖关系: #if !defined(HDR) #define HDR /* hdr.h文件的内容放在这里*/ #endif #if SYSTEM == SYSV #define HDR "sysv.h" #elif SYSTEM == BSD #define HDR "bsd.h" #elif SYSTEM == MSDOS #define HDR "msdos.h" #else #define HDR "default.h" #endif #include HDR #ifndef HDR #define EDR /* hdr.h文件的内容放在这里*/ #endif