第一本C语言笔记(上)
1. 一般地源程序文件到可执行程序文件经过如下四步:
预处理 —— 头文件内容插入、宏替换、删注释(#include、#define)
编译 —— 把预处理过的源文件编程汇编文件 *.c -> *.S *.S文件中已经包含机器码和文件格式等
汇编 —— 把汇编程序编程目标代码 *.S -> *.o
链接 —— 把*.o文件和库文件链接到一起,生成可执行程序 *.o + *.so -> *.out
2. 二进制文件与文本文件
所有文件都采用二进制方式记录数字。
如果文件里的所有二进制内容都对应字符,则这种文件叫做文本文件。
除了文本文件以外的所有文件叫做二进制文件。
文本文件可以当做二进制文件使用。
3. 标识符
第一个单词可以是英文单词或者下划线
后面的字符可以是英文单词、下划线或者阿拉伯数字
大小写敏感
标识符的长度没有限制,但是计算机只会截取前面的一部分使用
标识符的书写方式:
驼峰式——XiAn 下划线方式——xi_an
4. 内存、字节、存储区
程序中的所有数字必须记录在内存中;
内存由大量的字节构成,每个字节可以用来记录一个数字,每个字节都有一个编号,不同字节的编号不同。这个编号叫做字节的地址。
内存中所有字节的地址都是从0开始向正数方向增长的;
可以把几个相邻的字节合并成一个整体用来记录一个数字;
一个存储区只能记录一种类型的数字;
存储区也有地址,它的地址是他所包含的所有字节中地址最小的字节的地址;
程序中使用变量代表存储区,程序中对变量的操作就是对它所代表的存储区的操作。
d变量声明语句会让计算机为变量分配存储区。
赋值操作符(=)左边的内容应该可以代表存储区,这种内容叫左值;赋值操作符右边的内容应该可以代表数字,这种内容叫做右值;赋值语句可以把右边的数字放在左边的存储区里。
变量的初始化——在声明变量的时候立即对变量进行赋值。
可以用%p作为占位符把地址数据显示在屏幕上。
5. 数据类型
(1)char
一共包含256(2^8)个不同的整数,每个整数可以代表一个字符。
0 — 127 -128—-1 有符号char
0—255 无符号char
字符和整数之间可以相互替换。
'd' - 'a' == 'D' - 'A' == '3' - '0' == 3 - 0 == 3
转义字符
'\n' 换行
'\r' 回车(让它后面紧挨着的内容出现在本行的最左端,覆盖原有字符)
'\\' 代表字符'\'
'\'' 代表字符'''
... ...
#include <stdio.h>
//将一个小于255的十进制的正整数转换成二进制并打印出来(\r的使用)
void TransToBinary(int Num)
{
printf(" %d\r", Num%2);
Num /= 2;
printf(" %d\r", Num%2);
Num /= 2;
printf(" %d\r", Num%2);
Num /= 2;
printf(" %d\r", Num%2);
Num /= 2;
printf(" %d\r", Num%2);
Num /= 2;
printf(" %d\r", Num%2);
Num /= 2;
printf(" %d\r", Num%2);
Num /= 2;
printf("%d\n", Num%2);
return;
}
int main(int argc, char **argv)
{
int num = atoi(*(argv+1));
TransToBinary(num);
return 0;
}
[root@localhost 0623]# vim transTobinary.c
[root@localhost 0623]# gcc transTobinary.c
[root@localhost 0623]# ./a.out 10
00001010
[root@localhost 0623]# ./a.out 255
11111111
(2)整数
程序中不带小数点的数字默认是整数类型;
如果在不带小数点的数字后面加上u表示这个数字类型是无符号整数类型。
(3)浮点型
程序中带小数点的数字默认是双精度浮点型;
如果在小数点后面加上f表示这个数字的类型是单精度浮点型。
(4)布尔类型
C99规范中引入了布尔类型。
(5)数据类型和占位符的对应关系
char/unsigned char %c
short %hd
unsigned short %hu
long %ld
unsigned long %lu
int %d(十进制)%o(八进制) %x(十六进制)
unsigned int %u
float %f 或者 %g
double %lf 或者 %lg
指针类型保存的地址(*) %p
char* 保存的字符串 %c
(6)不同数据类型的存储区在内存里包含的字节个数不同。
char/unsigned char 1个字节
short/unsigned short 2个字节
int/unsigned int 4个字节
long/unsigned long 4个字节
float 4个字节
double 8个字节
(7)sizeof
sizeof关键词后面的小括号里面可以写任意能当数字使用的内容。
sizeof小括号里如果修改了任何存储区的内容,这个修改不会真正发生。例如:sizeof(num=10);不会使num的值编程10。
6. scanf 语句
可以在一条语句里获得多个数字并记录到不同的存储区里。例如 scanf("%d %d", &num1, &num2)。
如果用户输入的格式和程序希望的格式不同,程序可能无法再获取后面的数字。
7. 二进制
(1)任何一个数字既可以用十进制方式表示,也可以用二进制方式表示。
(2)非负数二进制与十进制的转换
二进制的非负数 ——> 十进制的非负数
0000 1010 = 2^3 + 2^1 = 8 + 2 = 10
十进制的非负数 ——> 二进制的非负数
除2取余法 示例见上文转义字符
(3)负数的二进制和十进制之间是不能直接转换的,需要借助该负数的相反数(即对应的正数)
转换过程分为三步:
计算相反数
根据相反数进行计算
根据转换结果再计算相反数
把二进制数字中每个数位内容变成相反数字(0/1),然后再加1,就得到了该二进制数的二进制相反数。
有符号类型最左边的二进制数位叫符号位,符号位是0表示非负数,符号位是1表示负数,符号位能帮助我们判断符号,但它并不是符号。
当把占地大的整数赋值给占地小的整数类型存储区时,只能保留最后的二进制数位,这会导致数据丢失;当把占地小的有符号整数类型数字赋值给占地大的整数类型存储时,扩展的所有二进制数位上都填充符号位,这样可以保证符号位不变。
(4)十六进制和八进制都可以看做是二进制的简写方式。
8. 运算符
(1)如果参与除法(/)计算的两个数字都是整数,结果只会保留整数部分。
(2)赋值语句可以当做数字使用,这个数字就是赋值语句完成后存储区里的数字。
(3)可以在一条语句里使用多个赋值操作符,这个时候先计算右边的,后计算左边的。
(4)符合赋值操作符的优先级和赋值操作符的优先级一样低。例如:n* = 2 + 3; 先计算加法,再乘等。
(5)不要在一条语句里对同样一个变量多次进行自增或者自减计算。
(6)“与”(&&)和“或”(||)都具有短路特性
如果前一个逻辑表达式的结果可以决定整个逻辑表达式的结果,则后一个逻辑表达式将被忽略。
0 && num++; //后面的自加不会运算
1 || num++; //后面的自加不会运算,num的值不变
(7)位操作符——对二进制数进行操作
~ 取法操作符 0变1,1变0
& 按位与 两个位都为1时,结果才为1
| 按位或 两个位都为0时,结果才为0
^ 按位异或 两个位相同为0,相异为1
<< 按位左移 各二进位全部左移若干位,高位丢弃,低位补0
>> 按位右移 各二进位全部右移若干位,对无符号数,高位补0
例如,位操作符简单使用
ch ^ 0010 0000 变换大小写
num & 1 == 1 为真表示num为奇数,否则为偶数
0000 0011 3
0000 0011 3 << 2
0000 1100 12
左移时右边空出来的位置一定填充0。
有符号类型整数右移后左边空出来的位置一定填充符号位;无符号类型整数右移后左边空出来的位置一定填充0。
一般情况下,数字向左移动n位,相当于乘以2的n次方;数字向右一定n位,相当于除以2的n次方。
所有位操作符不会修改存储区内容,而只会得到一个新数字(类似加减乘除)。
三目操作符中,不要在问号后面写赋值操作符。
#include<stdio.h>
//将一个小于255的十进制的正整数转换成二进制并打印出来(位运算符的使用)
void TransToBinaryByBit(int num)
{
unsigned char ch = 0x80;
int i = 0;
for(i=0; i<7; i++)
{
printf("%d", (num & ch) != 0);
ch >>= 1;
if(3 == i)
{
printf(" ");
}
}
printf("%d\n", (num & ch) != 0);
}
int main(int argc, char** argv)
{
int num = atoi(*(argv+1));
printf("num = %d\n", num);
TransToBinaryByBit(num);
return 0;
}
[root@localhost 0624]# vim TransToBinaryByBit.c
[root@localhost 0624]# gcc TransToBinaryByBit.c
[root@localhost 0624]# ./a.out 10
num = 10
0000 1010
[root@localhost 0624]# ./a.out 255
num = 255
1111 1111
9. 类型转换
(1)不同类型的数出现在一条表达式中时,发生强制类型转换。例如:-7 + 3u > 0 的结果是1。
(2)类型转换不会修改任何存储区的内容,计算机会分配一个新的存储区,把转换后的结果记录在新的存储区里。然后用这个新的存储区进行后面的计算。
(3)赋值运算符(=),从右向左执行,例如:num = ch = 300;
(4)逻辑运算符(>、<、==、!=)从左向右执行,例如:3 < 7 < 5; 结果为真,返回1。
10. 流程控制
(1)if分支中的逻辑表达式有先后顺序,如果前分支的逻辑表达式为真,就忽略后面分支的逻辑表达式,可以利用这一点简化后面的逻辑表达式。
(2)if分支里的逻辑表达式可能是不完整的,必须结合前面的所有逻辑表达式一起理解。
(3)如果for循环正常结束,则循环变量一定落在指定的数字范围之外。
(4)如果循环采用break语句结束,则循环结束后循环变量一定落在指定的数字范围之外。
(5)for循环可能不执行大括号里面的语句(一开始循环条件就不成立)。
(6)for循环中,小括号中最前面的部分和最后面的部分可以用逗号连接多条语句,但是中间的语句必须是逻辑表达式。
(7)所有的死循环都必须使用break语句结束。
(8)使用for循环打印图案
54321
5432
543
54
5
#include <stdio.h>
void PrintByFor()
{
int i = 0;
for(i=54321; i>=5; i /= 10)
{
printf("%d\n", i);
}
}
int main()
{
PrintByFor();
return 0;
}
[root@localhost 0624]# vim PrintByfor.c
[root@localhost 0624]# gcc PrintByfor.c
[root@localhost 0624]# ./a.out
54321
5432
543
54
5
(10)将一个十进制数字转换成二进制
#include <stdio.h>
void TransToBinaryByFor(int num)
{
unsigned char ch = 0x0;
for(ch=0x80; ch>=1; ch>>=1)
{
printf("%d", (num & ch)!= 0);
if(ch == 0x10)
{
printf(" ");
}
}
printf("\n");
}
int main(int argc, char** argv)
{
int num = atoi(*(argv+1));
TransToBinaryByFor(num);
return 0;
}
[root@localhost 0624]# vim TransToBinaryByFor.c
[root@localhost 0624]# gcc TransToBinaryByFor.c
[root@localhost 0624]# ./a.out 10
0000 1010