一.数组与指针
1.指针数组(存放指针的数组)
例如:char *p[10]; float *p[10];int * q[4][3];
♦ 一个数组,及其元素均为指针类型的数据——指针数组
一维指针数组:类型名 数组名[数组长度];
二维指针数组:类型名 数组名[行][列];
• 一维数值指针数组:
int array[8] = {1,2,3,4,5,6,7,8};
int *p[6] = {&array[0],&array[1],&array[2],&array[3]};
p[4] = &array[4];
p[5] = &array[5];
• 二维数值指针数组:
int *a[2][3];
a[0][1] = &array[0];
• 指针数组与字符串的联系:
char *name[2] = {"hello","world"};
name[0] = "hello";
name[1] = "world";
数组中存放的是字符串的首地址,而不是字符串。
//指针数组 #include <stdio.h> int main(int argc,char *argv[]) { char *name[] = {"Follow me","BASIC","Grate Wall"}; int i; for(i = 0;i < 3;i++) { printf("%s",name[i]); } return 0; }
2.数组元素指针(指向数组元素的指针)
♦ 数组元素在内存中分配的地址——数组元素的指针
• 定义一个指向数组元素的指针
int a_1[10];
int a_2[5][5];
int *p,*q;
p = &a_1[0]; //一维数组元素的指针
q = &a_2[1][1]; //二维数组元素的指针
• 在C语言中,设定一维数组名代表了数组第一个元素的地址。
int array[5];
int *p;
p = array[0]; 等价于 p = array;
• 引用数组元素
1.下标法:array[1];
2.指针法:*(p + i);或*(array+i);
• array为数组的首地址是一个常量,不能进行array++或++array操作。
• p是变量,其值为array数组的首地址,可以进行++操作。
//采用指针法引用数组元素 #include <stdio.h> int main(int argc,char *argv[]) { int a[5],i,*pa,*pb; pa = a; //数组名代表数组首地址 pb = &a[0]; //数组的第一个元素的地址也代表数组首地址 for(i = 0;i < 5;i++) { a[i] = i; printf("a[%d] = %d ",i,a[i]); //0 1 2 3 4 printf("a[%d] = %d ",i,*(a+i)); //0 1 2 3 4 printf("a[%d] = %d ",i,*(pa+i)); //0 1 2 3 4 printf("a[%d] = %d ",i,*pb++); //0 1 2 3 4 } //printf("a[%d] = %d ",i,a++); //a为常量,不能进行++操作 return 0; }
3.数组指针(行指针)(指向数组的指针)
♦ 数组元素在内存中的起始地址——数组元素的指针
• 定义一个数组元素的指针
int a[10] = {1,2,3,4};
int *p = a; //p为数组元素的指针
• 数组在内存中的起始地址称为数组的指针
int a[10] = {1,2,3,4};
int (*q)[10] = &a; //q为指向数组的指针
a为一维数组名,代表数组第一个元素的指针。
&a:对a取地址,代表将整个数组当做一个整体,将这个地址付给q——数组指针。
//数组指针 #include <stdio.h> int main(int argc,char *argv[]) { int a[5] = {1,2,3,4,5}; int (*p)[5] = &a; //定义一个一维数组,指向一个一维数组 printf("%d ",*((int *)(p+1)-1)); //5,p+1移动整个数组的长度 return 0; }
• 指向二维数组的数组指针
int a[3][4] = {{1,3,2,4},{5,6,7,34},{5,9,6,8}};
int (*p)[4] = a;
p等价于指向二维数组第0行,可完全替代a的作用。
• 二维数组名
• 二维数组名是一个二级指针
• a代表了二维数组第0行的行地址
• a+1代表了第一行的行地址
• *(a+1)代表了第1行第0列元素的地址
• *(a+1)+2代表了第1行第2列元素的地址
• *(*(a+1)+2)代表了第1行第2列元素
• 多维数组与指针
表示形式 | 含义 |
a | 数组名,指向一维数组a[0],即0行首地址 |
a+1、&a[1] | 1行首地址 |
*(a+0)、*a、a[0] | 0行0列元素地址 |
*(a+1)、(*(a+1)+0)、a[1] | 1行0列元素a[1][0]的地址 |
*(a+1)+2、a[1]+2、&a[1][2] | 1行2列元素a[1][2]的地址 |
*(a[1]+2)、*(*(a+1)+2)、a[1][2] | 1行2列元素a[1][2]的值 |
//数组指针 #include <stdio.h> int main(int argc,char *argv[]) { int a[3][5]={ {1,2,3,4,5}, {6,7,8,9}, {14,12,23,66} }; int i,j; int (*p)[5] = a; //定义一个数组指针指向一个二维数组 for(i = 0;i < 5;i++) //采用数组指针打印二维数组的第一行 printf("%d ",*(p[0]+i)); for(i = 0;i < 5;i++) //采用数组指针打印二维数组的第二行 printf("%d ",*(p[1]+i)); for(i = 0;i < 3;i++) //采用数组指针打印二维数组的全部内容 { for(j=0;j<5;j++) { printf("%d ",*(p[i]+j)); if(j == 4) printf(" "); } } return 0; }
二.函数与指针
1.指针作函数参数
• 指针变量作函数的参数
//指针作函数的参数 #include <stdio.h> int exchange1(int p1,int p2) { int temp; temp = p1; p2 = p1; p2 = temp; } int exchange2(int *p1,int *p2) { int temp; temp = *p1; *p1 = *p2; *p2 = temp; } int main(int argc,char *argv[]) { int a,b; scanf("%d %d",&a,&b); //a = 9,b = 3 if(a > b) //exchange1(a,b); //a = 9,b = 3 exchange2(&a,&b); //a = 3,b = 9 printf("a = %d,b = %d ",a,b); return 0; }
• 一维数组名作函数的参数
//一维数组名作函数的参数 #include <stdio.h> // int max(int a[],int num) //数组作形参时,无需指定下表 int max(int *a,int num) //数组名作形参时,将演变为指针 { int i,max; max = a[0]; for(i = 0;i < num;i++) { if(a[i] > max) max = a[i]; } printf("a_len = %d ",sizeof(a)); //a_len = 4 return max; } int main(int argc,char *argv[]) { int a[5] = {4,7,9,6,5}; printf("max = %d ",max(a,5)); //max = 9 return 0; }
• 数组名作形参时,无需指定其下标。
• 数组名作形参时,将演变为指针。
• 二维数组名作函数的参数
//一维数组名作函数的参数 #include <stdio.h> // int max(int a[][3],int x,int y) //二维数组名作形参时,行可以不写出来,列必须写出来 int max(int (*a)[3],int x,int y) //数组指针作形参,完全等价于二维数组的应用 { int i,j,max; max = a[0][0]; for(i = 0;i < x;i++) { for(j = 0;j < y;j++) { if(a[i][j] > max) max = a[i][j]; } } printf("a_len = %d ",sizeof(a)); //a_len = 4 return max; } int main(int argc,char *argv[]) { int a[2][3] = { {4,7,9}, {11,6,5} }; printf("max = %d ",max(a,2,3)); //max = 11 return 0; }
• 二维数组名作参数,行可以不写出来,列必须写出来。
• 数组指针作形参,完全等价于二维数组的应用。
2.指针作函数返回值
• 返回指针值的函数,一般定义形式:类型名 *函数名(参数列表);例如:int *fun(int x,int y);
• 在调用时要先定义一个适当的指针来接收函数的返回值,这个适当的指针其类型应为函数返回指针所指向的类型。
char *pc = NULL;
pc = (char *)malloc(100 * sizeof(char)); //表示分配100个字节的内存空间,并强制转换为字符数组类型,函数的返回值为指向该字符数组的指针,把该指针赋予指针变量pc
3.指向函数的指针(函数指针)
♦ 一个函数在编译时被分配一个入口地址,这个地址就称为——函数的指针。
• 函数名代表函数的入口地址。
• 指向函数指针变量的定义格式:int (*p)(int,int);
例如:int (*p)(int,int);
p = max; //将函数的入口地址赋给函数指针变量p
c = (*p)(a,b); //调用max函数
• 说明:
• p不是固定指向哪个函数的,而是专门用来存放函数入口地址的变量,在程序中把哪个函数的入口地址赋给他,它就指向哪个函数。
• p不能像指向变量的指针变量一样进行p++、p--等无意义的操作。
♦ 函数指针的应用——回调函数(钩子函数)
• 函数指针变量常见的用途之一是把指针作为参数传递到其它函数。
• 指向函数的指针也可以作为参数,以实现函数地址的传递,这样就能够在被调用的函数中使用实参函数。
//函数指针 //process称为回调函数,process并没有直接调用max、min、add函数,而是通过fun函数指针接收相应函数首地址,进行调用,调用后把结果返回给主调函数main #include <stdio.h> int max(int x,int y) { return x>y?x:y; } int min(int x,int y) { return x<y?x:y; } int add(int x,int y) { return x+y; } int process(int x,int y,int (*fun)(int ,int )) //回调函数 { int result; result = (*fun)(x,y); return result; } int main(int argc,char *argv[]) { int a,b,result; printf("input a and b:"); scanf("%d %d",&a,&b); result = process(a,b,max); printf("max = %d ",result); result = process(a,b,min); printf("min = %d ",result); result = process(a,b,add); printf("add = %d ",result); return 0; }
三.其它特殊指针
1.main函数带参 ./a.out hello peter ……
• main函数可以接收来自操作系统或者其它应用传递的参数。
• int main(int argc,char *argv[])
• 说明:
• argc代表参数的个数
• argv存放各参数首地址(系统把每个参数当作一个字符串放在系统缓冲区,把首地址放在指针数组中)
• ./a.out hello peter …… 依次存放在argv[0] argv[1] argv[2] …… 中
//main函数传参 //如执行 ./a.out hello lucy //输出: // argc = 3 // argv[0] = ./a.out // argv[1] = hello // argv[5] = lucy #include <stdio.h> int main(int argc,char *argv[]) { int i; printf("argc = %d ",argc); for(i = 0;i < argc;i++) printf("argv[%d] = %s ",i,argv[i]); return 0; }
2.指向指针的指针 char **p
♦ 一个变量有自己的地址,我们可以用一个指针变量指向它,指针变量同样也是变量,我们也可以定义另一个指针指向这个指针变量——指向指针的指针
int a;
int *p = &a;
int *q = &p;
♦ 通过函数改变指针的指向
//通过函数改变指针的指向 #include <stdio.h> #include <string.h> #include <stdlib.h> void GetMemory1(char *p,int num) //泄露一块内存 { p = (char *)malloc(num * sizeof(char)); } void GetMemory2(char **p,int num) //在子函数内部改变主调函数定义指针的指向 { *p = (char *)malloc(num * sizeof(char)); } int main(int argc,char *argv[]) { char *str = NULL; // GetMemory1(str,100); //段错误 GetMemory2(&str,100); //hello kitty strcpy(str,"hello kitty"); printf("str = %s ",str); free(str); return 0; }
3.void类型的指针 void *指针
♦ void指针是一种很特别的指针,并不指定它是指向哪一种类型的数据,而是根据需要转换为所需数据类型。
int a = 0;
float b = 0;
void *p = &a;
void *q = &b;
♦ 常用于函数的返回值和参数,用于不确定类型指针指向
void malloc(unsigned int num_types);
void memset(void *str,char ch,size_t n);
4.函数指针数组
函数指针数组的定义 例如:int * (*p[10])(int a[3][4],int num) //定义了一个函数指针数组,带有两个参数,返回值为int *类型