1、关键字
static
1、函数体内的变量,这个变量只被声明一次。
2、在模块内的变量,只能被模块内函数使用,不能被模块外函数访问,表示本地全局变量
extern
1、引用同一文件变量,使用在声明之前时,可以使用关键字extern,让声明在程序任意位置。
2、引用另一个文件中的变量,extern可以引用其他文件中的全局变量,而且extern只需要指明数据类型和数据名称
3、引用另一个文件中的函数,可以不用包含头文件引用函数。
new/delete malloc/free
1、new/delete是操作符,malloc/free是库函数
2、new/delete可以调用构造函数/析构函数,m/f 只是分配内存。
struct 和 union区别
1、联合体公用一块地址空间,联合体变量长度等于最长的成员的长度
2、联合体对不同成员赋值,会将其他成员重写。
const
1、定义变量为常量
2、修饰参数为常量
3、修饰返回值为常量
总结:将一个变量变为只读模式
sizeof和strlen
1、sizeof是运算符,strlen是函数
2、sizeof可以用类型、函数作为参数,strlen只能计算 char*,还必须以/0结尾
3、sizeof编译的时候计算,strlen是运行期计算,表示字符串长度,不是内存大小。
typedef和 define
1、都是替对象取一个别名,增强程序的可读性
2、typedef用来定义类型别名,不止包含内部类型还包含自定义类型,方便记忆
3、define为预处理指令,不做正确性检查,只有带入之后才能发现
4、define不仅可以给类型取别名,还能定义常量、变量、编译开关。
5、define没有作用域限制,typedef有。
define和const ,谁定义常量最好
1、define只是文本替换,声明周期止于编译期,不分配内存空间,存在于代码段。const常量存在于数据段,堆栈中分配了空间。
2、const有数据类型,编译器可以对const进行安全检查。
3、const有保护常量不被修改的作用,提高程序的健壮性。
总结:一般倾向于用const定义常量
2、内存
C语言五大区域:
栈 区: 由编译器自动分配释放 存放函数形参和局部变量
堆 区: 由程序员分配和释放 调用malloc来进行分配
代码区: 存放程序的二进制代码
文字常量区: 存放字符串、数字等常量
全局区(静态存储区): 存放全局变量和静态变量
什么是代码段,数据段,bss段,堆,栈?
代码段:存放程序 执行代码 的一块区域,通常是只读
数据段:存放 已初始化的全局变量 和 已初始化为非0的静态变量
BSS 段:未初始化的全局变量 和 未初始化的静态变量或者初始化为0的静态变量
数据段和BSS段本质上都是静态区,存放全局变量和静态变量的
堆: 存放进程中被动态分配的内存段。
栈: 存放程序临时变量
堆和栈的区别
1、申请方式。栈为操作系统自动分配/释放,堆为手动
2、申请大小,栈空间有限,向低地址拓展的连续区域,堆是向高地址拓展的不连续区域,链表储存的空闲地址。
3、申请效率,栈是系统自动分配,速度快,不可控。堆是由new分配,速度比较慢,容易产生内存碎片。
栈的作用
1、储存临时(局部)变量
2、多线程编程的基础。每个线程至少有一个栈用来存储临时变量和返回的值。
为什么不能共用一个栈:
1、内核栈大小有限,用户程序调用次数可能很多。
2、用户栈空间不能提供相应保护措施
为什么堆的空间是不连续的?
1、堆包含一个链表来维护已用和空闲的内存块。
2、分配的空间在逻辑地址(虚拟地址)上是连续的,但在物理地址上是不连续的
什么是用户栈和内核栈?
内核栈 :内存中属于操作系统空间的一块区域。
作用:
1、保存中断现场
2、保存调用的参数、返回值、函数局部变量
用户栈:用户进程空间的一块区域,
作用:
用于保存用户空间子程序间调用的参数,返回值以及局部变量。
C语言内存分配方式
1、静态储存区分配
2、栈上分配
3、堆上分配
内存泄漏
申请了没有释放,由程序申请的一块内存,没有任何指针指向它,这个内存就泄露了。
避免内存泄漏方法
1、分配的内存以链表管理,使用完毕后从链表删除,程序结束的时候检查链表
2、良好的编程习惯,在设计内存的程序段,检验出内存泄漏,使用了内存分配的函数,使用完毕后将使用的相应函数释放掉
3、指针
数组指针和指针数组
数组指针,本质是一个指针,指向一个数组
指针数组,本质是一个数组,里面装的是指针。
函数指针和指针函数
函数指针,本身是一个指针,指向一个函数的地址
指针函数,本身是一个函数,返回值是指针。
指针和数组名区别
指针保存的是地址,数组保存的是数据,只有一个数组名表示第一个元素的地址
指针间接访问,数组直接下标或者偏移量
sizeof 有区别,指针为指针大小,数组为全体数据大小
指针常量,常量指针、指向常量的指针*************
int *const p 指向地址不变,地址值可变
int const *p 指向地址可变,地址值不能边
const int * const p 都不能变
指针与引用区别
都是地址,指针是地址,应用是别名
引用本质是指针常量,对象不变,对象的值可变
++不同,指针是地址自增,引用是对象自增
指针需要解引用
指针可为空,引用不行
sizeof不同 一个是指针大小一个是对象大小
野指针
指向不可用内存的指针,指针被创建时如果没有初始化就是野指针
指针被free、delete时没有指向NULL就是野指针
指针超出了变量的地址范围
4、预处理
预处理器标识#error的目的是什么?
遇到#error就会生成一个编译错误提示信息,并停止编译
#include"" 和 include<>区别
<>号先搜索标准库搜索系统文件比较快,“”号先搜索工作路径搜索自定义文件比较快
头文件作用
通过文件调用库功能,源码保护
头文件加强类型安全检查,编译器报错
5、 变量
全局变量和静态变量
全局变量作用域为程序块,局部变量为当前函数
全局变量储存在静态区,后者为栈
全局变量生命周期为主函数,局部变量生命周期在局部函数中,甚至循环体内
重载与覆盖
1、覆盖是子类和父类的关系,垂直关系,重载是一个类之间的关系,水平关系
2、覆盖一对一,重载多个方法
3、覆盖由对象类型决定,重载根据调用的参数表决定
C语言函数调用方法
使用栈来支持函数调用操作,栈被用来传递参数,返回值,局部变量等。
函数调用主要操作栈帧结构
select函数
int select(int maxfdp,fd_set *readfds,fd_set *writefds,fd_set *errorfds,struct timeval *timeout);
1
fork wait exec函数
1、附近产生的子进程使用fork拷贝出一个父进程的副本
数组的下标可以为负数吗?
可以,数组下标指地址偏移量,根据偏移量能定位得到目标地址。
inline函数和宏定义的区别
1、内联函数在编译时展开,而宏在预编译时展开。
2、在编译的时候,内联函数直接被嵌入到目标代码中去,而宏只是一个简单的文本替换。
3、内联函数可以进行诸如类型安全检查、语句是否正确等编译功能,宏不具有这样的功能。
4、宏不是函数,而inline是函数。
5、宏在定义时要小心处理宏参数,一般用括号括起来,否则容易出现二义性。而内联函数不会出现二义性。
6、inline可以不展开,宏一定要展开。因为inline指示对编译器来说,只是一个建议,编译器可以选择忽略该建议,不对该函数进行展开。
7、宏定义在形式上类似于一个函数,但在使用它时,仅仅只是做预处理器符号表中的简单替换,因此它不能进行参数有效性的检测,也就不能享受C++编译器严格类型检查的好处,另外它的返回值也不能被强制转换为可转换的合适的类型,这样,它的使用就存在着一系列的隐患和局限性。
宏和函数的优缺点
1、函数调用时,先求出实参表达式的值,然后带入形参。而使用带参数的函数只是进行简单的字符替换
2、函数调用实在程序运行时处理的,分配的临时的内存单元;而宏展开则是在编译时进行的,在展开时不分配i内存单元,不进行值的传递,也没有"返回值的概念"
3、函数实参形参都要定义类型,二者要求一致 ,宏不存在类型问题,宏没有类型,宏的参数只是一个符号代表,展开时代入指定的字符就行,宏定义时字串可以是任意内心的数据
4、函数只可以得到一个返回值,宏可以设法得到多个
5、使用宏次数多时,展开后源程序长,每次展开都使程序增长,而函数调用不使源程序变长。
6、宏的替换不占用时间,只占用编译时间,函数调用占用运行时间。
简单回答:宏由编译计算,增加编译时间,函数运行的时候计算,增加运行时间;函数的返回值入口参数有数据类型,宏只是简单的符号加减。
strcpy()和memcpy()的区别
1、复制的内容不同。strcpy只能复制字符串,而memcpy可以复制任意内容,例如字符数组、整型、结构体、类等。
2、复制的方法不同。strcpy不需要指定长度,它遇到被复制字符的串结束符"\0"才结束,所以容易溢出。memcpy则是根据其第3个参数决定复制的长度。
3、用途不同。通常在复制字符串时用strcpy,而需要复制其他类型数据时则一般用memcpy
\r 和 \n 的作用:
\r是表示回车(CR),ascii码值为13
注意回车和换行是不一样的
c语言编程时(windows系统)
\r 就是return 回到本行行首这就会把这一行以前的输出覆盖掉,但不会移动到下一行
\n表示将光标移动到下一行,但不会移动到行首。
单独一个\r或\n都不是一般意义上的回车+换行,\r\n放在一起才是。
通常在写程序的时候只要一个\n就可以了,这是因为编译器会自动将\n替换成\r\n。
1.7 容器与算法
map与set区别和底层实现
1、底层实现都是红黑树
2、map是键值对,关键字起到索引作用,值表示与索引相关联的数据,set是关键字的集合并且每个元素只包含一个关键字。
3、set迭代器是const不能修改元素值,map允许修改value不能修改key
4、map支持下标操作,set不支持,map可以用key作为下标,set用find
引用与指针的区别
1、引用必须被初始化,指针不必。
2、引用初始化以后不能被改变,指针可以改变所指的对象。
3、不存在指向空值的引用,但是存在指向空值的指针。
静态成员函数与非静态成员函数的区别
前者没有 this 指针,后者有 this 指针。
静态成员函数只要用来访问静态数据成员,而不访问非静态成员
1.9 面对对象
面向对象和面向过程有什么区别?
面向过程就是分析出解决问题所需要的步骤,然后用函数把这些步骤一步一步实现,使用的时候一个一个依次调用就可以了;面向对象是把构成问题事务分解成各个对象,建立对象的目的不是为了完成一个步骤,而是为了描叙某个事物在整个解决问题的步骤中的行为。
1、面对对象以对象为中心,面向过程以过程为中心
2、面对对象把代码封装成一个整体,其他对象不能直接修改其数据。面向过程直接使用程序来处理数据,各模块存在控制与被控制的关系。
3、面对对象是将问题分为不同的对象,给予对象赋予属性和行为。面对过程则是将事件分为不同的步骤,按照步骤完成编程。
面对对象的基本特点
1、封装: 把过程和数据封装起来,只有定义的接口才能调用
2、继承:子类继承父类的功能
3、多态:不同的对象对从父类继承的同一动作做出不同的反应,
4、抽象:不打算了解问题全部,只关注当前目标。过程抽象和数据抽象。过程抽象是指任何操作都被当成实体看待,不在乎它是不是由其他子函数完成。
1.11数据结构
链表和数组的区别
数组在内存中栈上按顺序存储的,而链表是在堆上随机存储的。
要访问数组中的元素可以按下标索引来访问,速度比较快,如果对他进行插入操作的话,就得移动很多元素,所以对数组进行插入操作效率很低. 由于连表是随机存储的,链表在插入,删除操作上有很高的效率(相对数组)
如果要访问链表中的某个元素的话,那就得从链表的头逐个遍历,直到找到所需要的元素为止,所以链表的随机访问的效率就比数组要低 。