目录
1. 字符指针变量和字符串
在指针的类型中我们知道有一种指针类型为字符指针 char*
int mian()
{
char ch = 'w';
char* ph = &ch;
*ph = 'w';
return 0;
}
我们可以把字符的地址存入字符指针变量中去,
除此之外,我们也可以存入一个字符串。
int main()
{
const char* pstr = "abcdef g";
printf("%s\n", pstr);
return 0;
}
那么我们是把整个字符串都存入这个指针变量了吗?(为了防止字符串被修改,我们const修饰指针,使的即使我们获得了指针却不能修改变量)
我们可以思考一下,在32位环境下的指针变量大小是4个字节,“abcdef g”每一个字符加上空格代表1个字节,已经超过了指针变量能存储的最大值
字符串被const修饰:
int main()
{
char str1[] = "hello";
char str2[] = "hello";
const char *str3 = "hello";
const char *str4 = "hello";
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 str4 are same\n");
else
printf("str3 and str4 are not same\n");
return 0;
}
我们先来看第一行和第二行的代码,str1和str2是两个不同的字符数组,当一个字符串要存入这个字符数组时,此时数组名就是该数组首元素的地址,地址是,存入字符串的首元素的地址,虽然这两个字符串内容相同,但他们存储的位置不可能相同,所以首元素的地址不可能相同
再来分析三四两行的代码,都是一个常量字符串存入2个指针变量,因为这个字符串被const修饰,在内存中独占一份,不会被修改。
2. 数组指针变量
2.1 数组指针变量是什么?
我们在第一章的时候学习了指针数组,意思是一个数组,里面存放的类型都是指针
字符数组--存放字符的数组 char arr[];
整形数组--存放整形的数组 int arr[]
指针数组--存放指针的数组 int* arr[](这里写的是整形指针的数组)
那么数组指针是什么呢?是数组还是指针?
答案:指针!
字符指针--指向字符的指针
整形指针--指向整形的指针
数组指针--指向数组的指针!
int *p1[ 10 ];int (*p2)[ 10 ];
int (*p)[ 10 ];
2.2 数组指针变量怎么初始化
数组指针变量是⽤来存放数组地址的,那怎么获得数组的地址呢?就是我们之前学习的 &数组名
int arr[ 10 ] = { 0 };&arr; // 得到的就是数组的地址
int (*p)[ 10 ] = &arr
i n t (*p) [ 10 ] = &arr;| | || | || | p 指向数组的元素个数| p 是数组指针变量名p 指向的数组的元素类型
2.3 数组指针的解引用
(*p)[10],这样就可以解引用一个数组指针,举例:
int main()
{
int arr[3] = { 1,2,3 };
int(*p)[3] = arr;
printf("%d ", (*p)[0]);
printf("%d ", (*p)[1]);
printf("%d ", (*p)[2]);
return 0;
}
3. ⼆维数组传参的本质
void test(int arr[][4], int x, int y)
{
int i = 0;
int j = 0;
for (i = 0; i < x; i++)
{
for (j = 0; j < y; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
}
int main()
{
int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
test(arr, 3, 4);
return 0;
}
我们这⾥实参是⼆维数组,形参也写成⼆维数组的形式,那还有什么其他的写法吗?
void test(int (*p)[4], int x, int y)
{
int i = 0;
int j = 0;
for (i = 0; i < x; i++)
{
for (j = 0; j < y; j++)
{
printf("%d ", *(*(p+i)+j));
//(*p[i] + j);
}
printf("\n");
}
}
int main()
{
int arr[3][4] = { {1,2,3,4},{2,3,4,5},{3,4,5,6} };
test(arr, 3, 4);
return 0;
}
or
void test(int (*p)[4], int x, int y)
{
int i = 0;
int j = 0;
for (i = 0; i < x; i++)
{
for (j = 0; j < y; j++)
{
printf("%d ", *p[j]);
}
printf("\n");
p++;
}
}
4. 函数指针变量
4.1 函数指针变量的创建
我们来看这么一串代码
void test()
{
printf("hh\n");
}
int main()
{
printf("%p\n", test);
printf("%p\n", &test);
return 0;
}
运行的结果是
我们发现函数的地址也很神奇,居然和数组的地址有如此相像,函数的名字也等于函数的地址
void test()
{
printf("hh\n");
}
int Add(int x, int y)
{
return x + y;
}
int main()
{
void (*pf1)() = &test;
void (*pf2)() = test;
int(*pf3)(int, int) = Add;
int(*pf3)(int x, int y) = &Add;
return 0;
}
分析:
int (*pf3) ( int x, int y)| | ------------| | || | pf3 指向函数的参数类型和个数的交代| 函数指针变量名pf3 指向函数的返回类型int (*) ( int x, int y) //pf3 函数指针变量的类型
4.2 函数指针变量的使用
#include <stdio.h>
int Add(int x, int y)
{
return x+y;
}
int main()
{
int(*pf3)(int, int) = Add;
printf("%d\n", (*pf3)(2, 3));
printf("%d\n", pf3(3, 5));
return 0;
}
5. 函数指针数组
int * arr [ 10 ];
// 数组的每个元素是 int*int ( * parr1 [ 10 ]) ();int * parr2 [ 10 ] ();int ( * ) () parr3 [ 10 ];
5.1指向函数指针数组的指针
作了解不深究
void test(const char* str)
{
printf("%s\n", str);
}
int main()
{
//函数指针pfun
void (*pfun)(const char*) = test;
//函数指针的数组pfunArr
void (*pfunArr[5])(const char* str);
pfunArr[0] = test;
//指向函数指针数组pfunArr的指针ppfunArr
void (*(*ppfunArr)[5])(const char*) = &pfunArr;
return 0;
}
6.回调函数
回调函数就是一个通过函数指针调用的函数。如果你把函数的指针(地址)作为参数传递给另一个函数,当这个指针被用来调用其所指向的函数时,我们就说这是回调函数。回调函数不是由该函数的实现方直接调用,而是在特定的事件或条件发生时由另外的一方调用的,用于对该事件或条件进行响应。
我们先了解一下,转移表实现一个计算器
#include <stdio.h>
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int x, y;
int input = 1;
int ret = 0;
do
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf(" 0:exit \n");
printf("*************************\n");
printf("请选择:");
scanf("%d", &input);
switch (input)
{
case 1:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = add(x, y);
printf("ret = %d\n", ret);
break;
case 2:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = sub(x, y);
printf("ret = %d\n", ret);
break;
case 3:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = mul(x, y);
printf("ret = %d\n", ret);
break;
case 4:
printf("输入操作数:");
scanf("%d %d", &x, &y);
ret = div(x, y);
printf("ret = %d\n", ret);
break;
case 0:
printf("退出程序\n");
break;
default:
printf("选择错误\n");
break;
}
} while (input);
return 0;
}
可以进行一些简单算术操作 ,我们只需要设置好几个函数,接着我们选择数字进入需要的函数,来进行运算。但是我们这样的代码太冗长,而且出现了太多重复代码,我们可以用上面学到的知识来进行优化。
void meun()
{
printf("*************************\n");
printf(" 1:add 2:sub \n");
printf(" 3:mul 4:div \n");
printf(" 0:exit \n");
printf("*************************\n");
printf("请选择:");
}
int add(int a, int b)
{
return a + b;
}
int sub(int a, int b)
{
return a - b;
}
int mul(int a, int b)
{
return a * b;
}
int div(int a, int b)
{
return a / b;
}
int main()
{
int input = 1;
int x = 0;
int y = 0;
int (*parr[5])(int, int) = { 0,add,sub,mul,div };
do
{
meun();
scanf("%d", &input);
if (input>0 && input<=4)
{
printf("请输入操作数:\n");
scanf("%d %d", &x, &y);
int ret = (*parr[input])(x, y);
printf("ret = %d\n", ret);
}
else if (input == 0)
{
printf("退出计算机\n");
}
else
{
printf("输入错误\n");
}
} while (input);
return 0;
}
我们把函数都存入一个函数指针数组中,需要的时候直接调用就不需要switch语句来写冗长的代码了。
下节我们会介绍qsort库函数是怎么调用函数的和如何实现的,还望指正