此章节干货较少,笔记也比较混乱,理解为主
static 和 inline
静态变量
1 |
|
内联函数
内联函数(Inline Function)是 C++ 中的一种函数优化技术,它的作用是让编译器将函数的代码直接嵌入到调用该函数的地方,而不是通过函数调用的机制进行跳转。这种优化有助于减少函数调用的开销(尤其是对于短小的函数),从而提高程序的执行效率。
递归函数不能用,本身就需要栈空间。
可以用一个inline声明一个内联函数 内联函数会建议编译器把这个函数处理成内联代码以提升性能
始终是建议,具体编译器是否采纳,由编译器决定。。。
1 | inline int add(int a, int b) |
从编译器的角度理解定义和声明
函数声明(也称为函数原型)是告诉编译器函数的名称、返回类型和参数类型的声明,但不包含函数体。它主要用于在调用函数之前告诉编译器函数的存在及其接口。
1 |
|
函数定义是函数的实际实现,包含了函数的返回类型、参数以及函数体。函数定义包括了函数的逻辑实现,并且会占用内存资源。
- 函数声明只提供函数的接口信息,不包含实现,通常用于让编译器在调用函数之前知道函数的存在。
- 函数定义提供了函数的完整实现,包含了函数的所有细节。
变量和函数的不同(声明 定义)
extern int x;
// 声明变量 x 声明,默认是extern
extern
关键字用于声明变量而不定义它。这样可以在多个文件之间共享变量。例如,当你需要在多个源文件中使用同一个全局变量时,可以使用 extern
来声明该变量。
x86 32位windows操作系统下内存分配
头文件和源文件
头文件用于声明类、函数、变量、常量等信息,但不包含具体的实现
头文件只有在调用的时候才被编译,主要是用来写声明的,也可以写定义。
函数只要是在一个工程里就不能重复定义(重载除外)
情况 1️⃣ 静态函数,只在所在的cpp文件中有效,加上 static
关键字,那么不同的cpp文件中的静态函数都有各自的空间,如果不加,那么整个工程里面的相同函数名的函数都用的同一块空间。
情况 2️⃣ 内联函数 也是一样的
如果是变量的话,要用 extern
关键字,只声明不定义,那写在头文件里没问题。
静态变量也是在各自的源文件里有效。static
关键字
重复调用的情况不会出现问题,原因是 #pragma once
但有的编译器不支持这个命令了
头文件保护机制(Include Guard)。头文件保护机制的目的是防止头文件被多次包含,避免重复定义和潜在的编译错误。
1 |
- **
#ifndef _HEMATH_
**:#ifndef
是 “if not defined” 的缩写,意思是“如果未定义”。- 它检查是否已定义名为
_HEMATH_
的宏。如果没有定义,那么接下来的代码会被编译;如果已经定义,则跳过。 - 这个宏通常是头文件的唯一标识符,用来确保头文件只被包含一次。
- **
#define _HEMATH_
**:#define
用于定义宏_HEMATH_
。一旦定义了这个宏,后续的#ifndef
就会失败,避免重复包含该头文件。- 宏
_HEMATH_
在第一次包含该头文件时定义,以后任何包含此头文件的地方都会跳过其中的内容。
- **
#endif
**:- 这是
#ifndef
的结束标志,表示条件编译的结束。它标志着条件保护代码块的结束。
- 这是
1 | // 示例:`hemath.h` |
1 | // 在源文件中使用 |
这样,第二次 #include
时,头文件的内容就不会被重新包含,从而避了重复定义的问题。
- 宏名字(
_HEMATH_
)可以自定义,但最好使用独特且具有全局唯一性的名字,通常使用项目名或文件名来避免与其他库的宏冲突。 - 另外,也可以考虑使用
#pragma once
来替代传统的#ifndef
方式,这样做更简洁,并且现代编译器通常会优化它。
C语言不支持函数重载,所以有多个源文件包含相同的函数,C语言把函数名加_ 但是C++有函数重载不能这样处理,.c 文件和 .cpp 文件若有相同的函数,也不会出错,因为编译器处理之后 的名字不一样
1️⃣ 定义在C语言里面的函数,不能在C++中直接访问 (要使用extern关键字才行)
1 | extern "C" int ave(); // 加上这个就能在cpp文件里面运行c中的函数了 |
通过这种手段,C++可以调用C语言代码
2️⃣在C语言中调用C++的函数
1 |
|
为了让C语言文件知道C++中定义的函数,在C++的头文件中使用 extern "C"
来声明这些函数。这样,C语言和C++都可以包含这个头文件。
1 | // example.h (C++头文件) |
3️⃣ 在C语言代码中,使用extern “C” 声明该函数。
1 | // example.c (C文件) |
C 和 c++ 源文件混用的问题
如果.c 文件 和 .cpp 文件重名(都叫a),那编译的时候,会有俩a.obj,一个文件下目录下有俩a.obj 那么就会出问题,尽量不要让C++和C文件重名
创建自己的SDK
1 |
|
函数调用约定
函数调用约定是函数调用与被调用者之间的一种协议,这个协议主要规定了以下两个内容:
如何传递参数 如何恢复栈平衡
1 | int __cdecl ave(int a, int b) |
1 | int __stdccall ave(int a, int b) |
递归函数
递归函数是指在函数的定义中,调用了它自身的函数。递归的核心思想是将复杂的问题分解为更简单的同类问题。通过递归,问题可以被拆解成较小的子问题,直到达到最简单的基本情况(即递归的终止条件)。
1 | int ave(int x) |
示例
1 |
|
编译器
1️⃣ 未定义行为
C++标准未作规定的行为,称为未定义行为,未定义行为的结果是不确定的,具体在不同的编译器下会有不同的效果;
c=2*a++ + ++a*6;
这里先算a++
还是先算 ++ a
就是一个未定义行为
int x = -25602;
x=x>>2;
x的结果在不同编译器下是不确定的,这也属于未定义行为。
One Definition Rule
ODR是一系列规则,而不是一个规则,程序中定义的每个对象都对应着自己的规则;
但是基本上来讲,任何变量、函数、类、枚举、模板,概念在每个转换单元中都只允许有一个定义;非inline的函数或者变量,在程序中,有且只有一个定义
仅在自己的转换单元有效:
1️⃣ const
类型的变量 :全局const变量,没有特别声明,只在当前转换单元内可见。静态const变量,在类或函数中的作用域内有效,不会暴露到其他地方。外部const变量,使用extern
关键字使得变量拥有外部链接属性。
1 | const int a = 10; // 只在当前源文件有效 |
2️⃣ static
类型的变量 :全局static变量 仅在定义它的源文件内有效,不能被其他源文件访问。局部static变量 只在函数内有效,但它们的生命周期持续整个程序运行周期,只有在第一次调用时初始化,之后的调用将保留上一次修改的值。
1 | static int global_var = 10; // 只能在当前源文件有效 |
3️⃣ inline
变量:在当前转换单元内生效,但是可以跨多个转换单元共享。inline
变量的作用类似于常量,允许在多个源文件中定义和使用,而不发生重复定义链接错误。
inline
变量通常用在头文件中定义常量或其他需要跨多个文件共享的变量。inline
变量遵循一翻译单元一实例的规则,保证多个源文件中不会出现重复定义的问题。
1 | // header.h |
总结:仅在当前翻译单元生效的:static
和 默认的 const
变量。 可以跨多个翻译单元共享:inline
变量。
名称的链接属性:
1️⃣ 内部链接属性:该名称仅在本转换单元中有效。
2️⃣ 外部链接属性:该名称在其他的转换单元中也有效。
3️⃣ 无链接属性: 该名称仅仅能够用于该名称的作用域内访问。