深圳专业网站建设企业,wordpress加速 redis,厦门seo推广外包,化妆品网站html模板指针是C语言的精髓所以内容可能会比较多#xff0c;需要我们认真学习 目录
1、字符指针
2、指针数组
3、数组指针
3.1数组指针的定义
3.2数组名vs数组名
3.3数组指针的使用
4、数组传参和指针传参
4.1一维数组传参
4.2二维数组传参
4.3一级指针传参
4.4二级指…
指针是C语言的精髓所以内容可能会比较多需要我们认真学习 目录
1、字符指针
2、指针数组
3、数组指针
3.1数组指针的定义
3.2数组名vs数组名
3.3数组指针的使用
4、数组传参和指针传参
4.1一维数组传参
4.2二维数组传参
4.3一级指针传参
4.4二级指针传参
5、函数指针
6、函数指针数组
7、指向函数指针数组的指针
8、回调函数
8.1回调函数定义
8.2qsort库函数的用法
1、字符指针
在指针的类型中我们知道一种指针类型为字符指针char*
一般使用 int main() { char chw; char* pcch;//*表示pc是指针char表示pc指向的对象ch他的类型是char return 0 } #includestdio.h
int main()
{char* pcabcdef;printf(%s\n,pc);return 0;
}
看这样一个代码的运行结果 这里需要注意pc里面放的并不是整个字符串而是a的地址非要放在pc4字节中是放不下的而打印的是字符串%s我们只要知道首字符a的地址就能向后访问得到整个字符串的地址在char* 前面加上const会更加好一些。看这样一道题
#includestdio.h
int main()
{const char* p1 abcdef;const char* p2 abcdef;char arr1[] abcdef;char arr2[] abcdef;if (p1 p2)printf(p1p2\n);elseprintf(p1!p2);if (arr1 arr2)printf(arr1arr2);elseprintf(arr1!arr2);return 0;
} 这说明p1和p2指向同一个字符a字符串abcdef是常量字符串放在内存中的只读内存区不能改变它的值没有必要存在多份只在内存中存一份所以p1和p2都指向a的地址p1就等于p2.而arr1和arr2不相等是因为arr1[ ]和arr2[ ]是两个独立的数组每一个都在内存中开辟了一份独立的空间所以地址肯定是不相同的。
2、指针数组
顾名思义指针数组就是存放指针的数组本质是一个数组 int* arr1[10];//整型指针的数组 char* arr2[10];//一级字符指针的数组 char **arr[5];//二级字符指针的数组 #includestdio.h
int main()
{int arr1[] { 1,2,3,4 };int arr2[] { 2,3,4,5 };int arr3[] { 3,4,5,6 };int* arr[3] { arr1,arr2,arr3 };//每个数组的首元素地址,每个元素的类型是int*int i 0;//代表arr的每个元素的下标for (i 0; i 3; i){int j 0;//j代表的是arr1、arr2、arr3中每个元素的下标for (j 0; j 4; j){printf(%d , *(arr[i] j));//arr[i]代表各个数组的数组名即每个数组的首元素地址加上j并解引用就拿到了所有元素//因为*(pi)等价于arr[i],所以有可以写成这样的形式//printf(%d ,*(*(arri)j));或者//printf(%d ,arr[i][j]);}printf(\n);}return 0;
} 用了一个指针数组把三个一维数组关联起来了模拟实现二维数组,但本质上并不是二维数组因为这三个数组在内存中并不是连续存放的但二维数组在内存中是连续存放的。
3、数组指针
3.1数组指针的定义
数组指针的本质是指针。
整型指针int* p能够指向整形数据的指针
浮点型指针float* p能够指向浮点型数据的指针
所以说数组指针就是能够指向数组的指针。比如
int (*p)[10];*p代表p是指针指向的是整型数组中的10个元素每个元素是int类型。
注意[ ]的优先级要高于*号因此必须加上来保证p先和*相结合。
3.2数组名vs数组名
对于下面的数组 int arr[10]; arr和arr的区别
我们知道arr是数组名数组名表示数组首元素的地址。那么arr表示的是什么
#includestdio.h
int main()
{int arr[10] { 0 };printf(%p\n, arr);printf(%p\n, arr[0]);printf(%p\n, arr);return 0;
}
//数组名通常表示的是数组首元素的地址但有两个例外
//1.sizeof(数组名),这里的数组名表示的是整个数组
//2.数组名这里的数组名依然表示的是整个数组所以arr取出的是整个数组的地址
这里我们会发现运行结果显示三个地址是完全相同的但是arr的步长和arr的步长是不同的arr1类型是int*跳过4个字节但arr1跳过2816进制字节即40字节。那么应该怎么存放整个数组的地址呢
存放数组首元素的地址int *parr
存放整个数组的地址int (*p)[10]数组指针用来存放整个数组的地址。它的类型为int (*)[10]。
3.3数组指针的使用
#includestdio.h
int main()
{int arr[] { 1,2,3,4,5,6,7,8,9,10 };int(*p)[10] arr;int sz sizeof(arr) / sizeof(arr[0]);int i 0;for (i 0; i sz; i){printf(%d , *(*p i));//因为p指向的是整个数组,(不包含数组大小*p表示的是整个数组的内容数组名即首元素地址//也可以这样说*p表示从p所指向的内存地址开始的数组内存的布局。这通常被理解为该数组本身实际上它只是一个别名或引用指向已经存在的数组//简单理解就是如果int* parr那*p就是找到arr的内容如果int (*p)arr[10]arr,则*p就是找到*arr即arr数组名}return 0;
}
这样用我们会感到相当别扭很不舒服所以很不建议这样用 建议大家用在二维数组上。
#includestdio.h
void print1(int arr[3][5], int r, int c)
{int i 0;for (i 0; i r; i){int j 0;for (j 0; j c; j){printf(%d ,arr[i][j]);}printf(\n);}
}
void print2(int (*p)[5], int r, int c)
{int i 0;for (i 0; i r; i){int j 0;for (j 0; j c; j){printf(%d ,*(*(pi)j));//p相当于是第一行的地址解引用相当于首元素地址}printf(\n);}
}
int main()
{int arr[3][5] { 1,2,3,4,5,2,3,4,5,6,3,4,5,6,7 };print1(arr, 3, 5);print2(arr, 3, 5);//传过去的是二维数组的首元素地址那二维数组的首元素地址是什么呢//其实是二维数组的第一行所以传过去的其实是数组内容为5个整型的一维数组的地址return 0;
}
int (*parr[10])[5];parr先和[5]配对说明是个数组数组的类型是int (*)[5]是个数组指针。所以parr就是存放数组指针的数组 。
4、数组传参和指针传参
写代码时难免会把数组或指针传给函数那么函数的参数该如何设计呢
4.1一维数组传参 #includestdio.h void test1(int arr1[]) { } void test1(int arr1[10]) { } void test1(int* arr1) { } void test2(int *arr2[10]) { } //10可以省略 void test2(int** arr2) { } int main() { int arr1[10] { 0 }; int* arr2[10] { 0 }; test1(arr1); test2(arr2); return 0; } 4.2二维数组传参 #includestdio.h void test(int arr[3][5]) { } void test(int arr[][5]) { }//二维数组传参函数的形参设计只能省略行因为对于一个二维数组可以不知道有多少行但是必须知道一行有多少个元素 void test(int (*p)[5]) { }//二维数组传参传的是第一行元素的地址需要拿一个数组指针才能接收 int main() { int arr[3][5] {0}; test(arr); return 0; } 4.3一级指针传参 #includestdio.h void print(int* p, int sz) { int i 0; for (i 0; i sz; i) { printf(%d , *(p i)); } } int main() { int arr[10] { 1,2,3,4,5,6,7,8,9,10 }; int* p arr; int sz sizeof(arr) / sizeof(arr[0]); print(p, sz);//p是一级指针变量 return 0; } 4.4二级指针传参 #includestdio.h void print(int** ptr) { printf(%d\n, **ptr); } int main() { int n 10; int* p n; int** pp p; print(p); return 0; } 当函数的参数为二级指针的时候可以接收什么参数
可以说指向一级指针的变量也可以是指向一级指针的数组二级指针变量本身
5、函数指针
指向函数的指针就叫做函数指针用来存放函数的地址
#includestdio.h
int Add(int x, int y)
{return x y;
}
int main()
{printf(%p\n, Add);return 0;
} 由此可见打印出来的值就是Add函数的地址。对于函数来说函数名和函数名都是函数的地址
那怎么用指针把函数地址存起来呢
拿此例子来说int (*pf)(int,int)Add;
#includestdio.h
int Add(int x, int y)
{return x y;
}
int main()
{int (*pf)(int, int) Add;int ret (*pf)(2, 3);//解引用就相当于找到了这个函数*是可以省略的printf(%d\n, ret);return 0;
} int main() { (*(void(*) () )0)();//把0强制转换成函数指针类型我们可以认为0就是一个地址解引用调用这个函数但什么参数都没有传所以本质上是一次函数调用调用的是0作为地址处的函数 void(* signal(int,void(*)(int) ) )(int); //signal与括号先结合是函数名其中void(*)(int)是函数指针类型去掉 signal(int,void(*)(int) )我们会发现剩下部分也是返回也是一个函数返回类型。也就是说这个代码是一次函数声明。声明的signal函数第一个参数的类型是int第二个参数的类型是函数指针。该函数指针指向的函数参数是int返回类型是void而signal函数的返回类型也是一个函数指针该函数指针指向的参数是int返回类型也是void。 return 0; } 函数指针的用途(写一个计算器能够实现简单的加法、减法、除法、乘法
#includestdio.h
void menu()
{printf(*******************\n);printf(****1.add 2.sub****\n);printf(****3.mul 4.div****\n);printf(**** 0.exit ****\n);printf(*******************\n);
}
int add(int x, int y)
{return x y;
}
int sub(int x, int y)
{return x - y;
}
int mul(int x, int y)
{return x * y;
}
int div(int x, int y)
{return x / y;
}
//回调函数
void calc(int (*p)(int, int))
{int x 0;int y 0;int ret 0;printf(请输入两个操作数:\n);scanf(%d%d, x, y);ret (*p)(x, y);printf(%d\n, ret);
}
int main()
{int input 0;do{menu();printf(请选择);scanf(%d, input);switch (input){case 1:calc(add);break;case 2:calc(sub);break;case 3:calc(mul);break;case 4:calc(div);break;case 0:printf(退出程序\n);break;default:printf(选择错误请重新选择\n);break;}} while (input);return 0;
}
6、函数指针数组
把存放函数的地址存到一个数组中那么这个数组就叫做函数指针数组如何定义函数指针数组 int add(int x, int y) { return x y; } int sub(int x, int y) { return x - y; } int mul(int x, int y) { return x * y; } int div(int x, int y) { return x / y; } int main( ) { int (*arr[4])(int,int){add,sub,mul,div};//arr先于[4]结合表明本质是个数组,数组每个元素的类型为int (*)(int,int)即函数指针类型 int i 0; for (i 0; i 4; i) { int ret arr[i](8, 4); printf(%d , ret); } return 0; } 有什么用途呢以上面模拟计算机为例我们还可以对代码进行简化并且增加计算机功能时也相对比较容易极大简化了修改功能时的工作量。
#includestdio.h
void menu()
{printf(*******************\n);printf(****1.add 2.sub****\n);printf(****3.mul 4.div****\n);printf(**** 0.exit ****\n);printf(*******************\n);
}
int add(int x, int y)
{return x y;
}
int sub(int x, int y)
{return x - y;
}
int mul(int x, int y)
{return x * y;
}
int div(int x, int y)
{return x / y;
}
int main()
{int x 0;int y 0;int input 0;int ret 0;//转移表int (*pfarr[5])(int, int) { 0,add,sub,mul,div };//之所以第一个元素放0是为了与为了调用时和菜单选项对应起来比如输入下标为1是调用的是add函数do{menu();printf(请选择);scanf(%d, input);if (input 0){printf(退出程序\n);}else if (input 1 input 4){printf(请输入两个操作数\n);scanf(%d%d, x, y);ret pfarr[input](x, y);printf(%d\n, ret);}else{printf(选择错误请重新选择\n);}} while (input);return 0;
}
这个代码现在就变得清爽了许多非常的简洁。
7、指向函数指针数组的指针
指向函数指针数组的指针本质上就是一个指针指针指向一个数组数组里面的元素都是函数指针 int main() { int (*pfarr[5])(int, int) { 0,add,sub,mul,div }; int (*(*ppfarr)[5])(int,int) pfarr; return 0; } 但用的相对并不多以后会详细介绍
8、回调函数
8.1回调函数定义
回调函数就是通过函数指针调用的函数。如果把函数的指针作为参数传递给另一个函数当这个指针被用来调用其所指向的函数时我们就说这是回调函数。回调函数不是由该函数的实现方式直接调用而是在特定的事件或条件发生时有另外的一方的调用的用于对该事件或条件进行响应。
8.2qsort库函数的用法
qsort是C标准库stdlib.h中的一个函数用于对数组进行快速排序。它接受一个指向要排序的数组的指针、数组中元素的数量、每个元素的大小字节为单位以及一个比较函数作为参数。 //qsort的声明 void qsort(void *base, size_t nmemb, size_t size, int (*compar)(const void *, const void *)); base指向要排序的数组的第一个元素的指针数据的起始位置 nmemb数组中元素的数量 size数组中每个元素的大小 compar一个指向比较函数的指针该函数用于确定元素的排序顺序 比较函数应该接受两个只想要比较的元素的指针并返回一个整型表示他们的相对顺序。如果第一个元素排在第二个元素之前返回负数第一个数小于第二个数反之返回整数相等则返回0.
qsort具体要怎么用呢现在我们对整型数组arr[10]{10,9,8,7,6,5,4,3,2,1}排成升序之前我们学过冒泡排序的思想现在我们换种方法来实现
void print(int arr[],int sz)//打印数组内容
{int i 0;for (i 0;i sz;i){printf(%d , arr[i]);}printf(\n);
}
//比较两个整型元素e1指向一个整数e2也指向一个整数
int cmp_int(const void* e1, const void* e2)
{return (*(int*)e1 - *(int*)e2);//强制类型转换成int*类型
}//如果我们像排成降序只需要把e1改成e2把e2改成e1逻辑相反
int main()
{int arr[10] { 10,9,8,7,6,5,4,3,2,1 };int sz sizeof(arr) / sizeof(arr[0]);qsort(arr, sz, sizeof(arr[0]), cmp_int);print(arr, sz);return 0;
}
//void* 是无具体类型的指针不能解引用操作也不能加减整数
qsort不仅仅可以排序整型数据也可以排结构体数据也可以排字符数据
我们来看对结构体数据排序的实例
#includestdio.h
#includestring.h
#includestdlib.h
struct Stu
{char name[20];int age;};
int cmp_stu_by_name(const void* e1, const void* e2)
{return strcmp(((struct Stu*)e1)-name, ((struct Stu*)e2)-name);//e1是指针变量//strcmp函数如果第一个字符串比第二个大返回正数小于返回负数否则返回0
}
int main()
{struct Stu s[] { {张三,15},{李四,16},{王五,18}};int sz sizeof(s) / sizeof(s[0]);qsort(s, sz, sizeof(s[0]), cmp_stu_by_name);int i 0;for (i 0; i sz; i){printf(name%s age%d\n,(si)-name,(si)-age);//s虽然本身不是指针是数组名但会退化为指向第一个元素的指针。这是为了与期望接受指针的函数qsort兼容//也可以用s[i].name,s[i].age的形式来访问结构体成员}return 0;
}
}