变量是一段内存单元首地址的命名(寄存器变量另说), 有三个维度:型、址、值。
型,也就是类型(type),决定了它需要内存空间的大小,它的编码规则(如原码、补码、IEEE 754标准的浮点数编码、ASCII、unicode等字符编码),值空间,运算规则等。
址,也就是地址(address),数据、代码都要存储到内存空间,可以被寻址。
值,也就是数据值(value),地址所指示的一段内存空间,按其类型所指示的长度和编码规则,对其全部的二进制位所解析出的值。
指针变量也是变量,具有上述变量所说的型、址、值,但他的特殊性在于是一指针变量,是一指向他处的变量,存储的是一地址值,其特点就是“我中有他”,所以除了己型、己址、己值以外,还蕴涵有他型、他址、他值的内容。
其中己值就是他址,看以下简单示例:
#include <iostream>
using namespace std;
#include <typeinfo>
int main()
{
int t = 31;
int* p = &t; // 己址、己值、他址、他值、他型、己型
cout << "己址:" << &p << endl;
cout << "己值:" << p << endl; // 或&t,己值就是他址
cout << "他址:" << p << endl; // 或&t,他址就是己值
cout << "他值:" << *p << endl; // 解引用
cout << "他型:" << typeid(*p).name() << endl;
cout << "己型:" << typeid(p).name() << endl;
cin.get();
return 0;
}
/*
己址:0012FF40
己值:0012FF44
他址:0012FF44
他值:31
他型:int
己型:int *
*/
指针变量除了可用于申请堆空间,构造链式存储等数据结构等功能以外,还可以用于传址。
C++语言中的函数都有一段自己独属的内存空间,称为栈帧。如果主调函数给被调函数传值,前者传完值后,彼此互不影响。如果是传址就不一样了,被调函数因为拿到的是主调函数的地址,便有机会去操作这个地址对应的内存空间了。
区分了己型、己址、己值,他型、他址、他值的概念,便可以轻松领会传址的一些细微之处:
void func(int* p)
{
int i = 44;
*p = i; // 左值是他值(p的解引用)才可以影响到他
p = &i; // 左值是己值(或他址)影响不到他(因为p没有解引用),改变的是自己
}
分析以下代码:
void GetMemory0(char *p, int n)
{
p = new char[n]; // 左值p这里是己值,指向一段堆空间,对主调函数没有影响
// 左值解引用p(*p)才可以影响p对应的值
delete[] p; // 按成对编码原则,需要在主调函数中释放p并置NULL
} // 此函数一旦被调用,p因为在栈空间,会被释放,主调函数将没有机会去delete[] p
void Test0(void)
{
char *str = NULL;
GetMemory(str,100); // str还是NULL,同时内存泄漏
strcpy(str, "hello world"); // 复制给NULL属非法操作
printf(str);
delete[] str; // delete[] 操作空指针也是错误
str = NULL;
}
当指针变量用做函数参数时,如果想通过被调函数修改主调函数的值,需要在函数体中解引用做为左值。一个什么样的变量解引用后还是一个指针?那就是二级指针:
void GetMemory(char **p, int num)
{
*p = new char[num]; // 左值是p的解引用,是他值
//delete[] p; // 按成对编码原则,需要在被调函数中释放并赋值NULL
}
void Test(void)
{
char *str = NULL;
GetMemory(&str, 100); // 实参是一个二级指针
strcpy(str, "hello");
printf(str);
delete[] str;
str = NULL;
}
当然也可以是指针引用,因为引用是一个由解译器实现了解引用的常量,一个指针类型的常量:
void GetMemory2(char *&p, int num) //p引用一个指针char*,
{
p = new char[num]; // 可以被返回地址的函数赋值
}
void Test2(void)
{
char *str = NULL;
GetMemory2(str, 100); // 注意参数是str,而不是&str
strcpy(str, "hello");
cout<< str << endl;
delete[] str;
str = NULL;
}
引用因为由编译器实现了对指针的解引用,所以在函数体中无需再解引用,直接使用,所以更简洁。
对于上述实例,也可以返回一个指针:
char *GetMemory3(int num)
{
char *p = new char[num];
return p;
}
void Test3(void)
{
char *str = NULL;
str = GetMemory3(100);
strcpy(str, "hello");
cout<< str << endl;
delete[] str;
str = NULL;
}
ref
http://mallocfree.com/interview/c-13-leak.htm
本文暂时没有评论,来添加一个吧(●'◡'●)