学习笔记---指针法访问数组、数组的实质、数组/指针作为函数参数
来源:互联网 发布:网络红猫包子脸猫 编辑:程序博客网 时间:2024/06/02 04:24
指针法访问数组
首先,通过一个小程序来初步窥探数组的实质:
#include <stdio.h>#include <stdlib.h>/*这个程序用于测试数组的实质*/#define n 5int main(){ int a[n]={123,5,9,11,33}; printf("%d\n",a);//如果直接输出数组名字所代表的值 printf("%x\n",a);//如果用16进制输出数组名字所代表的值 printf("%x\n",&a[0]);//如果用16进制输出数组第一个元素的地址 printf("%d\n",a[0]); printf("%d\n",*a);//如果对数组的名字使用取指针运算。。 return 0;}结果:
解析:
1.当输出数组名所代表的值时,发现输出的不是数组内所有的元素,甚至不是数组内任何一个元素。
2.用十六进制输出数组名所代表的值和数组第一个元素的地址值,发现他们是相同的。
3.对数组名使用取指针运算,得到的结果就是数组第一个元素的值。
4.综上所述,可以得出结论:数组名代表的是数组第一个元素的地址值。
数组和指针
已知:数组名代表数组的起始地址(数组首元素的地址)。
因此:
当 int a[10],*p;
p=&a[0];等价于p=a;
由此,进行以下测试:
代码示例:
#include <stdio.h>#include <stdlib.h>/*这个程序用来测试数组的本质核心:数组的本质是在内存空间中定义一段连续的存储单元,数组名等同于指向数组首元素的指针*/int main(){ int a[10]={1,3,5,7,9,11,13,15,17,19},*p,i;//定义一个整型的数组和指针 p=a;//使得指针指向数组中第一个元素的地址 /*用传统的方式输出数组中的元素*/ printf("output1:\t"); for(i=0;i<10;i++) printf("%d ",a[i]); /*因为p是数组中第一个元素的地址,所以。。*/ printf("\noutput2:\t"); for(i=0;i<10;i++) printf("%d ",*(p+i)); /*因为a也是数组中第一个元素的地址,所以。。*/ printf("\noutput3:\t"); for(i=0;i<10;i++) printf("%d ",*(a+i)); /*更激进的尝试。。。*/ printf("\noutput:4\t"); for(i=0;i<10;i++) printf("%d ",p[i]); return 0;}结果:
解析:
1.p+1代表的是p加一个内存单元的值,如果p代表数组第一个元素的地址,那么p+1代表的就是数组第二个元素的地址。
2.验证得出,以上4中方法都能输出数组中的值。
3. [ ] 这种形式的常用于数组之后的符号,其实是一种运算符。
注1:
该运算符的运算实质:
1.按(a+i*d)计算数组元素的地址(d为数据类型占用的字节数,如int a[] 则d的值为int型代表的4字节)
2.取出地址所指向的单元的值
注2:
对以上内容的图解:
结论:
引用数组元素有两种方法
1.下标法:
示例:a[i] p[i]
特点:简洁明了
2.指针法:
示例:*(a+i) *(p+i)
特点:效率高
指针法作为C语言中特色的数组使用方法,虽然比起下标法更难驾驭,但为了写出效率更高的代码。这是必须要学会的东西——指针法标准写法:
p=a;while(p<a+10) printf("%d",*p++);
以上指针法调用数组的代码中,大量运用了指针的加法(p+1、p++)
这里,将指针的加减等运算法则做一个深入的解析
指针变量加减一个整数:
例如:
p++,p--,p+i,p-i,p+=i,p-=i;
结果为p的值加减一个p所代表的数据类型的存储单元
如int *p;则p-1为p的值减去int所代表的4字节存储单元,而double *p;则p-1为p的值减去double 所代表的8字节存储单元。
两个指针变量相减:
例如:
int a[10];
int *p2=&a[4],*p1=a;
此时:
p2-p1=(a+4)-(a+1)=4-1=3
所以:
两个指针变量相减,结果为两指针之间的元素个数。
两个指针相加:
例如:
int a[10];
int *p2=&a[4],*p1=a;
此时:
p2+p1=(a+4)+(a+1)=2*a+5注意:
因为2*a代表的地址值可能是没有被注册使用的,或者已经被其他程序使用的内存空间!所以贸然调用可能会出现不可预知的后果!
所以:
2*a+5这个地址值没有使用价值(这也可以看成一种野指针),我们规定两个指针变量不能相加。
指针变量的比较
有意义的比较——指向同一个数组的两个指针进行比较:
例如:
int a[10];
int *p2=&a[4],*p1=a;
此时,p2>p1为真(实为比较它们的地址值)无意义的比较——指向不同数组的两个指针进行比较:
例如:
int a[10],b[10];
int *p2=a,*p1=b;
此时,p2>p1可能为真,也可能为假(因为a和b的数组代表的内存空间每次运行都是不同的)
不同数据类型的指针的比较和运算:
当希望比较两个不同数据类型的指针的大小时,需要进行强制类型转换
例如:
/*以下代码只是为了示范,无实际使用价值*/int a=2,*p1;double b=3.0,*p2;p1=&a,p2=&b;if(p1>(int)p2){.....}
指针和NULL:
指针可以被赋值为NULL(所谓的空指针),也可以和NULL进行等值判断
例如:
char *p5=NULL;if(p5==NULL) ....
指向函数的指针
格式:函数类型 (*指针变量名)(函数形参表);
例如:
int (*p)(int,int);
此时,p就是一个指向函数的指针。
注意:
因为函数是保存在内存的程序区(而非静态/动态数据区)中的,所以当p指向函数时,p指向的就是程序区中内存空间!这意味着通过指针,我们甚至可以操控程序区中的内容!
使用方法:
int (*p)(int,int);int max(int,int);p=max;
如上,可以看到p=max这一句代表的意义:正如数组名代表着数组的起始地址,函数名也代表这函数的起始地址!
指针、数组和函数
数组名作为函数实参
示例:
int f(int array[],int n);int main(){ int a[10],b; .... b=f(a,5) ....}
数组作为函数参数和变量作为函数参数有很大的不同,利用以下代码说明
代码示例:
#include <stdio.h>#include <stdlib.h>/*这个程序用来测试数组名作为函数参数的机制*/void fun(int a[],int b);int main(){ int c[]={2,4},d=10; printf("%d\t%d\t%d\n",c[0],c[1],d); fun(c,d); printf("%d\t%d\t%d\n",c[0],c[1],d); return 0;}void fun(int a[],int b){ int i; for(i=0;i<2;i++) { a[i]*=10; } b*=10;}结果:
解析:
如上,在自定义函数中分别对形式参数b和数组a[]的值进行修改,结果对a的修改影响到了主函数中的c,而对b的修改却没有影响到d。
参数的传递
地址传递:
实质:
将地址作为实际参数传递至自定义函数中
解析:
如上,用fun(c,d)调用函数fun时,实际参数列表中的c是c数组的名字,而c数组的名字代表的是c数组所占内存空间的起始地址!
所以fun中的a数组被赋值时实际上是:a=c;参照上方指针法调用数组的内容,很容易能理解:a作为一个指向c数组的指针被使用了!
当fun中对a数组进行操作时,实际上是通过c数组的指针直接操作c数组。这就是为什么fun中对c的操作能被保留
值传递:
实质:
将值作为实际参数传递至自定义函数中
解析:
在函数初步篇已解析,不做赘述。
代码示例:
#include <stdio.h>#include <stdlib.h>/*这个程序用来演绎冒泡排序算法原理:1.比较相邻的元素。如果第一个比第二个大,就交换他们两个。2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。3.针对所有的元素重复以上的步骤,除了最后一个。4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。冒泡排序法的精巧之处在于:一轮比对完之后,就能获得一组数据中最大的数,然后下一轮获得次大的数,如此重复。同时,相比于选择排序法的一轮比对完只交换一对数字。冒泡排序几乎每次比对都交换一对数字。这使得效率大大提高。如不理解可以通过这里的几段舞蹈直观的认识:http://dapenti.com/blog/more.asp?name=xilei&id=65524*/void bubblesort(int [],int n);//核心算法int main(){ int i; int d[10]={51,29,34,11l,547,2,43,66,20,5}; bubblesort(d,10); for(i=0;i<10;i++) printf("%d ",d[i]); return 0;}void bubblesort(int a[],int n){ int i,j,t; for(j=0;j<n-1;j++) { for(i=0;i<n-j-1;i++) { if(a[i]>a[i+1]) { t=a[i]; a[i]=a[i+1]; a[i+1]=t; } } } return;}结果:
解析:
这里再次放出冒泡排序法的代码,可以看出:bubblesort()函数就是利用了数组作为函数参数时的特性,使得在自定义函数中对数组做的排序到主函数中依然生效。
指针作为函数参数
如果将上述冒泡排序代码进行修改:
#include <stdio.h>#include <stdlib.h>/*这个程序用来演绎冒泡排序算法原理:1.比较相邻的元素。如果第一个比第二个大,就交换他们两个。2.对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。3.针对所有的元素重复以上的步骤,除了最后一个。4.持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。冒泡排序法的精巧之处在于:一轮比对完之后,就能获得一组数据中最大的数,然后下一轮获得次大的数,如此重复。同时,相比于选择排序法的一轮比对完只交换一对数字。冒泡排序几乎每次比对都交换一对数字。这使得效率大大提高。如不理解可以通过这里的几段舞蹈直观的认识:http://dapenti.com/blog/more.asp?name=xilei&id=65524*/void bubblesort(int *p,int n);//核心算法int main(){ int i; int d[10]={51,29,34,11l,547,2,43,66,20,5}; bubblesort(d,10); for(i=0;i<10;i++) printf("%d ",d[i]); return 0;}void bubblesort(int *p,int n)//将形参中的数组换成了指针{ int i,j,t; for(j=0;j<n-1;j++) { for(i=0;i<n-j-1;i++) { if(*(p+i)>*(p+i+1)) { t=*(p+i); *(p+i)=*(p+i+1); *(p+i+1)=t; } } } return;}结果:
解析:
1.函数依然正常运行,这原因应当很容易理解。只是把实参中的数组地址赋值给形参中的指针而已。
2.由上可以推断,形参和实参的定义可以是数组、数组,数组、指针,指针、数组,指针、指针等组合。
核心思想:
数组即指针
指针分类:
指针常量
例如:
int main(){ int a[5]={0,1,2,3,4};}这里的a看似是数组,其实就是指针,是指针常量
注1:因为a代表的是数组的起始地址,所以a的值必须是确定且固定的的(为了防止内存的调度出错),即:a虽然也是指针,但a是指针常量
注2:这里如果使用a++或a+=1;等代码将出错,因为a代表的虽然是指针。但是一种常量指针,而非变量。类比数据类型的变量和常量之别,我们无法给常量赋值。
指针变量
例如:
void f(int arr[],int n);int main(){ int a[5]={0,1,2,3,4} f(a,n); return 0;}void f(int arr[],int n){ arr+=3; printf("%d\n",arr[1]);}这样的函数并不会出错,因为在f中,arr看似是一个数组名(指针常量)实际上却是一个指向a函数的指针变量。
注1:当执行f函数时,先给arr创建内存空间,然后将传入的a的地址赋给arr。
注2:作为变量,arr的值是可以被改变的。所以arr+=3是合法的。最后这段程序的输出将是4。
数组类型:
作为实参的数组:
声明时分配数组空间
其名字代表一个不可改变的固定的地址
作为形参的数组:
调用时分配指针空间,接受传递的地址
总结:作为形参的数组实际上就是指针变量,作为实参的数组实际上就是指针常量(数组的首地址的值)
- 学习笔记---指针法访问数组、数组的实质、数组/指针作为函数参数
- 指针数组,数组指针,函数指针,main函数实质,二重指针,函数指针作为参数,泛型函数
- 指针数组,数组指针,函数指针,main函数实质,二重指针,函数指针作为参数,泛型函数
- 数组、指针作为函数参数
- 数组作为函数参数的指针问题
- 数组指针,指针数组,二维数组作为参数传递给以指针的指针作为形参的函数
- 学习:“指针数组”、“ 数组指针 ”、“函数指针”、“ 函数指针数组”、“指向函数指针数组的指针”
- 数组、指针数组、数组指针、动态数组、数组作为函数参数
- 数组、指针和字符串:数组的存储与初始化、对象数组、数组作为函数参数
- 数组作为函数参数、scanf初始化指针
- 关于main函数:指针数组作为main函数的参数
- 怎么使用二维数组的指针作为函数的参数
- 指针数组作为函数参数的使用问题
- C++指针【数组、字符串作为函数的参数】
- 作为参数的数组实际上是指针
- 传递二维数组作为参数的指针
- 带指针参数的函数指针数组
- “作为函数参数的二维数组”即“怎么给函数传二维数组的指针”
- jquery插件 jsp+servlet+uploadify3.1 文件上传
- Servlet
- 学习JAVA Web必须了解的几个概念(一)
- js/jquery判断浏览器的方法小结
- SynchronousQueue,LinkedBlockingQueue,ArrayListBlockingQueue比较
- 学习笔记---指针法访问数组、数组的实质、数组/指针作为函数参数
- python实现 爬取twitter用户姓名
- 2055: 80人环游世界
- dubbo入门(1)
- SLAM 入门资料收集整理
- java学习笔记————本质篇
- 引水入城
- 每天学点tp-模型(二)
- [CSAPP] 信息的表示和处理(二)