当前位置: 首页 > article >正文

深入理解 C 语言函数指针的高级用法:(void (*) (void *)) _IO_funlockfile

深入理解 C 语言函数指针的高级用法

函数指针是 C 语言中极具威力的特性,广泛用于实现回调、动态函数调用以及灵活的程序设计。然而,复杂的函数指针声明常常让即使是有经验的开发者也感到困惑。本文将从函数指针的基本概念出发,逐步解析复杂的函数指针声明,结合实际应用场景,帮助高级 C 程序员深入理解和掌握这一工具。


1. 基本概念:什么是函数指针?

函数指针 是指向函数的指针,它允许程序在运行时调用不同的函数。

基本形式

返回类型 (*指针名)(参数列表);

例子

int add(int a, int b) {return a + b;
}int (*func_ptr)(int, int) = add; // 定义一个指向 add 的函数指针
printf("%d\n", func_ptr(3, 5));  // 输出 8

2. 深入分析 (void (*) (void *)) 语法

让我们逐层解析以下语法:

(void (*) (void *))
  • void *:参数是一个通用指针,可以传递任何类型的指针。
  • void (*):表示一个返回值为 void 的函数指针。
  • 完整含义:这是一个指向以 void * 为参数,返回值为 void 的函数的指针。

例子

void my_callback(void *arg) {printf("Callback invoked with arg: %p\n", arg);
}void (*callback)(void *) = my_callback; // 定义函数指针
callback((void *)0x1234);               // 调用回调函数,输出: Callback invoked with arg: 0x1234

3. 函数指针的高级用法
3.1 回调机制

回调函数是函数指针最常见的应用场景之一。在系统设计中,通过函数指针可以让调用者决定函数的具体实现。

例子:注册回调

#include <stdio.h>typedef void (*Callback)(int); // 定义函数指针类型void event_handler(int event_id) {printf("Event %d handled!\n", event_id);
}void register_callback(Callback cb) {cb(42); // 调用回调
}int main() {register_callback(event_handler); // 输出: Event 42 handled!return 0;
}

3.2 动态函数调用

动态加载库时,可以通过 dlsym 动态解析符号,使用函数指针调用目标函数。

例子:动态加载库函数

#include <dlfcn.h>
#include <stdio.h>int main() {void *handle = dlopen("libm.so.6", RTLD_LAZY); // 加载数学库if (!handle) {perror("dlopen failed");return 1;}double (*cos_func)(double) = dlsym(handle, "cos"); // 获取 cos 函数地址printf("cos(0) = %f\n", cos_func(0)); // 输出: cos(0) = 1.000000dlclose(handle); // 关闭库return 0;
}

3.3 多级指针与函数指针数组

函数指针可以存储在数组中,甚至通过多级指针访问。

例子:函数指针数组

#include <stdio.h>int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }int main() {int (*operations[2])(int, int) = {add, subtract}; // 函数指针数组printf("Add: %d\n", operations[0](10, 5));        // 输出: Add: 15printf("Subtract: %d\n", operations[1](10, 5));   // 输出: Subtract: 5return 0;
}

3.4 嵌套函数指针

函数指针本身可以作为参数或返回值,用于更复杂的逻辑设计。

例子:返回函数指针

#include <stdio.h>int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }// 这里的解释见下文
int (*get_operation(char op))(int, int) {if (op == '+') return add;if (op == '-') return subtract;return NULL;
}int main() {char op = '+';int (*operation)(int, int) = get_operation(op); // 获取函数指针printf("Result: %d\n", operation(10, 5));       // 输出: Result: 15return 0;
}

4. 实际案例:_IO_cleanup_region_start

回到具体的例子:

_IO_cleanup_region_start((void (*) (void *)) &_IO_funlockfile, s);
  • 语法分解

    • (void (*) (void *)):这是类型强制转换,表示将 _IO_funlockfile 转换为 void (*)(void *) 类型的函数指针。
    • _IO_funlockfile:用于解锁流的函数,其原型可能为:
      void _IO_funlockfile(void *);
      
  • 作用

    • _IO_funlockfile 注册为清理函数,并将 s(流指针)作为参数。当 _IO_cleanup_region_end 被调用时,清理函数会被触发,执行流解锁操作。

