二级指针也是一个普通的指针变量,只是它里面保存的值是另外一个一级指针的地址定义:
int guizi1 = 888;
int *guizi2 = &guizi1; //1 级指针,保存 guizi1 的地址 int **liujian = &guizi2; //2 级指针,保存 guizi2 的地址,guizi2 本身是一个一级指针变量
// demo 8-12.c #include <stdio.h> #include <stdlib.h> int main(void){ int guizi2 = 888; //存枪的第 2 个柜子 int *guizi1 = &guizi2; //存第 2 个柜子地址的第一个柜子 int **liujian = &guizi1; //手握第一个柜子地址的刘建 printf("刘建打开第一个柜子,获得第二个柜子的地址:0x%p\n", *liujian); printf("guizi2 的地址:0x%p\n", &guizi2); int *tmp; tmp = *liujian; printf("访问第二个柜子的地址,拿到枪:%d\n", *tmp); printf("刘建一步到位拿到枪:%d\n", **liujian); //缩写成 **liujian system("pause"); return 0;
二级指针的用途:
1.普通指针可以将变量通过参数“带入”函数内部,但没办法将内部变量“带出”函数
2.二级指针可以不但可以将变量通过参数函数内部,也可以将函数内部变量“带出”到函数外部。
#include <stdio.h> #include <stdlib.h> void swap(int *a, int *b){ int tmp =*a; *a= *b; *b= tmp; } void boy_home(int **meipo){ static int boy = 23; *meipo = &boy; } int main(void){ //int x=10, y=100; //swap(&x, &y); //printf("x=%d, y=%d\n", x, y); int *meipo = NULL; boy_home(&meipo); printf("boy: %d\n", *meipo); system("pause"); return 0; }
项目精讲-多级指针的定义、使用
1. 可以定义多级指针指向次一级指针比如:
int guizi1 = 888;
int *guizi2 = &guizi1; //普通指针
int **guizi3 = &guizi2; //二级指向一级
int ***guizi4 = &guizi3; //三级指向二级
int ****guizi5 = &guizi4; //四级指向三级
// demo 8-14.c #include <stdio.h> #include <stdlib.h> int main(void){ int guizi1 = 888; int *guizi2 = &guizi1; //普通指针 int **guizi3 = &guizi2; //二级指向一级 int ***guizi4 = &guizi3; //三级指向二级 int ****guizi5 = &guizi4; //四级指向三级 printf("柜子 2 拿枪: %d\n", *guizi2); printf("柜子 3 拿枪: %d\n", **guizi3); printf("柜子 4 拿枪: %d\n", ***guizi4); printf("柜子 5 拿枪: %d\n", ****guizi5); system("pause"); return 0; }
指针和数组的纠缠
1. 指针表示法和数组表示法
数组完全可以使用指针来访问, days[3] 和 *(days+3) 等同
#include <stdio.h> #include <stdlib.h> void print_months1(int days[], int months){ int index = 0; for (index = 0; index < months; index++){ //数组表示法 printf("Month %2d has %d days.\n", index+1, days[index]); //指针表示法 //printf("Month %2d has %d days.\n", index+1, *(days+index)); } } void print_months2(int *days, int months){ int index = 0; for (index = 0; index < months; index++){ //指针表示表示法 printf("Month %2d has %d days.\n", index+1, *(days+index)); //数组表示法 printf("Month %2d has %d days.\n", index+1, days[index]); } } int main(void) { int days[12]={31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; /*int index = 0; for (index = 0; index < 12; index++){ //数组表示法 //printf("Month %2d has %d days.\n", index+1, days[index]); //指针表示法 printf("Month %2d has %d days.\n", index+1, *days+index); }*/ print_months1(days, 6); //print_months2(days, 12); system("pause"); return 0; }
2. 存储指针的数组
定义:类型 *指针数组名[元素个数] ;
// demo 8-16.c #include <stdio.h> #include <stdlib.h> int main(void) { int girls[4][3]={{173, 158, 166}, {168, 155, 171}, {163, 164, 165}, {163, 164, 172}}; //int x1,y1, x2,y2; int *qishou[2];//定义一个有两个元素的指针数组,每个元素都是一个指针变量 if(girls[0][0] > girls[0][1]){ qishou[0] = &girls[0][0]; qishou[1] = &girls[0][1]; }else { qishou[0] = &girls[0][1]; qishou[1] = &girls[0][0]; } for(int i=2; i<12; i++){ //girls[i/3][i%3] if(*qishou[1] >= girls[i/3][i%3]){ continue; } //候选者高于第二位棋手候选女兵 //1.候选者比"冠军"矮 if(girls[i/3][i%3] <= *qishou[0]){ qishou[1] = &girls[i/3][i%3]; }else { //2.候选者比"冠军"高 qishou[1] = qishou[0]; qishou[0] = &girls[i/3][i%3]; } } printf("最高女兵的身高: %d , 次高女兵的身高: %d\n", *qishou[0], *qishou[1]); system("pause"); return 0; }
指针和二维数组
1. 指向数组的指针 int (*p)[3]; //定义一个指向三个成员的数组的指针
访问元素的两种方式: 数组法: (*p)[j] 指针法: *((*p)+j
#include <stdio.h> #include <stdlib.h> int main() { /*据同学们报告,A 栋学生楼有学生用高倍望眼镜偷看别人洗澡, 宿管办领导决定逐个宿舍排查,得到的线报是 A0 到 A3 宿舍的 某个子最矮的男生。 */ int A[4][3]={{173, 158, 166}, {168, 155, 171}, {163, 164, 165}, {163, 164, 172}}; int (*p)[3]; //定义一个指向三个成员的数组的指针 int * boy = NULL; p = &A[0]; //第一种 数组下标法 /*for(int i=0; i<4; i++){ for(int j=0; j<3; j++){ printf(" %d", (*p)[j]); //(*p) 等同于 a[0] ,a[0][0]等同于 (*p)[0] } printf("\n"); p++; }*/ boy = &(*p)[0]; //boy = (*p); //第二种 指针访问法 //int a[3]; int * p ; p = a; 数组成员: *p *(p+1) *(p+2) for(int i=0; i<4; i++){ for(int j=0; j<3; j++){ printf(" %d", *((*p)+j)); if( *boy > *((*p)+j)){ boy = (*p)+j; } } printf("\n"); p++; } printf("偷窥的学生是: %d\n", *boy); system("pause"); return 0; }
使用普通指针访问二维数组
int A[4][3];
int *p;
//定义一个指针 p = A[0]; 或者 p=&A[0][0];
访问元素的两种方式
#include <stdio.h> #include <stdlib.h> int main() { /*据同学们报告,A 栋学生楼有学生用高倍望眼镜偷看别人洗澡, 宿管办领导决定逐个宿舍排查,得到的线报是 A0 到 A3 宿舍的 某个子最矮的男生。 */ int A[4][3]={{173, 158, 166}, {168, 155, 171}, {163, 164, 165}, {163, 164, 172}}; int *boy = NULL;//坏男孩 int *p =NULL; //定义指针,用以遍历二维数组 //p = A[0]; p = &A[0][0]; boy = p; for(int i=1; i<4*3; i++,p++){ if(*boy > *p){ //*p 可以替换成 A[i/3][i%3] boy = p; } } printf("偷窥的学生是: %d\n", *boy); //2.根据指针计算下标 int pos = boy - A[0]; printf("index: %d\n", pos); printf("位于 A[%d] 宿舍\n", pos/3); printf("是第 %d 个成员\n", pos%3); system("pause"); return 0; }
项目精讲-“我们不一样“之数组与指针的区别
数组:数组是用于储存多个相同类型数据的集合。
指针:指针是一个变量,但是它和普通变量不一样,它存放的是其它变量在内存中的地址。
1. 赋值
数组:只能一个一个元素的赋值或拷贝
指针:指针变量可以相互赋值
2. 表示范围
数组有效范围就是其空间的范围,数组名使用下表引用元素,不能指向别的数组
指针可以指向任何地址,但是不能随意访问,必须依附在变量有效范围之内
3. sizeof 数组:
数组所占存储空间的内存:sizeof(数组名) 数组的大小:sizeof(数组名)/sizeof(数据类型) 指针: 在 32 位平台下,无论指针的类型是什么,sizeof(指针名)都是
4. 在 64 位平台下,无论指针的类型是什么,sizeof(指针名)都是 8.
指针数组和数组指针
针指数组:
int *qishou[2];//定义一个有两个元素的指针数组,每个元素都是一个指针变量
int girl1= 167;
int girl2 = 171;
qishou[0] = &girl1;
qishou[1] = &girl2;
数组指针:
int (*p)[3]; //定义一个指向三个成员的数组的指针
访问元素的两种方式:
int A[4][3]={{173, 158, 166},
{168, 155, 171},
{163, 164, 165},
{163, 164, 172}};
p = &A[0];
数组法: (*p)[j]
指针法: *((*p)+j)
传参
数组传参时,会退化为指针!
(1)退化的意义:C 语言只会以值拷贝的方式传递参数,参数传递时,如果只拷贝整个数 组,效率会大大降低,并且在参数位于栈上,太大的数组拷贝将会导致栈溢出。
(2)因此,C 语言将数组的传参进行了退化。将整个数组拷贝一份传入函数时,将数组名 看做常量指针,传数组首元素的地址。
#include <stdio.h> #include <stdlib.h> /*------------------ <一维数组传参> -----------------------*/ /*方式一: 形参不指定数组大小 用数组的形式传递参数,不需要指定参数的大小, 因为在一维数组传参时,形参不会真实的创建数组, 传的只是数组首元素的地址。 */ void method_1(int arr[], int len) { for(int i=0; i<len; i++){ printf(" arr[%d] = %d\n", i, arr[i]); } } //方式二:指定数组大小 void method_2(int arr[10]) { for(int i=0; i<10; i++){ printf(" arr[%d] = %d\n", i, arr[i]); } } //方式三: 一维数组传参退化,用指针进行接收,传的是数组首元素的地址 void method_3(int *arr, int len) { for(int i=0; i<len; i++){ printf(" arr[%d] = %d\n", i, arr[i]); } } int main102() { int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; method_1(arr, 10); printf("------华丽的分隔线------\n"); method_2(arr); printf("------华丽的分隔线------\n"); method_3(arr, 10); system("pause"); return 0; } /*-------------------- <指针数组传参> -----------------------*/ //方式一: 指针数组传参,声明成指针数组,不指定数组大小 void method_4(int *arr[], int len) { for(int i=0; i<len; i++){ printf(" arr[%d] = %d\n", i, *arr[i]); } } //方式二: 指针数组传参,声明成指针数组,指定数组大小 void method_5(int *arr[10]) { for(int i=0; i<10; i++){ printf(" arr[%d] = %d\n", i, *arr[i]); } } //方式三: 二维指针传参 //传过去是指针数组的数组名,代表首元素地址,而数组的首元素又是一个指针, //就表示二级指针,用二级指针接收 void method_6(int **arr, int len) { for(int i=0; i<len; i++){ printf(" arr[%d] = %d\n", i, *(*(arr+i))); } } int main() { int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; int *arr_p[10] = {0}; for(int i=0; i<10; i++){ arr_p[i] = &arr[i]; } method_4(arr_p, 10); printf("------华丽的分隔线------\n"); method_5(arr_p); printf("------华丽的分隔线------\n"); method_6(arr_p, 10); system("pause"); return 0; }
void 类型指针
void => 空类型 void* => 空类型指针,只存储地址的值,丢失类型,无法访问,要访问其值,我们必须对这个指 针做出正确的类型转换,然后再间接引用指针。
所有其它类型的指针都可以隐式自动转换成 void 类型指针,反之需要强制转换
#include <stdio.h> #include <stdlib.h> int main(void){ int arr[]={1, 2, 3, 4, 5}; char ch = 'a'; void *p = arr;//定义了一个void 类型的指针 //p++; //不可以, void * 指针不允许进行算术运算 p = &ch; //其它类型可以自动转换成void * 指针 //printf("数组第一个元素: %d\n", *p); //不可以进行访问 printf("p: 0x%p ch: 0x%p\n", p, &ch); //强制类型转化 char * p1 = (char *)p; printf("p1 指向的字符是: %c\n", *p1); system("pause"); return 0; }
函数指针
#include <stdio.h> #include <stdlib.h> #include <string.h> int compare_int(const void *a, const void *b){ //printf("调用compare_int 啦,你好骚气哦! \n"); int *a1 = (int *) a; int *b1 = (int *) b; //printf("a 的地址: 0x%p b的地址: 0x%p\n", &a, &b); return *b1 - *a1; } int compare_char(const void *a, const void *b){ //printf("调用compare_char 啦,你好骚气哦! \n"); char c1 = *((char *) a); char c2 = *((char *) b); if(c1>='A' && c1<='Z') c1+=32; if(c2>='A' && c2<='Z') c1+=32; return c1 - c2; } int main(void){ int x = 10; int y = 20; //函数有没有地址? //printf("compare_int 的地址: 0x%p \n", &compare_int); //compare_int(&x, &y); //函数指针的定义 把函数声明移过来,把函数名改成 (* 函数指针名) int (*fp)(const void *, const void *); /*贝尔实验室的C和UNIX的开发者采用第1种形式,而伯克利的UNIX推广者却采用第2 种形式ANSI C 兼容了两种方式*/ fp = &compare_int; // (*fp)(&x, &y); //第1种,按普通指针解引的放式进行调用,(*fp) 等同于compare_int fp(&x, &y); //第2种 直接调用 //qsort 对整形数组排序 int arr[]={2, 10, 30, 1, 11, 8, 7, 111, 520}; qsort(arr, sizeof(arr)/sizeof(int), sizeof(int), &compare_int); for(int i=0; i<sizeof(arr)/sizeof(int); i++){ printf(" %d", arr[i]); } //qsort 可以对任何类型的数组进行排序 char arr1[]={"abcdefghiABCDEFGHI"}; qsort(arr1, sizeof(arr1)/sizeof(char)-1, sizeof(char), &compare_char); for(int i=0; i<sizeof(arr1)/sizeof(char)-1; i++){ printf(" %c", arr1[i]); } system("pause"); return 0; }