易混知识点
在学习C语言程序设计过程中,练习的习题记录自己易错易混的知识点,可能出现重复知识点,说明是自己反复错的,将来若学习到新的知识点,同样会再补充到终篇之中。
-
关于new delete 与malloc free 的联系与区别描述?
- 用malloc函数需要指定内存分配的字节数并且不能初始化对象,new 会自动调用对象的构造函数
- delete 会调用对象的destructor,而free 不会调用对象的destructor
- 都是在堆上取得内存。
-
char *p1 = "123", *p2 = "ABC", str[50]= "xyz"; strcpy(str + 2, strcat(p1, p2)); cout << str;
-
c++的内存分配地址有小到大分别是:动态内存区,包括栈和堆;代码内存区;静态内存区,包括全局变量,静态变量,只读变量(就是常量),按照定义的先后顺序分配地址;
本题中,p1,p2都是指针,是局部变量,在分配内存时会将其分配到栈区,且只会分配p本身所需要的内存空间,即p所指向的内容的地址大小的空间,并没有为字符串分配内存,所以p1="123"和p2="ABC"中 123和ABC都被分配在了文字常量区,既然是常量,大小就是不可变的,而strcat的第一个变量必须是可变的,所以程序会出错;
-
-
共用体(联合)是一种同一存储区域由不同类型变量共享的数据类型。 结构体是用同一个名字引用的相关变量的集合
-
总结一下引用和指针的区别:
1、引用是直接访问,指针是间接访问
2、引用是变量的别名,本身不单独分配自己的内存空间。指针有自己的内存空间。
3、引用一旦初始化,不能再引用其他变量。而指针可以!
-
函数参数类型以形参为准
-
1)在定义指针时,“*”号表示定义的变量是指针变量,变量的值只能存放地址。
2)一个类型的指针只能指向同类型的变量,不能指向其他类型的变量。(注意void类型的指针可以指向任何类型的指针)
3指针也可以被声明为全局、静态局部和局部的。 -
口诀为
括号成员第一; //括号运算符 成员运算符. ->
全体单目第二; //所有的单目运算符比如++、 --、 +(正)、 -(负) 、指针运算*、&
乘除余三,加减四; //这个"余"是指取余运算即%
移位五,关系六; //移位运算符:<< >> ,关系:> < >= <= 等
等于(与)不等排第七; //即== 和!=
位与异或和位或,“三分天下”***十; //这几个都是位运算: 位与(&)异或(^)位或(|)
逻辑或跟与,十二和十一; //逻辑运算符:|| 和 &&,注意顺序:优先级(||) 低于 优先级(&&)
条件高于赋值, //三目运算符优先级排到13 位只比赋值运算符和","高
逗号运算级最低! //逗号运算符优先级最低 -
指针变量确实可以指向不同类型的变量,但是这种情况下,有可能导致异常情况,建议不要使用;有一种可以使用的情况就是void *类型的指针变量,注意这里的void *代表的不是空指针类型,而是任意指针类型,也就是说,可以让这种类型的指针指向任何其他类型的变量,系统中的malloc函数的返回值类型就是这种通用的指针类型,这使得C程序变得更为的灵活
-
函数的隐含储存类型是extern,函数的形参或变量的储存类型为auto
① auto : 既陌生又熟悉,因为它可以省略,比如 int age; 在局部变量中,age的作用域就是当前局部变量的范围,一旦程序运行 出当前域,则此变量被隐藏或自动释放。故我们一般都不用它。
②static : 静态变量,程序在第一次给它赋值后,即使变量所在函数执行完,它也不会被释放,这就是静态变量,它的生命周期等 同于整个程序的运行周期。
③register : 寄存器型变量:这个关键字要求编译器尽可能的将变量存储在CPU内部寄存器中,而不是通过内存寻址访问,以提 高效率。但是这只是给系统的一个暗示,如果寄存器资源有限,系统也不会满足你的要求。register型变量存取速度 比内存快很多,一般你在一些系统库文件,或诸如单片机官方接口库中使用较多,正常我们能用上较少。
④extern : 外部变量:它属于变量声明,extern int a和int a的区别就是,前者告诉编译器,有一个int类型的变量a定义在其他地 方,如果有调用请去其他文件中查找定义
-
strlen 是函数,sizeof 是运算符。
strlen 测量的是字符的实际长度,以’\0’ 结束,也就是说不算最后的’\0’;
而sizeof 测量的是字符的分配大小,包括最后的’\0’;
帮助记忆的话,可以理解成运算符更加底层一下,会输出真实的内存占用情况,而函数有封装,会处理成用户想要看到的内容 -
常用数据类型对应字节数 可用如sizeof(char),sizeof(char*)等得出 32位编译器: char :1个字节 char (即指针变量): 4个字节(32位的寻址空间是2^32, 即32个bit,也就是4个字节。同理64位编译器) short int : 2个字节 int: 4个字节 unsigned int : 4个字节 float: 4个字节 double: 8个字节 long: 4个字节 long long: 8个字节 unsigned long: 4个字节 。 64位编译器: char :1个字节 char(即指针变量): 8个字节 short int : 2个字节 int: 4个字节 unsigned int : 4个字节 float: 4个字节 double: 8个字节 long: 8个字节 long long: 8个字节 unsigned long: 8个字节
-
如果 const 位于 * 的左侧,则 const 就是用来修饰指针所指向的变量,即指针指向为常量;
如果 const 位于 * 的右侧, const 就是修饰指针本身,即指针本身是常量。* (指针)和 const(常量) 谁在前先读谁 ;*象征着地址,const象征着内容;谁在前面谁就不允许改变。 -
const在*的左边,则指针指向的变量的值,不可直接通过指针改变(可以通过其他途径改变);
在*的右边,则指针的指向不可变。
简记为“左定值,右定向”。
-
常量指针:指向的地址可以变,但内容不可以重新赋值,内容的改变只能通过修改地址指向后变换。
指针常量:指向的地址不可以重新赋值,但内容可以改变,必须初始化,地址跟随一生。
-
如果是已经定义过的内容就只能用extern
一个变量的声明和定义是不一样的!
假如一个头文件里面有 int a = 0; 如果用头文件包含就会出错的!声明可以有多次但是定义只能有一次!
-
printf函数中,"%.5s“的意思是字符串最多输出5个字符。
追问
那如果是”%5s"的话是什么意思?
追答
"%5s"的意思是,输出至少5个字符,如果不够五个字符,左侧用空格补齐。 -
default一定最后执行
(1)k = 1时先匹配,发现无匹配,故执行default后c = 1,由于default后无break,故顺序执行case2(此时已不是匹配),c = 2,之后break
(2)k = 2时匹配case2,c加一变成3,之后break;
故c = 3
这里关键是default放在前面,且后无break,故匹配不到数据执行完default后还会再顺序执行下面内容,也可避免此种情况,因为下面已经无内容。 -
指针的类型用于确定指针所指的对象的类型,因此初始化或赋值时必须保证类型匹配。指针用于间接访问对象,并给予指针的类型提供可执行的操作,例如,int型指针只能把其指向的对象当作int型数据来处理,如果该指针指向了其他类型(如double类型)的对象,则在指针上执行的任何操作都有可能出错。 一个有效的指针必然是以下三种状态之一: 保存一个特定的对象的地址; 指向某个对象后面的另一对象; 或者是0值。
-
内存中数据,只有原码和补码之分,ASCII码还不是 转化成它对应的数值存放。内存里哪有ASCII码,需要对应的库转化。
-
一个简便的记忆法:0:48 A:65 a:97,数字连起来是486597 -> 486 597 -> 486 (486 + 111)
-
定义类型并不会分配空间,只有在定义变量时才会分配内存空间
-
数组的大小在声明时已经给出,而不是由初始化被赋值元素的个数决定的
-
从低到高的转换通常可以保持其值不变,而从高到低的转换可能会有数据的舍入,从而损失精度
-
自动变量
-
字节对齐的三个准则:
-
结构体变量的首地址能够被其最宽基本类型成员的大小所整除;
-
结构体每个成员相对于结构体首地址的偏移量都是成员大小的整数倍,如有需要编译器会在成员之间加上填充字节;
-
结构体的总大小为结构体最宽基本类型成员大小的整数倍,如有需要编译器会在最末一个成员之后加上填充字节。
-
-
先说宏和函数的区别:
-
宏做的是简单的字符串替换(注意是字符串的替换,不是其他类型参数的替换),而函数的参数的传递,参数是有数据类型的,可以是各种各样的类型.
-
宏的参数替换是不经计算而直接处理的,而函数调用是将实参的值传递给形参,既然说是值,自然是计算得来的.
-
宏在编译之前进行,即先用宏体替换宏名,然后再编译的,而函数显然是编译之后,在执行时,才调用的.因此,宏占用的是编译的时间,而函数占用的是执行时的时间.
-
宏的参数是不占内存空间的,因为只是做字符串的替换,而函数调用时的参数传递则是具体变量之间的信息传递,形参作为函数的局部变量,显然是占用内存的.
-
函数的调用是需要付出一定的时空开销的,因为系统在调用函数时,要保留现场,然后转入被调用函数去执行,调用完,再返回主调函数,此时再恢复现场,这些操作,显然在宏中是没有的.
内联函数与宏的区别:
1.内联函数在运行时可调试,而宏定义不可以;
2.编译器会对内联函数的参数类型做安全检查或自动类型转换(同普通函数),而宏定义则不会;
3.内联函数可以访问类的成员变量,宏定义则不能;
4.在类中声明同时定义的成员函数,自动转化为内联函数。 -
-
结构体中第一个成员不能缺省
-
指针传递只是传了一个地址copy, 在函数内部改变形参所指向的地址,不能改变原实参指向的地址,仅可以通过修改形参地址的内容,来达到修改实参内容的目的,所以如果想通过被调函数来修改原实参的地址或给重新分配一个对象都是不能完成的,只能使用双指针或指针引用。
-
子函数中传递的数组被当成指针对待,sizeof()以后只是一个指针的大小
-
C陷阱与缺陷里有说,运算符遵循贪心法原则,就是 i++ + j。++在运算结束后算
-
-
预处理命令行不能以分号结尾
-
预处理命令行可以出现在程序的最后一行
-
预处理命令行作用域是整个文件
-
-
static变量和全局变量都是有操作系统释放
- 局部变量存放在栈区
- new出来的变量存放在堆区,由程序员手动释放
- 静态变量存在局部限制,但存放在全局区
-
~、^、&、|、%等都必须两边是整型
-
函数 ftell 用于得到文件位置指针当前位置相对于文件首的偏移字节数。
-
*p++ 先解引用当前p指向的位置,再把p加1即 *p++ ==> *p; p++
-
数组名作为参数传递的时候将作为指针处理,可以通过指针名对值进行修改
-
字符串复制函数strcpy (字符数组1, 字符数组2),字符数组1的长度应大于等于字符数组2的长度
-
形参和实参,函数定义时的参数是形参,函数使用时传入的参数为实参;
实际运行时,要想要改变传入实参的值,需要传入该实参的指针或者引用(地址),修改该地址存储的值以达到修改的目的。否则,传入的实参仅仅作为该函数域范围内的局部变量,函数结束后,该变量会被清理。
-
-
define宏是在预处理阶段展开。const常量是编译运行阶段使用
- 预处理阶段做了的任务:
1:将头文件中的内容(源文件之外的文件)插入到源文件中
2:进行了宏替换的过程(简单的字符串替换),定义和替换了由#define指令定义的符号
3:删除掉注释的过程,注释是不会带入到编译阶段
4:条件编译
- 预处理阶段做了的任务:
-
- define宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存。const常量会在内存中分配(可以是堆中也可以是栈中)
- 宏没有类型,不做任何类型检查,仅仅是展开。const常量有具体的类型,在编译阶段会执行类型检查
- const定义的常量只有一次拷贝没毛病,而define定义的变量在内存中并没有拷贝,因为所有的预处理指令都在预处理时进行了替换
- 宏没有类型,不做任何类型检查,仅仅是展开。const常量有具体的类型,在编译阶段会执行类型检查
-
空类的大小原本是0,但是为什么实际上是1,主要的一个中心思想是在强调:任何相同类的不同对象应该拥有不同的地址。
-
在C中使用malloc时不需要强制类型转换,因为在C中从void*到其他类型的指针是自动隐式转换的;
- 在C++中使用malloc时必须要强制类型转换,否则会报错,但在c++中一般用new而不用malloc;
-
宏与类型无关,但是c++中函数必须指定返回类型,故宏可以做函数不能做的事
宏只是预定义的函数,在编译阶段不进行类型安全性检查,在编译的时候将对应函数用宏命令替换。对程序性能无影响。
-
i = i & (i-1),统计i二进制中有多少个1
- i = i | (i+1),统计i二进制中有多少个0
-
数组作为参数也表示指针
-
无符号整数➕有符号整数,有符号整数转换为无符号整数
-
static变量和全局变量都是有操作系统释放
局部变量存放在栈区 new出来的变量存放在堆区,由程序员手动释放 静态变量存在局部限制,但存放在全局区
-
函数参数的默认值不可以是局部变量,因为默认参数的函数调用是在编译时确定的,而局部变量的位置与值在编译时均无法确定
-
数组名作为函数参数传递给函数时,会退化为指针。
void UpperCase( char str[]) 等价于 void UpperCase( char *str) 所以sizeof(str)得到的是指针的大小
-
-
sizeof是运算符,strlen是函数。
-
sizeof可以用类型做参数,strlen只能用char*做参数,且必须是以’‘\0’'结尾的。
-
数组做sizeof的参数不退化,传递给strlen就退化为指针了。
-
大部分编译程序 在编译的时候就把sizeof计算过了 是类型或是变量的长度这就是sizeof(x)可以用来定义数组维数的原因
-
strlen的结果要在运行的时候才能计算出来,是用来计算字符串的长度,不是类型占内存的大小。
-
当适用于一个结构类型时或变量, sizeof 返回实际的大小,当适用于静态地空间数组, sizeof 返回全部数组的尺寸。sizeof 操作符不能返回动态地被分派了的数组或外部的数组的尺寸 ;(故上面函数内是错误的)
举例说明:char str[20]="0123456789"; int a=strlen(str); //a=10; >>> strlen 计算字符串的长度,以结束符 0x00 为字符串结束。 int b=sizeof(str); //而b=20; >>> sizeof 计算的则是分配的数组 str[20] 所占的内存空间的大小,不受里面存储的内容改变。 上面是对静态数组处理的结果。 如果是对指针,结果就不一样了 char* ss = "0123456789"; sizeof(ss) 结果 4 ===》ss是指向字符串常量的字符指针, sizeof 获得的是一个指针的之所占的空间,应该是整型的,32位系统是4;64位系统是8;
-
-
任何简单或复杂的算法都可以由顺序结构、选择结构(又叫分支结构,有单分支,双分支,多分支)和循环结构这三种基本结构组合而成
-
二维数组可以看成二级指针,
a[i]
或*(a+i)
可看成第i行的指针,a[i]+j
或*(a+i)+j
可看成第i行第j列的指针 -
计算整数x的二进制表示有多少个1:
x&=x-1
可以消除x最低位的1,while循环计数,直到x=0即可。或者 x &= (x - 1) -
只保留整数x最低位的1:
x&-x
,暨鼎鼎大名的lowbit -
enum aa{a=5,b,c} bb;是C语言中定义的枚举类型;意思是:定义aa这个数据类型,其取值范围是a,b,c三个数,其中,a=5, b=6,c=7(如果不给b,c指定数值,就是其前一个数+1)。 同时定义aa 这个数据类型的变量bb=(enum aa)5;和你学到的 int a = (int)b;功能一样,是把数值5转义成(enum aa)数据类型,同时赋值给变量bb;
-
return 后面括号的值不一定是函数的值,譬如函数返回值与return 类型不一致需要类型转换,返回值为 int ,retun 3.2 ,那么肯定会进行转换的
-
实型字面值常量有两种表示方式:小数形式和指数形式
小数形式:由最前面的额正负号,数字0-9和小数点组成,不允许有其他符号; 指数形式;包括指数和尾数两个不可缺少的部分,用符号E(e)分割;E(e)左边是尾数,为十进制整数或小数形式的实数,右边为指数,必须为十进制整数,表示乘以10的多少次方
-
BSS(Block Started by Symbol)通常是指用来存放程序中未初始化的全局变量和静态变量的一块内存区域。特点是:可读写的,在程序执行之前BSS段会自动清0。所以,未初始的全局变量在程序执行之前已经成0了。
-
A.单链表的每个节点都具有唯一的前驱节点和唯一的后继节点,所以当两个单链表存在相交的节点时,这两个链表则同时拥有这个节点,以及这个节点的所有后继节点,当这个公共节点是尾节点时,他们则只含有公共一个节点-------尾节点。
B.快慢指针是判断单链表是否有环的一种方法:两个指针,每次移动的步长为2叫做快指针,每次移动步长为1的指针叫做慢指针。快慢指针同时从头结点出发,当快指针率先到达NULL的时候,则说明此单链表中不存在环,当快指针追上慢指针的时候,说明此单链表中存在环。
C.有环的单向链表和无环的单向链表不能相交,因为当相交的时候,无环的单向链表也会被迫存在一个环,只不过这个环的”起点“可能不是原来单向链表的头结点。
4.两个单向链表之间相交可以存在环。
-
存在这样的线性表:表中各结点都没有直接前趋和直接后继。–空表
-
%%d=%%和d
%%在屏幕上显示为%d还是d 所以是和k没有关系了~
-
队列实现方式有链表存储和顺序表存储两种:链表存储可设计为带有尾指针的单链表,即可高效实现入队出队,无需循环链表;顺序表存储为解决存储空间浪费而设计为循环队列。 因此循环队列仅有顺序表存储结构,与循环链表毫无关系。
-
一个非空的数据结构如果满足以下两个条件:有且只有一个根结点;每一个结点最多有一个前件,也最多有一个后件,则称为线性结构,在数据结构中习惯称为线性表。双向链表结点具有两个指针域,属于线性结构, A 选项错误。循环链表所有结点的指针域都为非空,属于线性结构, B 选项错误。循环链表是链表,循环队列属于队列,队列只能在队尾入队,在排头退队,链表可以在任何位置插入、删除, C 选项错误。双向链表结点具有多个指针域, D 选项正确
-
%X.Ys的格式化输出,X是总长度,Y是从字符串中从左边取Y位,剩下的位数补空格
-
对于数据结构课程而言,简单地说,线性结构是n个数据元素的有序(次序)集合。
1.集合中必存在唯一的一个"第一个元素"; 2.集合中必存在唯一的一个"最后的元素"; 3.除最后元素之外,其它数据元素均有唯一的"后继"; 4.除第一元素之外,其它数据元素均有唯一的"前驱"。 数据结构中线性结构指的是数据元素之间存在着“一对一”的线性关系的数据结构。 非线性结构:数学用语,其逻辑特征是一个结点元素可能有多个直接前趋和多个直接后继。 A选项:在链表中,如果每个结点有两个指针域,则该链表一定是非线性结构,错,类似于传统的双链表,一个指向前一个结点,一个指向后一个结点,这种双链表还是一个线性结构。 B选项:在链表中,如果有两个结点的同一个指针域的值相等,则该链表一定是非线性结构。对,如果有两个结点的同一个指针域的值,那么被指向的这个点,有两个前驱,违背了唯一的特点,所以必须是非线性结构。 C选项:在链表中,如果每个结点有两个指针域,则该链表一定是线性结构,错。例如变种的双链表,一个指向后继结点,一个指向链表中的任意结点。如果指向同一结点的话,就类似B选项,所以这个选项是错的。 D选项:在链表中,如果有两个结点的同一个指针域的值相等,则该链表一定是线性结构,错。一个普通的链表中,不同的结点值可以相等,但是这种链表是线性结果。所以这个选项是错的
-
广义表的长度: 若广义表不空,则广义表所包含的元素的个数,叫广义表的长度,数第一层括号内的逗号数目。
**广义表的深度: 广义表中括号的最大层数叫广义表的深度。** 长度:最外层包含元素的个数,即去掉最外层括号后含有的元素个数。 深度:表中含有括号数最多的括号层数加一。 head :返回列表的第一个元素,(不带括号) tail:返回列表删除第一个元素后剩余的列表(带括号)
-
线性链表中的各元素在存储空间中的位置不一定是连续的,且各元素的存储顺序也是任意的
-
顺序表中逻辑相邻物理位置必定相邻。单链表中逻辑相邻物理位置不一定相邻。
-
结构体变量不管其包含有多少个成员,都应当看成是一个整体。在程序运行
期间,只要在变量的生存期内,所有成员一直驻留在内存中,不可能出现有的成员驻留内 存,有的成员不驻留内存的情况
-
C 语言中用 “%%” 打印输出字符 “%”, 所以 %%d, 输出为 %d 两个普通字符 , 而不是格式控制符 “%d” 的含义
-
ava中只有byte, boolean是一个字节, char是两个字节, 所以对于java来说127不会发生溢出, 输出328
- 但是对于c/c++语言来说, char是一个字节, 会发生溢出, 对127加一发生溢出,
0111 1111 --> 1000 0000, 1000 0000
为补码-128, 所以结果为200-128=72
- 但是对于c/c++语言来说, char是一个字节, 会发生溢出, 对127加一发生溢出,
-
广义表是一个递归的定义,它的元素可以是 (1)单个元素 (2)子表
一对确定的(表头,表尾)可以唯一确定一个广义表: 1. 表头:广义表的第一个元素(广义) ,可能是一个元素(狭义),也可能是一个子表(但它作为第一个元素(广义)) 2. 表尾:除表头外其余元素组成的子表,一定是一个表! 举一些特殊的例子: 表A = (e) ,则表头为e,表尾为() 表B = ( ) ,即空表,长度=0 表C = (( )),长度=1,表头为( ),表尾为( )
-
拆分二维数组
int a[4][3] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12 }; 拆分成: int b[3] = {1, 2, 3 }; int c[3] = {4, 5, 6 }; int d[3] = {7, 8, 9 }; int e[3] = {10, 11, 12 }; \2. 为何拆分? 以“b[3] = {1, 2, 3 }”为例:b是数组第一个元素的地址,这里b相当于整型指针!上述b,c,d,e都是整型指针。 那么就有:a[4] = { b, c, d, e }; 这是一个一维数组,其中的**元素都是整型指针**。a是什么?是数组a中第一个元素b的地址! 根据上述这种理解,发现可以很方便的解出这道题。 分析:AC选项先看“int ( * prt)[3]=a”,相当于:int b[3];int *prt = &b; 即定义了一个指向“数组第一个元素的地址”的指针prt;而从1,2分析来看,a表示的正是b的地址。所以,这里等价于:prt = a。 我们看AC选项,先把ptr都换成a。 A:* (( * prt+1)[2]) *a 即a[0],也就是b; ( b+1) 表示 元素2的地址,也就是a[0][1]的地址; (b+1)[2] → *( (b+1) + 2 ) = *(b+3) = b[3],越界了!其实就是c[0],VS上验证过,输出也是4. 而答案提供的相当于*(b[3]),连数组元素都算不上! *注:**下标和指针转化公式:***(a+n) = a[n]* C:( * prt+1)+2( * a+1)+2 等价于(b+1) + 2 = b+3,是4的地址,也就是c[0]的地址;同样错误。不过可以验证*(( * prt+1)+2),输出为4. 5. B选项分析:* ( * (p+5)) int *p = a[0],相当于int *p = b,遇到p直接用b替换就行了!* (p+5)等价于b[5],也就是c[2],元素6,前面还多个*,所以这个错的也很明显。 6. D选项 **下标和指针转化公式:*****(a+n) = a[n]**,这个正反都可以使用,而且很好用。
-
%% 可以输出 %
-
用十进制整数来表示输出的最少位数。若实际位数多于定义的宽度,则按实际位数输出,若实际位数少于定义的宽度则补以空格或0。
-
不同数据类型之间的差别在于数据的表示范围及精度上,一般情况下,数据的表示范围越大、精度越高,其类型也越“高级”。
-
赋值运算中如果左值精度比右值精度低,将会出现截断,会导致精度丢失。
-
当函数调用时,所传实参与形参类型不一致时,也会把实参自动转换为形参类型后再赋值(类型以形参为准)
-
-
C++
-
char、short、int、long、bool 基本类型都可以用于switch语句。
-
float、double都不能用于switch语句。
-
enum类型,即枚举类型可以用于switch语句。
-
所有类型的对象都不能用于switch语句。
-
字符串也不能用于switch语句
-
-
C语言中的文件的存储方式有(可以顺序存取,也可随机存取)
-
C语言是一种结构化程序设计语言
-
空指针是一个特殊的指针值。
- 空指针是指可以确保没有指向任何一个对象的指针。通常使用宏定义NULL来表示空指针常量值。NULL就代表系统的0地址单元
- 空指针确保它和任何非空指针进行比较都不会相等,因此经常作为函数发生异常时的返回值使用。
-
在printf中的%作为转义符,两个%才相当于1个%