5. 总结与注意事项
  • 掌握基本规则

    • 自内向外解析复杂的函数指针声明。
    • 使用 typedef 简化复杂的声明。
  • 注意线程安全

    • 多线程环境下,回调函数和动态函数调用需确保数据安全性。
  • 实际应用场景

    • 回调机制:如事件驱动、信号处理。
    • 动态函数调用:如 dlsym
    • 嵌套与多级指针:实现灵活的逻辑控制。
  • 代码示例总结
    通过本文的讲解和示例,高级开发者可以更深入地理解函数指针的高级用法,并在实际项目中灵活应用这一强大工具。

理解 int (*get_operation(char op))(int, int) 的语法

1. 分解声明

int (*get_operation(char op))(int, int) 是一个复杂的函数声明,逐步解析如下:

  1. get_operation(char op)
    表示这是一个函数,函数名为 get_operation,接收一个参数 op,类型为 char

  2. (*get_operation(char op))
    表明 get_operation 返回的是一个指针,而不是一个普通的值。

  3. (*get_operation(char op))(int, int)
    指针所指向的内容是一个函数,这个函数的参数为两个 int 类型,并返回一个 int

  4. 最终含义
    get_operation 是一个返回值为函数指针的函数,这个函数指针指向的函数,接受两个 int 参数,返回一个 int


2. 结合代码理解

我们来看代码:

int (*get_operation(char op))(int, int) {if (op == '+') return add; // 返回指向 add 函数的指针if (op == '-') return subtract; // 返回指向 subtract 函数的指针return NULL; // 无匹配时返回空指针
}
  • addsubtract
    这两个函数的签名为 int add(int a, int b)int subtract(int a, int b),返回一个 int,且参数为两个 int

  • return add
    add 本身是一个函数名,在此处被隐式转换为函数指针,作为 get_operation 的返回值。


3. 等价的 typedef 简化

函数指针的声明较复杂,可以用 typedef 简化:

#include <stdio.h>typedef int (*operation_t)(int, int); // 定义一个函数指针类型int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }operation_t get_operation(char op) { // 返回类型为函数指针类型if (op == '+') return add;if (op == '-') return subtract;return NULL;
}int main() {char op = '+';operation_t operation = get_operation(op); // 获取函数指针printf("Result: %d\n", operation(10, 5));  // 输出: Result: 15return 0;
}

4. 其他类似例子
返回函数指针的函数
#include <stdio.h>int multiply(int a, int b) { return a * b; }
int divide(int a, int b) { return b == 0 ? 0 : a / b; }int (*choose_function(char op))(int, int) {if (op == '*') return multiply;if (op == '/') return divide;return NULL;
}int main() {char op = '*';int (*func)(int, int) = choose_function(op); // 获取函数指针if (func != NULL) {printf("Result: %d\n", func(10, 5)); // 输出: Result: 50} else {printf("No valid function found.\n");}return 0;
}

解释

  1. choose_function(char op)
    函数名为 choose_function,接收一个 char 类型参数 op

  2. int (*choose_function(char op))(int, int)
    表示 choose_function 的返回值是一个指向函数的指针,这个函数接受两个 int 参数并返回一个 int


函数指针嵌套使用

函数指针还可以作为其他函数的参数或返回值。

例子:函数指针作为参数

#include <stdio.h>int calculate(int a, int b, int (*operation)(int, int)) {return operation(a, b);
}int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }int main() {printf("Add: %d\n", calculate(10, 5, add));       // 输出: Add: 15printf("Subtract: %d\n", calculate(10, 5, subtract)); // 输出: Subtract: 5return 0;
}

5. 总结
  1. 复杂函数指针声明的解析方法

    • 从内向外逐层解析。
    • 关注 ()* 的位置区分函数和指针。
  2. typedef 简化复杂声明

    • 使用 typedef 定义函数指针类型,使代码更易读。
  3. 实际场景

    • 返回函数指针常用于动态函数选择、策略模式。
    • 函数指针的灵活性使其在回调、动态库加载等场景非常有用。

通过例子可以看出,理解复杂的函数指针声明关键在于分解其语法,结合实际应用场景能更好地掌握这一强大的工具。

