C语言 可以从指针数组中的值初始化指针吗?

cbwuti44  于 2023-10-16  发布在  其他
关注(0)|答案(2)|浏览(126)

我基本上想有一个函数列表作为一个函数指针数组;然后我想管理一个索引变量到这个数组中,和一个指针变量,它接受这个数组中的值。考虑下面的C代码来说明这一点,它编译得很好:

#include <stdio.h>

void func_01() {
    printf("func_01\n");
}
void func_02() {
    printf("func_02\n");
}
void func_03() {
    printf("func_02\n");
}

enum func_choice_e {
    eFUNC01 = 0,
    eFUNC02 = 1,
    eFUNC03 = 2,
#define _NUM_FUNC_CHOICE 3
    NUM_FUNC_CHOICE = _NUM_FUNC_CHOICE,
};
typedef enum func_choice_e func_choice_t;

void* funcs_ptrs[_NUM_FUNC_CHOICE] = {
    &func_01,
    &func_02,
    &func_03,
};

func_choice_t my_func_choice_idx = eFUNC01;
void* my_func_choice_ptr = &func_01;

int main()
{
    my_func_choice_idx = eFUNC02;
    my_func_choice_ptr = funcs_ptrs[my_func_choice_idx];
    printf("Hello World %p\n", my_func_choice_ptr);

    return 0;
}

如上面的代码所示,在代码中,我可以通过索引变量(my_func_choice_ptr = funcs_ptrs[my_func_choice_idx];)设置指针变量。
然而,我想做一些类似的事情,也是为了初始化-最终,我想先手动初始化数组值和my_func_choice_idx-然后让指针自动初始化到数组中对应的第my_func_choice_idx个元素;因此,代替工作指针初始化器:

void* my_func_choice_ptr = &func_01;

我试过这些:

//void* my_func_choice_ptr = funcs_ptrs[my_func_choice_idx]; // error: initializer element is not constant
//void* my_func_choice_ptr = funcs_ptrs[eFUNC01]; // error: initializer element is not constant
//void* my_func_choice_ptr = funcs_ptrs[0]; // error: initializer element is not constant

.正如评论中的错误消息所暗示的那样,这些都不起作用。
我有点明白为什么funcs_ptrs[my_func_choice_idx]可能会失败-my_func_choice_idx毕竟是一个变量-但是当编译器到达my_func_choice_ptr = funcs_ptrs[0];时,它应该已经“知道”funcs_ptrs的地址,大小和初始值->所以我不明白为什么它不能将my_func_choice_ptr设置为funcs_ptrs的第一个条目?!
那么,是否有一些语法可以用于初始化,这将允许我将指针变量 * 初始化 * 为数组中某个条目的给定初始值(类似于在正常代码中工作的赋值my_func_choice_ptr = funcs_ptrs[my_func_choice_idx];)?

eyh26e7m

eyh26e7m1#

你声明的函数指针是错误的。函数指针不是void*,但需要有正确的签名:

typedef void(*func_ptr)(void);

你可以这样使用它:

const func_ptr funcs_ptrs[_NUM_FUNC_CHOICE] = {
    &func_01,
    &func_02,
    &func_03,
};

func_choice_t my_func_choice_idx = eFUNC01;
func_ptr my_func_choice_ptr = funcs_ptrs[eFUNC01];

如果你想使用my_func_choice_idx初始化它,你不能全局地这样做,因为它不是一个常量,但你可以在main中这样做:

int main(void) {
    func_ptr my_func_choice_ptr = funcs_ptrs[my_func_choice_idx];
}
hjqgdpho

hjqgdpho2#

void*是泛型 object 指针类型,可以在处理指向对象的指针时使用,即:指向变量的指针(或指向其他指针的指针)。

  • function* 指针不存在这样的类型,void*与函数指针 * 不 * 兼容。

在处理函数指针的最佳实践中,压倒性的强烈建议是 * 始终 * 使用typedef。存在两种“学校”/风格:

typedef void (*func_t) (void);  // define a function pointer type
...
func_t fptr; // function pointer

typedef void func_t (void); // define a function type
...
func_t* fptr; // function pointer

我推荐第二种风格,因为它更具可读性,并且使函数指针的使用与对象指针一致。通常,将指针隐藏在typedef后面也是不好的做法。如果我们不把指针隐藏在typedef下,它也会更容易把“常量指针”和“指向常量的指针”分开。
Other things/Misc code review:

  • void func_01()是一种老式格式,目前不推荐使用。它通常没有关系 * 除了 * 当处理函数指针时-正确的格式是void func_01(void)。否则,空括号意味着“函数接受任何参数”,这不是你想要的(特别是C和C++在这里是不同的)。即将到来的C23将最终解决这个问题,但对于旧的/现有的代码库,始终使用显式的(void)
  • 枚举是可疑的。首先,如果你不给枚举常量赋值,那么它们 * 保证 * 被枚举为0,1,2,3等。当你赋值时,列表中的下一个常量会被隐式地设置为前一个+ 1。当使用技巧来计算列表中可用枚举常量的数量时,0,1,2,3.我们使用它来让enum中的最后一个项目自动分配,从而表示可用的enum项目的数量。让编译器自动赋值会使代码更易于维护。

此外,typedef枚举可以简化,但这是一个风格的评论。同样,枚举标记在大多数时候也不是很有用。

  • 当声明一个对应于枚举项的函数指针数组时,我们可以使用枚举列表中的最后一项来检查数组和枚举之间的完整性,确保它们具有1对1的对应关系。建议使用_Static_assert
  • 函数指针数组几乎肯定应该是只读的,除非你有非常好的理由在运行时给它们赋值。通常不是这样的。
  • 给你个小提示:在函数内部,可以使用C特性__func__以字符串文字的形式获取函数名。因此,printf("func_01\n");可以重写为puts(__func__);,以减少代码重复一点点。
  • %p实际上并没有为函数指针定义好。如果你想打印他们的地址,最好先转换为uintptr_t

在修复了以上所有问题之后,代码可能看起来像这样:

#include <stdio.h>
#include <stdint.h>

void func_01(void) {
    puts(__func__);
}
void func_02(void) {
    puts(__func__);
}
void func_03(void) {
    puts(__func__);
}

typedef enum  {
    eFUNC01,
    eFUNC02,
    eFUNC03,
    NUM_FUNC_CHOICE,
} func_choice_t;

typedef void func_t (void);

// note const, the pointers themselves are read-only:
func_t* const funcs_ptrs[] = { // leave size blank here
    &func_01,
    &func_02,
    &func_03,
};

// ensure integrity of enum vs array:
_Static_assert(sizeof(funcs_ptrs)/sizeof *funcs_ptrs == NUM_FUNC_CHOICE,
               "func_choice_t doesn't match funcs_ptrs");

int main (void)
{
    func_choice_t my_func_choice_idx = eFUNC01;
    func_t* my_func_choice_ptr = &func_01;

    my_func_choice_idx = eFUNC02;
    my_func_choice_ptr = funcs_ptrs[my_func_choice_idx];
    printf("Hello World %lu\n", (uintptr_t)my_func_choice_ptr);
    my_func_choice_ptr(); // should print func_02

    return 0;
}

相关问题