1.取地址符&
&用来取变量的地址
输出变量的地址的时候使用:
printf("%p",a) //虽然有时候print("%d",&a)也能输出地址,但是这可能不是真正的a的地址,假如地址用8个字节表示,%d输出会默认向int类型转换,导致前面一部分数据丢失。
&只能对变量操作,下面这些都是错误的:
&(a+b)
& (a++)
对于数组 int a[50]
&a=a=&a[0],数组a的地址连续分配,假如a[0]是 0000 0000,则a是 0000 0004
在一个程序中,分配两个变量的时候,地址是从高到低分配的,因为这些变量存储在堆栈中,而堆栈是从上到下分配地址,例如
int a,b;
printf("%p %p",a,b) // 0001 0004 0001 0000
2.指针
指针就是保存地址的变量,前面提到过%d输出地址的时候可能会导致部分数据丢失,而指针就不会。
声明一个指针: 变量类型 *p 或者 变量类型* p //注意*是修饰p的,而不是修饰变量类型的
int *p,q //表示声明了一个指针q和一个int型变量q
int* p,q //同上
3. * 作为单目运算符
*p 作为单目运算符的时候表示取地址p指向的内容
*p =26 可以通过这种方式改变指针p指向的变量的内容
& 和 * 是两种相反的运算符,一个取地址,一个取地址所指向的变量的值
4. scanf("%d",i)
编译的时候可能不会报错,因为某些电脑是32位的,地址就是32位的,跟int型变量的长度可能一样,因此scanf函数会把i的值当作地址,但是运行的时候就很可能出错了,因为i的值一般不大,而低地址在内存中往往是操作系统或者其他程序使用的。
5.指针的使用场景
-swap交换两个变量的值
-传给函数的参数实际是需要带回结果的变量,这样的返回值有多个,而函数return 语句只能返回一个变量
-函数返回运算状态(成功/失败),指针返回运算结果。通常0表示成功,-1表示失败,当结果也可能取到-1/0时,就必须用指针返回,return 返回成功/失败的结果
-当传递很大的数组或者结构体的时候,可以使用指针传递,当希望这些数组或者结构体不被修改的时候,可以使用 fun(int const * p)或者fun(const int a[])(这种只适用于数组)
6.指针最常见的错误
-定义指针之后没有赋值就直接使用 ,即指针需要赋值之后才能使用 *指针 来读取指针所指向的变量,因为假如不初始化赋值指针,指针所在的内存单元存的值是未初始化的(随机的),因此指针指向的内存单元是未知的,运行的时候很有可能出错。
7.传入函数的数组的本质
函数参数表中的数组实际是指针
fun(int a[])、fun(int *a)、fun(int *)以及fun(int [])四种函数原型是等价的
在函数fun里面使用sizeof(a)其实是指针变量的size,这里也就是8字节的长度,我的电脑是64位的。只有在main函数里面使用sizeof,才表示数组的长度x数据类型的长度
int b[] =a //a是数组,这里是非法的,因为int b[] 等价于 int * const b,即数组一旦创建,其地址是无法改变的,因为地址要分配一段连续的内存,如果改变,无法保证改变后的地址后续连续的地址可以使用。
8.指针与const //这里的内容只适用于C99
int * const p=&a;
*p=26; //OK
p++; //Error,这种情况是不能修改指针变量的值的
const int *p=&a;
*p=26; //Error,这种情况是不能通过p修改a的,但是a可以修改
a=26; //OK
p=&b; //OK
总结:判断哪个不能修改是看const是在*前面还是后面,如果在前面,表示不能通过指针修改变量的值,如果在后面表示不能修改指针,因此 int const * p 和const int * p等价
9.const 数组
const int a[5]={1,2,3,4,5} //表示数组里面的每一个值都不能改变,而且数组必须通过这种初始化来赋值