上一次我们分享了指针的基本应用,这次我们扩展以下指针。
首先我们先来看看二维数组的调用方法。 我们分析一波,第一个num[i][j];这是我们最常见的访问方式,这就不讲了,然后第二种,我们知道数组名是数组的首地址,所以我们用地址偏移的方式来访问。对于这些转化我们只要记得这么一个,a[i]和*(a+i)(无条件等价)。 我们调用二维数组的方式有上面六种,想想数组和指针很相似,我们应该如何去定义一个指针来表示二维数组呢?答案很显然,需要一个行指针(也就是数组指针),这个行指针是指针,用来储存数组的。他的定义是:基类型 (*+变量名)[数组的列];例如:int (*p)[4]=num;//这里定义并初始化了。
这个指针数组本质是数组,然后这个数组里面储存的是指针(地址)。这个一般都是用在字符数组上面的。例如:
char *pstr[3]={“admin”, “lili”, “xiaoming”}; for(i=0; i<3; i++) { printf("%s\n", *(pstr+i)); //printf("%s\n", pstr[i]); }上面这两种方法都可以实现调用。
一般定义一个结构体是:结构体类型名+变量名+初始化,例如:STAFF_T staff={1001, “sxr”, “123456”, admin};那么我们定义一个结构体指针就是在变量名前加个*。例如:STAFF_T *psta=&staff;我们访问结构体成员的时候会有点不同的是之前是用点运算符,结构体指针是用箭头。例如:psta->ID; ④结构体指针数组
STAFF_T staff[3]={{1001, "sxr", "123456", 1},{1002, "wer", "123456", 2}{1003, "fgr", "123456", 3}}; STAFF_T *psta=staff; printf("%d\n", (psta+2)->ID);这里可以看出来,如果变成了数组的话,那么我们可以用指针偏移的方式来调用,还可以用之前的许多方式来调用。
显然指针是用来存放地址的,那么指针的指针就是用来放指针变量的地址的。这个一般都是在二维字符数组中使用的比较多,其他一般都不用。
char str[3][10]={"admin", "lili", "xiaoming"}; char *pstr[3]={str[0], str[1], str[2]}; char **pp=pstr; for(i=0; i<3; i++) { printf("%s\n", (*(pp+i))); }//
int i=5; int *pi=&i; int **ppi = π printf("%p\n", *ppi);⑥函数指针 函数指针是说定义一个指针来保存相同形式的函数的入口地址。那么我们就可以通过这个指针来访问这个函数了。
int max(int v1, int v2) { return (v1 > v2 ? v1 : v2); } int min(int v1, int v2) { return (v1 < v2 ? v1 : v2); } int sum(int v1, int v2) { return (v1 + v2); } int main() { int (*pfun)(int , int )=max; printf("%d\n", pfun(4,6)); }要注意这里的函数指针的形式要和要使用的函数的格式一样。定义的时候别忘了(),也就是(*pfun)。我们函数指针的用途是作为函数的形参,也就是回调函数。
我们可以把一系列相同的函数整合在一起,组成函数指针数组。例如:
int main() { int i=0; int (*pfun[3])(int , int )={max, min, sum}; for(i=0; i<3; i++) { printf("%d\n", pfun[i](4,6)); } }这里别忘了初始化,以及定义的格式。还有就是我们这里调用只能是用[ ]的方式,因为每一个函数的入口地址是不一样的,并不是连续的。
我们之前知道有一个数据类型是void类型的,这个类型我们不能直接定义变量,因为在系统开辟内存空间的时候,不知道应该给他开多大的内存空间。但是这个void的类型的可以定义一个指针变量,无类型的指针可以指向任何的数据类型。 它的定义是:
void *p;他的赋值要遵循这样的原则,可以将任意类型的指针赋给无类型指针,但不能将无类型指针赋给其它类型指针(如果要,那么需要进行强制转化)。
int a=10; int *q=&a,*m; p = q; /*允许赋值*/ m = (int *)p;//这里就进行了强制转化另外如果你直接将无类型指针进行算术运算的话,也是不行的,因为我们的算术运算是和指针的基类型的空间大小有关,而我们void类型空间大小不确定,所以无法进行运算,如果要进行那么需要强制转化一下。 我们一般会思考一个问题,那就是这个无类型指针有什么作用?我们在使用一些函数的时候,有时候会碰到不同类型的参数都要使用同一个函数,那么这时候的函数形参数据类型就可以定义为一个无类型指针,例如我们使用的memcpy函数(可以去C语言中看memcpy函数的原型,你就知道了)。除了这个时候,我们还会碰到返回值类型不确定的情况,这时候我们也可以用无类型的指针先代替,执行完成后在进行强制转化,例如我们后面会介绍的malloc函数(开辟空间的)。
可用于不允许被修改的变量和形式参数,数据保护。
char *strcpy(char *str1,const char *str2);例如这个strcpy这个函数,str2是源地址,因为我们在复制字符串的时候,不希望这个字符串被改变,不然我们复制的数据就改变了,所以我们加一个const进行修饰,表明这个内存对应的内容不能被修改。 这个const加在不同的地方是会有不同效果的。
const int *p; int const *p; //这上面的两个都表示p对应的内容不能被修改 int * const P; //这个就和之前的不一样,这个表示p储存的地址不能被修改 p=&a;//这个是错误的,在上面的情况下。我们总结一下,就是const右边是什么,那么这个什么就不能被改变。