本帖最后由 xiniuniu 于 2015-4-11 12:30 编辑
指针恐怕是C语言中最不容易掌握的一个概念了,但指针又是如此的重要,可以说不会指针,C语言就等于没学。 指针到底是一个什么东西呢? - #include <stdio.h>
- int main()
- {
- struct mySt
- {
- int age;
- float money;
- double weight;
- float heigh;
- };
- char *pCh = NULL;
- char *pStr = "Hello Pointer";
- short *pShort = NULL;
- int *pInt = NULL;
- float *pFloat = NULL;
- double *pDouble = NULL;
- struct mySt* pStruct = NULL;
- printf("sizeof(pCh) = %d\n", sizeof(pCh));
- printf("sizeof(pStr) = %d\n", sizeof(pCh));
- printf("sizeof(pShort) = %d\n", sizeof(pShort));
- printf("sizeof(pInt) = %d\n", sizeof(pInt));
- printf("sizeof(pFloat) = %d\n", sizeof(pFloat));
- printf("sizeof(pDouble) = %d\n", sizeof(pDouble));
- printf("sizeof(pStruct) = %d\n", sizeof(pStruct));
- getchar();
- return 0;
- }
复制代码
运行上边代码在编译目标为32位系统程序时,指针无论是指向基本数据类型,还是复杂数据类型,它所占的空间大小始终是4字节 再次把编译目标指定为64位程序,这时指针无论是指向基本数据类型,还是复杂数据类型,它所占的空间大小始终是8字节。 指针所占空间大小的决定因素:1. 最最根本的约定因素就是CPU。如果CPU是32位的,那么它所能够寻找的地址范围就在0 ~ 0xFFFFFFFF之间, 在这个区间的地址数据宽度都不会超过32位。指针变量的一个主要功能就是存放地址数据的,为了能够存放得下任意有效的地址,那么在32位CPU并且操作系统也是32位的情况下,指针所占内存宽度自然应该是4字节。 同理如果CPU是64的。它的寻址范围就是0~0xFFFFFFFFFFFFFFFF之间,在这个区间的地址数据宽度都不会超过64位,那么在64位操作系统下,并且生成的程序也是64的,在这程序当中,指针变量所占内存宽度自然应该是8字节。 2 如果编译器生成的目标程序是32位的,或者在64位CPU上运行的是32位操作系统。CPU虽然是64位的,但由于程序或操作系统寻址能力的限制,这时指针变量所占内存空间就是4字节32位。 因为64位是兼容32位的,所以32位程序和32位系统也能够正确运行
指针类型的声明方式:1 任意类型 * 变量名
例如2 *号可以写多个(能写多少个,没试过 ^^)
- int **p;
- double ********pUnknow;
复制代码
指针变量的赋值- char *x
- short *y
- int* z
- x = (char*)1;
- y = (short*)2;
- z = (int*)3;
复制代码 指针变量赋值,在声明一个指针变量的时候,就已经告诉编译器,这个变量应该存放的数据是一个地址,可以从这个地址中获取一个指定类型的数据。有了类型说明,编译器才会知道从那个地址开始应该读取多少个字节,并把这些数据解释为何种类型。所以给一个指针赋值时,赋值操作符“=” 右边的数据类型应当和指针声明的类型相同。如果等号右边所给的数据类型和指针变量类型不符,那么编译器就会报错。这样做的原因是为了程序设计的安全和正确性。假设我们指针变量类型为int* 而右边给的确是一个short*,如果编译器不报错,并编译通过,程序运行时读取那块地址开始的4字节空间,显然多读取了两个字节,你怎么知道多读的两个字节是什么。如果那多的两个字节是另外一个重要的数据变量呢,这时就会被你的误操作所修改,导致程序出错。如果我们的产品是为医疗设备做开发,很有可能会引起一场医疗事故。如果我们为某某原子机构做开发呢,有可能一个蘑菇云就诞生了!!! 所以指针变量赋值要保证两边类型一致,除非你知道自己为什么这样做。当你想把等号右边类型不符的数据强制赋值给指针,那就应该告诉编译器,编译器老哥,把这个数据当作和指针类型一样的类型处理,就这么干。既然我们程序员已经发号师令了,那这时编译器也就没什么话说了。
指针加, 指针减 指针就是为了寻址数据的,能过指针获取了一个字符串的首地址,根据字符串的特性我们就可以读出整条字符串的内容。同样如果获取了一个整型数组的首地址,并且知道数组的大小,那么就可以高效访问数组中任一元素。我们获得了有效数据的一个地址,32位或64位,而仅仅通过这32位或64位数据就可以实现对大量数据的操作,是不是很简洁高效。有点类似电视机的遥控器,小小的遥控器在手,多大的电视都要听我的话,对吧
- #include <stdio.h>
- int main()
- {
- char *pStr = NULL;
- int num[] = {1, 2, 3, 4, 5};
- int *pNum = num;
- pStr = "abcdefg";
- printf("%c\n", *(pStr++));
- printf("%c\n", *(pStr + 3));
- printf("%d\n", *(pNum++));
- printf("%d\n", *(pNum + 3));
- getchar();
- return 0;
- }
复制代码
pStr = "abcdefg"; 这条C语句的意思就是把常量字符串第一个字符的地址赋值给指针变量pStr;
C语言中只要是出现常量字符串的地址它返回的就是这个字符串第一个字符的地址。前边这条变量赋值更应该这样写
pStr = (char*)0x0040c0dc; 其实背后编译器也是这么干的。但直接用数字表达不够直观,二我们也不知道“abcdefg”应该存放在哪个位置
所以写成pStr = "abcdefg"; 让编译器去给这个常量字符串分配空间,并返回地址给我们的指针变量。
同样思考下printf("%d", 23); 实际上我们向printf函数中传入了两个参数,一个是字符串"%d"的首地址,一个是23
pStr++; 指针自身加1 。这时指针变量中的数值已经修改为0x0040de;在计算机中查找这块地址并取一字节,再把这一字节数据转换成
字符所以输出b
pStr + 3 就相当于 pStr中的数值 + sizeof(指针指向类型)*3, 所以 0x0040de + 1 * 3 得到地址0x0040c0e1, 输出结果为e
pNum用数组首地址初始化,所以此时pNum变量中存放的数值为0x0018ff44
pNum++ 这时指针自加显然已经不是简单的加1操作了。我们看到这时指针增加了4个字节,如果只增加一个字节,显然我们会得到错误 的结果。为了正确寻址数据,所以声明指针时要指定一种类型,知道了类型信息,那么编译器自然知道如何寻址和解释数据了 pNum + 3 相当于0x0018ff48 + 3 * sizeof(int) 所以最张输出结果为5 结论: 指针是可以进行加减操作的。目的是方便数据寻址 通过以上观察我们可以得出这样一个公式指针 +/- 数字 = 指针内容 +/- 数字*(sizeof(指针类型))那么指针乘除显然没有任何意义。
|