指针具有方便性,可以实现程序的高效执行,但是有一些疑惑。
有时候弄明白了,但是总忘,所以记录下来,以备查看。
有些不足的地方,以后想起来再进行补充。
凡是用到指针的地方,画个图出来!(经验总结)
1. 指针之间“=”赋值
用“=”赋值时,两个指针指向同一块地址空间。因此,下面的程序执行会提示出错,因为地址空间被释放了,即所谓的野指针。
int main()
{
char * p1=new char[32];
memset(p1, 0, 32);
strcpy(p1, "I love china");
char * p2=p1;
printf("p1 point to %p, p2 point to %p\n", p1, p2);
delete(p1);
printf("%s\n", p2);
}
运行结果如下:
p1 point to 0x8f2a008, p2 point to 0x8f2a008
即,这边没有输出p2所指的字符串
2. 给函数传指针时,最好指明所传数据的长度,便于函数进行取值。如果出现类型不一致时,直接用内存复制函数memcpy() 来实现相互转换。
例如:
int nGroupID;
char * cGroupID;
memcpy(cGroupID, &nGroupID, sizeof(int));
3. 关于char和string类型。个人感觉,string等同于char *,而且在windows中的CString也等同于LPCTSTR,更加深了这种想法。
所以,在使用char * 时,跟使用string相似。
void main()
{
char * p1="hello, world";
char * p2=p1;
printf("%s\n", p2);
}
4. 指针长度固定为4个字节(32位操作系统下)
void main()
{
char * p1="hello, world";
char * p2=p1;
printf("%d\n", sizeof(p2));
printf("%d\n", sizeof(*p2));
}
因此,作参数时,要注明该指针所指地址中的数据是有用的。
下面是一个示例:
#include <stdio.h>
#include <string.h>
char * add(char * x, int xlen, char * y, int ylen)
{
int len=xlen+ylen;
char * result=new char[len];
memset(result, 0, len);
memcpy(result, x, xlen);
memcpy(result+xlen, y, ylen);
return result;
}
void main()
{
char * p1="hello, world";
char * p2="i am byh";
char * p3=add(p1, sizeof("hello, world")-1, p2, sizeof("i am byh"));
printf("%s\n", p3);
}
5. 取指针内容。用*取指针时,所指向的仅仅是指针声明的类型所占内存空间中的第一个,而之后的并不会取到。
如下例子:
void main()
{
char * p1="hello, world";
char * p2= new char;
(*p2)=(*p1);
printf("%s\n", p2);
}
输出内容为:h葺葺葺葺葺?
即,只有第一个是有效的。
6. 指针的智能性。
在声明一个指针类型时,系统就为该指针所指向的空间分配好了该类型应该占用的空间。
用指针作为传出参数的例子:
void CGroupSockPool::getSock(int ID, SOCKET * pSock)
{
int pos=getGroupPos(ID);
if(pos>=m_nCurrentNum)
{
printf("没有找到对应的组\n");
pSock=NULL;
return;
}
(*pSock)=pool[pos]->sock;
}
7. 指针作为参数时,函数会复制指针
在所有的函数传参中,只有引用不会复制参数。对于这个问题之前一直有误解,直到昨天碰到问题的时候,请教同学才知道。(不是科班出身真是坑爹呀)
在传递指针给函数时,函数内部会分配指针(复制实参),指向与实参相同的地址空间。因此,对形参的操作只会改变内存所指向地址的内容,实参所指向的地址是不会发生变化的。
示例代码如下:
#include <stdio.h>
void swap(int * p1, int * p2)
{
int * temp=p1;
p1=p2;
p2=temp;
printf("in swap: (*p1)=%d, (*p2)=%d\n", *p1, *p2);
printf("in swap: address of p1 and p2: %p, %p\n", p1, p2);
}
void main()
{
int * a=new int;
int * b=new int;
(*a)=3;
(*b)=5;
printf("before swap: (*a)=%d, (*b)=%d\n", *a, *b);
printf("before swap: address of a and b: %p, %p\n", a, b);
swap(a, b);
printf("after swap: (*a)=%d, (*b)=%d\n", *a, *b);
printf("after swap: address of a and b: %p, %p\n", a, b);
}
输出结果如下:
before swap: (*a)=3, (*b)=5
before swap: address of a and b: 00370FE0, 00371018
in swap: (*p1)=5, (*p2)=3
in swap: address of p1 and p2: 00371018, 00370FE0
after swap: (*a)=3, (*b)=5
after swap: address of a and b: 00370FE0, 00371018
分析:
可以看到,尽管在swap()函数中,将指针p1和指针p2的所指内存进行了交换,但实际上复制了两个指针,并分别指向a和b指向的内存空间。在函数调用之后,复制的指针生命周期结束后销毁,对实参并没有影响!
8. 二级指针
有时候会用二级指针来存储一些数据。给该二级指针用new动态分配一个连续空间,然后用数组下标进行访问。
出错状况:在执行删除操作时,比较习惯用内存操作函数memcpy()直接用后面的内容把前面的内容覆盖掉,以达到删除的目标。
原因:用下标进行访问时,访问的是二级指针连续空间中的指针还是该指针指向的内容?经过试验,得出的结论是后者,即指针指向的内容。
定义了结构体:
typedef struct _Student
{
char name[16];
int ID;
}Student, * pStudent;
在实验中试了两种覆盖方法:
(1) memcpy(pStu+1, pStu+2, 3*sizeof(pStudent));
(2) memcpy(pStu[1], pStu[2], 3*sizeof(Student));
在用第二种方法时,会出现错误的结果,应该是为一级指针所指向的内存分配的空间不连续导致的;所以用一级指针来做删除会更可靠!
注意:用数组下标去访问时,访问得到的是一级指针所指向的内存空间,而非一级指针本身
总结:
1. 给函数传参时,除非明确指针所指内容的长度,否则一定要把指针所指内容的长度也作为参数传递过去。
2. 内存操作函数memset(), memcpy()包含在头文件#include <string.h>中,很有用。因为可以作为不同类型的转换手段。
注意:不同类型的数值表达方式不相同,所以内存所指的空间内容虽然相同,但表达出来可能就会有问题。
示例:
void main()
{
int ID=1234;
char cID[sizeof(int)]={0};
memcpy(cID, &ID, sizeof(int));
int ID2;
memcpy(&ID2, cID, sizeof(int));
printf("%p\n", *cID);
printf("%d\n", ID2);
}
输出结果:
-46
1234
分析原因:
1234在内存中表达为:(小端 表示 , 以字节为单位,内存中左低右高)0xd2 0x04 0x00 0x00
cID所指的字节:0Xd2, 二进制表示:1101 0010
首位为1,故取反,加一:0010 1110, 即-46