&和*
运算符“&”和“*”都是单目运算符。“&”是取地址运算符,用于返回一个操作数的地址。“*”是指针运算符,用于返回指定地址内保存的变量值。
int a = 1;
int *p = &a;
“&”和“*”优先级相同,按自右向左的方向结合。因此"&*p"等价于"p","*&a"等价于"a"。
占用的存储空间
指针存储的内容是变量的地址。指针所占存储空间的大小由机器(内存编址所需的大小)决定,不同类型的指针所占的存储空间是一样的。
32位机器上 是由 32bit 组成的二进制序列作为地址 —— 4个字节
64位机器上 是由 64bit 组成的二进制序列作为地址 —— 8个字节
不同类型的指针在加减时,地址增减的数量不同。char类型的指针+1指针会指向下一个字节的地址;int类型的指针+1则会指向四个字节后的地址。
指针与数组
指针跟数组的联系非常的紧密。数组名其实也是一个地址,是数组的起始地址。
#include<stdio.h>
int main() {
int* p, a[10];
p = a;
p = &a[0];//与上一行等价
int i;
for (i = 0; i < 10; i++) {
a[i] = 0;
p[i] = 0;
*(a + i) = 0;
*(p + i) = 0;
}
return 0;
}
上述代码中,for循环中的四行代码是等价的。由此可见,完全可以把指针当作数组来看待。
二维数组
假设有二维数组a[m][n],则
a[i][j]等价于*(a[i] + j)、*(*(a + i) +j)
若int **p = a或者 int **p = a[0] 、 int **p = a[0][0],则上面这行中的a都可以替换为p。
指针数组
二维数组在声明时,可以省略行下标,但不能省略列下标。也就是说,二维数组中的所有一维数组的长度是一样的。如果存在一个常量字符串数组,其中的字符串长度差异较大,为了能存下所有数组,就需要声明的二维数组列下标大于最长的字符串长度,造成很大的空间浪费。此时就可以使用指针数组,虽然指针所占的空间相对较大,但是,相对来说还是会节省很多空间。
char *test[]=
{"Chinese","math","English","chemistry","biology","physics"};
注意:因为指针没有数组的长度限制,所以这里的字符串最好是不变的,尤其是长度,如果边长的话,可能会引发一些意料之外的错误。
参数传递
C语言中实参变量和形参变量之间的数据传递是单向的“值传递”方式,将数据复制一份传递给被调函数。指针变量做函数参数也是如此,调用函数不可能改变实参指针变量的值,但可以改变实参指针变量所指向变量的值。
指针只是相对特殊的值,这个值代表着某个变量的地址,也因此会将其叫做“地址传递”。当形参指向别的地址后,实参的指向不会发生变化。
与Java对比
Java和C语言一样只有值传递,也同样存在类似“地址传递”的“引用传递”。
C语言的非指针类型与Java的基本数据类型在参数传递上类似,都是将数据复制一份传递。被调函数(方法)对值的修改,不会影响调用函数中的变量值。
Java中引用类型的参数传递,传递的是对象的引用,也叫“引用传递”。这个引用是个某个对象绑定的,当对象的地址发生变化时(垃圾回收时,对象的地址可能会发生变化),引用地址也会发生变化。而C的指针是不会自动变的。
如果形参重新赋值,也就是指向
另外Java中有些特殊的引用类型,基本类型的包装类,String类型。他们的属性都是final修饰的,new之后无法修改。传递的时候虽然传递的是引用,但是由于其没有setter方法,操作后返回的是一个新的对象。效果上看起来跟基本数据类型一样,不会对实参造成影响。