C语言知识-零零散散(一)
分类记忆C语言32个关键字
分类记忆
C语言编译过程
-
预处理,主要处理源代码中的预处理指令,引入头文件,去除注释,处理所有的条件编译指令,宏替换,添加行号。经过预处理指令后生成一个.i文件;
-
编译,编译过程所进行的是对预处理后的文件进行语法分析、词法分析、符号汇总,然后生成汇编代码。生成.s文件;
-
汇编,将汇编文件转换成二进制文件,二进制文件就可以让机器来读取。生成.o文件;
-
链接,将多个目标文件及所需要的库文件链接变成可执行文件
原码、反码、补码
在计算机中,一个数字是以二进制的形式存储的,也可以称为机器数。数字是有正负的,所以机器数的最高位表示符号位(0表示正数,1表示负数),其余的是数值位。我们人脑是可以知道最高位是符号位,然后可以对两个数字进行加减运算,但是让计算机进行识别,就显得没有那么的简单,那么为了可以让符号位也可以参与运算就产生了数字的原码、补码、反码,这样可以实现让计算机只进行加法运算(减法,即减去一个正数等于加上一个负数)。
原码
概念:最高位是符号位(0表示正数,1表示负数),数值位是数字本身绝对值的二进制数。
对于一个数字,正数的原码是其本身的二进制数;负数的原码是在其绝对值的基础上,最高位变为1。
例如:
数字 | 原码(8位二进制) |
---|---|
-10 | 1000 1010 |
+100 | 0110 0100 |
-1 | 1000 0001 |
反码
正数的反码是其本身;负数的反码是符号位保持不变,其他部分按位取反。
数字 | 反码(8位二进制) |
---|---|
-10 | 1111 0101 |
+100 | 0110 0100 |
-1 | 1111 1110 |
补码
一个正数的原码、反码、补码相同,负数的补码是其原码的基础上, 符号位不变, 其余各位取反, 最后+1(即,负数反码的基础上+1)。
数字 | 补码(8位二进制) |
---|---|
-10 | 1111 0110 |
+100 | 0110 0100 |
-1 | 1111 1111 |
sizeof关键字
sizeof不是函数,用于计算一个数据类型的大小,单位为字节。sizeof返回的是一个size_t类型的值,表示一个无符号的整数。
sizeof和strlen的区别?
- sizeof是一个操作符,strlen是库函数;
- sizeof的参数可以是数据的类型,也可以是变量,而strlen只能以结尾为‘\0‘的字符串作参数;
- 编译器在编译时就计算出了sizeof的结果。而strlen函数必须在运行时才能计算出来;
- sizeof计算的是数据类型占内存的大小(字符串,包括’\0’后的大小),而strlen计算的是字符串实际的长度(字符串不计算’\0’);
- 数组做sizeof的参数不退化,传递给strlen就退化为指针。
C语言中几种类型所占字节数
C语言中各类型变量所占字节数的问题。C标准并没有具体给出规定哪个基本类型应该是多少个字节数,其在内存中所占用的字节数与所选择的操作系统、编译器有关。但是,在long、int、char等数据类型中,long 类型整数的长度不能短于 int 类型, short 类型整数的长度不能短于 int 类型。(最简单的方法就是在你当前使用的开发环境中,用sizeof查看一下)
volatile关键字的作用?
volatile 关键字是一种类型修饰符,用它声明的类型变量表示可以被某些编译器未知的因素更改,比如:操作系统、硬件或者其它线程等。遇到这个关键字声明的变量,编译器对访问该变量的代码就不再进行优化,从而可以提供对特殊地址的稳定访问。声明时语法:volatile int varName
; 当要求使用 volatile 声明的变量的值的时候,系统总是重新从它所在的内存读取数据,即使它前面的指令刚刚从该处读取过数据。而且读取的数据立刻被保存。
volatile用在如下的几个地方:
- 中断服务程序中修改的供其它程序检测的变量需要加volatile;
- 多任务环境下各任务间共享的标志应该加volatile;
- 存储器映射的硬件寄存器通常也要加volatile说明,因为每次对它的读写都可能由不同意义;
printf格式化字符输出对应解释
printf格式字符
打印格式 | 对应解释 |
---|---|
%c | char,字符型 |
%d | int,有符号整型 |
%hd | short int,短整型 |
%ld | long,表示长整型 |
%hu | unsigned short,无符号短整型 |
%o | 无符号八进制整数 |
%u | 无符号十进制整数 |
%x,%X | 无符号十六进制整数,小写的x输出为小写,大写的X输出为大写 |
%f | float,单精度浮点数 |
%lf | double,双精度浮点数 |
%e,%E | 科学计数法表示的数字 |
%s | char*,输出字符串(以’\0’结束) |
%p | 指针,以十六进制输出结果 |
printf 控制符说明
字符 | 含义 |
---|---|
- | 有-表示左对齐输出,如省略表示右对齐输出 |
0 | 有0表示指定空位填0,如省略表示指定空位不填 |
m.n | m指域宽,即对应的输出项在输出设备上所占的字符数;n指精度,用于说明输出的实型数的小数位数,未指定n时,隐含的精度为n=6位 |
l,h | l表示long类型,h表示short类型 |
&&和&、||和|有什么区别
- &和|对操作数进行求值运算,&&和||只是判断逻辑关系;
- &&和||在在判断左侧操作数就能确定结果的情况下就不再对右侧操作数求值(&&和||会出现短路效应)。
数组
一维数组初始化
1. 给数组中的所有的元素赋值
int arr[6] = {1,2,3,4,5,6};
把要初始化的数值放在一个花括号内,从左到右把值赋值给相应的下标;初始化的时候,使用逗号隔开,不是使用分号。需要注意的是,初始化的时候,初始列表中值的个数不能超过数组的长度,否则会报错。
2. 数组中的部分元素进行赋值
int arr[6] = {1,2,3};
上面代码初始化的方式是,把数组的前三个成员分别赋值为1、2、3,后面没有初始化的成员自动赋值为0;**需要注意的是,**如果想要把定义的一个数组初始值都设置为0,直接写 int arr[6] = {};
是错误的,在c语言中没有这种初始化的语法。如果直接写int arr[6];
表示的是定义一个含有六个整型变量的数组,并没有初始化,把整个数组的值初始化为零的正确写法为 int arr[6] = {0};
3.定义时不指定数组的长度,但必须要进行初始化
int arr[] = {1,2,3,4,5,6};
此种初始化方式,必须要有初始化的元素,初始化列表中元素的个数就是数组的长度。如果定义数组时不初始化,那么省略数组长度就是语法错误,即int arr[];
是错误的。
二维数组初始化
1.数组中的成员全部赋值
int arr1[3][4] = {{1,2,3,4}, {5,6,7,8},{9,10,11,12}};
int arr2[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
给一个二维数组初始化可以采用arr1
分段赋值的形式,也可以采用arr2
连续赋值的形式。
2.部分赋值
int arr[3][4] = {1,2,3,4};
此种赋值,即二维数组中的第一行赋值为1、2、3、4,其余成员的值为零。即,二维数组也是按照初始列表中给出的值进行依次赋值,没有给出的成员,默认赋值为零。
int arr[3][4] = {{1},{2},{3}}
在分段赋值的方式中,每一个行相当于一个一维数组,初始化的方式和一维数组相同,未给出的值默认初始化为零。
int arr[3][4] = {0};
数组全部成员初始化为零。
int arr[][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
如果已知二维数组中的全部数值,第一维的长度可以不写,系统可以默认计算出它的第一维度,第二维度必须要写。
C语言中,定义数组a,那么a和&a有什么区别?
- 数组名a表示数组的首地址,和&a[0]的地址相同,而&a是数组指针;
- 使用sizeof(a)计算的是数组a占用的内存,而sizeof(&a)是指针的大小;
- 使用数组对指针进行赋值时,
int* ptr1 = (int*)(&a+1)
表示的是ptr初始化的值为数组a的首地址加上整个数组的长度,int* ptr2 = (int*)(a+1)
表示的是ptr的初始值为数组a的首地址加上一个int类型数组的长度;如果int a[3] = {1,2,3};
那么*(ptr1-1) = 3
,*ptr2 = a[1] = 2
。
字符数组与字符串
字符数组与字符串区别?
在C语言中,没有字符串类型的数据结构,字符串实际上是以’\0’结尾的一维char数组,所以字符串一点是字符数组,字符数组不一定是字符串。如下面的例子:
char s1[] = {'h','e','l','l','o'};
char s2[] = {'h','e','l','l','o', '\0'};
其中的s1是字符数组,s2是一个字符串。再如下面字符串:
char s3[] = {'h','e','l','l','o', '\0','w','o','r','l','d','\0'};
printf("%s\n", s3);
输出结果为 hello
,s3的长度为12,但是对于字符串而已,遇到第一个\0
以后,后面的字符直接舍弃,所以输出为 hello
。
字符串的初始化
方式1:
char s1[] = {'h','e','l','l','o', '\0'};
char s2[100] = {'h','e','l','l','o'};
如果定义的字符数组,不指定长度,那么定义数组的最后一个元素一定要以\0
结束,如字符串s1;如果定义了字符数组的长度,那么字符串的长度一定要小于字符数组的长度,系统会默认把缺少的值设置为\0
,如字符串s2。
方式2;
char s3[] = {"hello"};
char s4[] = "hello";
采用这种方式,系统会在结尾自动补充\0
,所以数组s3,s4的实际长度为6。下面的这种方式进行初始化字符串是错误的。
char s5[];
s5 = "hello";
方式3:
char* s6 = "hello";
char* s7;
s7 = "hello";
可以使用指针变量初始化字符串,可以在定义时直接初始化,可以先定义后赋值。由于定义的字符指针变量并没有确定指向字符串的长度,所以可以进行多次赋值,但是定义的字符数组,有了确定的内存,所以只可以通过下标修改某个字符的值。