我是在B站鹏哥C语言那自学的C,每篇博客相当于对自己上课内容的复习,希望自己在大学这段时间多写博客,做到至少一周两更,如果内容有勘误欢迎指正,大家一起加油。
字符指针除了指向字符还可以指向字符串
int main()
{
//一般使用
char a = 'a';
char * pa = &a;
//指向字符串
char * ch = "hello world";//把"hello world"的首字符地址存储在指针变量ch
char arr[] = "hello world";//将整个字符串放在arr数组中
printf("%c\n",*ch);//打印首元素
printf("%s\n",ch);//打印ch字符串
printf("%s\n",arr);//打印arr数组
return 0;
}
我们来看一下下面这个代码
int main()
{
char str1[] = "hello world";
char str2[] = "hello world";
const char* str3 = "hello world";
const char* str4 = "hello world";
if(str1==str2)
printf("str1 & str2 are same\n");
else
printf("str1 & str2 are not same\n");
if(str3==str4)
printf("str3 & str4 are same\n");
else
printf("str3 & str4 are not same\n");
return 0;
}
指针数组是一个存放指针的数组
int main()
{
int a[5] = {1,2,3,4,5};
int b[] = {6,7,8,9,10};
int c[] = {0,2,4,6,8};
int *arr[3] = {a,b,c};//定义一个指针数组{a,b,c}都是首元素地址
int i = 0;
for(i = 0;i<3;i++)
{
int j = 0;
for(j = 0;j<5;j++)
{
// printf("%d ",*(arr[i]+j));
printf("%d ",arr[i][j]);//这两种打印方法结果相同
}
printf("\n");
}
return 0;
}
数组指针是指向数组的指针,其本质为指针
int main()
{
int arr[10] = {1,2,3,4,5};
int (*parr)[10] = &arr;//取出的是数组的地址
//parr就是一个数组指针
//arr - 数组名是首元素地址 - arr[0]的地址
printf("%p\n", arr);//取出首元素地址
printf("%p\n", arr+1);//取出第二个元素的地址(跳过4个字节)
printf("%p\n", &arr);//取出数组的地址
printf("%p\n", &arr+1);//取出下一个数组的地址(跳过40个字节)
return 0;
}
结果如下:
内存地址用16位方式存储:(E18)H-(DF0)H= (40)D
void print1(int (*arr)[5],int row,int col)//定义一个函数(变量是二维数组第一行地址,行,列)
{
int i = 0;
for(i = 0;i<row;i++)
{
int j = 0;
for(j = 0;j<col;j++)
{
printf("%d ",arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][5] = {{1,2,3,4,5},{2,3,4,5,6},{4,5,6,7,8}};
print1(arr,3,5);
//数组名arr,表示首元素地址
//二维数组里的首元素是二维数组的第一行
//这里传递的arr,相当于第一行的地址,是一维数组的地址
//可以用数组指针来接收
getchar();
return 0;
}
int arr[5];//一个整型数组
int *parr[5];//一个整型指针parr的数组
int (*parr2)[5];//一个数组parr2的指针,数组有5个元素,整型类型
int (*parr3[10])[5];//parr3是一个存放数组指针的数组,有10个数组指针,每个数组指针指向一个数组,这个数组有5个元素,都为整型类型
数组名是首元素地址但有两个例外:
1.&数组名 - 数组名表示整个数组, 取出的是整个数组的地址
2.sizeof(数组名) - 数组名表示整个数组,计算的是整个数组的大小,单位是字节
//一维数组传参
void test(int arr[])
{}//1.传入一个数组
void test(int arr[10])
{}//2.传入一个数组,arr[10]里面的10没有意义。
void test(int *arr)
{}//3.传入一个整型指针
void test(int *arr[20])
{}//4.
void test(int **arr)
{}//5.二级指针,**arr==&arr[]
int main()
{
int arr[10] = {0};//数组
int *arr2[20] = {0};//整形指针数组,存放int*
test(arr);//1,2,3都可以
test2(arr2);//3,4可以
}
void test(int arr[3][5])
{}//1.可以
void test(int arr[][])
{}//2.不行,只能省略行
void test(int arr[][])
{}//3.可以
//总结:函数形参的设计只能省略第一个[]的数字
//因为对一个二维数组,可不知道有多少行,但是必须知道一行多少元素
void test(int *arr)
{}//3.不行,传的是第一行低的值,是一维数组的地址
void test(int *arr[5])
{}//4.不行,这是指针数组
void test(int (*arr)[5])
{}//5.可以,指向5个元素的数组
void test(int **arr)
{}//6.不行,传的第一行一维数组的地址
int main()
{
int arr[3][5] = {0};
test(arr);
}
void print(int*p, int size)//自定义打印函数
{
int i = 0;
for(i =0; i<size; i++)
{
printf("%d ",*(p+i));
}
}
int main()
{
int arr[10] = {1,2,3,4,5,6,7,8,9,10};
int* p = arr;//整型指针p是arr数组首元素地址
int size = sizeof(arr)/sizeof(arr[0]);//判断有多少个元素
//一级指针p传给函数
print(p,size);
return 0;
}
结果如下:
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;
}
函数指针是指向函数的指针,存放函数地址的指针
int add(int x,int y)//定义一个函数
{
return x+y;
}
int main()
{
int (*pf)(int, int) = &add;//定义一个函数指针*pf
int (*pf)(int, int) = add;//add ==pf
//&函数名-取到函数的地址
//函数名 == &函数名
//数组名 != &数组名
int ret = (*pf)(3,5);//(*pf)解引用调用add函数,(3,5)传参
int ret = pf(3,5);
//两者等价
printf("%d",ret);
return 0;
}
((void ()())0)();
调用0地址处的函数
该函数无参,返回类型是void
void (signal(int,void ()(int)))(int);
1.signal 和 ()相结合, 说明signal是函数名
2.signal函数的第一个参数类型是int,第二个参数类型是函数指针
该函数指针,指向一个参数为int,返回类型是void的函数
3.signal函数的返回类型也是一个函数指针
该函数指针,指向一个参数为int,返回类型是void的函数
signal是一个函数声明
函数指针数组 - 存放函数指针的数组
函数指针数组的简单应用:简易计算器
#include
#include
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 menu()//创建计算器界面
{
printf("***************************\n");
printf("*****1.Add*****2.Sub*******\n");
printf("*****3.Mul*****4.Div*******\n");
printf("*********0.Exit************\n");
}
int calc(int (*pf)(int, int))//(int (*pf)(int,int))是函数指针
{
int x,y;
printf("输入你想计算的两个数");
scanf("%d %d",&x, &y);
return pf(x, y);
}
int main()
{
int input = 0;
do
{
int (*pfArr[5])(int,int) = {NULL, Add, Sub, Mul, Div};//函数指针数组
int x, y, ret = 0;
menu();
printf("请选择:>");
scanf("%d" ,&input);
if(input >=1 && input <= 4)
{
printf("输入你想计算的两个数");
scanf("%d %d",&x, &y);
ret = (pfArr[input])(x, y);
//pfArr就是函数指针数组
//(pfArr[input])调用函数指针数组里的元素,(x,y)调用输入的两个参数进行计算
printf("ret = %d\n", ret);
}
else if(input == 0)
{
printf("退出程序");
break;
}
else
{
printf("程序错误");
}
} while (input);
return 0;
}
int (pf)(int, int); //函数指针
int ( pf1[3])(int, int); //函数指针数组
int (* (*pf2)[3])(int, int) = &pf1;//指向函数指针数组的指针
pf2就是一个指向函数指针数组的指针
了解一下就可以了
回调函数就是一个通过函数指针调用的函数。如果你把函数指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所定向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应.
我们先来看一个qsort函数:
#include
#include
void qsort (void* base,//base放的是待排序数据中第一个对象的地址
size_t num,//排序数据元素的个数
size_t size,//排序数据中一个元素的大小,单位是字节
int (*compar)(const void*,const void*)//用来比较待排序数据中的2个元素的函数(需要自己定义函数)
);
void print(int arr[], int size)
{
int i = 0;
for(i =0; i<size; i++)
{
printf("%d ",arr[i]);
}
}//打印数组
int cmp_int(const void*e1, const void*e2)
{
return *(int*)e1-*(int*)e2;//(void*)类型指针要强制转化为(int*)整型指针,如果e1指向的元素 > e2指向的元素返回大于0的数字,如果小于返回小于0的数字
}
int main()
{
int arr[] = {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;
}