gpt4 book ai didi

c - 使用变量声明/定义实现弱链接是否可移植?

转载 作者:行者123 更新时间:2023-12-01 13:18:04 28 4
gpt4 key购买 nike

我最近了解到:

int a;

在文件范围内是一个变量声明,默认情况下具有外部链接。
所以我可以用它来实现弱符号/函数链接之类的东西:
cat >lib.c <<'EOF'
#include "lib.h"
#include <stdio.h>
#include <stdint.h>

// This is declaration
// It will be initialized to NULL in case no definition is found
void (* const lib_callback_pnt)(int);

void lib_callback_default(int a)
{
printf("%s %d\n", __func__, a);
}

void lib_call(int a)
{
printf("%s calling %p\n", __func__,
// this is not really portable
(void*)(uintptr_t)(intmax_t)lib_callback_pnt
);
// call callback
void (* const lib_callback_to_call)(int) =
lib_callback_pnt == NULL
? lib_callback_default
: lib_callback_pnt;
lib_callback_to_call(a);
}

EOF
cat >lib.h <<'EOF'
#ifndef LIB_H_
#define LIB_H_

extern void (* const lib_callback_pnt)(int);

void lib_callback_default(int a);
void lib_call(int a);

#endif
EOF
cat >main1.c <<EOF
#include "lib.h"

int main() {
lib_call(42);
}

EOF
cat >main2.c <<'EOF'
#include "lib.h"
#include <stdio.h>

static void my_lib_callback(int a)
{
printf("Hah! Overwritten lib callback!\n");
}

// this is definition
void (* const lib_callback_pnt)(int) = my_lib_callback;

int main() {
lib_call(42);
}

EOF
cat >Makefile <<'EOF'
CC=gcc
CFLAGS=-Wall -Wextra -pedantic -std=c11
all:
$(CC) $(CFLAGS) lib.c main1.c -o main1
$(CC) $(CFLAGS) lib.c main2.c -o main2
EOF

内部 lib.c库我声明了一个函数指针 void (* const lib_callback_pnt)(int) ,它用作回调。函数指针未在 lib.c 中初始化并将默认初始化为 NULL(导致静态存储持续时间)。

然后我有两个程序或用户应用程序,即 main1.cmain2.c .
main1.c只需调用调用回调的库函数 - 回调未初始化,因此默认初始化为 NULL - 我可以在库内部进行比较并适本地调用默认回调/选择操作。
main2.c然而声明函数指针 lib_callback_pnt带初始化 - 这是一个定义。所有源文件中都有这个变量的单一定义,因此链接器不会提示多个符号定义。当我们调用库时,指针被初始化,因此用户应用程序 main2已成功覆盖回调。

我们可以编译:
$ make
gcc -Wall -Wextra -pedantic -std=c11 lib.c main1.c -o main1
gcc -Wall -Wextra -pedantic -std=c11 lib.c main2.c -o main2

并调用:
$ ./main1 
lib_call calling (nil)
lib_callback_default 42
$ ./main2
lib_call calling 0x5627c07871cf
Hah! overwritten lib callback!

问题:
  • 这是便携的吗?这符合C标准吗?
  • 是否有/哪些库使用这种方法使用户应用程序能够传递用户定义的回调和/或参数?为什么不经常使用这种方法?
  • 最佳答案

    你的 lib.c 实际上确实定义了 lib_callback_pnt . C11 在 6.9.2p2 上说:

    A declaration of an identifier for an object that has file scope without an initializer, and without a storage-class specifier or with the storage-class specifier static, constitutes a tentative definition. If a translation unit contains one or more tentative definitions for an identifier, and the translation unit contains no external definition for that identifier, then the behavior is exactly as if the translation unit contains a file scope declaration of that identifier, with the composite type as of the end of the translation unit, with an initializer equal to 0.



    所以 lib_callback_pnt的声明在 lib.c 中是一个暂定定义。由于该翻译单元不包含 lib_callback_pnt 的任何其他声明明确定义它的行为应该与使用“ = 0”初始化它的真实定义相同。

    显然,默认情况下带有 ELF 输出的 gcc 并不完全遵循此要求。
    在我的 Linux 系统上,如果我 gcc -c lib.c; nm lib.o | grep lib_callback_pnt ,我得到:
    0000000000000008 C lib_callback_pnt

    还有我的 man nm文档解释“C”表示“通用符号”:

    "C" The symbol is common. Common symbols are uninitialized data. When linking, multiple common symbols may appear with the same name. If the symbol is defined anywhere, the common symbols are treated as undefined references.



    所以你的方法确实适用于 gcc/ELF,但根据 C 标准它是不正确的,所以你不能指望它与其他编译器一起使用。

    关于c - 使用变量声明/定义实现弱链接是否可移植?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52738218/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com