我正在从另一个人那里接管代码。我发现了一种对我来说似乎很奇怪的编码模式。这是针对嵌入式 32 位 PIC 处理器(如果相关的话)。
它们在头文件中有一个自定义 typedef,类似于以下内容:
typedef struct {
custom_object obj;
char** list;
uint8_t length;
//other variables here
} custom_list;
但是每当将此 custom_list 类型传递给函数时,都会像这样调用:
void custom_function(void* obj) {
custom_list *list = (custom_list*) obj;
//miscellaneous code here...
}
他们将变量作为指向 void 的指针传递,然后将其重新转换为指向函数内的 custom_list 的指针。
为什么不像这样直接将类型指针传递给 custom_list 呢?
void custom_function(custom_list* list) {
//miscellaneous code here...
}
将其作为指向 void 的指针传递然后重新转换它不是额外的步骤吗?这不是一个坏习惯吗……他们这样做只是为了解决编译器检查器错误吗?或者我错过了什么,第一种方法有什么优势或必要吗?
造成这种情况的原因可能有多种。例如,该函数可以用作某个地方另一个更通用函数的参数。
这里是一个简单的例子,不完全是实用的C代码,但是很容易理解。这是映射函数的简单实现,将给定的函数应用于数组中的每个元素。
#include <stdio.h>
#include <ctype.h>
void map(int len, void** array, void (*operator)(void*)) {
for (int i = 0; i < len; i++)
operator(array[i]);
}
void caesar_encode(void* val) {
char* str = val;
for (int i = 0; str[i]; i++) {
if (!isalpha(str[i]))
continue;
char offset = islower(str[i]) ? 'a' : 'A';
str[i] = (str[i] - offset + 4) % 26 + offset;
}
}
void main(int argc, char** argv) {
map(argc - 1, (void**) argv + 1, caesar_encode);
for (int i = 1; i < argc; i++)
printf("%s ", argv[i]);
printf("\n");
}
该程序将凯撒编码应用于传递给该程序的参数中的每个字母字符。
类似的一个实际示例是pthread_create,用于创建新的线程对象。
pthread_create(pthread_t *restrict thread, const pthread_attr_t *restrict attr, void *(*start_routine)(void *), void *restrict arg)
线程启动的函数必须具有签名
void *(*start_routine)(void *)
,允许您传递任何您喜欢的数据(例如打包在结构中)。
另一个原因可能是编写代码的人不喜欢不透明的指针类型。标题可能看起来像这样:
...
typedef struct custom_list* CustomList;
CustomList create_custom_list();
void destroy_custom_list(CustomList);
...
因此,也许编写代码的人只是不喜欢不透明的指针类型并且不习惯它们,或者代码来自它们出现之前的时代。我现在只是猜测。