1.细说指针
指针是一个特殊的变量, 它里面存储的数值被解释成为内存里的一个地址。要搞清一个指针需要搞清指针的四方面的内容: 指针的类型、指针所指向的类型、指针的值或者叫指针所指向的内存区、 指针本身所占据的内存区。
2.指针的类型
最简便办法就是把指针声明语句里的指针名字去掉,剩下的部分就是这个指针的类型。
int *ptr; //指针类型是int* char *ptr; //指针类型是char* int **ptr;//指针类型是int ** int (*ptr)[3];//指针类型是int(*)[3] int* (*ptr)[4];//指针类型是int* (*)[4] int (*func)(int,int); //指针类型 int(*)(int,int)
3.指针所指向类型
当你通过指针来访问指针所指向的内存区时,指针所指向的类型决定了编译器将把那片内存区里的内容当作什么来看看待。
从语法上看,你只须把指针声明语句中的指针的名字和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型。
int *ptr; //指针类型指向是int char *ptr; //指针类型是char int **ptr;//指针类型是int * int (*ptr)[3];//指针类型是int()[3] int* (*ptr)[4];//指针类型是int* ()[4] int (*func)(int,int); //指针类型 int()(int,int)
在指针的算术运算中, 指针所指向的类型有很大的作用 。
指针的类型(即指针本身的类型) 和指针所指向的类型是两个概念。
4.指针的值----或者叫 指针所指向的内存区或地址
指针的值是指针本身存储的数值,这个值将被编译器当作一个地址,而不是一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全都是32位长。指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型) 的一片内存区。
以后,我们说一个指针的值是 XX,就相当于说该指针指向了以 XX 为首地址的一片内存区域; 我们说一个指针指向了 某块内 存区域,就相当于说该指针的值是这块内存区域的首地址。指针所指向的内 存区和指针所指向的类型是两个完全不同 的概念。以后,每遇到一个指针,都应该问问:这个指针的类型是什么?指针指的类型是什么? 该指针指向了哪里?(重点注意)
5.指针本身所占内存空间的大小
只需要用sizeof函数测一下就知道了。在32位平台下,指针本身占据了4个字节的大小。
指针本身所占的内存空间的大小,在判断指针表达式是否能够作为左值时很有用。
6.指针运算
指针可以加上或减去一个整数。 指针的这种运算的意义和通常的数值的加减运算的意义是不一样的, 以单元为单位。
如:
char a[20]; int *ptr = (int *)a; //强制转换并不会改变a的类型 ptr++;
指针ptr的类型是int*,它指针想的内存空间的类型是Int,它被初始化为指向整型变量a。ptr++自加1,向前移动1一个int类型的长度,因此,ptr所指向的地址由原来的a[0],指向了a[4]。
在比如:
#include<iostream> using namespace std; int main() { int array[20] = { 0 }; int *ptr = array; for (int i = 0; i < 20; ++i) { /* code */ (*ptr)++; //每个单元加1 ptr++; //向后移动 } for (int i = 0; i < 20;i++) { cout << *(ptr + i) << endl; } system("pause"); return 0; }
在如:
char a[20] = "you are a girl"; int *ptr = (int*)a; ptr += 5;
误区:
#include<iostream> using namespace std; int main() { char a[20] = "you are a girl"; char *p = a; char **ptr = &p; cout << p << endl; cout << ptr << endl; cout << *ptr << endl; cout << **ptr << endl; ptr++; //指向类型是char* 执行此段 即 &p + 4 cout << ptr << endl; cout << *ptr << endl; cout << **ptr << endl; system("pause"); return 0; }
总结,一个指针ptrold加(减)一个整数n后,结果是一个新的指针ptrnew,ptrnew 的类型和 ptrold 的类型相同,ptrnew 所指向的类型和 ptrold所指向的类型也相同 。ptrnew 的值将比 ptrold 的值增加(减少) 了 n 乘sizeof(ptrold 所指向的类型) 个字节。就是说,ptrnew 所指向的内存区将比ptrold所指向的内存区向高(低)地址方向移动了n个sizeof(ptrold 所指向的类型)个字节。
指针不能进行加法运算,因为两个指针加减得到的结果,不知指向了哪个地方。但是两个指针可以进行减法运算。
7.运算符&和*
&p的运算结果是一个指针,指针的类型是a的类型加个*,指针所指向的类型是a的类型,指针所指向的地址,就是a的地址。
8.指针表达式
int array[10]; int *pa; pa = &a; int **ptr = &pa; *ptr = &b; pa = array; pa++; char *arr[20]; char **parr = arr; char *str; str == *parr; str == *(parr+1);
9.数组和指针的关系
数组名是一个常量指针。
int arr[10] = {1,2,3,4,5,6,7,8,9,10}; int *ptr = arr; char *str[3]={"hello world","hi,james","hello,xiaohua"}; //指针数组,每一个指针指向一个字符串的首地址 char s[80]; strcmp(s,str[0]);//等价于strcmp(s,*str); strcmp(s,str[1]);//等价于strcmp(s,*(str+1))
10.指针与结构体的关系
11.指针与函数的关系
12.指针类型转换
当我们初始化一个指针或给一个指针赋值时, 赋值号的左边是一个指针, 赋值号的右边是一个指针表达式。 在我们前面所举的例子中, 绝大多 数情况下, 指针的类型和指针表达式的类型是一样的, 指针所指向的类型和指针表达式所指向的类型是一样的。
float f = 12.3; float *fptr = &f; int *p = NULL;
上述例子中,我们想让指针p指向f,应该怎么办?
p = &f; //是这样么?
显然不对,因为指针p的类型是int * ,它所指向的类型是Int。而&f的结果是一个指针,其类型是float *,它所指向的类型是float。两者不一致。这样是行不通的。但是,为了实现我们的目录,我们可以尝试强制转换。
p = (int*)&f;
这样,我们可以得出,如果有一个指针p,我们需要把它的类型和指向的类型改为TYPE *TYPE,语法格式为:(TYPE*)p;
这样强制转换的结果是一个指针,该新指针的类型是TYPE*,它指向的类型是TYPE,它指向的地址是原指针指向的地址,而原来的指针Pde的一切属性都没有被修改。
一个函数如果使用了指针作为参数,那么在函数调用语句的实参和形参的结合过程总,必须保证类型一致,否则要进行强制转换。
void func(char*); int a = 123,b; func((char*)&a); void func(char *s){ char c; c = *(s + 3); *(s + 3) = *(s +0); *(s + 0) = c; }
那么,可不可以把一个整数赋值给指针呢?
unsigned int a; int *prt; a = 2034533223; ptr = 2034533223; //错误 ptr = a ; //错误
正确处理方式:
prt = (int*) a;
既然,能够把一个整数赋值给指针,那么能不能把指针里面的值当作一个整数取出来呢?
int a = 1223,b; int *ptr = &a; char *str; b = (int)ptr; //把指针的值当作一个整数取出来 str = (char *)b; //把這个值赋值给str
13.指针的安全问题
char s = ‘a‘; int *ptr; ptr = (int*)&s; *ptr = 1298;