C/C++关键字(sizeof(含内存对齐),typedef,static,const,inline)
01. sizeof()
sizeof()
是 C/C++ 中的一个运算符(不是函数),获取对象或类型以字节为单位的内存大小。它在编译时求值,不会产生运行时开销。
注意:
- 空类或结构体的大小至少为 1 字节(确保对象有唯一地址),不会实际执行表达式!
sizeof
统计内存大小(包括\0
)。- 当数组名做参数传递给函数时,会退化成指针,
sizeof()
计算的大小实际上是在当前机器下指针大小(4/8字节);
void func(int arr[]) {//指针退化
cout << sizeof(arr); // 输出指针大小(如 4/8)
}
main...
int arr[10];
func(arr);
- 考虑内存对齐下的计算:
struct AlignedStruct {//结构体对齐
char a; // 1 byte
int b; // 4 bytes(对齐到4字节边界)
double c; // 8 bytes(对齐到8字节边界)
short d; // 2 bytes
};
main...
sizeof(AlignedStruct) //24
union myunion{//联合对齐
struct{
int x;
int y;
int z;
}u;
int k;
}a;
main...
a.u.x=4;
a.u.y=5;
a.u.z=6;
a.k=0;
printf(“%d\n",a.u.x);
首先这个联合体的大小得看其中最大成员结构的内存大小,结构体u的大小为12字节,而k是4字节,这个联合体一共是12字节。x和k的内存空间是同一块,k赋值为0后,通过x去访问,它的值还是0。
struct mybit {//位域对齐
unsigned int a : 20; // 20 bits
unsigned int b : 15; // 15 bits
};
main...
sizeof(bf2)//8字节
此处采用的是大端方式进行储存: 即与我们正常阅读的顺序相同,实际开发中可能需要考虑编译器采用的方式为准!
02. static
静态变量存储在静态存储区,其初始值默认为0,且不会被重复初始化(初始化一次)。静态变量分为两种:
- 静态局部(外部)变量:定义在函数内部,其生命周期持续到程序结束,但作用域仅限于该函数内。函数退出时,静态局部变量不会被释放,仅在程序终止时销毁。
- 静态全局变量:定义在文件作用域内,其作用域仅限于当前文件,其他文件无法访问,从而避免了命名冲突。
静态局部变量的作用域仅限于定义它的源文件内,这是由static关键字与extern链接性的组合特性决定的。当在文件作用域声明一个带static关键字的局部变量时,它具有以下特点:
- 该变量的存储期是永久性的(程序整个运行期间)
- 作用域被限制在声明它的源文件内
- 其他源文件无法通过extern声明来访问这个变量
- 这种变量通常用于控制单个源文件内的全局状态
内存分区图:
- 内存栈区:存放局部变量名
- 内存堆区:存放
new
或者malloc
出来的对象 - 常数区:存放局部变量或者全局变量的值
- 静态区:用于将存放全局变量或者静态变量
指针指向字符串时,字符串是常量,存储在常量区,而指针存储在栈区,不能对其操作修改。
char *p="abcdef";//abcdef是常量不可修改
03. typedef与define
typedef
: 定义一个新类型,有类型检查,编译时处理
define
: 只是简单替换,无类型检查,预编译是处理
typedef struct Student {
int age;
} S;
//此时 S 等价于 struct Student,但两个标识符名称空间不相同。
04. inline内联函数
inline
是一个关键字,编译器将函数内联展开后将函数体直接替换到调用处,避免传统函数调用的开销(如压栈、跳转、返回等)。适用于小型、频繁调用无复杂控制流的函数。缺点是过度内联会增加二进制文件大小,可能降低缓存命中率,实际调试时无法单步调试。
特性:
- 相当于把内联函数里面的内容写在调用内联函数处;
- 相当于不用执行进入函数的步骤,直接执行函数体;
- 相当于宏,却比宏多了类型检查,真正具有函数特性;
- 不能包含循环、递归、switch 等复杂操作;
- 在类声明中定义的函数,除了虚函数的其他函数都会自动隐式地当成内联函数。
// 内联函数定义
inline int add(int a, int b) {
return a + b;
}
class A {// 类里面默认inline
int size() const { return size_; } // 隐式内联
}
inline int size() const { return size_; } // 类外面需要显式内联
05. const
const
是只读变量,即const
变量值不可修改,但**不是编译期常量表达式。实际编码记忆(左定值右定向)
- 修饰变量,说明该变量不可以被改变;
- 修饰指针,分为指向常量的指针和指针常量;
- 常量引用,经常用于形参类型,即避免了拷贝,又避免了函数对值的修改;
- 修饰成员函数,说明该成员函数内不能修改成员变量。
//const必须放在类型说明符的前面或后面
//const出现在*左边,如const char* p,表示p所指向的对象内容不可变,指针本身可以改变;
//const出现在*右边,如char* const p,表示p是个常量指针,即指针本身不能改变,而指向的对象可变;
int b;
const int *a = &b; // 1
const * int a = &b; // 2 语法:*不能跑到 类型 前面~
const int* const a = &b; // 3
int const* const a = &b; // 4
--------------------------------------------------------------------------------
int const *a 和 const int *a 意义相同,作用等价 同理,本题3、4意义相同
const int *a 这里const 修饰的是int,而int定义的是一个整值
int *const a 这里const修饰的是 a ,a代表的是一个指针地址 因此不能赋给a其他的地址值,但可以修改a指向的值
const int * const a 这个代表a所指向的对象的值以及它的地址本身都不能被改变
常量函数参数: 不修改传递的参数本身
bool operator>(const Date& d){
d._year=10;//这里不能对其做修改
return !(*this < d);
}...
private:
int _year;
常量成员函数: 成员函数只能调用其他 const
成员函数,不可修改其他…
class Date {
int _year;
public:
int getYear() const {
// _year = 10; // 错误:不可修改成员变量
return _year;
}
};
返回常量值: 返回值不可被修改
const int* findValue(const Date& d, int year) {
// 返回指向常量的指针
}
后续不固定补充中…