当前位置:网站首页>【C补充】指向指针或函数的指针
【C补充】指向指针或函数的指针
2022-08-04 09:14:00 【一苇以航fp】
一、指向指针的指针
想通过函数修改指针指向的内存位置,即修改指针变量的值时,需要使用指向指针的指针。
例如:
struct node{
int value;
struct node *next;
};
//在链表开头加入新结点
struct node *add_to_list(struct node *list, int n)
{
struct node *new_node = (struct node*)malloc(sizeof(struct node));
if(new_node == NULL){
printf("Error: malloc failed, no space left.\n");
exit(EXIT_FAILURE);
}
new_node->value = n;
new_node->next = list;
return new_node;
}
若是想直接返回新链表的首地址:
...
new_node->value = n;
new_node->next = list;
list = new_node;
return list;
由于函数是通过创建参数副本的方式执行的,因此上面的办法在函数调用结束之后对 list 的修改会失效。list 作为一个变量,函数修改任何外部变量都需要通过指向该变量指针来修改变量的值,即便这是一个指针变量。因此,要通过函数修改外部指针变量的值,需要用到指向指针的指针。
以下为正确代码:
void add_to_list(struct node **list, int n) //参数改为指向指针的指针
{
struct node *new_node = (struct node*)malloc(sizeof(struct node));
if(new_node == NULL){
printf("Error: malloc failed, no space left.\n");
exit(EXIT_FAILURE);
}
new_node->value = n;
new_node->next = *list;
*list = new_node;
}
//函数的调用
add_to_list(&first, n); //first为函数首地址
二、指向函数的指针
函数指针: 指向函数的指针
指针函数:返回指针的函数
函数占用内存单元,因此每个函数都有地址。
2.1 函数指针作为参数
举例:求函数的积分
//形式1:
double integrate(double (*f)(double), double a, double b);
//形式2:
double integrate(double f(double), double a, double b);
- 形式1中,
(*f)
左侧的 double 为 f 所指向的函数的返回类型,右侧的 double 为所指向函数的参数表。形式2同理。
调用举例:
#define PI 3.14159;
double integrate(double (*f)(double), double a, double b);
...
double result;
result = integrate(sin, 0, PI/2);
...
double integrate(double (*f)(double), double a, double b)
{
...
y = (*f)(x); //通过*f调用f函数
...
}
说明:
sin
后面没有圆括号,编译器会视为指针,因此不会调用函数库中的 sin 函数代码,而是给 integrate 传递一个指向 sin 函数的指针。类比 a 和 a[i] 与 f 和 f(x) 的关系。如果 f(x) 是函数,则编译器将 f(x) 当作函数来调用,而将 f 视为指向 f(x) 的指针。*f
表示f指向的函数,x 是函数调用的实际参数- 在 main 函数中,调用integrate函数时,函数体内每一次 *f 都是对 sin 函数的调用
注意:
- C语言允许使用 f(x) 来调用 f 所指向的函数(形式2),但(*f)(x)更符合真实情况,因为此处的 f 是指针,而不是函数名。
2.2 qsort
函数
所在头文件:<stdlib.h>
函数功能:对任意数组排序
函数原型:void qsort(void* base, size_t n, size_t size, int(*compar)(void*, void*) );
- 其中
int(*compar)(void*, void*)
中的 int 表示 compar 函数的返回类型,(*compar)右侧圆括号的内容为 compar 函数的参数表。compar 为指向函数的指针,因此通过(*compar)来调用 compar 函数。 - compar函数确定排序规则,为用户自主编写确定,qsort传递给compar两个数的地址(以void*形式传入),qsort依据compar的返回值确定这两个数的排列顺序。
compar函数返回值:
返回值 | 含义 |
---|---|
< 0 | 第一个参数放前面 |
0 | 不变 |
> 0 | 第2个参数放前面 |
2.2.1 compar
函数的编写方式举例:
//整型数组
int compar(void* a, void* b)
{
return (*(int*)a - *(int*)b ); //当待排序的数组元素为int类型时
}
//字符串数组
int compar(const void* str1, const void* str2)
{
return strcmp(*(char**)str1, *(char**)str2);
}
//字符串链表
struct node{
char name[50];
struct node* next;
};
int compar(const void* p, const void* q)
{
return strcmp(((struct node*)p)->name, ((struct node*)b)->name);
}
- 在第一个中,由于整数相减可能导致数据溢出,因此如果待排序整数是任意给定的,则用 if 语句来比较
*(int*)a
和*(int*)b
更安全。 - 在第3个中,强制类型转换符
()
的优先级低于取结构成员符->
,因此需要多一个括号
2.2.2 qosrt
完整用法
#include <stdio.h>
#include <stdlib.h>
...
int compar(const void* a, const void* b){
return ( *(int*)a - *(int*)b ); //按从小到大重新排序
}
int main(){
int a[5] = {
5,4,3,2,1};
qsort(a, 5, sizeof(int), compar);
for(int i=0; i<5; i++){
printf("%d ", a[i]); //输出1 2 3 4 5
}
return 0;
}
2.3 函数指针的其他用途
C语音把函数指针也视为存储数据的指针,因此可以用变量存储函数指针,定义返回函数指针的函数。
void (*pf)(int); //定义各个可以存储函数指针的变量pf
pf = f; //f本身是该函数的指针,不能用取地址符&f
(*pf)(i); //调用f(i)
pf(i); //调用f(i)
- 以上定义中,
pf
可以指向任何带有 int 型参数并返回 void 型值的函数
函数指针数组
//定义一个函数指针数组
void (*file_name[])(void) = {
new_cmd;
open_cmd;
close_cmd;
close_all_cmd;
save_cmd;
save_as_cmd;
save_all_cmd;
print_cmd;
exit_cmd;
};
...
void new_cmd(){
...
}
void open_cmd(){
...
}
...
void exit_cmd(){
...
}
2.4 应用举例:列三角函数表
#include <stdio.h>
#include <math.h>
void tabulate(double (*f)(double), double first, double last, double incr);
int main(){
double initial, final, increment;
printf("Enter initial value: ");
scanf("%lf", &initial);
printf("Enter final value: ");
scanf("%lf", &final);
printf("Enter increment: ");
scanf("%lf", &increment);
printf("\n x sin(x)"
"\n ------- -------\n");
tabulate(sin, initial, final, increment);
printf("\n x cos(x)"
"\n ------- -------\n");
tabulate(cos, initial, final, increment);
printf("\n x sin(x)"
"\n ------- -------\n");
tabulate(sin, initial, final, increment);
return 0;
}
void tabulate(double (*f)(double), double first, double last, double incr)
{
double x;
int i, num_intervals;
num_intervals = ceil((last - first) / incr);
for(i = 0; i < num_intervals; i++){
x = first + i*incr;
printf("%10.5f %10.5f\n", x, (*f)(x));
}
}
说明:
double ceil(double x)
函数:返回大于或等于 x 的最小整数 (以double类型返回)。
输出结果示例:
三、受限指针 restrict
(C99)
int * restrict p;
若 p 所指向的对象只能通过 p 来访问或修改。
举例:
int * restrict p;
int * restrict q;
p = (int*)malloc(sizeof(int));
q = p;
*q = 0; //未定义行为,无法通过q修改p指向的内容
如果一个对象有多种访问方式,这些访问方式互称为 “ 别名 ” 。
应用举例:void *memcpy(void * restrict s1, void * restrict s2, size_t n);
void *memmove(void * s1, void * s2, size_t n);
memcpy
和memmove
都是复制内存块的内容,区别在于原内存和目的地是否允许重叠,前者不允许重叠,后者可以重叠。
四、弹性数组成员 (C99)
当结构体的最后一个成员是数组时,其长度可以省略。
struct vstring{
int len;
char str[];
};
...
struct vstring *str = malloc(sizeof(struct vstring) + n);
str->len = n;
边栏推荐
猜你喜欢
随机推荐
Producer and Consumer Problems in Concurrent Programming
MindSpore:【mindinsight】【Profiler】用execution_time推导出来的训练耗时远小于真实的耗时
csdn图片去水印 | 其他方法无效时的解决方案
PD 源码分析- Checker: region 健康卫士
户外徒步旅行
LVGL's multi-language conversion tool -- a good assistant for font settings
leetcode经典例题——49.字母异位词分组
Oracle怎么获取当前库或者同一台服务器上某几个库的数据总行数?
用OpenGL绘制winXP版扫雷的笑脸表情
MindSpore:Ascend运行出现问题
How to import data from PG to kingbaseES
DNS 查询原理详解—— 阮一峰的网络日志
[Punctuality Atomic STM32 Serial] Chapter 1 Learning Method of the Book Excerpted from [Punctuality Atomic] MiniPro STM32H750 Development Guide_V1.1
leetcode动态规划经典例题——53.最大子数组和
【正点原子STM32连载】第四章 STM32初体验 摘自【正点原子】MiniPro STM32H750 开发指南_V1.1
[Punctuality Atom STM32 Serial] Chapter 3 Development Environment Construction Excerpted from [Punctual Atom] MiniPro STM32H750 Development Guide_V1.1
[Punctuality Atom STM32 Serial] Chapter 2 STM32 Introduction Excerpted from [Punctual Atom] MiniPro STM32H750 Development Guide_V1.1
leetcode经典例题——56.合并区间
ZbxTable 2.0 重磅发布!6大主要优化功能!
双指针方法