一、指针的一些概念
1. 指针的类型
- 把指针声明语句中的指针名字去掉,剩下的就是指针本身的类型
2. 指针所指向的类型
- 把指针声明语句中的指针名字和名字前的指针声明符号*去掉,剩下的就是指针指向的类型
3. 指针的值——指针所指向的内存区或地址
- 指针的值是指针本身存储的数值,该值被编译器当作一个地址(在32位程序中,所有类型的指针的值都是一个32位的整数,因为32位程序的内存地址都是32位长。)
- 指针所指的内存区就是从指针的值所代表的内存地址开始,长度为
sizeof(指针指向的类型)
的一篇内存区。 - 我们说一个指针的值是XX,就相当于说该指针指向了以XX为首地址的一片内存区。
- 同理,我们说一个指针指向了某个内存区,相当于说该指针的值是该内存区域的首地址。
4. 指针本身所占据的内存区
- 用
sizeof(指针的类型)
测试一下就知道了
二、指针的算术运算
**0.**指针可以加上或者减去一个整数,这种运算的意义并不是平常的数值的加减。
- example 1
1 | char a[20] = {0}; |
指针ptr的类型为int*,它指向的类型为int,被初始化为指向整型变量a。
在第三句中,指针ptr被加上了1,编译器是这样进行处理的:
把指针ptr的值加上
sizeof(int)
,在32位程序中是被加上了4,因为32位程序中int
占4字节。由于地址是用字节作为单位,所以ptr指向的地址由原来的a的地址向高的地方增加了4个地址。
char类型的长度是一个字节,所以原来的ptr是指向数组a从第0个单元开始的4个字节,现在指向了从第4个单元开始的4个字节
- example 2
1.**可以使用一个循环和一个指针**遍历一个数组。
1 | int array[20] = {0}; |
该例子中将整形数组中各个单元的值都加1。同时每次循环都将指针加1个单元,所以每次循环都能访问数组的下一个单元。
- example 4
1 | char a[20] = "You_are_a_girl"; |
该例子中,指针ptr被加上了5个单元,编译器这样处理:
将char的值加上
sizeof(int) * 5
,在32位程序中即乘了4*5=20。指针的值代表了该指针以这个值为地址指向了某个内存区,所以指针ptr所指向的地址向高地址的方向移动了20个字节
然而ptr加上5(即地址增加20个字节后)已经指向了数组a的合法范围之外,虽然应用时会出现问题,但在语法上没有问题。
**2.**总结
一个指针ptrold加或减一个整数n后,结果是一个新的指针ptrnew,两个指针的类型相同,指向的类型也相同。
ptrnew的值比ptrold的值增加或者减少了
n * sizeof(int)
个字节,即ptrnew指向的内存区比ptrold指向的内存区向高或低地址的方向移动了n * sizeof(int)
个字节。指针进行加减:两个指针不能进行加法操作,这是非法操作,结果毫无意义。两个类型相同的指针可以进行减法运算,一般在数组上应用。
三、运算符&和*
0. &是取地址运算符,*是间接运算符。
&a 运算的结果是一个指针,指针的类型是 *a,指针指向的类型是a的类型,指针指向的地址是a的地址。
*p的类型是p指向的类型,所占用的地址是p指向的地址。
四、指针表达式
0. 一个表达式的结果如果叫指针,那么这个表达式就叫指针表达式。
- example
1 | int a, b; |
1. 指针表达式的结果是一个指针,所以也具有指针的4个要素:
指针的类型
指针指向的类型
指针指向的内存区
指针自身占据的内存
2. 一个指针表达式的结果指针已经具有了自身占据的内存的时候,这个指针表达式就是一个左值,否则就不是一个左值。
五、指针和数组的关系
0. 数组的数组名可以看作是一个指针
example
1 | int array[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, value; |
总结:
声明一个数组TYPE array[n],则这个数组名有两重含义:
代表整个数组,类型是TYPE[n]。
是一个常量指针,类型是TYPE*,该指针指向的类型是TYPE,该指针的内存区就是数组的第0号单元,且该指针自己占有单独的内存区,且该指针的值不能修改,array++是错误的写法,这不代表array[1]。
六、指针和结构类型的关系
结构类型还未学习,之后补充
七、指针和函数的关系
0. 可以把一个指针声明成一个指向函数的指针。
- example
1 | int fun1 (char*, int); |
可以把指针作为函数的形参。在函数表达式中,可以用指针表达式来作为实参。
八、指针类型转换
初始化一个指针或者给一个指针赋值时,赋值号左边时一个指针,右边是一个指针表达式。绝大多数情况下,指针的类型和指针表达式的类型是一样的,指针所指向的类型和表达式所指向的类型是一样的。
1 | float f = 12.3; |
如果我们想让指针p指向实数f,我们应该怎样操作?
是“ p = &f; ”吗?
不对,因为左右两侧的指针和指针所指向的类型不同。所以直接赋值不行。
为了实现这个目的,需要使用强制类型转换:
p = (int*) &f;
如果一个指针p,需要把它指向的类型改为”TYPE”,语法格式为:
(TYPE*)p;
。这样的类型转换得到一个新指针,类型为TYPE*,指向的地址就是原指针指向的地址。原来p的一切属性都没有被修改。