目录
一.字符指针
何为字符指针?
让我们来看个例子
int main(){
char ch = 'w';
char *pc = &ch;
*pc = 'b';
printf("%c\n",ch);
return 0;
}
其中pc就是字符指针
其中要注意的是
char* p = "abcdef";
printf("%s\n",p);
“abcdef”是常量字符串,存储在常量存储区:这是一块比较特殊的存储区,他们里面存放的是常量,不允许修改。防止被修改,可以在char前加入const!
例题一
int main() {
const char* p1 = "abcdef";
const char* p2 = "abcdef";
char arr1[] = "abcdef";
char arr2[] = "abcdef";
if (p1 == p2) {
printf("p1==p2\n");
}
else {
printf("p1!=p2\n");
}
if (arr1 == arr2) {
printf("arr1==arr2\n");
}
else {
printf("arr1!=arr2\n");
}
return 0;
}
打印结果为
为什么呢?
p1 和p2指向的是同一个常量字符串,C/C++会把常量字符串放在单独的内存区域,当几个指针指向同一字符串,实际上会指向同一内存!因此p1==p2!
但在arr数组中,他们会开辟不同的空间 ,所以arr1!=arr2!
二.指针数组
指针数组是数组,用来存放指针的数组!
注意与后面要学习的数组指针要区分!!!
int arr[10];//整型数组
char ch[5];//字符数组
//指针数组
int* arr2[6];//存放整型指针的数组
char* arr3[5];//存放字符指针
注意 arr2的数组元素的类型为 int*
同样地,arr3 即为char*
如果我要将上面的代码打印出来,可加入————
for (i = 0; i < 3; i++) {
int j = 0;
for (j = 0; j < 5; j++) {
printf("%d ", *(parr[i] + j));
}
printf("\n");
}
这里的 *(parr[i]+j) +j代表arr中的元素展开
同样可写成 parr[i][j]
虽是二维数组的形式,arr1,arr2,arr3并不是连续存在的,所以本质上不是二维数组!!
三.数组指针
注意 这里是指针
定义:指向数组的指针
注意区分
int *p1[10];
int (*p2)[10];
第一个是指针数组,本质是数组,存放的是指针
第二个是数组指针,本质是指针,指针指向的是数组
该数组有十个元素,每个元素都是int 类型
三.二 数组名
1.数组名通常指的是数组首元素地址,但有俩个例外
1.sizeof(数组名)——> 计算的是整个数组的大小,单位是字节
2.&数组名——> 表示的依然是整个数组的地址!!!!
注意区分解引用
char *arr[5]={0};
char *(*pc)[5]=&arr;
(*pc)才是解引用,char *是类型!!!
并且 数组指针与二级指针有着本质上的区别
又比如说
p是指向数组的,*p其实就相当于数组名,数组名又是数组首元素的地址
所以 *p本质上是数组首元素的地址
要记住
优先级顺序 () > []> *
(*p)[n]:根据优先级,先看括号内,则p是一个指针,这个指针指向一个一维数组,数组长度为n,这是“数组的指针”,即数组指针;
*p[n]:根据优先级,先看[],则p是一个数组,再结合*,这个数组的元素是指针类型,共n个元素,这是“指针的数组”,即指针数组。
那么如何使用数组指针呢?
在一个二维数组中
例如 int arr[3][5];
arr是数组名,表示的是第一行也就是序列为0 的一行的地址(将一行当成一个元素)
看上述代码 其中的print2(arr,3,5); arr代表的是第一行的地址,第一行的地址是一维数组的地址,是一个指向一位数组的指针
int(*p)[5]:
p的类型是: int(*)[5];
p指向的是一个有五个元素的整型数组
p+1——>跳过一个有五个int元素的数组!!
我们已经学习很多数组和指针,让我们来回顾一下
int arr[5]; arr是整型数组
int *parr1[10]; parr1是整型指针数组
int (*parr2)[10]; parr2是数组指针
int (*parr3[10])[5]; parr3是存放数组指针的数组
考虑优先级
再比如,
int (*parr3[10])[5]={&arr1,&arr2,&arr3};
这是存放数组指针的数组
存放的是arr的地址;该数组类型是 int(*)[5];
四.数组参数、指针参数
先来个小小的题目
1.一维数组传参
以上那组传参是对的?
答案是:以上都可以
其中要注意的是arr2是首元素地址,类型是int*
指针对指针可以传,数组对数组,数组对指针;
2.二维数组传参
#include<stdio.h>
void test(int **ptr)
{
printf("num=%d\n",**ptr);
}
int main(){
int n=10;
int*p=&n;
int **pp=&p;
test(pp);
test(&p);
return 0;
}
总结:实参形参二者类型要一致
五.函数指针
对于函数来说,&函数名==函数名,都是其地址形式!!!
那么函数指针是什么形式呢?
以上述代码为例,可将add函数写作
int (*pf)(int,int)=&add
这里的*(解引用)可以不加,但如果加必须带有括号!!(优先级)
下面来举几个例子
四.2
计算器
五.函数指针数组
我们通过编写一段还原计算器小部分内容的代码来学习这节内容!
void menu() {
printf("*******************\n");
printf("***1.add 2.sum ***\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 input = 0;
int x = 0;
int y = 0;
int ret = 0;
int (*arr[5])(int, int) = { 0,add,sub,mul,Div}; //这里的将几个函数地址作为数组
do {
menu();
scanf_s("%d", &input);
printf("请输入俩个操作数 > \n");
scanf_s("%d %d", &x, &y);
ret = arr[input](x, y);
printf("%d\n", ret);
}while(input);
return 0;
}
六.指向函数指针数组的指针
七.回调函数
1.回调函数的定义:
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
我们再来学习一下新的函数
qsort
我们可知qsort是一种快速排序思想实现的一个函数!
它可以排任何类型!
void qsort
( void *base, //你要排序的数据的初始地址
size_t num, //待排序的数据元素个数
size_t width, //待排序的数据元素大小
int ( *cmp )(const void *e1, const void *e2 ) ); //函数指针————比较函数
举一个很简单代码
#include<stdio.h>
#include<stdlib.h>
int cmp_int(const void* e1, const void* e2) {
return (*(int*)e1 - *(int*)e2);
}
int main() {
int arr[] = { 2,3,4,6,1 };
int sz = sizeof(arr) / sizeof(arr[0]);
qsort(arr, sz, sizeof(arr[0]), cmp_int);
for (int i = 0; i < sz; i++) {
printf("%d ", arr[i]);
}
return 0;
}
其中 cmp_int 可以理解为回调函数!
还有,e1-e2 代表的是 升序排列,如果想降序 可以将其交换位置!
qsort除了可以排整形数据,还可以排列结构体!