指针内存:指针与内存

什么是指针?
其实指针就像是其它变量一样,所不同的是一般的变量包含的是实际的真实的数据,而指针是一个指示器,它告诉程序在内存的哪块区域可以找到数据.指针是一个数据类型,本身也需要占用四个字节的存储空间。.所以用sizeof(void*)获得的值为.
作为一个C++程序员,指针的直接操作内存,在数据操作方面有着速度快,节约内存等优点,仍是很多C++程序员的最爱.指针是一把双刃剑,用好了它,你就会发现指针有多么的方便,反之,你可能就头疼了,往往会出现意想不到的问题.
为了方便,人们赋予了指针极大的权利,然而就是这些权利,使指针在为程序员提供种种便利的同时,也可能对程序造成巨大的破坏.

例5: 指针的权利

#include <iostream>
using namespace std;

class A
{
int value;
public:
A( int n = 0 ) : value( n ) {}
int GetValue()
{
return value;
}
};

int main()
{
A a;
*( (int *)&a ) = 5;

return 0;
}


2. 指针与数组

C/C++程序中,指针和数组在不少地方可以相互替换着用,让人产生一种错觉,以为两者是等价的.
数组要么在静态存储区被创建(如全局数组).要么在栈上被创建/数组名对应着(而不是指向)一块内存,其地址与容量在生命期内保持不变,只有数组的内容可以改变.
指针可以随时指向任意类型的内存块,它的特征是"可变",所以我们常用指针来操作动态内存.指针远比数组灵活,但也更危险.


例6: 指针与数组的比较-修改内容

#include <iostream>
using namespace std;

int main()
{
char a[] = "hello";
a[0] = 'X';
cout << a << endl; // Xello

char *p = "world"; // p 指向常量字符串
p[0] = 'X'; // 编译器不能发现该错误
cout << p << endl; // 运行错误

return 0;
}

在例中,字符数组a 的容量是个字符,其内容为hello\0.a的内容可以改变,如a[0] ='X' .
指针p 指向常量字符串"world"(位于静态存储区.内容为world\0).常量字符串的内容是不可以被修改的.从语法上看,编译器并不觉得语句p[0] = 'X'有什么不妥.但是该语句企图修改常量字符串的内容而导致运行错误.


例7: 指针与数组的比较-内容复制与比较

#include <iostream>
using namespace std;

int main()
{
/* 数组*/
char a[] = "hello";
char b[10];
strcpy(b, a); // 不能用b = a;
cout << strcmp(b, a) << endl; // 不能用b == a

/* 指针*/
int len = strlen(a);
char *p = new char(len+1);
strcpy(p, a); // 不要用p = a;
cout << strcmp(p, a) << endl; // 不要用p == a

return 0;
}
不能对数组名进行直接复制与比较,例中,若想把数组a的内容复制给数组b,不能用语句b = a ,否则将产生编译错误.应该用标准库函数strcpy进行复制.同理,比较b和a的内容是否相同,不能用b==a 来判断,应该用标准库函数strcmp进行比较.
语句p = a 并不能把a的内容复制指针p,而是把a的地址赋给了p.要想复制a的内容,可以先为p申请一块容量为strlen(a)+1个字符的内存,再用strcpy进行字符串复制.同理,语句p==a 比较的不是内容而是地址,应该用库函数strcmp来比较.


例8: 指针与数组的比较-内存容量

#include <iostream>
using namespace std;

void fun(char a[100])
{
cout << sizeof(a) << endl; // 4 (字节)
}

int main()
{
char a[] = "hello world";
char *p = a;
cout << sizeof(a) << endl; // 12 (字节)
cout << sizeof(p) << endl; // 4 (字节)

fun(a);

return 0;
}
用运算符sizeof可以计算出数组的容量(字节数).示例中,sizeof(a)的值是(注意别忘了'\0').指针p指向a,但是sizeof(p)的值却是.这是因为sizeof(p)得到的是一个指针变量的字节数,相当于sizeof(char*),而不是p所指的内存容量.C/C++语言没有办法知道指针所指的内存容量,除非在申请内存时记住它.
注意当数组作为函数的参数进行传递时,该数组自动退化为同类型的指针.例中,不论数组a的容量是多少,sizeof(a)始终等于sizeof(char *).


3. 指针参数

如果函数的参数是一个指针,不要指望用该指针去申请动态内存.


例9: 指针作为函数的参数

#include <iostream>
using namespace std;

void getMemory(char *p, int num)
{
p = new char(num);
}

int main()
{
char *str = NULL;
getMemory(str, 100); // str 仍然为NULL
strcpy(str, "hello"); // 运行错误

return 0;
}
例中,语句getMemory(str, 200)并没有使str获得期望的内存,str依旧是NULL,为什么?
毛病出在函数getMemory中.编译器总是要为函数的每个参数制作临时副本,指针参数p的副本是_p,编译器使_p = p.如果函数体内的程序修改了_p的内容,就导致参数p的内容作相应的修改.这就是指针可以用作输出参数的原因.
在本例中,_p申请了新的内存,只是把_p所指的内存地址改变了,但是p丝毫未变.所以函数GetMemory并不能输出任何东西.事实上,每执行一次getMemory就会泄露一块内存,因为没有用delete释放内存.
如果非得要用指针参数去申请内存,有两种方法: 1.改用"指向指针的指针"; 2.用函数返回值来传递动态内存.
例10: 指针参数与生命期

#include <iostream>
using namespace std;

int *g;

void fun(int *q)
{
q = new int(10);

g = q;
cout << "in function" << endl;
cout << "&p: " << &q << endl;
cout << "p : " << q << endl;
Tags:  指针与引用的区别 引用与指针 指向栈内存的指针 指针内存

延伸阅读

最新评论

发表评论