第1章
1.1 创建一个C程序
这里使用的不是学校上课用的编程软件,而是Dev C++ 5.4.0。安装的时候可以选择中文。
新建一个新文件即可。后缀名默认是.cpp,不过c++的编译器是能识别c语言书写的绝大多数代码的(如果有特例会说明)。
1.2 C程序的基本组成
int main() //函数头
{ /*函数体开始*/
return 0; //语句,推出主函数,结束程序的运行并返回0
} /*函数体结束*/
与pascal或者Python不同,C语言中不存在“函数”之外的代码。在上面的代码中,我们定义了一个名字为main,返回值为整形(int)的函数。
C语言有三个基本概念:函数、语句、注释。上面的代码只有一个名为main的函数,一个语句以及几句注释。
函数包括函数头和函数体。int main()是函数头,main是函数名,括号()中是函数的参数表。main前面的int就是函数返回值的类型。这三样也被称为函数原型。至于为什么需要返回一个值,之后的例子会有体现。函数体由被{}括起来的一系列语句组成。说明函数要进行的操作和运算。语句是C语言的基本执行单位,一般以‘;’分号结束。一个程序可能有很多函数,但main函数有最高的优先级,是整个程序的入口和出口。通常return 0表示程序正常结束,非0则表示非正常结束(但在某些操作系统或编译器中,main函数可以没有返回值,写成void main())。在通常情况下,一个main函数至少应包含一个return 0语句。
注释也是重要的组成部分。//是单行注释的标志,注释符号开始到这一行结束的所有字符全部是注释的一部分。也可以用/* */创建一个多行注释。被注释的内容改变颜色和字体,很容易看出。
除了自定义函数之外,语句中会包含一些语言设计者“规定”的函数,比如printf(),这时候我们就需要调用安装编译器时已经安装好的库:
#include<stdio.h>
int main()
{
printf(“Hello,world!\n”)
return 0;
}
上面的样例有一个小问题,那就是printf()语句后面没有加“;”,结果就是编译器会报错。错误信息通常会包含错误位置,类型和原因。看懂错误信息对于改正程序的错误,也就是debug很重要。“\n”是一个换行符,但在这个程序的输出中是看不出来的。知道常见函数所属于的库函数是很有用的。
常用转义符:
\n 换行符 \r 回车符
\b 退格符(把光标向左移动一位,也就是说之后输出的内容会覆盖前面的一位)
\\ 反斜线 \? 问好
\' 单引号 \" 双引号
常用的函数:
scanf():从键盘上按照规定格式读入数据。
srand((unsigned) time(NULL)); rand(); :生成一个伪随机数。
putchar():向终端屏幕输出一个字符。
fgets():从键盘上读入一行字符。
time():获得当前时间。
rand():生成一个随机数。
常用字段类型说明符:
%d/%x int 有符号的十进制/十六进制
%f/%lf float/double 实数/实数
%c/%s char/char 字符/字符串
第2章
2.0
2.0.1
位是指二进制的位,它是计算机能处理的最小单位。一个字节是八位二进制数,计算器的内存按字节分配。int变量占据4字节,char变量占据1字节。
十进制转n进制:辗转相除后倒序输出;n进制转十进制:各位按权展开。
二进制转八进制/十六进制:由于三位二进制数等于一位八进制数,四位二进制数等于一位十六进制数,
2.0.2
原码:即二进制表示。最高位符号位(0代表整数,1代表负数),其余位则是二进制。共八位,因符号位占一位,故表示范围为+-127。
反码:正数的反码与原码相同,负数的反码为其绝对值原码按位取反。
补码:正数的补码与原码相同,负数的补码为其绝对值原码按位取反后加一(即反码加一),有进位丢弃即可。
2.1 常量
如果我们需要多次用到一个极大的数字或者一个极长的字符串,我们不妨在程序的开头定义一个名字较短的常量,这样就可以节省我们的时间。
首先是整数。除了十进制之外,C语言中的整数也可以用十六进制或八进制表示。十六进制由前缀0x引导,数字0~9和字母a~f(大小写均可)组成。a~f对应十进制的10到15。八进制整数以0开头。定义常量的语句如下:
#include<stdio.h>
#define A 90
int main()
{
printf("%d",A);
return 0;
}
缩进、换行和参数的命名全看个人习惯。不如把90换成0x5a试试看?换成0132呢?
然后是实数。在C语言中有两种表现方式:算术法和科学表示法。科学表示法的指数部分是e或E开头的整数。比如0.12E3表示120。
字符和字符串常量外观类似。把字符和字符串放在一对引号中即可。单引号代表字符,双引号代表字符串。但字符串和字符哪怕表面上内容相同,实际内容也不一样。比如‘a’和“a”,不仅类型不同,前者还比后者少了一个结束符‘\0’。
当多个字符串连续出现,且各个字符串之间只有空白符如空格、换行符、制表符等时,编译系统会自动把他们连接在一起。但如果换行书写,就必须把每一行的字符串都用双引号括起来,否则会出现编译错误。
2.2 变量
总体上来说,变量分为局部变量和全局变量。全局变量定义在函数之外,所有函数中都可以使用。局部变量则定义在函数内,只有在被定义的那个函数中才有意义。C语言中,变量在使用前必须定义(某些语言中可以先用再定义,这和编译器的运行方式有关)。
单从有无小数部分来看,数值变量可以分为只能表示整数的定点型和和可以表示实数的浮点型两大类。
定点型又可称为整型,分为char,short,int,long四个小类。每个小类又可以根据是否能表示负数而分为有符号数和无符号数。
部分整型数据的数值表示范围如下:
char -128~127 unsigned char 0~255
int -2147483648~2147483648 10位
unsigned int 0~4294967295
long与int范围相同,unsigned long与unsigned int范围相同。如果需要大一些的数字,可以用long long类型(19位)。需要存储更大的数字的话,就需要数组了。
浮点型数据分为float和double两个小类。float类型是单精度类型,表示范围约为-3.4*10^38~3.4*10^38。能表示的绝对值最小的数值为10^(-44.85)。而有效数字大约相当于十进制的7位。double类型是双精度类型,有效数字相当于十进制的15位。除了一般的表示方法之外,浮点数还可以用科学计数法表示:xey。x表示一个数字,ey表示10的y次方(y∈R)。同时e可以用E代替。默认的实数类型是double,要想用float需要在数据后面加后缀f或F。
‘
定义变量 :变量类型 变量名;
变量名必须以字符或下划线开头,有数字、字母、下划线组成。
赋值操作的表达式为
<变量名>=<表达式>;
变量名即之前声明过的名称,表达式是某个算术式。“;”不要漏掉。需要注意的是“=”两边应当是类型相同的数据类型。当然,如果实在不一样,我们可以在表达式前面写上(类型)来将等号右边的数据类型变的与左边相同。不过,数据的“宽度(即所占用字节数)”不同。显然将宽度大的数据结构转换为宽度小的类型的时候数据会有损坏或者丢失,而宽度小的类型转换为宽度大的类型则没有任何问题。
赋值运算从右往左结合,比如:a=b=5; 这个操作先把5给了b,再把b的值给a。
闲谈一句,在变量的使用中还有const这个奇怪的东西,这个单词的作用是把变量编程一个“常量”,也就是你不能改变它的值:
const n=5;
2.3 字符和字符串
字符常量
用一对单引号括起来的字符称为字符常量。字符常量实际上是一个小整数,可以用十进制或者八进制或者十六进制表示。但如果是用\+数字的表示方法,则只有十进制和八进制。十进制表示时,\xxx,八进制表示时,\0xxx。
字符串常量
用双引号括起来的一组字符叫做字符串常量。编译器会自动在其后添加‘\0’作为结束符。“x”和‘x’实际上不一样,但strlen不会计入\0。
2.4 算术表达式
2.4.1 加减乘除
加 + 减 - 乘 * 除 / 取余(模) %
注意:5/2=2(相当于取整) 5.0/2.0=2.5 5.0/2=2.5
*/%的优先级高于+和-
2.4.2 ++a和a++
运算中通常会遇到对变量的值加1或减1的情况。++是对变量值加1的运算符,--是减一。++和--的位置对于表达式的值有影响。
int j=1;
v=++j; //v==2
v=j++; //v==1
2.4.3 位运算
位运算只能应用于整型和字符型变量。
位运算包括“与” '&'(相同为真)“或” '|'(有真则真)“异或” '^'(不同则真)“取反”’~‘、”左移“’<<‘、”右移“’>>‘。
x<<y的结果等于x的每一位数在二进制的情况下左移y位,超过八位二进制数的部分舍弃。不难理解这相当于将x*(2^y)。同理,x>>y的结果等于x/(2^y)。
使用位运算,可以不用第三个变量交换两个变量的值:
a=a^b; b=b^a; a=a^b;
取反运算~的优先级比算术、关系、逻辑和其他位运算符都要高。
2.5 类型转换
2.5.1 自动类型转换
C语言的类型转换通常是自动的。对于字符和整数,能用字符的地方就可以使用整数。整数转化为字符时,高于八位的部分舍弃。
普通整数和无符号整数混用,则普通整数转化为无符号整数。
较低的类型和较高的类型一起运算,会转化为较高类型。
赋值号右边表达式的类型会自动转换成左边变量的类型。
高低层次关系(从高到低):
long double、double、float、unsigned long int、long int、unsigned int、short int、unsigned char、char
2.5.2 强制类型转换
(类型名) 表达式
把double类型转换为int型相当于取整,稍微再操作一下就可以得到小数部分。例如:
int_part=(int) x;
decimal_part=x-(int) x;
有趣的是:
int x;
float f=0.04;
x=(int) (f*100);
结果会是3,而不是4。这是因为C语言在强制转换的时候,是按照该浮点数在计算机内部的实际值操作的。存储的时候f会略小于0.4……而如果用double类型,结果就会是4了。除此之外,还有一个方法,那就是在类型转换前将变量加上“DEALT”,一个略大于数据精度误差的小数:x=(int) ((f+DEALT)*10000);
强制类型转换的优先级高于各种算术运算符,因此当对一个计算表达式进行类型转换时,需要使用括号将整个式子括起来,否则将只转换紧跟着的运算对象。
不同类型的变量相互赋值,宽度大的类型赋值给宽度小的类型会让数值精度降低。对于宽度有下列不等式:
double>float>int>short>char
2.6 数据输入/输出
stdin,通常指键盘;stdout,通常指屏幕显示器。
常用输出函数为printf()。举个例子。
printf(“Hello world!”); 结果为:Hello world!
int a=1,b=2; printf(“%d%d”,a,b); 结果为:12
int a=1,b=2; printf(“%d %d”,a,b); 结果为:1 2
int--%d int--%x(十六进制)double--%e(实数的科学表示法)%f(实数的常规表示法)
char--%c(一个字符) char*--%s(字符串)
除了数字和字符之外,printf()括号中包含的内容还可以有转义符。同时可以有许多操作。
'%'和变量类型间写'-'表示这个数字左对齐;写一个数字表示输出这个数据的最小宽度(默认右对齐,不足补空格);最常用的是'.'后面加一个数字x, 表示保留x位小数。
例子:
"%-4d" "%.2f"
printf函数中,如果出现同时输出i++,i,++i的情况,那么遵循以下两条准则:
一,从右往左计算。
二,先算有++的部分,第二轮再算本体
int i=0;
printf("%d %d %d",++i,i,i++);
上述代码的输出结果是 2 2 0。
解释:先算i++,这个参数的值是0,i的值变为1;再算++i,i的值变为2,这个参数的值为2;最后算i,i的值为2。
常用输入函数为scanf()。括号中的内容为变量类型和变量所在的地址。
scanf(“%d”,&a);
%f-float %lf-double %s-字符串(读入字符串的时候遇到空格或者回车就会停止)
上面的语句可以实现读入a的值。如果scanf语句要读入多个变量,会用空格和回车来分割。scanf的返回值是成功读入变量的个数,到读入末尾没有数据的时候返回EOF。
如果在读入整型变量后,输入一个回车,再输入一个字符,那么程序将不能正确读入字符。这个时候可以选择在一个scanf语句中,两个%d间加入一个'\n',或者在两个scanf语句中间加一个getchar()语句。
同时,scanf读入字符串的时候遇到空格会停下。要解决这个问题,可以使用gets()函数。也有一种方法,是将scanf()函数写成:
scanf("%[^\n]",s);
2.7 宏和常量
#define s1 s2
上面的语句定义了一个宏。当编译器在系统中检测到s1的时候,自动替换成s2。
需要注意的是程序很笨……它只会做最简单的替换。比如:
#define A 3.5
#define S(x) A*X*X
S(x+y);
上面表达式的会变成什么样子:3.5*x+y*x+y
所以不要吝惜你的括号。
相比之下,const定义的是一个常量。是有确实的存储地址的值。所以会更准确一些。使用方法就是在const <类型> <变量名>=<值>;
2.8 枚举常量
定义枚举常量需要用关键字enum引导的枚举符表。举个例子:
enum{A,B,C,D,E=50,F,G,H,I,J,K};
上面的语句中包含11个枚举符,定义了从A到K共11个枚举常量。当枚举常量形式和E一样的时候,这个枚举符的值就是等号后的表达式的值。否则就是前面枚举常量的值+1。像A这样的第一个枚举常量,它的值默认是0。所以上述枚举常量的值分别为0,1,2,3,50,51,52,53,54,55,56。