当前位置:首页 > 科技  > 软件

还是结构体指针写的代码看着舒服

来源: 责编: 时间:2024-05-16 17:42:44 263观看
导读一直觉得C语言较其他语言最伟大的地方就是C语言中的指针,有些人认为指针很简单,而有些人认为指针很难,当然这里的对简单和难并不是等价于对指针的理解程度。为此在这里对C语言中的指针进行全面的总结,从底层的内存分析,彻

一直觉得C语言较其他语言最伟大的地方就是C语言中的指针,有些人认为指针很简单,而有些人认为指针很难,当然这里的对简单和难并不是等价于对指针的理解程度。Jub28资讯网——每日最新资讯28at.com

为此在这里对C语言中的指针进行全面的总结,从底层的内存分析,彻底让读者明白指针的本质。Jub28资讯网——每日最新资讯28at.com

Jub28资讯网——每日最新资讯28at.com

Jub28资讯网——每日最新资讯28at.com

一、指针变量

Jub28资讯网——每日最新资讯28at.com

首先读者要明白指针是一个变量,为此作者写了如下代码来验证之:Jub28资讯网——每日最新资讯28at.com

#include "stdio.h"int main(int argc, char **argv){    unsigned int a = 10;    unsigned int *p = NULL;    p = &a;    printf("&a=%d/n",a);    printf("&a=%d/n",&a);    *p = 20;    printf("a=%d/n",a);    return 0;}

Jub28资讯网——每日最新资讯28at.com

这说明编译器确实是在解引时无法确定*p的大小,因此这里必须告诉编译器p的类型或者*p的大小,如何告诉呢?很简单,用强制类型转换即可,如下:Jub28资讯网——每日最新资讯28at.com

*(int*)p

这样上面的程序就可以写为如下:Jub28资讯网——每日最新资讯28at.com

#include <stdio.h>int main(int argc, char **argv){    int a=10;    void *p;    p=&a;    printf("p=%d/n",*(int*)p);    return 0;}

编译运行后:Jub28资讯网——每日最新资讯28at.com

Jub28资讯网——每日最新资讯28at.com

可以看到结果确实是正确的,也和预期的想法一致。由于void指针没有空间大小属性,因此void指针也没有++操作。Jub28资讯网——每日最新资讯28at.com

Jub28资讯网——每日最新资讯28at.com

六、函数指针

Jub28资讯网——每日最新资讯28at.com

1. 函数指针使用

函数指针在Linux内核中用的非常多,而且在设计操作系统的时候也会用到,因此这里将详细讲解函数指针。既然函数指针也是指针,那函数指针也占用4个字节(32位编译器)。Jub28资讯网——每日最新资讯28at.com

下面以一个简单的例子说明:Jub28资讯网——每日最新资讯28at.com

#include <stdio.h>int  add(int a,int b){    return a+b;}int main(int argc, char **argv){    int (*p)(int,int);    p=add;    printf("add(10,20)=%d/n",(*p)(10,20));    return 0;}

程序运行结果如下:Jub28资讯网——每日最新资讯28at.com

Jub28资讯网——每日最新资讯28at.com

可以看到,函数指针的申明为:Jub28资讯网——每日最新资讯28at.com

Jub28资讯网——每日最新资讯28at.com

函数指针的解引操作与普通的指针有点不一样。Jub28资讯网——每日最新资讯28at.com

对于普通的指针而言,解引只需要根据类型来取出数据即可,但函数指针是要调用一个函数,其解引不可能是将数据取出,实际上函数指针的解引本质上是执行函数的过程,只是这个执行函数是使用的call指令并不是之前的函数,而是函数指针的值,即函数的地址。Jub28资讯网——每日最新资讯28at.com

其实执行函数的过程本质上也是利用call指令来调用函数的地址,因此函数指针本质上就是保存函数执行过程的首地址。函数指针的调用如下:Jub28资讯网——每日最新资讯28at.com

Jub28资讯网——每日最新资讯28at.com

为了确认函数指针本质上是传递给call指令一个函数的地址,下面用一个简单例子说明:Jub28资讯网——每日最新资讯28at.com

Jub28资讯网——每日最新资讯28at.com

上面是编译后的汇编指令,可以看到,使用函数指针来调用函数时,其汇编指令多了如下:Jub28资讯网——每日最新资讯28at.com

0x4015e3    mov    DWORD PTR [esp+0xc],0x4015c00x4015eb    mov    eax,DWORD PTR [esp+0xc]0x4015ef    call   eax

分析:第一行mov指令将立即数0x4015c0赋值给寄存器esp+0xc的地址内存中,然后将寄存器esp+0xc地址的值赋值给寄存器eax(累加器),然后调用call指令,此时pc指针将会指向add函数,而0x4015c0正好是函数add的首地址,这样就完成了函数的调用。Jub28资讯网——每日最新资讯28at.com

细心的读者是否发现一个有趣的现象,上述过程中函数指针的值和参数一样是被放在栈帧中,这样看起来就是一个参数传递的过程。Jub28资讯网——每日最新资讯28at.com

因此可以看到,函数指针最终还是以参数传递的形式传递给被调用的函数,而这个传递的值正好是函数的首地址。Jub28资讯网——每日最新资讯28at.com

从上面可以看到函数指针并不是和一般的指针一样可以操作内存,因此作者觉得函数指针可以看作是函数的引用申明。Jub28资讯网——每日最新资讯28at.com

2. 函数指针应用

在linux驱动面向对象编程思想中用的最多,利用函数指针来实现封装,下面以一个简单的例子说明:Jub28资讯网——每日最新资讯28at.com

