数组的定义
数组就是一类具有相同数据类型的数据集合。数组的第一次定义也叫数组的初始化。数组作为集合既可以有确定的数据元素也可以在定义的时候将元素个数省略。
int a[3] = {1,2,3}; //数组有确定个数的元素 int a[] = {3,4,2,5,2}; //数组的元素个数省略 int a[5] = {1,2,3}; //数组的元素确定,可以定义有限个 int a[10] = {[1] = 10,[4] = 5}; //数组的指定元素进行初始化
数组的元素个数应该为常量,如果直接用变量定义编译报错。
int c = 5; int a[c] = {1,2}; //直接报错 int a[c]; //这样初始化编译通过 a[1] = 1; a[2] = 3; int b[c];
数组与指针的关系
1.首地址
在定义数组时数组作为首地址出现,它既是数组定义的标识符,也是数组的首地址。
int a[10] = {1,2,3,4,5,6,7,8,9,0}; for(int i = 0; i < 10; i++) { printf("%p\t",a+i); printf("%d\n",a[i]); } printf("%p",a);
运行结果如下:
0x7fff5fbff8a0 1 //a的首地址 0x7fff5fbff8a4 2 0x7fff5fbff8a8 3 0x7fff5fbff8ac 4 0x7fff5fbff8b0 5 0x7fff5fbff8b4 6 0x7fff5fbff8b8 7 0x7fff5fbff8bc 8 0x7fff5fbff8c0 9 0x7fff5fbff8c4 0 0x7fff5fbff8a0 //a作为数组名的首地址
数组的所有元素及元素的地址都被遍历出来了,其中数组名a的确表示的是数组的首地址。
2.整个数组的地址
数组有自己的首地址,但是把数组当作一个整体来看也有一个地址,这个地址和首地址相同。
int num[5] = {10,20,30,40,60}; printf("%p\n",&num[0]); printf("%p\n",&num); printf("%p\n",&num[0]+1); printf("%p\n",&num+1);
运行结果为:
0x7fff5fbff8b0 0x7fff5fbff8b0 0x7fff5fbff8b4 0x7fff5fbff8c4
从结果上来看数组的首地址和整个数组的地址是相同的,但是在对其地址进行运算时出现了差别。对数组的首地址进行运算时,+1操作移动4个字节的空间,而对整体数组的地址而言,+1就是移动了整个数组这么大空间的字节数即20个字节。这表明了对于数组的一个元素来说它的数据类型是int,所以对它操作就是一个int型的整数,而对于&num来说,这样的数据类型就是一整个数组,对它操作就是移动整个数组的空间。
指针数组与数组指针
1.指针数组
指针数组就是数组中的每一个元素都是指针。由于指针数组中存的都是指针,所以不同数据类型的指针有着不同的用途。
int *num[10]; int n[10]; for(int j = 0; j < 10; j++) { scanf("%d",&n[j]); num[j] = &n[j]; } for(int k = 0; k < 10; k++) { printf("%d\n",*num[k]); } char *name[5] = {"lsmseed","seed","buster","ages","devel"}; for(int i = 0; i < 5; i++) { printf("%s\n",name[i]); }
运行结果:
10 20 30 40 50 60 70 80 9 0 10 20 30 40 50 60 70 80 9 0 lsmseed seed buster ages devel
从结果可以看出,整型指针数组可存储多个整型数据的指针,而字符指针数组可以方便的存取多个常量字符串。
2.数组指针
数组的指针,即指向数组的指针。听上去和没解释差不多,但是确实是一个很精辟的解释,它和指针数组的含义有相同也有不同。数组的指针说的是指针,指针数组里存的是指针,但是数组指针强调的是指针的意义,就是将数组的地址传给指针,或者说是指针里存的是数组的地址。而指针数组强调的是数组这个数据类型,虽然指针数组里存的都是指针,但是,指针数组更倾向于将指针变量当作是一些元素来对待。
int ages[5] = {2,3,8,4,2}; for(int i = 0; i < 5; i++) { printf("ages[%d]=%d\n",i,ages[i]); } int *p = ages; for(int j = 0; j < 5; j++) { printf("*(p+%d)=%d\n",j,*(p+j)); } for(int k = 0; k < 5; k++) { printf("p[%d]=%d\n",k,p[k]); }
运行结果:
ages[0]=2 ages[1]=3 ages[2]=8 ages[3]=4 ages[4]=2 *(p+0)=2 *(p+1)=3 *(p+2)=8 *(p+3)=4 *(p+4)=2 p[0]=2 p[1]=3 p[2]=8 p[3]=4 p[4]=2
数组指针常用于数组的遍历,定义一个整型数组的指针指向数组名或是数组的首地址,通常就可以通过数组的指针变量来代替数组名来遍历数组。
指针函数与函数指针
1.指针函数
指针函数就是定义个带指针的函数,函数确定一个返回值且这个返回值是一个指针类型。
char *name() { char *n = "seed"; return n; } int main(int argc, const char * argv[]) { printf("%s\n",name()); return 0; }
结果如下:
seed
指针函数name直接返回了字符串seed的地址给主函数,所以指针函数返回的都是运行结果的变量地址。
2.函数指针
函数的指针,即指向函数的指针。又一个和没解释一样的概念。函数的指针也是一个和指针函数相对的概念。函数的指针强调的是指针变量的概念。每一个函数都有一个入口地址,这个入口地址就是函数指针发挥起作用的地方。当你调用函数时,可以定义一个函数指针来存这份入口地址来间接调用函数。
#include <stdio.h> int max(int *a,int len) { int num = a[0]; for(int i = 0; i < len; i++) { if(a[i] > num) num = a[i]; } return num; } int min(int *a,int len) { int num = a[0]; for(int i = 0; i < len; i++) { if(a[i] < num) num = a[i]; } return num; } int main(int argc, const char * argv[]) { int a[5] = {10,49,50,20,88}; int len = sizeof(a)/sizeof(int); printf("max = %d min = %d\n",max(a,len),min(a, len)); int (*p)(int *b, int a); p = max; printf("p(a,len) = %d\n",p(a,len)); p = min; printf("p(a,len) = %d\n",p(a,len)); return 0; }
运行结果:
max = 88 min = 10 p(a,len) = 88 p(a,len) = 10
函数指针p存了函数max的入口地址也就是函数max的地址后,直接调用指针变量p就能实现函数max的功能。同时指针变量p还可以赋值成其他函数的入口地址,这样又能调用其他函数。函数的指针极大地方便了不同函数地调用,只要有函数地入口地址就可以间接调用任何函数,不用理会函数地具体实现。
函数指针还可以当作一种类型来使用,方便定义其他地函数指针。
typedef int (*p)(int a,int b); int sum(int a, int b) { return a + b; } int main(int argc, const char * argv[]) { int a = 10; int b = 20; printf("%d\n",sum(a,b)); p plus = sum; printf("%d\n",plus(10,20)); return 0; }
运行结果:
30 30
运行结果可以看出,利用typedef取的别名p可以定义新的函数指针,将新的函数指针赋值成其他函数的入口函数一样可以间接调用其他函数。这样也相当于给已有函数取了一个别名。