这个大长篇相当于是自己对于c语言学习的一个总结,会持续更新完善。
后续会在寒假整理一些经典的例题附带题解,当然希望我学到的东西、总结的经验,能够给后来者提供一个更好的学习途径,从入门到精通而不再是放弃。
也欢迎读者提出宝贵的意见和建议,也欢迎你跟我一起成长。
内存
编址:内存以字节为单位线性连续编址,即按照0x0000,0x0001,0x0002,…的方式;从低地址端开始向高地址端为每一个内存字节进行顺序连续编号
基本的单位代换
1byte = 8 bit 即一字节等于八位,1位就相当于一个二进制位‘0’或‘1’
字节(Byte)=8位(bit)
1KB( Kilobyte,千字节)=1024B
1MB( Megabyte,兆字节)=1024KB
1GB( Gigabyte,吉字节,千兆)=1024MB
1TB( Trillionbyte,万亿字节,太字节)=1024GB
基本数据类型
1、整型
short:-32768~32767[-2^15]~[2^15 - 1]
存储长度为2 B
unsigned short存储长度为2 B,[无符号短整型]
int: -2147483648~2147483647[-2^31~2^31-1]
存储长度为4 B(一般电脑)
unsigned int: 存储长度为4 B,[无符号整型]
long:[-2^63~2^63-1]
存储长度为8 B
unsigned long存储长度为8 B,[无符号长整型]
2、字符型
char: -128~127
存储长度为一个字节
3、浮点型
float和double是由1位符号位+指数位+尾数位构成的,具体情况比较复杂,新手可暂时略过这一部分。
float 单精度4 B
double 双精度8 B
划重点!浮点数的存储是有误差的,这跟浮点型的存储机理有关,直接运算和比较会造成误差。
比较方法:将两数值之差同一个预定的小正数比较。
进制转换
- 十进制整数和二进制整数:除2取余法/按权相加
- 二进制转八进制:3位分组法。从整数部分的最低位起,每3位分成1组,高位部分不足3位则通过加前导0的方式补足3位,然后把每3位二进制数用对应的八进制数来表示即可。
- 八进制转二进制:八进制整数转换为二进制整数只需要将每一位八进制数用对应的二进制数表示即可。
数据的二进制表示
原码和补码
原码:对于一个二进制数X,如果规定用最高位为符号位,其余各位为该数的绝对值。并且规定符号位之值为0表示正,符号位之值为1表示负,则采用这种方式形成的二进制编码称为称为该二进制数X的原码。
补码:补码的定义是正数的补码等于正数的原码,负数的补码为其原码除符号位不动,其余各位变反再加1所得。
反码:对正数而言,其反码与原码、补码的表示相同;对负数而言,反码符号位的定义与原码、补码相同,但需要将对应原码的数值位按位变反。
正数的原码、补码、反码都相同
常量与变量
常量
1、整型常量
- 通过前缀字符区分不同的进制表示方式
十进制:无前缀
八进制:前缀为0
十六进制:前缀为0x或0X
- 整型常量可以带有后缀用以指定其类型
u(U)表示unsigned
l(L)表示long
ll(LL)表示long long
ul(UL)表示unsigned long
2、浮点型常量
- 带小数点的十进制数形式(23.3 , 12. , .231)以小数点开头和结尾都可以
- 指数形式(科学计数法)(23e-2 = 23x10-2 , .15e5 = 0.15x102)
3、字符常量
- 单引号包含的字符 ‘C’
- 只能包含一个字符
双引号包含的是字符串常量
区别 ‘a’ 和 “a”:
‘a’ : 字符常量,占1 B内存空间
“a” : 字符串常量,占2 B内存空间 (字符串常量以’\0’结尾,多存储了一个’\0’)
4、转义序列
常用的转义序列有:\n(换行),\t(制表),\0(空字符),\\(反斜杠),
用#define定义符号常量:
#define 标识符 常量 //注意不要加分号
标识符一般用大写,以区分变量,其后的书写即可用该标识符代表这一常量
用const定义符号常量:
const 类型名 标识符=常量;
const double pi = 3.1415926;
5、枚举常量的定义
关键字enum
enum 枚举名 {枚举常量};
enum week { SUN, MON, TUE, WED=3, THU, FRI, SAT ,};//结尾的逗号可有可无,枚举名也可以省略
在未指定值的缺省情况下,第一个枚举常量的值为0,以后的值依次递增1
可以指定一个或多个枚举常量的值,未指定值的枚举常量的值比前面的值大1
枚举变量的声明:enum week c1;
一个枚举变量的值是int型整数,但值域仅限于列举出来的范围
6、布尔类型
头文件stdbool.h定义宏名bool为_Bool, false和true分别为0和1
如何将一个较长的字符串写成多行?
这里就涉及到行连接问题:
1、在前一行的末尾输入续行符(\) 再换行
"hello,\
are you OK?" 换行后应紧靠行首
2、将字符串分段,分段后的每个字符串用双引号括起来。
“hello,”
“are you OK?”
变量
定义与命名
变量代表内存中具有特定属性的一个存储单元,它用来存放数据,这就是变量的值,在程序运行期间,这些值是可以改变的。
命名规则:以一个字母(a~z, A~Z)或下划线( _ )开头,后跟字母、下划线或数字(0~9),不使用c语言的保留关键字(即在c语言里有特定意义的名称)
以字母和下划线以外的符合开头、定义为保留字都是非法的
驼峰命名法
混合使用大小写字母来构成变量和函数的名字。
第一个单词以小写字母开始;从第二个单词开始以后的每个单词的首字母都采用大写字母例如:myFirstName、myLastName
程序员们为了自己的代码能更容易的在同行之间交流,所以多采取统一的可读性比较好的命名方式。
类型转换
C语言允许双精度、单精度、整型及字符数据之间混合运算
10 + ‘1’ + 6.5
但有一个规则: 先转换成同一类型(int 或 unsigned int)再计算。
任何表达式中的char、unsigned char、short、unsigned short都要先转换成int或unsigned int,如果原始类型的所有值可以用int表示,则转换成int,否则转换成unsigned int,把这称为整数提升
赋值转换:右操作数的值被转换为左操作数的类型
short s = 5;
double d = 2.9;
s = d; //s = 2(d转换成short后值为2)
d = s; //d = 2.0
强制类型转换:
(类型名)操作数
(int)i //将i转换为int类型
基本的输入和输出
1.getchar
函数的调用形式为:
getchar()
原型:int getchar(void);
调用时,圆括号中不能带参数,但必须保留圆括号,函数执行时从输入流中读取一个字符,并将所读取的字符(类型为unsigned char)转换为int类型后作为函数的返回值。
2. putchar
函数的调用形式:
putchar(c)
原型:int putchar(int c);
c为实际参数,它可为char、short与int类型的表达式,其值是要输出字符的字符码。函数正确执行时返回该字符码,否则返回EOF。
putchar(i =’ ’); //输出一个空格
putchar(i = 32); //输出一个空格
3.puts
函数的调用形式为:
puts(s)
原型: int puts(const char *s);
const char *s中的const表明字符指针s的值不会被该函数修改。
其中,s为实际参数,可以是字符串常量、字符数组名,或指向某字符串的字符指针变量.puts函数从s所指定的地址读取字符串输出到标准输出设备,并在串尾输出一个换行符’\n’。
字符串在内存缓冲区存储时串尾以空字符**’\0’作为结束标志,puts取字符串时从s指定的内存区依次取字符直至取到空字符为止**。puts函数正确执行时返回一个非负整数值,如果出错,则返回EOF。
4.gets
函数的调用形式为:
gets(s)
原型:char *gets(char *s);
gets从输入流中读取一行字符存放s指定的内存缓冲区,结尾的换行字符’\n’被空字符’\0’所替换,以作为字符串的结束标志。
函数正确执行时返回该内存缓冲区的首地址,即s的值;如果遇到文件尾或出错,则返回空指针NULL。
gets() 函数不进行边界检查,从而此函数对缓冲区溢出攻击极度脆弱。无法安全使用它(除非程序运行的环境限定能出现在 stdin 上的内容
5.printf
函数的调用形式:
printf(格式字符串, 数据项1, …, 数据项n)
*原型:int printf(const char format, …);
第一个形式参数format是一个字符串,称为格式字符串,用来指定输出数据的个数和输出格式。其余参数是要被输出的数据,参数的个数和数据类型应与格式字符串中转换说明的个数和转换字符(参见下表)一致。
printf函数的返回值是函数调用时实际输出到标准输出设备的字符个数。
转换说明
以%字符开始,以转换字符结尾:
转换字符 | 参数类型 | 输出格式 |
---|---|---|
d,i | int | 十进制整数 |
o | int | 八进制整数(不带前缀0) |
x,X | int | 十六进制整数(不带前缀0x或0X) |
ld,hd | long int,short int | 长整型/短整型 |
u | int | 无符号整型 |
c | char | 单个字符 |
s | **char *** | 字符串(必须以\0结束或在域宽说明中给出长度限制) |
p | void * | 指针值(地址) |
f | float/double | 小数形式的浮点数(小数部分位数由精度确定,缺省为6位) |
e, E | double | 标准指数形式的浮点数(尾数部分位数由精度确定) |
g, G | double | 在不输出无效零的前提下,按输出域宽度较小的原则从%f、%e中自动选择 |
Lf | long double | 长双精度浮点型 |
% | %%表示输出一个% |
域宽说明
在%和转换字符之间可以有域宽说明字符(%m.n+转换字符),用来指出输出数据的对齐方式、输出数据域的宽度、小数部分的位数等要求。
域宽说明字符 | 意义 |
---|---|
- | 表示左对齐输出,如省略表示右对齐输出。 |
+ | 带符号输出,输出正数时前面要加+号 |
空格 | 输出的第一个字符不是+或-时以空格为前缀 |
# | 对于o和x格式输出前缀0和0x,对于g格式不删除尾部零 |
m(正整数) | 输出数据的最小域宽(实际宽度小于m则左边补空格或0) |
0 | 在输出的域宽范围内用前导0补齐空位 |
. | 分隔域宽和精度,小数点前域宽可以省略 |
n(正整数) | 输出数据的精度。对于e、f格式为保留的小数位数,对g格式为保留的有效数字位数,对整数为至少应输出的数字的位数(用前导0补足),对字符串为至多输出的字符数目 |
* | 代表一个整数,其值由对应参数决定,可用于代替m或n表示可变宽域或精度 |
//可变域宽示范
int max=6;
printf(“%*c”, max,’ ’);
//输出6个空格(*代表的域宽由对应参数max决定)
6.scanf
函数的调用形式
scanf(格式字符串, 输入参数1, 输入参数2, …,输入参数n);
原型: int scanf(const char *format, …);
输入参数1至输入参数n可为基本类型或指针类型变量的地址(即指针),可以是字符数组名或指向字符数组首元素的指针变量。scanf函数正确执行时,返回值为被转换并赋值的数据的个数,遇到文件尾或出错时返回EOF。
scanf函数的格式字符串与printf函数相似
在实际使用中,scanf函数的格式字符串一般只需包含转换说明。
对于scanf函数格式字符串中除空格和制表符外的其他普通字符,在输入流中相应位置必须输入相同的字符,否则scanf函数读不到正确的数据.如果在scanf函数的格式字符串中加入了除空格和制表符以外的普通字符,不仅给数据输入带来麻烦,而且容易出错
char s[20];
int i;
scanf("%d %s",&i,s);