Advanced Use of Function Pointers in C

Function pointers are one of the most powerful features of C, allowing for dynamic function calls, callbacks, and flexible program design. However, complex function pointer declarations can be challenging even for experienced developers. This blog will explore advanced concepts and practical applications of function pointers, with a focus on parsing and utilizing challenging syntax like (void (*) (void *)).


1. What Are Function Pointers?

A function pointer is a pointer that points to a function’s memory address, enabling dynamic function calls during runtime.

Basic Syntax:

return_type (*pointer_name)(parameter_list);

Example:

int add(int a, int b) {return a + b;
}int (*func_ptr)(int, int) = add; // Define a function pointer to `add`
printf("%d\n", func_ptr(3, 5));  // Output: 8

2. Understanding (void (*) (void *))

Let’s break down the syntax (void (*) (void *)) step by step:

  1. void *: A generic pointer that can point to any type.
  2. void (*): A pointer to a function returning void.
  3. void (*) (void *): A pointer to a function that takes a void * as its argument and returns void.

This syntax is used to define or cast a function pointer.

Example:

void my_callback(void *arg) {printf("Callback invoked with arg: %p\n", arg);
}void (*callback)(void *) = my_callback; // Define a function pointer
callback((void *)0x1234);               // Invoke the function pointer, Output: Callback invoked with arg: 0x1234

3. Advanced Use Cases for Function Pointers

3.1 Callback Mechanisms

Callback functions are a common use case for function pointers, allowing a caller to pass a function as an argument to be executed later.

Example: Registering a Callback

#include <stdio.h>typedef void (*Callback)(int); // Define a function pointer typevoid event_handler(int event_id) {printf("Event %d handled!\n", event_id);
}void register_callback(Callback cb) {cb(42); // Call the callback
}int main() {register_callback(event_handler); // Output: Event 42 handled!return 0;
}

3.2 Dynamic Function Calls

Function pointers are essential in scenarios like dynamically loading libraries and calling functions at runtime.

Example: Using dlsym to Call a Function Dynamically

#include <dlfcn.h>
#include <stdio.h>int main() {void *handle = dlopen("libm.so.6", RTLD_LAZY); // Load the math libraryif (!handle) {perror("dlopen failed");return 1;}double (*cos_func)(double) = dlsym(handle, "cos"); // Resolve the `cos` functionprintf("cos(0) = %f\n", cos_func(0)); // Output: cos(0) = 1.000000dlclose(handle); // Close the libraryreturn 0;
}

3.3 Function Pointer Arrays

Function pointers can be stored in arrays to implement flexible dispatch tables or emulate polymorphism.

Example: Function Pointer Arrays

#include <stdio.h>int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }int main() {int (*operations[2])(int, int) = {add, subtract}; // Array of function pointersprintf("Add: %d\n", operations[0](10, 5));       // Output: Add: 15printf("Subtract: %d\n", operations[1](10, 5));  // Output: Subtract: 5return 0;
}

3.4 Nested Function Pointers

Function pointers can be used as return types, enabling the implementation of factory-like patterns.

Example: Returning a Function Pointer

#include <stdio.h>int add(int a, int b) { return a + b; }
int subtract(int a, int b) { return a - b; }int (*get_operation(char op))(int, int) {if (op == '+') return add;if (op == '-') return subtract;return NULL;
}int main() {char op = '+';int (*operation)(int, int) = get_operation(op); // Get a function pointerprintf("Result: %d\n", operation(10, 5));       // Output: Result: 15return 0;
}

4. Real-World Case: _IO_cleanup_region_start

In the context of the GNU C Library, the following code registers a cleanup function for a stream:

_IO_cleanup_region_start((void (*) (void *)) &_IO_funlockfile, s);

Explanation:

  1. Type Casting:

    • _IO_funlockfile is a function, likely declared as:
      void _IO_funlockfile(void *);
      
    • (void (*) (void *)) casts _IO_funlockfile to match the expected function pointer type.
  2. Usage:

    • _IO_cleanup_region_start registers _IO_funlockfile as a cleanup function for the stream s. This ensures that _IO_funlockfile(s) will be called even if the function exits early or encounters an error.

