目录
sizeof和strlen的区别
static
extern "C"
typedef
const
#define宏定义
const和#define定义的常量对比
内联函数
内联函数和#define的区别
sizeof和strlen的区别
- sizeof是一个操作符,strlen是库函数。
- sizeof的参数可以是数据的类型,也可以是变量,而strlen的参数只能以结尾为‘\0’的字符串作参数。
- 编译器在编译时就计算出了sizeof的结果,而strlen函数必须在运行时才能计算出来。并且sizeof计算的是数据类型占内存的大小,而strlen计算的是字符串实际的长度(不包含\0)
- strlen 计算时,如果字符串不以 '\0' 结尾,将继续向后查找,直到遇到 '\0'
char str[] = "hello";
sizeof(str) = 6; // hello(5) + \0(1) = 6
/* str不是个指针,而是一个字符数组! */
static
static 的功能:
- 构成隐藏, (static函数, static变量都行). 当同时编译多个文件的时候, 所有没加 static 前缀的函数和全局变量都具有全局可见性, 一旦函数和全局变量加了static之后, 就只能本文件中可见.
- 保持变量内容的持久性, 下次使用的时候还能取到原来的值.
- static 变量和全局变量都存储在静态区(数据段), 会在编译的时候就完成初始化
- 默认初始化为0. 全局变量也具有这一特性. 静态区在内存中所有的字节默认值都是0x00
static 的用处:
- 修饰局部变量. 局部变量是放在栈上的, 生命周期在包含语句块执行结束时便结束了. 但是如果使用 static 修饰局部变量之后, 该变量将会存放在静态区, 其生命周期会一直延续直到整个程序执行结束. 但是虽然存储空间和生命周期发生了改变, 该变量的作用域依旧不变, 还是限制在其语句块中.
- 修饰全局变量. 对于一个全局变量来说, 既可以被本文件访问到, 也可以被同一个工程的其他源文件访问到(添加extern关键字就行). 用 static 关键修饰之后, 改变了其作用域, 变成了只能本文件中可见.
- 修饰函数. 和修饰全局变量类似, 都是改变了作用域.
- 在类中使用:
- 修饰类中的函数, 则表示该函数属于一个类而不属于此类的任何对象, 静态成员函数无法访问自己类的非静态成员.
- 修饰类中的变量, 则表示该变量时是此类所有对象共享的, 不需要实例化对象即可访问, 不能在类内部初始化, 一般在类外部初始化, 并且初始化的时候不加static.
- why 类内声明 类外初始化
- 成员变量的初始化是在初始化列表阶段,静态的不属于某个对象,类里只是声明,但必须在类外初始化,它的生命周期是全局的,不属于某个对象,属于所有对象,所有类
- static 成员函数不能被 virtual 修饰, static 成员不属于任何对象或实例, 所以加上 virtual 没有任何实际意义. 静态成员函数没有 this 指针, 虚函数的实现是为每一个对象分配一个 vptr 指针, 而 vptr 是通过 this 指针调用的, 所以不能为 virtual.
this 指针问题,
当调用一个对象的非静态成员函数时, 系统会把该对象的起始地址赋给成员函数的 this 指针,
而静态成员函数不属于任何一个对象,
所以静态成员函数没有 this 指针,
既然它没有指向某一对象, 也就无法对一个对象中的非静态成员进行访问
extern "C"
- extern 用在函数/变量的声明前, 用来说明这个函数/这个变量是在别的地方定义的, 要在此处引用.
- 有了 #include 为啥还要 extern ? 因为 extern 能够提前进行声明, 会加速程序的编译过程, 这样能节省时间
- 在C++中, 导入C函数的关键字是 extern , extern "C" 的主要作用就是为了能够正确实现C++代码调用其他C语言代码, 加上之后, 会指示编译器这部分代码按照C语言的语法进行编译, 而不是C++的.
typedef
- typedef 用来定义一种数据类型的别名,增强程序的可读性。
- 执行时间不同:typedef 是编译过程的一部分,有类型检查的功能。
- typedef 定义是语句,因为句尾要加上分号。
const
- 防止一个变量被修改, 可以使用 const 关键字. 在定义 const 变量时需要对它进行初始化, 因为以后就没有机会再去改变它了; const 在定义的时候必须初始化, const 生效于编译阶段.
- 在一个函数中, const 可以修饰形参, 表示这个形参在函数内部不能改变其值.
- 对于类的成员函数, 可以指定其返回值是 const 类型, 可以限制返回值的用途,防止它被修改。
- 在类中, 若指定成员函数为 const 类型, 则表示其是一个常函数, 常函数不能修改该类的成员变量
- const 成员函数可以访问其他成员, 非 const 成员函数不能访问 const 成员
- const 对象只能调用 const 修饰的成员函数(此函数称为常函数)
#define宏定义
- #define宏是在预处理阶段展开
- #define宏仅仅是代码展开,在多个地方进行字符串替换,不会分配内存
- #define 定义的常量是不带类型的, 宏定义是没有类型检查的, 无论是对还是错都是直接替换, 因此 define 定义的常量不利于类型的检查
- 而define不是语句, 千万不能在句尾加分号
const和#define定义的常量对比
- const常量有类型检查,而#define定义的常量没有类型检查。编译器可以在编译时检查const常量的类型是否正确,而#define定义的常量只有在运行时才能进行检查。
- const常量存储在程序的数据段或代码段中,而#define定义的常量存储在预处理器的文本段中。
- const常量可以在调试时查看其值,而#define定义的常量无法在调试时查看其值。
- const常量的作用域是整个程序,而#define定义的常量只在当前文件或函数内有效。
- const常量必须在声明时进行初始化,而#define定义的常量可以在程序运行过程中进行初始化。
综上所述,const常量比#define定义的常量更加安全、可靠、易于调试和维护。因此,建议在编写程序时尽量使用const常量而不是#define定义的常量。
内联函数
- 内联函数则是在编译的时候进行代码插入
- 内联函数是在C++中用于提高函数调用效率的一种机制。内联函数通过在函数声明前加上inline关键字来定义,提示编译器将函数内容直接插入调用点处,而不是进行常规的函数调用。这样可以避免函数调用的开销。
- 内联函数适用于函数体较小且被频繁调用的情况。对于较大的函数体,内联可能会导致代码膨胀,影响可执行文件的大小。因此,编译器可以选择忽略内联函数的请求,而将其视为普通的函数调用。最终是否内联函数的决策由编译器决定。
- 内联函数的定义通常放在头文件中,以便在多个源文件中进行内联展开。这是因为编译器需要在调用点处看到函数的定义才能进行内联展开。
- 内联函数是没有地址的
- 内联函数不能有循环语句和递归调用等可能导致死锁或栈溢出的操作。此外,内联函数也不能有全局或静态变量,因为这些变量需要在编译时分配内存,而内联函数要求在编译时完成代码嵌入。
- 内联函数不能是虚函数,
内联函数是在编译时展开的,没有运行时的动态绑定,而虚函数需要运行时的动态绑定。这两者的性质和用途不同,因此通常不能将内联函数声明为虚函数。
内联函数是在代码中直接展开, 减少函数调用的开销, 虚函数是为了在继承后对象能够准确的执行自己的动作, 这是不可能统一的
内联函数是在编译时被展开的, 虚函数是在运行时才能动态的绑定函数
内联函数的价值和意义
- 减少函数调用开销:内联函数的主要目的是减少函数调用的开销, 提高程序的执行效率。
- 内联函数适用于函数体较小且被频繁调用的情况。当函数被频繁调用时,内联展开可以减少函数调用的次数,从而提高程序的性能。
- 将较小的函数内联展开可以使代码更加紧凑,避免了函数调用的层层嵌套。这有助于提高代码的可读性和理解性,并简化代码的维护。
内联函数和#define的区别
- 宏定义是一种简单的文本替换机制,可以在编译前将指定的标识符替换为指定的文本。
- 内联函数一般用于函数体的代码比较简单的函数, 不能包含复杂的控制语句和递归等. 用于提高效率, 对于程序中需要频繁使用和调用的小函数非常有用.
- 宏函数在预处理阶段, 将所有的宏名用宏体来进行替换, 而内联函数则是在编译的时候进行代码插入, 编译器会把每处调用内联函数的地方直接把内联函数的内容展开, 这样就省去函数的调用的开销, 提高了效率.
- 宏定义是没有类型检查的, 无论是对还是错都是直接替换, 而内联函数在编译的时候会进行类型检查. 预处理器用复制宏代码的方式代替函数的调用, 省去了函数压栈退栈的过程, 提高了效率.