好友
阅读权限20
听众
最后登录1970-1-1
|
本帖最后由 君莫笑WXH 于 2017-3-15 20:04 编辑
指针应该算得上是C语言的精华,但也是难点。可以说同学C语言学的好坏,简单通过指针就可以看出来!
计算机中所有的数据都必须放在内存中,不同类型的数据占用的字节数不一样,
例如 int 占用4个字节,char 占用1个字节。为了正确地访问这些数据,必须为每个字节都编上号码,就像身份证号一样,每个字节的编号是唯一的,根据编号可以准确地找到某个字节。
我们将内存中字节的编号称为地址(Address)或指针(Pointer)int a;举个例子,a 就相当于你的名字,而指针指向的是你的身份证号码(唯一性)。
C语言用变量来存储数据,用函数来定义一段可以重复使用的代码,它们最终都要放到内存中才能供 CPU 使用。
Tip:理解
先来想象一下,你正在进行一个密室逃脱真人游戏,发现桌子上有一张纸条,纸条上写着“书柜第二层第三本书66页”,你肯定会跟随纸条的内容找到那一页的内容。那么接下来我们来看一段代码:
int a;int*
pi;pi=&a; //这里可以将 &a理解为返回a的地址
pi不就相当于那个纸条么,而纸条内的内容“书柜第二层第三本书66页”不就相当于a的地址,你在看到纸条,自然而然去访问到了这个地址上的内容。
数据和代码都以二进制的形式存储在内存中,计算机无法从格式上区分某块内存到底存储的是数据还是代码。当程序被加载到内存后,操作系统会给不同的内存块指定不同的权限,拥有读取和执行权限的内存块就是代码,而拥有读取和写入权限(也可能只有读取权限)的内存块就是数据。
CPU 只能通过地址来取得内存中的代码和数据,程序在执行过程中会告知 CPU 要执行的代码以及要读写的数据的地址。
如果程序不小心出错,或者开发者有意为之,在 CPU 要写入数据时给它一个代码区域的地址,就会发生内存访问错误。
这种内存访问错误会被硬件和操作系统拦截,强制程序崩溃,程序员没有挽救的机会。
CPU 访问内存时需要的是地址,而不是变量名和函数名!变量名和函数名只是地址的一种助记符,当源文件被编译和链接成可执行程序后,它们都会被替换成地址。编译和链接过程的一项重要任务就是找到这些名称所对应的地址。假设变量 a、b、c 在内存中的地址分别是 0X1000、0X2000、0X3000,那么加法运算c = a + b;将会被转换成类似下面的形式:0X3000 = (0X1000) + (0X2000);( )表示取值操作,整个表达式的意思是,取出地址 0X1000 和 0X2000 上的值,将它们相加,把相加的结果赋值给地址为 0X3000 的内存
一、基本使用先来看看下面的代码:
1. int i = 3;
2. intint *p;
3. p = &i;
4. printf("i 存放的内容的值: %d, i 自己所在的地址: %p\n", i, &i);
5. printf("p 存放的地址的值: %p; p 自己所在的地址: %p; p 存放的地址所指所存放内容的值: %d", p, &p, *p);
6. return 0; 变量i是int类型,所以存放的是int数据。变量p是int *类型,所以存放的是指向int类型的地址
。
1. int i = 3; 这句话执行完毕之后,变量i中的内容是3,假设变量i本身的内存地址为 "0x6666"。2. int *p; 只是为指针变量p申请了一块内存地址,假设它的内存地址为 "0x8888"。3. p = &i;
表示将变量i的地址赋值给指针p所存放的内容,至此,就呈现出了上图的情形。
所以程序中的那两句打印结果就很明显了。
输出:i 存放的内容的值: 3, i 自己所在的地址: 0x6666p 存放的地址的值: 0x6666; p 自己所在的地址: 0x8888; p 存放的地址所指所存放内容的值: 3
二、 交换两个整数的值
示例代码如下:
1.
2. int main()
3. {
4. int a = 3, b = 5;
5. int swap(int *a, int *b);
6. swap(&a ,&b);
7. printf("a: %d; b: %d", a ,b);
8. return 0;
9. }
10. int swap(int *x, int *y)
11. {
12. int temp = *x;
13. *x = *y;
14. *y = temp;
15. }
当程序执行完 int a = 3, b = 5, 然后调用swap(&a, &b); 注意,这里传递的是变量a, b 本身的地址。而函数swap的接受形参int *a, int *b均是指针变量,用来接受传递过来的变量a, b的地址,即传递过来的变量a, b 和 函数 swap中的形式参数a, b 完全是两回事;
1. int temp = *a, 定义一个临时变量temp,用来存储指针swap_x 所指向变量a所存储的值,
2. *x = *y, 表示将将指针swap_y所指向变量b的内容赋值给指针swap_x所指向变量a的内容
3. *y = temp, 就是将temp所存储的内容赋值给指针swap_y所指向的变量b存储的内容
三、 数组与指针
1.指针访问数组元素
不知道大家是否看了我上一个关于数组的总结,里面提到了,在你定义了一个数组 int a[];那么a代表数组的首地址,代表数组第一个元素的地址。大家来看下面一段代码:int i,*p,a[]={5,2,0,1,3,1,4};p=a;for(i=0;i<7;i++)printf(“%d”,p);很显然,这样也是输出数组a内的值。在看另一段代码:int i,*p,a[]={5,2,0,1,3,1,4};p=a;for(i=0;i<7;i++)printf(“%d”,*(p+i));以上两段代码输出内容是相同的,故不难理解“a代表数组首地址这句话”。其实,数组名也就是一个指针,它是该数组的首地址。
2.数组名与指针变量的区别
int i,*p,a[]={5,2,0,1,3,1,4};
p=a;
for(i=0;i<7;i++)
{
printf(“%d”,*p);*p++; //此时指针值被修改
}
可以看出,这一段代码也是将数组各元素输出。有些人可能会说,既然数组名也是一个指针,那么可不可以将代码{ }内的p改成a,你们可以试试,编译出错!!!其实指针p是一个指针变量,而a则是一个指针常量。上面的代码中,指针p在整个循环中,其值是不断递增的,也就是说指针值被修改了。而数组名是一个指针常量,其值是不能修改的,因此不能进行类似的操作:a++。而在前面,*(p+i)处,指针p的值始终未改变,所以,此处p可用a替换。
四、 传递 不知道大家知不知道C语言中函数参数的传递有哪几种? 值传递,指针传递,引用传递。我相信正是这几种参数传递的形式,才会让大家晕针。题一为 值传递,
值传递的一个错误认识
问:你认为这个函数是在做什么呀?
答:好像是对参数 x,y的值对调吧?
请往下看,我想利用这个函数来完成对a,b两个变量值的对调,程序如下:
问:Exchg1 ()里头的 printf(“x=%d,y=%d\n”,x,y)语句会输出什么啊?
再问:Exchg1 ()后的 printf(“a=%d,b=%d\n”,a,b)语句输出的是什么 ?
程序输出的结果是:
x=6 , y=4
a=4 , b=6 //为什么不是a=6,b=4呢?
奇怪,明明我把a,b分别代入了x,y中,并在函数里完成了两个变量值的交换,为什么a,b变量 值还是没有交换(仍然是a==4,b==6,而不是a==6,b==4)?
如果你也会有这个疑问,那是因为你跟本就不知实参a,b与形参x,y的关系了。
其实函数在调用时是隐含地把实参a,b 的 值分别赋值给了x,y,之后在你写的Exchg1函数体内再也没有对a,b进行任何的操作了。
交换的只是x, y变量。并不是a,b.当然a,b的值没有改变啦!函数只是把a,b的值通过赋值传递给了x,y,函数里头 操作的只是x,y的值并不是a,b的值。
这就是所谓的参数的值传递了。
题二为 指针传递
指针传递的目的,不是改变实参的值,而是通过改变*px,达到改变a的目的。之所以能通过改变*px,改变a的值,是因为px=&a,px代表的是a的地址。
把地址传递给函数,在函数里通过这个地址,就能改变里面存储的数据。这样就达到了调用函数交换的目的。
题三为 引用传递
与指针传递不同的是,形参a,b的地址也与x,y相同,交换a,b就相当于交换x,y。
针传递和引用传递一般适用于:函数内部修改参数并且希望改动影响调用者。对比值传递,指针/引用传递可以将改变由形参“传给”实参(实际上就是直接在实参的内存上修改,
不像值传递将实参的值拷贝到另外的内存地址中才修改)。指针/引用传递的另外一种用法是:当一个函数实际需要返回多个值,而只能显式返回一个值时,
可以将另外需要返回的变量以指针/引用传递给函数,这样在函数内部修改并且返回后,调用者可以拿到被修改过后的变量,也相当于一个隐式的返回值传递吧。
五、 二级指针
二级指针为了改变该指针指向的变量的指向。改变p指针所指的方向。
1. #include<stdio.h>
2. int main()
3. {
4. int a=12,*p,**ptr;
5. ptr=&p;
6. p=&a;
7. **ptr=34;
8. printf("%d %d %d\n",a,*p,**ptr);
9. }
**p是双重指针,p可以指向其一个指针的地址,即变量里面放数据,指针里面放变量的地 址,二级指针放指针的地址。
Tip:对未初始化指针取值使用指针,有一个规则需要特别注意:不能对未初始化的指针取值!!!
例:
int* pt; // 未初始化的指针
*pt = 5; // 一个可怕的错误!表示把数值5存储在pt所指向的地址中。
但是由于pt没有被初始化,因此它的值是随机的,不知道5会被存储在什么位置。这个位置也许对系统危害不大,但也许会覆盖程序数据或代码,导致程序的崩溃。
切记:当创建一个指针时,系统只分配了用来存储指针本身的内存空间,并不分配用来存储数据的内存空间。因此在使用指针之前,必须给它赋予一个已分配的内存地址。
再如:
char* name;
scanf("%s", name);
这可能会通过编译器,但是在读入name的时候,name会覆盖程序中的数据和代码,并可能导致程序异常终止。这是因为scanf()把信息复制到由参数给定的地址中,而在这种情况下,参数是个未初始化的指针,name可能指向任何地方。
这些都是自己根据一些书本内容,博客加上自己理解总结出来的,希望对大家有用!大神勿喷,感觉初学指针的小伙伴还是有帮助的! |
免费评分
-
查看全部评分
|