指针进阶(二)
继上文,指针进阶(上)
这篇文章我们将结束指针进阶这一块知识~
文章目录
函数指针
函数指针是指向函数的指针变量。与指向变量和对象的指针类似,函数指针存储了函数的内存地址,可以用于间接调用该函数。函数指针的类型与函数的类型相匹配,包括返回类型和参数类型。
下面是一个简单的函数指针的示例:
c
#include <stdio.h>
int add(int a, int b) {
return a + b;
}
int multiply(int a, int b) {
return a * b;
}
int main() {
int result;
// 声明函数指针变量并初始化为指向add函数的指针
int (*ptr)(int, int) = add;
// 通过函数指针调用add函数
result = ptr(3, 4);
printf("Addition result: %d\n", result);
// 重新指向multiply函数
ptr = multiply;
// 通过函数指针调用multiply函数
result = ptr(3, 4);
printf("Multiplication result: %d\n", result);
return 0;
}
//输出结果
Addition result: 7
Multiplication result: 12
在上面的示例中,我们声明了一个函数指针变量ptr
,它的类型为int (*)(int, int)
,即指向参数为两个整数、返回类型为整数的函数指针。初始化时,我们将它指向了函数add
,通过ptr
间接调用add
函数并打印结果。接着,将ptr
重新指向了函数multiply
,并再次通过ptr
间接调用multiply
函数并打印结果。
函数指针适用以下情况:
- 回调函数:函数指针可以作为参数传递给其他函数,用于实现回调机制。这使得函数能够在特定事件发生时调用传递的函数指针。
- 函数选择和切换:函数指针可以根据条件选择不同的函数来执行,从而实现函数的动态切换和选择。
- 函数数组:函数指针可以作为元素存储在数组中,通过数组索引可以方便地调用对应的函数。
- 函数指针表:函数指针可以作为函数指针表(函数指针数组)的一部分,提供多个函数以供选择和调用。
有意思的代码
c
//代码1
(*(void (*)())0)();
//代码2
void (*signal(int , void(*)(int)))(int);
解读
代码1:(*(void (*)())0)();
这行代码使用函数指针调用一个地址为0的函数。在这里,(void (*)())
是一个函数指针类型,表示指向返回类型为void
,参数为空的函数。0
表示一个空指针,(void (*)())0
将地址为0的空指针强制转换为函数指针类型。最后,(*()
语法对函数指针进行间接调用,()
表示调用函数。由于这里的函数指针指向了空指针,实际上最终会引发一个错误,因为地址为0处没有有效的函数。
代码2:void (*signal(int, void(*)(int)))(int);
这行代码是一个函数声明,声明了一个名为signal
的函数,该函数接受两个参数:一个整型参数和一个指向函数的指针参数,返回一个指向函数的指针。参数中的void(*)(int)
表示接受一个整型参数并返回void
的函数指针类型,整个返回类型void (*)(int)
表示指向接受一个整型参数并返回void
的函数的指针类型。
这行代码的功能是实现了一个用于处理信号的函数,它的参数包括信号的编号和一个指向信号处理函数的指针。函数signal
的返回值是一个指向处理特定信号的函数的指针。
代码2简化:
c
typedef void(*pfun_t)(int);
pfun_t signal(int, pfun_t);
void (*signal(int, void(*)(int)))(int);
这行代码声明了一个名为signal
的函数,它接受两个参数:一个整型参数和一个指向接受整数参数并返回void
的函数的指针。返回类型是指向接受整型参数并返回void
的函数的指针。
这行代码的作用是定义一个函数,用于处理信号。它的参数包括信号的编号和一个指向信号处理函数的指针。函数signal
根据传入的信号编号,返回一个指向处理该信号的函数的指针。
typedef void(*pfun_t)(int); pfun_t signal(int, pfun_t);
这段代码是对代码2的一种简化形式。首先,使用typedef
关键字定义了一个新类型pfun_t
,它是一个函数指针类型,表示指向接受整型参数并返回void
的函数的指针。
然后,函数声明中使用这个新类型pfun_t
来简化函数的返回类型和第二个参数的类型定义。相当于将代码1中的返回类型和第二个参数的部分提取出来,并用pfun_t
代替。这样做的好处是可以增加代码的可读性和可维护性,使得函数声明更加清晰和简洁。
最终函数signal
的作用与代码1中相同,用于处理信号,输入参数包括信号的编号和一个指向信号处理函数的指针,并返回一个指向处理该信号的函数的指针。
函数指针数组
函数指针数组是指一个数组,其中的每个元素都是一个函数指针 。换句话说,函数指针数组是一个存储了多个函数指针的数组变量。
函数指针是一个指向函数的指针变量,它可以指向特定函数的起始地址。通过函数指针,我们可以在代码中动态地调用不同的函数。
函数指针数组的声明形式如下:
返回类型 (*数组名[数组长度])(参数列表);
其中,返回类型
是函数指针指向的函数的返回类型,*数组名[数组长度]
表示一个函数指针数组的声明,参数列表
表示函数指针指向的函数的参数列表。
下面是一个示例,展示了一个包含两个函数指针的函数指针数组的声明和使用:
c
#include <stdio.h>
void func1() {
printf("This is function 1\n");
}
void func2() {
printf("This is function 2\n");
}
int main() {
void (*funcPtrArray[2])(); // 声明函数指针数组
funcPtrArray[0] = func1;
funcPtrArray[1] = func2;
funcPtrArray[0](); // 调用函数指针数组的第一个函数指针,相当于调用函数 func1
funcPtrArray[1](); // 调用函数指针数组的第二个函数指针,相当于调用函数 func2
return 0;
}
运行这段代码会输出以下结果:
This is function 1
This is function 2
这个示例定义了一个名为funcPtrArray
的函数指针数组,包含两个函数指针元素。然后,将函数func1
赋值给数组的第一个元素,将函数func2
赋值给数组的第二个元素。通过调用函数指针数组中的函数指针,实际上是调用了对应的函数。
函数指针数组的用途:转移表
下面我们看一个例子:写一个计算器
c
#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( "*************************\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");
breark;
default:
printf( "选择错误\n" );
break;
}
} while (input);
return 0;
}
我们换成函数指针数组实现:
c
#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;
int(*p[5])(int x, int y) = { 0, add, sub, mul, div }; //转移表
while (input)
{
printf( "*************************\n" );
printf( " 1:add 2:sub \n" );
printf( " 3:mul 4:div \n" );
printf( "*************************\n" );
printf( "请选择:" );
scanf( "%d", &input);
if ((input <= 4 && input >= 1))
{
printf( "输入操作数:" );
scanf( "%d %d", &x, &y);
ret = (*p[input])(x, y);
}
else
printf( "输入有误\n" );
printf( "ret = %d\n", ret);
}
return 0;
}
指向函数指针数组的指针
指向函数指针数组的指针是一个 指针
指针指向一个 数组 ,数组的元素都是 函数指针。
当我们有一个函数指针数组时,我们可以定义一个指向该数组的指针,这个指针指向函数指针数组的首地址。通过这个指针,我们可以访问和操作函数指针数组中的元素。
下面是一个示例:
c
#include <stdio.h>
void func1() {
printf("This is function 1\n");
}
void func2() {
printf("This is function 2\n");
}
int main() {
void (*funcPtrArray[2])() = {func1, func2}; // 定义函数指针数组并初始化
void (*(*ptrToArray)[2])() = &funcPtrArray; // 定义指向函数指针数组的指针,并将其指向函数指针数组的地址
// 通过指针访问函数指针数组中的元素
(*ptrToArray)[0](); // 调用函数指针数组的第一个元素,相当于调用函数 func1
(*ptrToArray)[1](); // 调用函数指针数组的第二个元素,相当于调用函数 func2
return 0;
}
运行这段代码会输出以下结果:
This is function 1
This is function 2
在这个示例中,我们首先定义了一个包含两个函数指针元素的函数指针数组 funcPtrArray
,并且使用两个函数 func1
和 func2
对其进行初始化。
然后,通过将 funcPtrArray
的地址赋给指向函数指针数组的指针 ptrToArray
,我们创建了一个指向函数指针数组 funcPtrArray
的指针 ptrToArray
。
通过使用 (*ptrToArray)[0]
来访问函数指针数组的第一个元素,并且使用 (*ptrToArray)[1]
来访问函数指针数组的第二个元素,并通过调用函数指针来执行相应的函数。
通过指向函数指针数组的指针,我们可以在运行时动态地访问和操作函数指针数组中的元素,这为灵活地处理函数指针数组提供了一种方式。
回调函数
回调函数是指在编程中,将一个函数作为参数传递给另一个函数,并在后者内部调用该函数的过程。回调函数的使用可以增加代码的灵活性和扩展性,使得代码模块化,易于维护和重用。
一个常见的应用场景是在事件处理机制中,当某个事件发生时,调用传递的回调函数来处理事件。回调函数可以根据需要进行定义和实现,提供了一种在运行时动态决定代码行为的机制。
下面给个例子:
首先,我们需要定义一个函数类型作为回调函数的类型。函数类型定义的一般形式是:返回类型 (*函数类型名称)(参数列表)。
例如,假设我们有一个回调函数类型 CallbackFunc
,定义如下:
c
typedef void (*CallbackFunc)(int);
在这个例子中,我们定义了一个返回类型为 void
,参数为 int
的回调函数类型 CallbackFunc
。
接下来,我们可以编写一个接收回调函数作为参数的函数,如下所示:
c
void performTask(int value, CallbackFunc callback) {
// 执行任务
printf("Performing task with value %d\n", value);
// 调用回调函数
callback(value);
}
在 performTask
函数中,我们执行了某些任务,并将传递给该函数的 value
参数作为参数调用了传递的回调函数 callback
。
最后,我们可以定义和实现一个具体的回调函数,然后将其作为参数传递给 performTask
函数,如下所示:
c
void callbackFunction(int value) {
printf("Callback function called with value %d\n", value);
}
int main() {
int value = 10;
performTask(value, callbackFunction);
return 0;
}
在 main
函数中,我们定义了一个值 value
,然后将 callbackFunction
作为回调函数传递给了 performTask
函数。
运行这段代码,会输出以下结果:
Performing task with value 10
Callback function called with value 10
在运行时,首先调用了 performTask
函数,输出了任务执行的信息。然后,在 performTask
函数内部调用了回调函数 callbackFunction
,并将传递的 value
参数作为参数传递给回调函数。回调函数执行后,输出了相应的信息。
注意
在使用回调函数时,我们需要确保回调函数的定义和声明与预期的函数指针类型相匹配,以确保参数类型和返回类型的一致性。
指针和数组例题
例题1
一维数组
c
//一维数组
int a[] = {1,2,3,4};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a+0));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(a[1]));
printf("%d\n",sizeof(&a));
printf("%d\n",sizeof(*&a));
printf("%d\n",sizeof(&a+1));
printf("%d\n",sizeof(&a[0]));
printf("%d\n",sizeof(&a[0]+1));
解析:
-
printf("%d\n",sizeof(a));
:sizeof(a)
返回整个数组的大小。在这种情况下,数组a
所占的总字节数为 16 (4个整型元素每个占4个字节)。因此,输出结果为16
。 -
printf("%d\n",sizeof(a+0));
:a+0
是一个指针,指向数组a
的第一个元素。sizeof(a+0)
返回指针的字节数,无论指针指向数组还是其他类型的对象,指针本身所占的字节数是固定的。在大多数系统上,指针占用的字节数通常为4或8。因此,输出结果为4
或8
。 -
printf("%d\n",sizeof(*a));
:*a
是数组a
的第一个元素的值。sizeof(*a)
返回元素类型的大小,即int
类型所占的字节数。通常情况下,在大多数系统上,int
类型占用的字节数为 4。因此,输出结果为4
。 -
printf("%d\n",sizeof(a+1));
:a+1
是一个指针,指向数组a
的第二个元素。与上述第二个例子类似,指针所占的字节数固定,在大多数系统上为 4 或 8。因此,输出结果为4
或8
。 -
printf("%d\n",sizeof(a[1]));
:a[1]
是数组a
的第二个元素的值。sizeof(a[1])
返回元素类型的大小,与第三个例子中sizeof(*a)
的输出结果相同,为4
。 -
printf("%d\n",sizeof(&a));
:&a
是指向整个数组a
的指针。sizeof(&a)
返回指针的字节数大小,与第二个例子中sizeof(a+0)
的输出结果相同,为4
或8
。 -
printf("%d\n",sizeof(*&a));
:*&a
实际上是数组a
本身,所以*(&a)
等价于a
。因此,sizeof(*&a)
返回整个数组的大小,与第一个例子中sizeof(a)
输出结果相同,为16
。 -
printf("%d\n",sizeof(&a+1));
:&a+1
是一个指针,指向数组a
之后的位置。与第二个和第四个例子相似,指针的字节数大小固定,在大多数系统上为 4 或 8。因此,输出结果为4
或8
。 -
printf("%d\n",sizeof(&a[0]));
:&a[0]
是指向数组a
的第一个元素的指针。sizeof(&a[0])
返回指针的字节数大小,与第二个例子中sizeof(a+0)
输出结果相同,为4
或8
。 -
printf("%d\n",sizeof(&a[0]+1));
:&a[0]+1
是一个指针,指向数组a
的第二个元素的位置。与第二个和第四个例子类似,指针的字节数大小固定,在大多数系统上为 4 或 8。因此,输出结果为4
或8
。
总结起来,sizeof
运算符返回的是一个对象或类型所占的字节数。对于数组名,sizeof
返回的是整个数组所占的字节数;对于指针和数组元素,sizeof
返回的是指针或元素类型的字节数。需要注意的是,sizeof
例题2
c
//字符数组
char arr[] = {'a','b','c','d','e','f'};
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));
char arr[] = "abcdef";
printf("%d\n", sizeof(arr));
printf("%d\n", sizeof(arr+0));
printf("%d\n", sizeof(*arr));
printf("%d\n", sizeof(arr[1]));
printf("%d\n", sizeof(&arr));
printf("%d\n", sizeof(&arr+1));
printf("%d\n", sizeof(&arr[0]+1));
printf("%d\n", strlen(arr));
printf("%d\n", strlen(arr+0));
printf("%d\n", strlen(*arr));
printf("%d\n", strlen(arr[1]));
printf("%d\n", strlen(&arr));
printf("%d\n", strlen(&arr+1));
printf("%d\n", strlen(&arr[0]+1));
char *p = "abcdef";
printf("%d\n", sizeof(p));
printf("%d\n", sizeof(p+1));
printf("%d\n", sizeof(*p));
printf("%d\n", sizeof(p[0]));
printf("%d\n", sizeof(&p));
printf("%d\n", sizeof(&p+1));
printf("%d\n", sizeof(&p[0]+1));
printf("%d\n", strlen(p));
printf("%d\n", strlen(p+1));
printf("%d\n", strlen(*p));
printf("%d\n", strlen(p[0]));
printf("%d\n", strlen(&p));
printf("%d\n", strlen(&p+1));
printf("%d\n", strlen(&p[0]+1));
解析:
printf("%d\n", sizeof(arr));
输出结果为6
。sizeof(arr)
返回的是arr
数组的大小,而不是数组元素的数量。printf("%d\n", sizeof(arr+0));
输出结果为4
。sizeof(arr+0)
返回的是指针arr+0
的大小,即sizeof(char*)
。printf("%d\n", sizeof(*arr));
输出结果为1
。*arr
相当于arr[0]
,即返回数组中第一个元素的大小。printf("%d\n", sizeof(arr[1]));
输出结果为1
。arr[1]
返回数组中第二个元素的大小。printf("%d\n", sizeof(&arr));
输出结果为4
。&arr
返回的是数组指针的大小,即sizeof(char(*)[6])
。printf("%d\n", sizeof(&arr+1));
输出结果为4
。&arr+1
返回的是指向下一个数组的指针大小。printf("%d\n", sizeof(&arr[0]+1));
输出结果为4
。&arr[0]+1
返回的是指向下一个字符的指针大小。printf("%d\n", strlen(arr));
输出结果为6
。strlen(arr)
返回的是字符串的长度,以空字符\0
结尾。printf("%d\n", strlen(arr+0));
输出结果为6
。strlen(arr+0)
返回的是字符串的长度,以空字符\0
结尾,arr+0
是指向arr
字符串的指针。printf("%d\n", strlen(*arr));
输出结果为Error
。*arr
在此处是无效的,因为strlen()
函数接受的参数应为一个指针类型。printf("%d\n", strlen(arr[1]));
输出结果为Error
。arr[1]
在此处是无效的,因为strlen()
函数接受的参数应为一个指针类型。printf("%d\n", strlen(&arr));
输出结果为4
。strlen(&arr)
返回的是字符串的长度,以空字符\0
结尾,&arr
是指向arr
字符串的指针。printf("%d\n", strlen(&arr+1));
输出结果为Error
。&arr+1
在此处是无效的,因为strlen()
函数接受的参数应为一个指针类型。printf("%d\n", strlen(&arr[0]+1));
输出结果为5
。strlen(&arr[0]+1)
返回的是从&arr[0]+1
开始的字符串的长度,以空字符\0
结尾。printf("%d\n", sizeof(p));
输出结果为4
。sizeof(p)
返回的是指针p
的大小,即sizeof(char*)
。printf("%d\n", sizeof(p+1));
输出结果为4
。sizeof(p+1)
返回的是指针p+1
的大小,即sizeof(char*)
。printf("%d\n", sizeof(*p));
输出结果为1
。*p
相当于p[0]
,即返回指针p
指向的字符的大小。printf("%d\n", sizeof(p[0]));
输出结果为1
。p[0]
返回指针p
指向的字符的大小。printf("%d\n", sizeof(&p));
输出结果为4
。&p
返回的是指向指针p
的指针大小,即sizeof(char**)
。printf("%d\n", sizeof(&p+1));
输出结果为4
。&p+1
返回的是指向下一个指针的指针大小。printf("%d\n", sizeof(&p[0]+1));
输出结果为4
。&p[0]+1
返回的是指向下一个字符的指针大小。printf("%d\n", strlen(p));
输出结果为6
。strlen(p)
返回的是字符串的长度,以空字符\0
结尾。printf("%d\n", strlen(p+1));
输出结果为5
。strlen(p+1)
返回的是从p+1
开始的字符串的长度,以空字符\0
结尾。printf("%d\n", strlen(*p));
输出结果为Error
。*p
在此处是无效的,因为strlen()
函数接受的参数应为一个指针类型。printf("%d\n", strlen(p[0]));
输出结果为Error
。p[0]
在此处是无效的,因为strlen()
函数接受的参数应为一个指针类型。printf("%d\n", strlen(&p));
输出结果为Error
。&p
在此处是无效的,因为strlen()
函数接受的参数应为一个指针类型。printf("%d\n", strlen(&p+1));
输出结果为Error
。&p+1
在此处是无效的,因为strlen()
函数接受的参数应为一个指针类型。printf("%d\n", strlen(&p[0]+1));
输出结果为5
。strlen(&p[0]+1)
返回的是从&p[0]+1
开始的字符串的长度,以空字符\0
结尾。
例题3
c
//二维数组
int a[3][4] = {0};
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(a[0][0]));
printf("%d\n",sizeof(a[0]));
printf("%d\n",sizeof(a[0]+1));
printf("%d\n",sizeof(*(a[0]+1)));
printf("%d\n",sizeof(a+1));
printf("%d\n",sizeof(*(a+1)));
printf("%d\n",sizeof(&a[0]+1));
printf("%d\n",sizeof(*(&a[0]+1)));
printf("%d\n",sizeof(*a));
printf("%d\n",sizeof(a[3]));
解析:
printf("%d\n",sizeof(a));
输出结果为48
。sizeof(a)
返回的是二维数组a
的大小,即sizeof(int[3][4])
。在这种情况下,数组的大小为3行4列,每个元素的大小为sizeof(int)
。因此,总大小为3 * 4 * sizeof(int)
,即48
。printf("%d\n",sizeof(a[0][0]));
输出结果为4
。sizeof(a[0][0])
返回的是二维数组a[0][0]
单个元素的大小,即sizeof(int)
。printf("%d\n",sizeof(a[0]));
输出结果为16
。sizeof(a[0])
返回的是一维数组a[0]
的大小,即sizeof(int[4])
。在这种情况下,一维数组的大小为4个元素,每个元素的大小为sizeof(int)
。因此,总大小为4 * sizeof(int)
,即16
。printf("%d\n",sizeof(a[0]+1));
输出结果为4
。sizeof(a[0]+1)
返回的是指针a[0]+1
的大小,即sizeof(int*)
。printf("%d\n",sizeof(*(a[0]+1)));
输出结果为4
。sizeof(*(a[0]+1))
返回的是指针a[0]+1
所指向的元素的大小,即sizeof(int)
。printf("%d\n",sizeof(a+1));
输出结果为4
。sizeof(a+1)
返回的是指针a+1
的大小,即sizeof(int(*)[4])
。指针a+1
实际上指向第二行。printf("%d\n",sizeof(*(a+1)));
输出结果为16
。sizeof(*(a+1))
返回的是指针a+1
所指向的一维数组的大小,即sizeof(int[4])
。在这种情况下,一维数组的大小为4个元素,每个元素的大小为sizeof(int)
。因此,总大小为4 * sizeof(int)
,即16
。printf("%d\n",sizeof(&a[0]+1));
输出结果为4
。sizeof(&a[0]+1)
返回的是指针&a[0]+1
的大小,即sizeof(int(*)[4])
。指针&a[0]+1
实际上指向第二行。printf("%d\n",sizeof(*(&a[0]+1)));
输出结果为16
。sizeof(*(&a[0]+1))
返回的是指针&a[0]+1
所指向的一维数组的大小,即sizeof(int[4])
。在这种情况下,一维数组的大小为4个元素,每个元素的大小为sizeof(int)
。因此,总大小为4 * sizeof(int)
,即16
。printf("%d\n",sizeof(*a));
输出结果为16
。sizeof(*a)
返回的是指针a
所指向的一维数组的大小,即sizeof(int[4])
。在这种情况下,一维数组的大小为4个元素,每个元素的大小为sizeof(int)
。因此,总大小为4 * sizeof(int)
,即16
。printf("%d\n",sizeof(a[3]));
输出结果为Error
。a[3]
超出了数组a
的范围,因为数组a
只有3行。因此,该代码将导致越界访问错误。
至此,指针就讲到到这了~接下来会持续更新,敬请期待