目录
1.字符指针(char*)
int main()
{
char str1[] = "hello CSDN.";
char str2[] = "hello CSDN.";
const char* str3 = "hello CSDN.";
const char* str4 = "hello CSDN.";
if (str1 == str2)
printf("str1 and str2 are same\n");
else
printf("str1 and str2 are not same\n");
if (str3 == str4)
printf("str3 and str 4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
str1和str2是两个独立的数组
str3和str4中的hello CSDN.是常量字符串,常量字符串不能被修改,所以这个常量字符串没必要存放两份,只存一份就够(内存优化)
如果要比较字符串的内容,使用:strcmp
2.指针数组
指针数组的本质是数组,里面存的是指针
int * arr1[ 10 ]; / /整形指针数组
char* arr2[ 4 ]; / /一级字符指针数组
char** arr3[ 5 ]; / /二级字符指针数组
用指针数组模拟实现二维数组
3.数组指针
3.1数组指针的定义
数组指针是指针
去掉名字就是它的类型
这里的(*p)就相当于arr
一般情况下,数组指针应用于二维数组
数组指针的应用
p+i相当于第i的地址
3.2 &数组名和数组名
数组名是数组首元素的地址
有两个例外:
- 1.sizeof(数组名)
- 2.&数组名
int arr [ 5 ] ; / /整型数组
int *parr1 [ 10 ] ; / /指针数组
int ( *parr2 )[10 ] ; / /数组指针
int ( *parr3 [10 ])[ 5 ] ; parr3 是存放数组指针的数组
/ /将名字parr3 [ 10 ] 去掉
/ /int (*) [ 5 ] - ->数组的元素类型
4.数组参数、指针参数
4.1一维数组传参
#include<stdio.h>
void test ( int arr [ ])
{}
void test ( int arr[ 10 ])
{}
void test ( int *arr )
{}
void test2 ( int * arr[ 20 ])
{}
void test2 ( int **arr){}
int main()
{
int arr[ 10 ]={ 0 };
int *arr2[20]={ 0 };
test2(arr2);
}
/ /以上写法均可以
4.2二维数组传参
void test ( int arr [ 3 ] [ 5 ] ) / / ok
{ }
void test ( int arr [ ] [ ] )
{ }
void test ( int arr [ ] [ 5 ] ) / / ok
{ }
void test ( int* arr )
{ } / /二维数组名是数组第一行的地址,而这个函数是整型指针,所以不行
void test ( int* arr [ 5 ] )
{ } / /二维数组名是数组首元素地址,而这个函数的参数是数组,所以不行
void test(int(*arr)[5])/ / ok
{ }
void test ( int ** arr )
{ } / /传过来是一维数组的地址,二级指针是用来接收一级指针变量的地址
int main( )
{
int arr [ 3 ] [ 5 ] = { 0 } ;
test ( arr ) ;
}
/ / 二维数组传参,函数形参的设计只能省略第一个[ ]的数字。
/ / 因为对一个二维数组,可以不知道有多少行
/ / 但是必须知道一行多少元素,这样才方便运算
4.3一级指针传参
一级指针p
当函数的参数为二级指针的时候,可以接受什么参数?
void test(char **p)
{
}
int main()
{
char c='b';
char*pc=&c;
char**ppc=&pc;
char*arr[10];
test(&pc);
test(ppc);
test(arr);
return 0;
}
5.函数指针
函数名就是函数的地址,无论有没有&都是函数的地址,没有区别
此时pf就是函数指针变量
/ /代码1
( * ( void ( * ) ( ) ) 0 ) ( )
将0强制类型转换为void(*)( )--->把0当作一个函数的地址
然后解引用0的地址,找0地址处的函数--->调用该地址处的函数
把0直接转换一个void(*)()的函数指针,然后去调用0地址处的函数
/ /代码2
void ( *signal ( int ,void(*)(int) ) ) (int);
这里是函数声明,没有传参数,不是函数调用,函数的定义需要有大括号,故不是函数的定义,但是又有函数的参数类型,所以这里是函数的声明
signal是函数名,signal的参数一个是int类型,另一个是void(*)(int)函数指针类型,然后这个signal函数的返回类型是void (*)(int)函数指针类型
简而言之:
上述代码是一次函数声明
声明的函数:signal
signal函数的第一个参数是int类型的
signal函数的第二个参数是一个函数指针类型,该函数指针指向的函数参数是int,返回类型是void
signal函数的返回类型也是一个函数指针类型,该函数指针指向的函数参数是int,返回类型是void
注意:这个写法是固定的,不能这样写void(*) (int)signal (int , void(*)int))
这个代码看起来很繁琐,可用typedef重新命名一个名字,使这行代变得简洁
typedef void ( *pf_t )( int ) / /重新定义的类型名必须放在它的内部这是语法要求
/ /那么上面的代码可以写为
pf_t signal( int , pf_t );
6.函数指针数组
函数指针数组可以存放多个参数相同、返回类型相同的函数的地址
函数指针 int (*pf ) ( int , int )
函数指针数组 int ( *pfArr[ 4 ] ) ( int , int )
用函数指针数组写一个计算器
#include<stdio.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 input = 0;
do
{
menu();
printf("请选择:>");
scanf("%d", &input);
int x = 0;
int y = 0;
if (input == 0)
{
printf("退出计算器\n");
break;
}
else if (input >= 1 && input <= 5)
{
printf("请输入两个操作数:>");
scanf("%d %d", &x, &y);
int(*pfArr[5])(int, int) = { 0,Add,Sub,Mul,Div };
int ret = (*pfArr[input])(x,y);
printf("%d\n", ret);
}
else
{
printf("输入错误,请重新输入\n");
}
} while (input);
return 0;
}
7.指向函数指针数组的指针
8.回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现直接调用,而是再特定的事件或条件发生时由另外的以方调用的,用于该事件或条件进行响应
此时的calc就是回调函数
冒泡排序的缺点: 只能排序整型的数据
而qsort函数能够排任意类型的数据
qsort函数的使用:
void qsort(void* base,size_t num,size_t witdh,int(*cmp)(const void* e1,const void* e2))
void*base--->待排序的数据首元素的地址,也就是指向起始位置
size_t num--->待排序数据的元素个数
size_t witdh--->待排序数据中每个元素的大小 (单位是字节)
int (*cmp)(const void*e1,const void *e2)--->比较两个元素的大小的函数指针
两个整型使用关系运算符比较大小
两个字符串用库函数strcmp比较大小
两个结构体需要指定方式进行比较(比如:一个人是按照名字进行比较还是用年龄进行比较)
由于比较方式的不同,比较方法单独分装一个函数
void*:
void*可以接受任意类型的指针,但是void*类型的指针不能++,因为不知道能跳过几个字节,不过,想要void*++,需要将void*类型的指针强制类型转换为其他类型的指针,并且解引用*,便可让它++
将冒泡排序改为可以排任意元素的排序(根据qsort函数)---->用冒泡排序模拟实现qsort函数
冒泡排序中的比较部分分装一个函数指针(因为qsort函数就是用函数指针)来比较大小