指针
笔记。from 解决C/C++指针的一切问题。 讲的很好。
#1 指针变量
特殊的变量,存储变量在内存中的起始地址。比如int占4字节,指针指向int开头的那个地址。
int *p;
申明指针变量。变量类型(int,char,double或自定义类型等) *变量名
C++中,用 & 得到变量的起始地址
要用对应类型的指针变量存储对应类型的变量地址。指针只知道起始地址,C++中,该变量占用多少内存,如何操作内存由数据类型决定,因此变量和指针变量都需要知道对应的数据类型。
申明变量,系统在内部跟踪内存单元;申明指针,由程序直接访问该内存单元。
int x;
int *p=&x;
将 x 的地址给赋值给 p。说 p 指向 x,p的基类型是 int。如果基类型不符,编译器会发出警告,但是可以强制转换。
多个指针可以指向同一个变量。
char x;
int *p=(char *)&x
这样做有风险。
指针也是变量,会占用内存,64位系统都占用8字节,32位都占用4字节。
C++中,指针是复合数据类型,是基于其他类型定义的类型,也可以像其他数据类型一样操作。
#2 使用
指针要赋初始值。
*是解除引用。对指针操作,可以得到所指向地址的值。
void f(int *p){
*p=1;
}
int main(){
int x=0;
f(&x); //在这之后 x=1
return 0;
}
把函数的形式参数声明为指针,调用的时候传入实际参数地址,可以通过解引用的方法直接对内存中的数据操作,修改实际参数的值。(这个操作叫地址传递)
这样做的意义:1.可以在函数中修改实参;2.可以减少内存拷贝,提高性能。(比如说,某个 string 占用40字节,而在函数中调用指针可能就只要8字节)
#3 const 修饰
用 const 修饰变量会让变量变为只读,不能修改。
1.常量指针
const int *p=&x;
在类型前面添加const。此时不能通过解引用修改(*p)内存中的值,但能直接对变量(x)做修改。可以更改指向的变量。
可以修饰函数的形参,表示不希望在函数中修改内存中的值。(更好的明白代码意图)
2.指针常量
int const *p=&x;
此时指向的对象不能改变(必须初始化),但可以通过解引用修改值。
C++将指针常量做了特殊处理后,有一个操作,叫引用。
3.常指针常量
const int const *p=&x;
指向的对象不能改变,也不能通过解引用修改值。也有常引用。
在工程中,只有常量指针比较常见。
#4 void 关键字
void f(int x){
}
表示函数没有返回值。
int f(void){
}
表示函数没有参数(也可以让参数列表空着)
函数的形参用 void*,表示接受任意类型变量的地址。
void f(void *p){
}
int main(){
int x;
char y;
f(&x),f(&y); //这样操作都是可以的,都可以被void*接受
return 0;
}
- 不能直接用void声明普通变量
- 不能对 void* 解引用,需要先强制转换。
- 将 void* 赋值给其他指针,将其他指针赋值给 void* 不需要强制转换。
void* 只保存了地址,不包含数据类型,没有确定内存大小,编译器无法解释数据,因此不能解引用,指针运算等操作(GNU好像可以?但不是标准C++行为)。
在 C/C++ 的库函数中常见 void* 。
#5 C++的内存模型
粗略的区分,C++将空间分为5个区,地址从低到高分别是:
- 代码区(.text段)。存放可执行代码,不允许修改,允许执行。
- 常量区(.rodata段)。存放字符串字面量,常量等。一般是不允许修改的。(字符串字面量就是 "hello" 这样用引号括起来的字符串,是只读的)
- 全局/静态存储区(.bss段和.data段)。存放全局变量,静态变量。从程序开始到结束一直存在。(如果你想知道的话,在语言中不区分.bss和.data,在编译时有区别。.data存储已初始化且非0的全局变量/静态变量,而.bss存未初始化和零初始化全局变量/静态变量。.bss只记录大小,加载时由系统清零;.data会存入文件。运行时修改为0的值,变量还在原来的段里,只是内存中的值被修改)
- 堆(heap)。内存是向上增长的。存储由程序动态开辟空间的变量,用 new 与 delete。没有释放的空间在程序结束后会被回收。像 vector 这样的 STL 也是由程序动态分配的。
- 栈(stack)。内存是向下增长的。在编译器在需要时分配,在不需要时自动清除的存储区。存储局部变量,函数参数等。
再上面是系统内核,是不允许直接交互的。

这张图描述基本是正确的。可以去问ai。
堆和栈区别:
- 管理方式:栈由系统自动管理,出作用域是自动释放;堆手动管理,若程序不释放,程序结束后将由操作系统释放。
- 空间大小:堆内存64位系统受限于物理内存,特别大;栈空间很小,Linux 通常是 8MB,WIndows 通常是 1MB,超过空间会发生栈溢出(Stack Overflow),触碰到系统保护的地址,会立刻使程序崩溃,Linux 收到
SIGSEGV(段错误),Windows 抛出栈溢出异常。栈空间可以用指令修改。 - 分配方式:堆是动态分配;栈有静态也有动态分配(自动释放)。
- 分配效率:栈是系统提供的数据结构,底层有支持,进栈出栈有专门指令,效率较高;堆由C++函数库提供,用链表管理。
- 碎片:栈服从先进后出,不会产生内存碎片;堆如果频繁的分配和释放,会造成空间的不连续,容易产生碎片,导致性能下降。
- 增长方向不同。
只有1,2比较重要。

浙公网安备 33010602011771号