5. Tips for Working with Complex Function Pointers

  1. Break Down the Declaration:

    • Use tools like cdecl to decode complex function pointer syntax.
    • Parse the declaration from the inside out.
  2. Use typedef for Clarity:

    • Simplify complex declarations by using typedef.

    Example:

    typedef void (*Callback)(void *);
    Callback cb = my_callback;
    
  3. Understand Function Pointer Arrays:

    • Arrays of function pointers can emulate polymorphism and simplify dispatch tables.
  4. Dynamic Typing:

    • Combine function pointers with void * for maximum flexibility.

6. Conclusion

Function pointers are a cornerstone of advanced C programming, enabling dynamic function calls, callbacks, and flexible system design. Understanding and mastering their syntax, particularly in complex cases like (void (*) (void *)), empowers developers to write efficient and modular code. By breaking down declarations and exploring real-world examples, you can confidently integrate function pointers into your projects.

后记

2025年1月27日于山东日照。

相关文章:

深入理解 C 语言函数指针的高级用法:(void (*) (void *)) _IO_funlockfile

深入理解 C 语言函数指针的高级用法 函数指针是 C 语言中极具威力的特性&#xff0c;广泛用于实现回调、动态函数调用以及灵活的程序设计。然而&#xff0c;复杂的函数指针声明常常让即使是有经验的开发者也感到困惑。本文将从函数指针的基本概念出发&#xff0c;逐步解析复杂…...

【观察】甲骨文:以AI为中心,开启企业级平台架构“新革命”

知名科技杂志《连线》创始主编凯文凯利曾预测&#xff1a;“在未来的 100 年里&#xff0c;人工智能将超越任何一种人工力量&#xff0c;将人类引领到一个前所未有的时代。” 可以看到&#xff0c;随着近两年AIGC与大模型的快速爆发&#xff0c;AI在千万行业中的重要性愈发突出…...

react native在windows环境搭建并使用脚手架新建工程

截止到2024-1-11&#xff0c;使用的主要软件的版本如下&#xff1a; 软件实体版本react-native0.77.0react18.3.1react-native-community/cli15.0.1Android Studio2022.3.1 Patch3Android SDKAndroid SDK Platform 34 35Android SDKAndroid SDK Tools 34 35Android SDKIntel x…...

C语言从入门到进阶

视频&#xff1a;https://www.bilibili.com/video/BV1Vm4y1r7jY?spm_id_from333.788.player.switch&vd_sourcec988f28ad9af37435316731758625407&p23 //枚举常量 enum Sex{MALE,FEMALE,SECRET };printf("%d\n", MALE);//0 printf("%d\n", FEMALE…...

Python案例--养兔子

兔子繁殖问题是一个经典的数学问题&#xff0c;最早由意大利数学家斐波那契在13世纪提出。这个问题不仅在数学领域具有重要意义&#xff0c;还广泛应用于计算机科学、生物学和经济学等领域。本文将通过一个具体的Python程序&#xff0c;深入探讨兔子繁殖问题的建模和实现&#…...

Mybatis——sql映射文件中的增删查改

映射文件内的增删查改 准备工作 准备一张数据表&#xff0c;用于进行数据库的相关操作。新建maven工程&#xff0c; 导入mysql-connector-java和mybatis依赖。新建一个实体类&#xff0c;类的字段要和数据表的数据对应编写接口编写mybatis主配置文件 public class User {priva…...

goframe 博客分类文章模型文档 主要解决关联

goframe 博客文章模型文档 模型结构 (BlogArticleInfoRes) BlogArticleInfoRes 结构体代表系统中的一篇博客文章&#xff0c;包含完整的元数据和内容管理功能。 type BlogArticleInfoRes struct {Id uint orm:"id,primary" json:"id" …...

人工智能在医疗领域的应用有哪些?

人工智能在医疗领域的应用十分广泛&#xff0c;涵盖了诊断、治疗、药物研发等多个环节&#xff0c;以下是一些主要的应用&#xff1a; 医疗影像诊断 疾病识别&#xff1a;通过分析 X 光、CT、MRI 等影像&#xff0c;人工智能算法能够识别出肿瘤、结节、骨折等病变&#xff0c;…...

学习第七十六行