#include <stdio.h>typedef struct TFT_DISPLAY{    int   pix_width;    int   pix_height;    int   color_width;    void (*init)(void);    void (*fill_screen)(int color);    void (*tft_test)(void);}tft_display;static void init(void){    printf("the display is initialed/n");}static void fill_screen(int color){    printf("the display screen set 0x%x/n",color);}tft_display mydisplay={    .pix_width=320,    .pix_height=240,    .color_width=24,    .init=init,    .fill_screen=fill_screen,};int main(int argc, char **argv){    mydisplay.init();    mydisplay.fill_screen(0xfff);    return 0;}

上面的例子将一个tft_display封装成一个对象,上面的结构体成员中最后一个没有初始化,这在Linux中用的非常多。Jub28资讯网——每日最新资讯28at.com

最常见的是file_operations结构体,该结构体一般来说只需要初始化常见的函数,不需要全部初始化。Jub28资讯网——每日最新资讯28at.com

上面代码中采用的结构体初始化方式也是在Linux中最常用的一种方式,这种方式的好处在于无需按照结构体的顺序一对一。Jub28资讯网——每日最新资讯28at.com

3. 回调函数

有时候会遇到这样一种情况,当上层人员将一个功能交给下层程序员完成时,上层程序员和下层程序员同步工作,这个时候该功能函数并未完成,这个时候上层程序员可以定义一个API来交给下层程序员。Jub28资讯网——每日最新资讯28at.com

而上层程序员只要关心该API就可以了而无需关心具体实现,具体实现交给下层程序员完成即可(这里的上层和下层程序员不指等级关系,而是项目的分工关系)。Jub28资讯网——每日最新资讯28at.com

这种情况下就会用到回调函数(Callback Function),现在假设程序员A需要一个FFT算法,这个时候程序员A将FFT算法交给程序员B来完成,现在来让实现这个过程:Jub28资讯网——每日最新资讯28at.com

#include <stdio.h>int  InputData[100]={0};int OutputData[100]={0};void FFT_Function(int *inputData,int *outputData,int num){    while(num--)    {    }}void TaskA_CallBack(void (*fft)(int*,int*,int)){    (*fft)(InputData,OutputData,100);}int main(int argc, char **argv){    TaskA_CallBack(FFT_Function);    return 0;}

上面的代码中TaskA_CallBack是回调函数,该函数的形参为一个函数指针,而FFT_Function是一个被调用函数。Jub28资讯网——每日最新资讯28at.com

可以看到回调函数中申明的函数指针必须和被调用函数的类型完全相同。Jub28资讯网——每日最新资讯28at.com

本文链接:http://www.28at.com/showinfo-26-88716-0.html还是结构体指针写的代码看着舒服

声明:本网页内容旨在传播知识,若有侵权等问题请及时与本网联系,我们将在第一时间删除处理。邮件:2376512515@qq.com

上一篇: 彻底明白Filter与Interceptor

下一篇: 一个小技巧,写出丝滑的 Python 代码

标签:
  • 热门焦点
  • 7月安卓手机性能榜:红魔8S Pro再夺榜首

    7月份的手机市场风平浪静,除了红魔和努比亚带来了两款搭载骁龙8Gen2领先版处理器的新机之外,别的也想不到有什么新品了,这也正常,通常6月7月都是手机厂商修整的时间,进入8月份之
  • 5月iOS设备性能榜:M1 M2依旧是榜单前五

    和上个月一样,没有新品发布的iOS设备性能榜的上榜设备并没有什么更替,仅仅只有跑分变化而产生的排名变动,刚刚开始的苹果WWDC2023,推出的产品也依旧是新款Mac Pro、新款Mac Stu
  • 线程通讯的三种方法!通俗易懂

    线程通信是指多个线程之间通过某种机制进行协调和交互,例如,线程等待和通知机制就是线程通讯的主要手段之一。 在 Java 中,线程等待和通知的实现手段有以下几种方式:Object 类下
  • 三万字盘点 Spring 九大核心基础功能

    大家好,我是三友~~今天来跟大家聊一聊Spring的9大核心基础功能。话不多说,先上目录:图片友情提示,本文过长,建议收藏,嘿嘿嘿!一、资源管理资源管理是Spring的一个核心的基础功能,不
  • 苹果公司要求三星和LG Display生产「无边框」OLED iPhone显示屏

    据 The Elec 报道,苹果已要求其供应商为未来的 iPhone 型号开发「无边框」OLED 显示面板。苹果显然已要求三星和 LG Display 开发新的 OLED 显示面
  • SN570 NVMe SSD固态硬盘 价格与性能兼具

    SN570 NVMe SSD固态硬盘是西部数据发布的最新一代WD Blue系列的固态硬盘,不仅闪存技术更为精进,性能也得到了进一步的跃升。WD Blue SN570 NVMe SSD的包装外
  • 华为举行春季智慧办公新品发布会 首次推出电子墨水屏平板

    北京时间2月27日晚,华为在巴塞罗那举行春季智慧办公新品发布会,在海外市场推出之前已经在中国市场上市的笔记本、平板、激光打印机等办公产品,并首次推出搭载
  • “买真退假” 这种“羊毛”不能薅

    □ 法治日报 记者 王春   □ 本报通讯员 胡佳丽  2020年初,还在上大学的小东加入了一个大学生兼职QQ群。群主&ldquo;七王&rdquo;在群里介绍一些刷单赚
  • 利用职权私自解除被封帐号 Meta开除20多名员工

    11月18日消息,据外媒援引知情人士表示,过去一年时间内,Facebook母公司Meta解雇或处罚了20多名员工以及合同工,指控这些人通过内部系统以不当方式重置用户帐号,其
Top