一、地址
int *p = NULL;
cout<<p<<endl;//输出00000000,而不是0
2.指针可以通过内存地址直接访问数据,可避免在程序中复制大量的代码。因此指针效率最高
例:a[j]:a+j
a[i][j]:a[i]+j
二、内存
1.数据在内存中的存放形式
1)栈区:由编译器自动分配和释放
一般存放函数的参数值、局部变量的值等
2)堆区:由程序员分配及释放。若程序员不释放,程序结束后可能由OS回收
3)寄存器区:用来保存栈顶指针和指令指针
4)全局区(静态区):全局变量和静态变量是存储在一起的。初始化的和未初始化的是分开的。
程序结束后由系统释放
5)文字常量区:程序结束后由系统释放
存放常量字符串
6)程序代码区:存放函数体的二进制代码
2.堆与栈的区别:
堆 | 栈 | |
---|---|---|
内存申请方式 | 程序员自己申请,申请时需要指明申请的大小 | 系统自己分配 |
系统响应方式 |
遍历内存空闲地址链表,找到比申请的要大的堆结点,将其 中申请的大小分配给程序,程序空间放入空闲链表中 | 栈的剩余空间不足时会overflow |
最大空间大小 | 由系统中的有效虚拟内存决定 | 2M |
执行效率 | 慢、易产生内存碎片、灵活 | 快、无内存碎片 |
补充:
(1)指针对内存数据有保护作用
栈可以为它其中的某个内存单元命名,但堆中的每个内存单元都是匿名(这是对数据的保护)的。必须先在堆中申请一个内存单元地址,然后把它的保存在一个指针中,只有该指针才可以访问该内存单元的数据。
(2)delete运算符只能删除堆中的空间,删除栈中的空间会导致出错
int main()
{
A a;
A *p = new A;
A *q = &a;
delete p;//正确,因为p指向堆中的未命名空间
delete q;//错误,因为q指向栈中的空间
return 0;
}
3.内存泄漏
class A
{};
int main()
{
A *p = new A;//没有释放
return 0;
}
(2)指针是一个局部变量,在释放内存之前,因为离开作用域而消失了
class A
{};
void Test()
{
A *p = new A;
}
int main()
{
//无法释放
return 0;
}
(3)用父类指向一个子类对象,析构函数没有实现多态
class A
{};
class B:public A
{
int x;
};
void Test()
{
A *p = new A;
}
int main()
{
A *p = new B;//只释放了基类所占用的空间,x所占用的空间没有释放
delete p;
return 0;
}
三、堆
1.访问堆中成员的两种方式
访问堆中成员有两种方式,它们是等价的:A *p = new A();
1)(*p).get(); 2)p->get
例:
class A
{
int x;
public:
A(int i):x(i){}
int get(){return x;}
};
int main()
{
A *p = new A(1), *q = new A(2);
cout<<(*p).get()<<' '<<q->get()<<endl;
return 0;
}
输出:1 2四、栈
1.在函数调用时的入栈和出栈的顺序:
入栈顺序:1)被调用函数的下一行地址2)参数(从右往左)3)函数的全局变量
出栈的顺序相反。
2.栈中的对象,遇到“}”时自动析构
例:class A
{
public:
~A(){cout<<"析构"<<endl;}
};
void Test()
{
A a;
}
int main()
{
Test();
return 0;
}
输出:析构