提高github下载速度方法 1.github转码云 2.https://github.com.cnpmjs.org com后面加东西 对于面试笔试&#xff0c;最好方法刷力扣&#xff0c;1000题包进大厂的...

C#System.Threading.Timer定时器意外回收注意事项

System.Threading.Timer定时器使用时会出现意外回收的情况。具体解释如下: 只要在使用 Timer,就必须保留对它的引用。对于任何托管对象,如果没有对 Timer 的引用,计时器会被垃圾回收。即使 Timer 仍处在活动状态,也会被回收。 实例对比测试 实例 定义两个类,其中一个…...

若依基本使用及改造记录

若依框架想必大家都了解得不少&#xff0c;不可否认这是一款及其简便易用的框架。 在某种情况下&#xff08;比如私活&#xff09;使用起来可谓是快得一匹。 在这里小兵结合自身实际使用情况&#xff0c;记录一下我对若依框架的使用和改造情况。 一、源码下载 前往码云进行…...

Java基础教程(007):方法的重载与方法的练习

文章目录 6.5 方法的重载6.6 方法练习数组遍历数组最大值 6.5 方法的重载 在 Java 中&#xff0c;方法的重载是指在同一个类中定义多个方法&#xff0c;这些方法具有相同的名称&#xff0c;但参数列表不同。方法的重载是一种实现多态的方式&#xff0c;允许一个方法名以不同的…...

Day27-【13003】短文,线性表两种基本实现方式空间效率、时间效率比较?兼顾优点的静态链表是什么?如何融入空闲单元链表来解决问题?

文章目录 本次内容总览第四节&#xff0c;两种基本实现方式概览两种基本实现方式的比较元素个数n大于多少时&#xff0c;使用顺序表存储的空间效率才会更高&#xff1f;时间效率比较&#xff1f;*、访问操作&#xff0c;也就是读运算&#xff0c;读操作1、插入&#xff0c;2、删…...

Linux 小火车

1.添加epel软件源 2.安装sl 3. 安装完成后输入&#xff1a; sl...

论文笔记(六十三)Understanding Diffusion Models: A Unified Perspective(四)

Understanding Diffusion Models: A Unified Perspective&#xff08;四&#xff09; 文章概括学习扩散噪声参数&#xff08;Learning Diffusion Noise Parameters&#xff09;三种等效的解释&#xff08;Three Equivalent Interpretations&#xff09; 文章概括 引用&#xf…...

python 统计相同像素值个数

目录 python 统计相同像素值个数 最大值附近的值 python 统计相同像素值个数 import cv2 import numpy as np import time from collections import Counter# 读取图像 image cv2.imread(mask16.jpg)# 将图像转换为灰度图像 gray_image cv2.cvtColor(image, cv2.COLOR_BGR2…...

YOLOv8:目标检测与实时应用的前沿探索

随着深度学习和计算机视觉技术的迅速发展&#xff0c;目标检测&#xff08;Object Detection&#xff09;一直是研究热点。YOLO&#xff08;You Only Look Once&#xff09;系列模型作为业界广受关注的目标检测框架&#xff0c;凭借其高效、实时的特点&#xff0c;一直迭代更新…...

docker配置mysql并使用mysql connector cpp编程

mysql 配置mysql使用docker 这里使用docker安装了&#xff0c;比较简洁&#xff0c;不想使用了直接就可以把容器删掉&#xff0c;首先获取下镜像&#xff0c;如下命令 docker pull container-registry.oracle.com/mysql/community-server这里直接默认使用最新版本的mysql了 …...

go理论知识——Go Channel 笔记 [特殊字符]

go理论知识——Go Channel 笔记 &#x1f4dd; 1. 基本概念 &#x1f9e0; 1.1 Channel 是什么&#xff1f; Channel 是 Go 语言中用于在不同 Goroutine 之间进行通信的机制。Channel 是类型安全的&#xff0c;意味着你只能发送和接收特定类型的数据。 1.2 Channel 的创建 …...

论文阅读笔记:MambaOut: Do We Really Need Mamba for Vision?

