int vari[10];
vari is array of intint vari[10][3];vari is array of array(3 elements) of intint *vari[10]vari is array(10 elements) of pointer to intint(*vari)[3];vari is pointer to array(3 elements) of intint vari[10][3]; vari往右看,是一个数组,有10个元素,元素的类型是int[10]。4 函数类型、声明与指向函数的指针变量的类型、声明将上述数据的符号[]改成(),数组元素的个数改成参数类型,则成了函数的声明,其理解规则基本一致。int arr(int); // 标识符arr→往右看→是函数符号→arr是一个函数,其参数类型是int→往左看,函数返回值的类型,类型是int。其类型可以理解为int(int),也就是挖掉标识符以后剩下的内容。函数元素的类型当然可以是指针变量,如int *,则写成int* arr(int); // 其类型可以理解为int*(int)函数的声明也是分裂式的,右边部分是函数符号及参数类型,左边部分是返回值类型。核心是右边的(),所以先往右看。指向函数指针的声明,也是标识符用(*p)填充:int (*p)(int); // 标识符p→往右看→是一个右括号(括号的中止)→往左看,是一个指针声明符号,所以p是一个指针,括号内的内容看完了,往右看,是函数符号→p指向一个函数,参数类型是int→往左看,函数元素的类型,类型是int。其类型可以理解为int(*)(int),也就是挖掉标识符以后剩下的内容。同样的int*(*p)(int); // 也是按上述思路去理解,p是指针,指向参数类型是int的函数,函数返回值类型是是int*。英文的表达是先干先枝,好像更准确:int func(int vari);
func is function(pararmeter is int vari) returning intint (*func)(int vari);func is pointer to function(pararmeter is int vari) returning int5 与数组、函数相关的类型与声明的理解的规则其类型和声明的写法是分裂式的。对于数组,右边是数组符号、数组元素数量,数组元素的类型写在左边。对于函数,右边是函数符号、函数参数类型,函数返回值类型写在左边。核心是右边,以理解的规则是,从标识符开始,往右看,看是数组还是函数,及元素数量或函数参数类型,然后再往左看,对应元素类型或返回值类型。如果有括号,则先理解括号内的部分,规则也是先右后左(也可以理解为符号[]和()相对于*,有较高的优先级)。(在按上述规则理解时,如果先看到的是右括号,自然是优先级的括号,如果先看到的是左括号,则一般是函数标识符括号)。二级指针和二维数组也可以应用于上述规则:int n = 5; int *p = &n; int **pp = &p; // p先右后左,右边没有,左边首先是*,表示p是一个指针,指向的类型是int* int vari[10][3]; // vari往右看,是一个数组,有10个元素,元素的类型是int[10]。6 指针变量与数组名数组名的实质是一个指针变量,指向一块内存的基地址,数组元素的分量以其地址为基准进行偏移,如:int arr[5]; arr[3] = 6; *(arr 3) = 6; // []写法是指针写法的语法糖注意arr 3运算时,指针的移动是按arr元素的尺寸进行移动的,移动的字节数是3*sizeof(int),为了方便指针的算术运算,在c或c 编译器中,要求数组名要蜕变为指向数组首元素的指针。因为这里数组本身的尺寸是5*sizeof(int),按数组自身的尺寸移动指针没有任何意义。int flag = 999; int arr[5] = {1,2,3,4,5}; arr[3] = 6; *(arr 3) = 6; // []写法是指针写法的语法糖 //int *p = &arr; // cannot convert from 'int (*)[5]' to 'int *' int (*pp)[5] = &arr; pp ; // pp指向了整个数组的后一个元素flag(栈地址是递减分配的) int a = **pp; // a = 999 int *p = arr; p ; //p指向了数组元素的下一个元素 int b = *p; // b = 2 printf("%d %d\n",a,b); // 999 2数组名的上下文中,只有三种情况表示数组本身,其它情形都蜕变为指向数组首元素的指针。int arr[3][4] = {{1},{2},{3,10,11,12}}; // 情形一,声明时 int n = sizeof(arr); // 情形二,使用sizeof时 int (*parr)[3][4] = & arr; // 情形三,取址时 int (*parr)[4] = arr; // 其它情况都蜕变为指向数组首元素的指针 int a = *(*(arr 2) 3); // arr[2][3]是指针写法的语法糖 //int **pp = arr; // cannot convert from 'int [3][4]' to 'int ** ' // pp和arr类型完全不一致,这的类型是int**,arr的类型是int[3][4],蕴含有长度信息。 printf("%d\n",arr[2][3]); // 12 printf("%d\n",a); // 12数据与指针的等价关系:比较一下指针和数组:7 指向数组或函数的指针变量的使用情形主要用做函数参数。7.1 数组指针用作函数参数#include #include void func(int(*p)[4]){} void func2(int**pp){} int main() { int arr[3][4] = {{1},{2},{3,10,11,12}}; int (*p)[4] = arr; func(p); func(arr); int **pp = (int**)malloc(sizeof(int*)*3); func2(pp); return 0; }一元数组和二元数组用做函数参数:#include #include using namespace std; void func0(int arr[10]){} void func2(int arr[]){} void func3(int *arr){} void func4(int *arr[20]){} void func5(int **arr){} void onedimension(){ int arr[10] = {0}; int *arr2[20] = {0}; func0(arr); func2(arr); func3(arr); func4(arr2); func5(arr2); } void test0(int arr[3][5]){} void test2(int arr[][5]){} void test3(int (*parr)[5]){} void twodimension(){ int arr[3][5]={0}; test0(arr); test2(arr); test3(arr); } int main() { onedimension(); twodimension(); return 0; } 7.2 函数指针用作函数参数#include bool comp(int a,int b){return false;} void sort(int(*p)[4],bool(*pf)(int,int)){} int main() { bool(*pf)(int,int) = comp; int arr[3][4] = {{1},{2},{3,10,11,12}}; int (*p)[4] = arr; sort(p,pf); sort(arr,pf); return 0; }使用总结:8 数组与函数指针变量其元素或返回值混合声明的理解直接看代码和备注:int(*fp)(int); // 函数指针 int * arr[5]; // 指针数组 int(*fa[5])(int); // 函数指针数组,(*fa[5])要用括号,返回类型int(int)要分裂 int(* fpp())(int); // 函数指针函数,(* fpp())要用括号,返回类型int(int)要分裂 int(* fpa())[5]; // 数组指针函数,(* fpa())要用括号,返回类型int[int]要分裂 int(* fppp(int(*fp2)(int a)))(int b); //函数fppp有一个函数指针做参数,返回一个函数指针 // 函数可以返回函数指针或数组指针,不能返回函数或数组 // 当先从fp2开始理解时,发现其包括在一个()中,所以是一个函数参数 // 当先从fppp开始时,发现其是一个指针,所以当一个声明中有多个标识符时,从最左边的一个开始 int(*parr[10])[5]; // 数组指针数组以上代码可以看到,写函数指针函数(一个函数返回一个函数指针)或数组指针函数(一个函数返回一个数组指针)时,需要用到括号来标明优先级,然后将函数的返回类型与参数(或数组的元素类型与数组的元素个数)分开写到括号的前后。9 二级指针应用的场合当需要一个主调函数调用一个被调函数来修改主调函数内或全局的指针变量时,被调函数需要使用一个二级指针或一个指针引用int n = 5; int m = 6; int *p = &n; int *p2 = &m; int **pp = &p; // p先右后左,右边没有,左边首先是*,表示p是一个指针,指向的类型是int* int **pp2 = &p2; int *&r = p; void func1(int *p){ p = &m; // 如果指向的不是动态内存,函数调用结束后p本身(局部变量)将不复存在 *p = 55; // 函数内解引用指针变量参数,产生副作用,更新p指向的变量的值为55 } void func2(int** pp){ pp = pp2; // 如果指向的不是动态内存,函数调用结束后p本身(局部变量)将不复存在 *pp = p2; // 函数内解引用指针变量参数,产生副作用,更新pp指向的变量的值为p2(一级指针) } void func3(int*&r){ r = p2; // 引用自动解引用 }另外,二级指针也用于动态内存分配场合:int **arr = (int**)malloc(sizeof(int*)*n); for(int i=0;i