本章主要内容:
1、介绍一些基本概念,变量与常量、算术运算、控制流、函数、基本输入输出等。
2、对于一些重要特性,比如指针、结构、C语言中十分丰富的运算符集合、部分控制流语句以及标准库等,暂不讨论。
在本章,无法找到任何特定语言特性的完整说明,只是简略说明。这些内容会在后续的章节中再做详细的介绍。可以把本章作为后续各章详细讲解的内容的框架。
1.1 入门
打印hello,world
(1)编写程序文本(2)编译(3)加载(4)运行
hello, world的程序如下:
#includemain(){ printf("hello, world!");}
在UNIX系统中,必须新建一个以.c为扩展名的文件,比如hello.c;
然后,编译:cc hello.c ;
若编译构成顺利,将生成一个a.out的可以执行文件;
运行 a.out,输出hello,world;
在其他的操作系统中,编译、加载、运行,会有不同的规则。
一个程序,都是有函数和变量组成的;
函数包含语句;
语句用来指定所要执行的计算操作;
变量用来存储计算过程中使用的值;
函数的命名没有限制,但main是一个特殊的函数名,
每个程序的都从main函数的起点开始运行,这意味着每个程序都需要在某个位置包含一个main函数;
#include
用于告诉编译器在本程序中包含标准输入输出库的信息。
main()
函数之间交换数据的一种方式是,调用函数向被调用函数提供一个值(参数)列表;
函数后面的一对圆括号将参数列表括起来。
printf("hello, world!");
调用函数时,只需要使用函数名+圆括号括起来的参数表,比如printf(字符串);
用双括号括起来的字符序列称为字符串或者字符串常量。
C语言中,字符序列\n表示换行符,遇到它时,输出打印换行,从下一行的左端行首开始。
无法使用程序的换行替代\n,printf函数永远不会自动换行。eg:
#includemain(){ printf("hello, "); printf("world!");}
输出结果:【没有换行】
再eg:
#includemain(){ printf("hello, "); printf("world!"); printf("\n");}
输出结果:【有换行】
类似于\n的成为转义字符序列;
转义字符序列表示无法输入的字符或不可见字符。
其他的字符序列如:
\t 制表符;\b 回退符;\' 双引号; \\ 反斜杠
写入程序,并查看下效果
#includemain(){ printf("hello, "); printf("\t\'\"\\world! "); printf("\n");}
输出效果是:
1.2 变量与算术表达式
代码如下:
#include/* * 当fahr=1,20,40,,,300时, * 分别打印华氏温度和摄氏温度的对照表 */int 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 += step; }}
本次代码比上次的复杂些,程序引入了一些新的概念,注释,声明,变量,算术表达式、循环以及格式化输出。
输出效果是:
0 -17 20 -6 40 4 60 15 80 26 100 37 120 48 140 60 160 71 180 82 200 93 220 104 240 115 260 126 280 137 300 148
C语言中,所有变量都必须先声明再使用,声明通常放在函数的起始处;
声明用于说明变量的属性,由一个类型名和变量名组成;
其中,int类型表示整型;float表示浮点型;另外,C还提供了,
char 字符--一个字节
short 短整型
long 长整型
double 双精度浮点型
这些数据类型的大小取决于具体的机器。另外,还存在这些基本数据类型的数组、结构、联合,指向这些类型的指针以及返回这些类型值的函数。
while循环:
while循环语句的执行方式是这样的:
先测试圆括号中的条件,如果条件为真,则执行循环体;然后在测试圆括号中的条件,如果为真,则再次执行循环体;
单圆括号中的条件为假是,循环结束。
while循环的循环体可以是花括号括起来的一条或者多条语句;也可以是不用花括号的单条语句;
建议:
(1)把while控制的语句缩进一个制表位,方便阅读;
(2)每行只书写一个语句
(3)运算符两边各加一个空格字符
printf 通用输出格式化函数,
printf函数的第一个参数中的各%分别对英语第一个、第二个、...参数,它们在数目和类型上都必须匹配;
printf并不是C语言的一部分,C语言本身并没有定义输入、输出功能。
printf只是标准库中的一个函数而已;
对输入、输出的介绍在第7章,若想了解数据输入,可以先阅读第7章对scanf函数的介绍。
scanf类似于printf函数,用于读入数据而不是输出数据。
printf("%3d \t %6d \n", fahr, celsius);
%3d %6d可以对输出做格式化处理;
这样fahr占3个字宽,celsius占6个字宽;输出结果如下:
0 -17 20 -6 40 4 60 15 80 26 100 37 120 48 140 60 160 71 180 82 200 93 220 104 240 115 260 126 280 137 300 148
另一个严重的问题,我们采用的是整型数据,得到的数据并不是特别的准确。为了得到更精确的数据,我们采用浮点算术运算代替整型算术运算。
代码修改如下:
#include/* * 当fahr=1,20,40,,,300时, * 分别打印华氏温度和摄氏温度的对照表 */int main() { float fahr, celsius; int lower, upper, step; lower = 0; /* 温度表的下限 */ upper = 300; /* 温度表的上限 */ step = 20; /* 步长 */ fahr = lower; while (fahr <= upper) { celsius = 5 * (fahr - 32) / 9; printf("%3.0f \t%6.1f \n", fahr, celsius); fahr += step; }}
介绍点除法的计算规则,
在前一个程序中没有使用5/9,在本程序中可以使用。
因为按照整型除法的计算规则,它们相除并舍位后得到的结果是0.
但是,常数中的小数点表明该常数是一个浮点数,因此,5.0/9.0是两个浮点数相除,结果不被舍位。
而且,若某个算术运算符有一个浮点型和一个整型,则在开始运算之前整型操作数会被转换成浮点型。
注意:
赋值语句fahr = lower;
与条件测试语句while(fahr <= upper);
也是按照这种方式执行的,即在运算之前先把int类型的操作数转换为float类型的操作数。
%3.0f表示fahr至少占3个字符宽,且不带小数点和小数部分;
%6.1f表示celsius至少占6个字符宽,且小数点后面有1为数字;
%.2f指定待打印的浮点数的小数点后有两位小数,但宽度没有限制;
%f仅仅要求按照浮点数打印该数。
另外,printf还支持下列格式说明:%o表示八进制;%x表示十六进制;%c表示字符;
%s表示字符串;%%表示百分号本身。
1.3 for语句
#include/* * 当fahr=1,20,40,,,300时, * 分别打印华氏温度和摄氏温度的对照表 */int main() { int fahr; for (fahr = 0; fahr <= 300; fahr += 20) { printf("%3d \t %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32)); }}
使用for语句的改进点:
去掉大部分变量,而只使用了一个int的fahr。计算摄氏温度的表达式现在变成了printf函数的第三个参数,不再是单
独的赋值语句。
for语句的圆括号共包含三个部分;
(1)初始化部分,仅在进入循环前执行一次。fahr=0
(2)控制循环的测试或条件部分。循环控制对该条件求值,如果为真,则执行循环体。fahr<=30
(3)此后,执行第三部分fahr += 20,以将循环变量fahr增加一个步长,并再次对条件求值。若为false,循环终止
实际编程中,可以选择for或者while,主要看哪种更清晰。
for语句比较适合初始化和增加步长都是单条语句并且逻辑相关的情形,因为它将循环控制语句集中放在一起,且比
while语句更紧凑。
1.4 符号常量
在程序中使用300、20等类似的“幻数”并不是一个好习惯,它们几乎无法提供什么信息,而且使程序修改更为困难。
#define可以把符号名(或称为符号常量)名定义为一个特定的字符串;
#define 名字 替换文本
在该定义之后,程序中出现的所有在#define中定义的名字都将用相应的替换文本替换。
符号常量名通常用大写字母拼写,这样可以很容易与小写字母拼写的变量名相区别。
注意,#define指令行的末尾没有分号。
代码如下:
#include#define LOWER 0#define UPPER 300#define STEP 20/* * 当fahr=1,20,40,,,300时, * 分别打印华氏温度和摄氏温度的对照表 */int main() { int fahr; for (fahr = LOWER; fahr <= UPPER; fahr += STEP) { printf("%3d \t %6.1f\n", fahr, (5.0 / 9.0) * (fahr - 32)); }}
1.5 字符输入/输出
看一组与字符型数据处理有关的程序。
标准库提供了一次读/写一个字符的函数,其中最简单的是getchar和putchar两个函数。
(1)每次调用时,getchar函数从文本流中读入下一个输入字符,并将其作为结果值返回。
在执行c=getchar() 之后,变量c将包含输入流中的下一个字符。这个字符通常是通过键盘输入的,关于从文件输入字符的方法,在第7章介绍。
(2)每次调用putchar函数时将打印一个字符。eg:putchar(c)
putchar与printf这两个函数可以交替调用,输出的次序与调用的次序一致。
1.5.1 文件复制
最简单的例子就是把输入一次一个字符地复制到输出:
代码如下:
#includeint main() { int c; c = getchar(); while(c != EOF) { putchar(c); c = getchar(); }}
字符在键盘、屏幕或其他的任何地方无论以什么形式表现,它在机器内部都是以位模式存储的。
char类型专门用于存储这种字符型数据,当然任何整型也可以用于存储字符型数据,因为某些潜在的重要原因,在此我们使用int类型。
那么,如何区别文件中有效数据与输入结束符的问题。
C采取的办法是:没有输入时,getchar返回一个特殊值,这个值成为EOF(end of file)。我们在声明变量c时,必须让它大到足以存放getchar函数返回的任何值。
这里之所以不把c声明为char类型,是因为它必须足够大,除了能存储任何可能的字符外还要能存储文件结束符EOF。因此,将c声明为int类型。
这个字符赋值程序可以编写的更精炼一些。C中,类似于c = getchar()之类的复制操作是一个表达式,并且具有一个值,即赋值后左边变量保存的值。也就是说,赋值可以作为更大的表达式的一部分出现。
代码改造如下:
#includeint main() { int c; while((c = getchar()) != EOF) { putchar(c); }}
1.5.2 字符计数
对字符进行计数,代码如下:
#includeint main() { int nc = 0; while(getchar() != EOF) { nc++; } printf("%ld", nc);}
(1)引入了新的运算符++;功能是+1,相当于nc=nc+1
++nc更精炼一些,且通常效率也更高。对应的是--。
++和--两个运算符可以作为前缀运算符,也可以作为后缀运算符。
(2)使用long类型的变量来存放计数值,没有使用int类型。
long至少要占用32位存储单元。某些机器上long与int类型的长度相同,在另一些机器上,int类型可能只有16位,这样,有可能使int类型的计数变量溢出。 %ld告诉printf函数其对应的参数是long整型。
使用for循环编写该程序的写法:
#includeint main() { int nc = 0; for (nc = 0; getchar() !=EOF; nc++) ; printf("%ld", nc);}
该程序中,for循环语句的循环体是空的。但C语言的语法规则要求for循环语句必须有一个循环体,因此用单独的分号代替。单独的分号成为空语句。
1.5.3 行计数
统计输入中的行数。
标准库保证输入文本流以行序列的形式出现,每一行以换行符结束。因此,统计行数等价于统计换行符的个数。
代码如下:
#includeint main() { int nl = 0; int c; while((c = getchar()) != EOF) { if (c == '\n') nl++; } printf("%d", nl);}
(1)
双等于号==是C语言中表示“等于”关系的运算符。
C语言将 等于号= 作为复制运算符。
(2)
单引号中的字符表示一个整型值,该值等于此字符在机器字符集中对应的数值,我们称之为字符常量。
它只不过是小的整型数的另一种写法而已。例如:
‘A’是一个字符常量,在ASCⅡ字符集中其值为65。
字符串常量中使用的转义字符也是合法的字符常量。比如‘\n’表示10。
(3)
我们要注意,'\n'是单个字符,在表达式中它不过是一个整型数而已;
而"\n"是一个仅包含一个字符的字符串常量。
1.5.4 单词计数
我们要介绍的这个程序用于统计行数、单词数与字符数。
这里对单词定义比较宽松,它是任何其中不包含空格、制表符或换行符的字符序列。
#includeint main() { int nc, nw, nl, c; nc = nw = nl = 0; while((c = getchar()) != EOF) { nc++; if (c == '\n') nl++; if (c == ' ' || c == '\n' || c=='\t') nw++; } printf("nc: %d, nw:%d, nl:%d", nc, nw, nl);}
(1)nl = nw = nc = 0;
注意:在兼有值与赋值两种功能的表达式中,赋值结合次序是由右至左。
所以,上面等同于nl = (nw = (nc = 0));
(2)运算符|| 代表逻辑或。
c == ' ' || c == '\n' || c=='\t'
运算符&代表逻辑与。它仅比||高一个优先级。
由&&或||连接的表达式由左至右求值,并保证在求值过程中只要能够判断最终的结果为真或假,求值就立即停止。
1.6 数组
啊啊
1.7 函数
啊啊