论文阅读笔记&#xff1a;MambaOut: Do We Really Need Mamba for Vision? 1 背景2 创新点3 方法4 模块4.1 Mamba适合什么任务4.2 视觉识别任务是否有很长的序列4.3 视觉任务是否需要因果token混合模式4.4 关于Mamba对于视觉的必要性假设 5 效果 论文&#xff1a;https://arxi…...

games101-(3/4)变换

缩放&#xff1a; 对称 切变 旋转 考虑&#xff08;1.0&#xff09;这个点 同理考虑&#xff08;0&#xff0c;1&#xff09;点即可 齐次方程 考虑在二维的坐标点后面增加一个维度 所有的仿射变换都可以写成齐次坐标的形式 a b c d 是线性变换 tx ty 是平移&#xff1b; …...

【Linux】磁盘

没有被打开的文件 文件在磁盘中的存储 认识磁盘 磁盘的存储构成 磁盘的效率 与磁头运动频率有关。 磁盘的逻辑结构 把一面展开成线性。 通过扇区的下标编号可以推算出在磁盘的位置。 磁盘的寄存器 控制寄存器&#xff1a;负责告诉磁盘是读还是写。 数据寄存器&#xff1a;给…...

ElasticSearch-文档元数据乐观并发控制

文章目录 什么是文档&#xff1f;文档元数据文档的部分更新Update 乐观并发控制 最近日常工作开发过程中使用到了 ES&#xff0c;最近在检索资料的时候翻阅到了 ES 的官方文档&#xff0c;里面对 ES 的基础与案例进行了通俗易懂的解释&#xff0c;读下来也有不少收获&#xff0…...

海浪波高预测(背景调研)

#新星杯14天创作挑战营第7期# ps&#xff1a;图片由通义千问生成 历史工作&#xff1a; 针对更高细粒度、更高精度的波浪高度预测任务&#xff1a; Mumtaz Ali 等人提出了一种多元线性回归模型&#xff08;MLR-CWLS&#xff09;&#xff0c;该模型利用协方差加权最小二乘法&a…...

景联文科技加入AIIA联盟数据标注分委会

2025年1月16日&#xff0c;中国人工智能产业发展联盟&#xff08;简称AIIA&#xff09;数据委员会数据标注分委会&#xff08;以下简称“分委会”&#xff09;正式成立。景联文科技成为第一批AIIA联盟数据标注分委会委员单位。 数据标注分委会的成立旨在搭建数据标注领域产学研…...

【Healpix】python一种用于将球面划分为均匀区域的技术

Healpix 1、简介2、Healpix的基本原理3、Healpix的优点4、安装及使用4.1 安装healpy4.2 创建Healpix地图4.3 读取和写入Healpix数据4.4 数据插值 5、案例5.1 案例一&#xff1a;宇宙微波背景辐射&#xff08;CMB&#xff09;分析5.2 案例二&#xff1a;星系分布分析5.3 案例三&…...

chrome源码剖析—进程通信

Chrome 浏览器采用多进程架构&#xff08;multi-process architecture&#xff09;&#xff0c;这种架构使得每个浏览器标签、扩展、插件、GPU 渲染等都在独立的进程中运行。为了确保不同进程之间的高效通信&#xff0c;Chrome 使用 进程间通信&#xff08;IPC, Inter-Process …...

自定义数据集 使用tensorflow框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测

在 TensorFlow 中实现逻辑回归、保存模型并加载模型进行预测的过程可以分为以下几个步骤&#xff1a; 准备数据&#xff1a;创建或加载你的自定义数据集。构建逻辑回归模型。训练模型。保存模型。加载模型。使用加载的模型进行预测。 import tensorflow as tf import numpy as…...

antdesignvue统计数据源条数、计算某列合计值、小数计算不精确多了很多小数位

1.在</a-table>下方加如下代码 <div>数据总条数&#xff1a;{ {tableData.length}}&nbsp&nbsp&nbsp&nbsp&nbsp&nbsp <template>A列合计&#xff1a;{ {sum}}</template> </div> 注&#xff1a;tableData为<a-tabl…...

【MySQL】--- 复合查询 内外连接

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; MySQL &#x1f3e0; 基本查询回顾 假设有以下表结构&#xff1a; 查询工资高于500或岗位为MANAGER的雇员&#xff0c;同时还要满足他们的姓名首字母为…...