内容简介
本书一共有八个项目,分别是导言、类型、运算符与表达式、控制流、函数与程序结构、指针与数组、结构、输入与输出、UNIX系统接口。
前言
本书是C语言基础,其实就是把《The C Programming Language》的读书笔记。
读书要写笔记,读一点写一点,可以写成有系统的笔记。
每天写作的内容不能确定,有空就写,没空就不写。方证大师说:“练一天有一天的好处,练一时有一时的好处。”写一段有一段的好处,写一句有一句的好处。
温度转换程序
#includeint main() { int fahr, celsius; int lower, upper, step; lower = 0; upper = 300; step = 20; fahr = lower; while (fahr <= upper) { celsius = 5 * (fahr - 32) / 9; printf("%d\t%d\n", fahr, celsius); fahr = fahr + step; } }
在1.2节变量与算术表达式中讲过一个温度转换程序,程序如下。
#includeint main() { int fahr, celsius; int lower, upper, step; lower = 0; upper = 300; step = 20; fahr = lower; while (fahr <= upper) { celsius = 5 * (fahr - 32) / 9; printf("%d\t%d\n", fahr, celsius); fahr = fahr + step; } }
在这个程序里面,变量upper和step是幻数。幻数没有具体意义,不能提供信息,修改困难。幻数的处理办法是定义幻数,通过符号常量将其定义为字符串。符号常量的语法如下。
#define 名字 替换文本
使用名字的本质是使用“替换文本”
注意
符号常量不等于变量,不出现在声明中,全用大写,没有分号。
用符号常量对温度转换程序进行修改
#include#define LOWER 0 #define UPPER 300 #define STEP 20 main() { int fahr; for(fahr = LOWER; fahr <= UPPER; fahr = fahr + STEP) printf("%3d %6.1d\n",fahr,(5.0/9.0)*(fahr-32)); }
函数为“计算过程的封装”,也就是说函数是对计算过程进行的封装。比如一个一元二次方程,我们列竖式进行计算,列竖式就是计算过程。函数将这个计算过程进行封装,使用者将看不到计算过程。函数就是一个黑盒子。
使用函数,不用管函数是如何实现的。只需要知道函数具有哪些功能,无需管功能是怎样实现的。
函数分为两种,一种是自定义函数,一种是函数库函数。
自定义函数,需要使用者自己定义,要学习函数定义的方法。
函数库函数,是已经封装好的函数,如printf()、getchar()、putchar()函数等。
对于函数库函数,使用者只需要知道函数具有的功能即可,无需自定义函数的功能。
下面自定义一个指数计算函数,函数命名为power(m,n),能够实现的功能是计算整数m的n次幂。
该程序分为三步完成,第一步是定义函数,第二步是主程序调用函数,第三步是合并程序
第一步,定义函数
#includeint power(int base,int n) { int i,p; p = 1; for(i=1;i<=n;++i) p = p * base; return p; }
第二步:main()函数调用power(m,n)函数
int power(int m,int n); main() { int i; for(i=0;i<10;++i) printf("%d %d %d\n",i,power(2,i),power(-3,i)); return 0; }
第三步:合并程序
#includeint power(int m,int n); main() { int i; for(i=0;i<10;++i) printf("%d %d %d\n",i,power(2,i),power(-3,i)); return 0; } int power(int base,int n) { int i,p; p = 1; for(i=1;i<=n;++i) p = p * base; return p; }
难点
变量base干什么的?
程序的目的是连续输出2的0~9次幂,-3的0~9次幂。也就是20~9,(-3)0~9
找个数带进去试试,不明白自定义函数的调用不要紧,先带进去,明天再学自定义函数的调用方法。
函数定义的一般形式
返回值类型 函数名(参数列表)
{
声明部分
语句序列
}
函数定义的位置随意,但整体是不能分隔的。
要清楚函数定义、函数调用。函数定义是指函数声明,函数调用是指传递参数。
比如
printf("%d %d %d \n",i,power(2,i),power(-3,i))
这是main()函数调用power()函数的过程,一共调用了两次,第一次是power(2.i),第二次是power(-3.i)
调用过程是指main()函数向power()函数传递2个参数,第一次传递(2,i)两个参数;第二次传递(-3,i)两个参数。
调用完成后,power()函数向main()函数返回一个格式化的整数并打印。
打印是printf()函数执行的。不是power()函数执行的。
详细地说,main()函数除了调用power()函数外,还调用了printf()函数。这两个被调用的函数不同,power()函数是自定义函数,printf()函数是函数库函数。函数库函数是不用声明的,直接用即可。power()函数是需要声明的。
power()函数
函数声明(函数定义)的语句如下。
int power(int base,int n)
返回值类型为int,函数名为power,参数列表中有两个参数,参数类型声明为int。函数声明中的参数只对此函数可见,其他函数不可见。power中的i和main中的i没有关系。
注意
函数定义中的参数(int base,int n)是形式参数,
函数调用中与形式参数对应的值是实际参数。
分别简称为形参和实参。
函数中的最后一个语句序列是
return p;
power()函数计算的值通过return语句返回给main()函数,也就是将p指返回给power(2,i),也就是说
power(2,i)计算的结果是一个p,这个参数通过“%d”打印出来了。
变量base是干什么的?
变量base是形参,占位置的。
函数调用的方法,定义函数后,main函数传过来一个实参,power接收到实参后进行计算,出来结果后,返回一个p,然后用printf函数打印出来了。
以后main函数都要写return 0;
main函数要向执行环境汇报状态,0表示正常状态。表示正常结束了。执行环境需要得到0这个状态。
习题
重新编写1.2节的温度转换程序,使用函数实现温度转换计算。
1.2节温度转换程序如下
#includeint main() { int fahr, celsius; int lower, upper, step; lower = 0; upper = 300; step = 20; fahr = lower; while (fahr <= upper) { celsius = 5 * (fahr - 32) / 9; printf("%d\t%d\n", fahr, celsius); fahr = fahr + step; } }
使用函数实现温度转换计算,步骤如下。
第一步:函数定义
int conversion() { int fahr, celsius; int lower, upper, step; lower = 0; upper = 300; step = 20; fahr = lower; while (fahr <= upper) { celsius = 5 * (fahr - 32) / 9; printf("%d\t%d\n", fahr, celsius); fahr = fahr + step; } return 0; }
第二步:main函数调用
#includeint conversion(); int main() { conversion(); }
第三步:合并程序
#includeint conversion(); int main() { conversion(); } int conversion() { int fahr, celsius; int lower, upper, step; lower = 0; upper = 300; step = 20; fahr = lower; while (fahr <= upper) { celsius = 5 * (fahr - 32) / 9; printf("%d\t%d\n", fahr, celsius); fahr = fahr + step; } return 0; }
运行结果如下。
注意
第一,函数里面没有参数,如果有参数,没处都要用参数,在主函数中也要定义参数。
第二,没有参数可以直接运行出结果。
第三,严格按照函数调用的格式书写,比如main函数上面要声明函数(定义函数),初次定义函数,容易忽略定义。
所有函数中的参数,都是通过值来传递的。
在函数的调用中分为主调函数和被调函数。比如上节的程序,其中main函数就是主调函数,power函数就是被调函数,被调函数中的参数int power(int base,int n)是通过值来传递的。main函数将值传递给临时变量n,被调函数使用临时变量n进行计算。
被调函数不能直接修改主调函数中变量的值,也就是说,power函数不能直接修改main函数传递过来的n的值。这里说的是不能“直接修改”,可以通过其他操作进行间接的修改。比如指针地址的方法修改主调函数中的参数值。
如果被调函数要修改主调函数中的参数值(变量值),主调函数要向被调函数提供“待设置的变量的地址”,地址是指向变量的至真。而被调函数要将对应参数声明为指针类型,通过指针间接访问或修改变量。
通常情况下,被调函数不能修改主调函数中参数的值,只能修改私有的临时变量的值。
主调函数传递的参数值(实参)存储在被调函数的临时变量中(形参)。
/*power函数求底数n的n次幂*/ int power(int base,int n) { int p; for(p = 1;n > 0; --n) p = p*base; return p; }
其中,变量base和n是形参,参数值由主函数传递过来。实参传递给形参n之后,power函数对形参n进行计算操作,循环递减,改变了n的值,这是可以的。因为变量n是power的局部变量,是私有的。变量n在power函数中的改变不会影响到主函数中的原始n值(参数值)。
如果参数是数组,情况比以上要复杂。
数组名作为参数,传递的是数组其实元素的位置或地址,并不是传递数组中元素的本身。被调函数通过数组下标访问或修改数组元素的值。
通过一个程序来解释字符数组。程序是这样的,读入一组文本行,并把最长的文本行打印出来。
伪代码如下。
while(还有未处理的行)
if(该行比已处理的最长行还要长)
保存该行
保存该行的长度
打印最长的行
这个设计思路很简单,只有三步:读入新行、测试读入的行、保存该行。
这个程序用两个独立函数来实现。第一个函数为getline,用于读取输入的下一行,读到文件末尾符号返回信号,遇到结束符返回0,返回该行长度;第二个函数为copy,保存新行长度,保存新行内容。
打印最长文本行程序编写步骤如下。
第一步,声明getline函数
int getline(char s[],int lim) { int c,i; for(i=0;i第二步,声明copy函数
void copy(char to[],char from[]) { int i; i=0; while((to[i]=from[i])!='\0') ++i; }第三步,main函数
#include#define MAXLINE 1000 int getline(char line[],int maxline); void copy(char to[],char from[]); main() { int len; int max; char line[MAXLINE]; char longest[MAXLINE]; max = 0; while((len=getline(line,MAXLINE)>0)) if(len>max) { max=len; copy(longest,line); } } 第四步,合并程序
#include#define MAXLINE 1000 int getline(char line[],int maxline); void copy(char to[],char from[]); main() { int len; int max; char line[MAXLINE]; char longest[MAXLINE]; max = 0; while((len=getline(line,MAXLINE)>0)) if(len>max) { max=len; copy(longest,line); } } int getline(char s[],int lim) { int c,i; for(i=0;i 解读程序
声明getline、copy函数,假定他们存放在一个文件里
main函数和getline函数之间通过一对参数和一个返回值进行数据交换。
getline函数中的参数声明
int getline(char s[], int lim)其中s为数组,lim为整型。一般数组都是要进行长度声明的,为什么在getline函数里面没有呢?因为我们知道main函数进行了调用,直接在main函数里面进行了声明。因此就没有再getline函数中声明长度。
getline函数用return语句将值返回给调用者。
这个值声明为int类型。
copy函数声明为void类型,表明不返回任何值。只执行一些操作。
1.10 外部变量与作用域
外部变量是指全局变量,全局范围内都能访问的变量。
局部变量是指在某个函数中声明的变量,只能在这个函数中用,其他函数不能使用。
main函数中的变量只能main函数用,这是局部变量,在调用时变量存在,结束时变量消失,并没有存储变量的值。局部变量在每次调用时值都是不同的。每次进入函数时都要显式赋值。
外部变量声明在所有函数外边,所有函数都能调用,全局范围内可以访问,其值一直存在,是不变的。
第一,定义在所有函数外
第二,只能定义一次
第三,编译后分配存储单元
第四,访问时函数要先声明
下面改写1.9节的打印最长文本行程序,程序一共分为五部分
第一,头文件声明
#include#define MAXLINE 1000 int max; char line[MAXLINE]; char longese[MAXLINE]; int getline(void); void copy(void); 第二,主函数调用
main() { int len; extern int max; extern char longest[]; max = 0; while( (len = getline()) > 0) if( len > max ) { max = len; copr(); } if(max>0) printf("%s",longest); return 0; }第三,getline()函数
int getline(void) { int c,i; extern char line[]; for(i=0;i第四,copy()函数
void copy(void) { int i; extern char line[],longest[]; i=0; while((longest[i]=line[i])!='\0') ++i; }第五,合并函数
#include#define MAXLINE 1000 int max; char line[MAXLINE]; char longese[MAXLINE]; int getline(void); void copy(void); main() { int len; extern int max; extern char longest[]; max = 0; while( (len = getline()) > 0) if( len > max ) { max = len; copr(); } if(max>0) printf("%s",longest); return 0; } int getline(void) { int c,i; extern char line[]; for(i=0;i 项目二 类型、运算符与表达式
常用的基本数据类型有常量和变量。
声明语句是定义变量的名字、类型、初始值。
运算符指定将要进行的操作
表达式是将变量和常量连接起来组合成新的值。
类型决定数据对象可取的值的返回、可执行的操作。
整型分为带符号的和无符号的。无符号的可以有无符号常量、十六进制常量、
浮点型可以进行单精度运算,也可以进行long、double运算
字符串常量可以在编译时进行拼接。
如果将常量定义成const类型,常量的数值是不能再改动的。
在进行算术过程时,不同数据类型之间要进行数据类型转换。C语言里面有自动强制转换规则。这样设置的目的是适应更多数据类型。
2.1 变量名
变量名由数字和字母组成,下划线会被当成字母,但一般不用下划线命名。
下划线用于库例程的命名,为了区分,不将下划线用于变量名。
C语言区分大小写
变量名用小写,符号常量也能够大写
关键字不能用作变量名
2.2 数据类型及长度
常用的数据类型有char、int、float、double,长度不确定,char是16位,int有16位有32位的。
还有限定符,int的限定符是short和long。比如short int sh; long int counter;
char的限定符是signed和unsigned,其中unsigned限定符的结果一定是正值和0,符合2的n次幂定律。比如char占8位,unsigned char占0-255位,signed char占-128到127位
项目三 控制流
项目四 函数与程序结构
项目五 指针与数组
项目六 结构
项目七 输入与输出
项目八 UNIX系统接口