
前体知识:格式控制符%p可以输出十六进制格式的地址;%lu可以输出无符号十进制整数地址。
9.1.1 取地址运算
运算符:&
&:获得变量的地址,它的操作对象必须是变量。

32位,即x86下编译运行时相同,64位下正常的地址是八字节,int是四字节)
#include<stdio.h>
int main()
{
int i;
printf("0x%x\n",&i);//0x开头,输出16进制数,因为c不会自动补上0x
return 0;
}
//程序先给了一个warning,然后输出了0xbff12d70。
warning的提示是,如果想输出地址,不应该用%x而是%p,%p会自带0x开头输出十六进制的变量地址。
因此我们将上面打印一句改为 printf("%p",&i);
&不能取的地址
必须是明确的变量
//都不行!!!!! 这三种都不能编译,必须是一个明确的变量
p=(int)&(p+i);
p=(int)&(++i);
p=(int)&(i++);
&不能对没有地址的东西取地址
试试&能取的地址
变量地址(前面已试过,可行)
相邻变量(连续定义)的地址(地址也相邻)
int i=0;//最好初始化这样能确定其地址
int p;
//相邻变量
printf("%p\n",&i);
printf("%p\n",&p);

//十六进制的c=12,因此两变量地址相差4,即一个int的字节大小。我们此时是在32位架构下编译,因此说明了,相邻变量在内存中的地址也是相邻的。
结果:

sizeof(&变量)(前面已试过,可行)
数组的地址
数组单元的地址
相邻数组单元的地址
#include<stdio.h>
int main()
{
int a[10];
printf("%p\n",&a);//0xbff8dd44
printf("%p\n",a);//0xbff8dd44 数组变量的名字 试图把它当成地址输出
printf("%p\n",&a[0]);//0xbff8dd44 取出a[0]的地址
//printf("%p\n",a[0]); 结果:0000000000000001 视频里面没有这句
printf("%p\n",&a[1]);//0xbff8dd48 a[0]和a[1]的地址相差4,为一个int的值。
return 0;
}

9.1.2 指针
如果能将变量地址取出,然后传递给一个函数,是否能通过这个变量的地址实现在函数中访问变量呢?
有没有感觉似曾相识:scanf("%d",&i);
思考:我们将一个地址传给scanf,它可以从其中取得我们输入的整数并放到变量i中。那么它是如何做到的呢?它是如何保存变量的地址的呢?什么样的变量可以接收取地址符得到的地址呢?
指针
概念:
一个指针类型的变量就是保存地址的变量。
通常用p表示指针(point)
int *p=&i;
//p是一个指针,指向一个int类型,并把i的地址交给p(也就是 p的值=i的地址)

int i;
int* p=&i;//将i的地址交给p,或者说 p指向i,此时p的值=i的地址
*的写法:
二者的意思都一样,都表示p是一个指针指向int,*p是int类型,q是一个普通的int型变量
int* p,q;//星号*靠近int
int *p,q;//星号*远离int
指针变量:
指针变量的值是内存的地址,与之相对的,普通变量的值就是实际的值

作为参数的指针(函数中使用指针)
void f(int *p);
//函数在被调用时可以得到某个变量的地址
int i=0;
f(&i);
//在函数里可以通过这个指针访问外面的局部变量i
举例说明:


*运算符
引入一个新运算符:*
*是一个单目运算符,用来访问指针的值所表示的地址上的变量。
*p:访问对应地址上的变量
#include <stdio.h>
void f(int *p);
int main()
{
int i=33;
f(&i);
}
void f(int *p){
printf("p=%p\n",p);//p=000000000062FE1C
printf("*p=%d\n",*p);//*p=33
}
p是指针,保存地址的变量,p的值就是i的地址
*p:通过p这个指针,我们访问到了p所指的int i里面的值,*p的值就是i的值
printf中的*p作为一个整体可以看作一个整数 (int *p)
*p值的传递
函数的调用时发生的参数的转移,是一种值的传递,传值进函数后就与原来变量没关系了。
然而当传递进去的是地址时,就可以在函数内部访问外部变量,得以修改其值。
修改*p相当于是修改i。
如原变量为i=6,*p=&a;*p=5,则此时i的值更改为5.

*p可以做左值也可以做右值。
int k=*p;
*p=k+1;
【*p】意思是我要取得p这个指针所指的变量。(访问指针的值所表示的地址上的变量)
左值:放在赋值号左边,不是变量,而是值,实际上是表达式计算的结果。
a[0] =2;
*p =3;//因此如果*p为左值时,代表了将 右边的计算结果赋值给对应地址上的变量。
指针运算符——& 与*
&是取得变量的地址,
*是取得对应地址的变量。
//从字面上来看,二者互为对方的逆过程。事实上二者确实是互为反作用。
*&y--*(&y):对一个地址取对应的变量==y
&*y---&(*y):对一个指针取对应的地址==指针(因为存放变量的地址),也就是对 指针取地址还是指针
传入地址
int x;scanf("%d",x);
没写&,但是编译没报错。为什么
如果正好是32位架构,整数和地址所占字节一样大,编译器会认为这就是你传入的地址。因此scanf函数不能将你输入的值正确传入给变量,而是传到了别的地方。因此运行一定是会报错的。
9.1.3 指针的作用
指针应用场景1 :交换变量的值
void swap(int *pa,int *pb)
{
int t=*pa;
*pa=*pb;
*pb=t;
}

此时只需要传入两个地址即可完成交换,原理是相同的,但传入指针可以改变原始值。
定义函数返回多个值:
正常函数只能返回一个值也就是说,其作用是返回多个值,且某些值只能通过指针带回。传入的参数是需要进行编辑的变量。

指针应用场景2:在函数中与return分工返回值和状态
一般用return0或-1等不属于有效范围内的值表示出错(多在文件操作中出现)
任何数值都是有效的话:函数通过return返回运算状态,指针返回结果。
//两个整数作除法
#include<stdio.h>
int divine(int a,int b,int *result);
int main(void)
{
int a=5;
int b=2;
int c;
if(divine(a,b,&c))//看函数divine的返回值 即:ret
{
printf("%d/%d=%d\n",a,b,c); //用指针返回值
}
return 0;
}
int divine(int a,int b,int *result)
{
int ret=1;
if(b==0) ret=0;//(除数是0) 失败
else
{
*result=a/b;
}
return ret;//成功返回1 失败返回0(除数是0)
}
当被除数=0,即b=0时,函数会直接返回状态值ret为0,而函数又位于if条件处,此时不会输出任何值。当正常时就会将a/b=c输出。实现了分开返回值与状态。
这种方法的适用条件是运算中可能会出错,在C++/JAVA等语言中可以采用异常机制来解决这个问题。
指针最常见的错误
定义指针变量,但没有指向任何变量就使用了指针
int k;
k=12; //可以!!
——————
int *p;
*p=12;// 不可以!!!程序可能会崩溃!!给的不是地址 //程序大概率会出错,且具有不确定性。
————————
*p=5;
printf("%d",i);