繁体   English   中英

将const char *传递给需要char *的外部库中的函数

[英]Passing const char* to function from external library that takes a char*

考虑一下我在动态外部库libExternal.dylib具有以下功能:

void print(char* str)
{
    // Changes the first char to 'a' and prints the string
    *str = 'a';
    printf("%s\n", str);
}

接下来,我有一个可执行文件,可加载此外部库并调用函数(省略了错误检查):

int main(int argc, const char * argv[])
{
    void* hLib = dlopen("libExternal.dylib", RTLD_LAZY | RTLD_LOCAL);

    typedef void(*printFunc)(const char*);
    printFunc func = (printFunc)dlsym(hLib, "print");

    std::string test = "hello";
    func(test.c_str());

    dlclose(hLib);

    return 0;
}

如您所见,库中定义的函数将char*作为参数。 使用dlsym ,我获得了一个采用const char*const char* 而且有效!

我的问题是,这怎么可能? 动态加载器会忽略const类型吗? 我真的在任何地方都找不到答案,所以请帮助我! :)

编辑:我知道此代码是错误的,我只是想了解这怎么可能。

它有效,但这并不意味着它是正确的。

它不会忽略const类型,您正在将外部函数转换为接受const类型的函数:

typedef void(*printFunc)(const char*);
                         ^^^^^^^^^^^

printFunc func = (printFunc)dlsym(hLib, "print");
                 ^^^^^^^^^^^

并尝试使用正确的函数签名来避免由于修改const值而导致的未定义行为

const char *传递给foo(char *str)是未定义的行为(UB)。

它可以工作,可能不行。 它肯定不能在阻止写入const内存的系统上工作。 其他系统宽松,后续操作可能/可能无法按预期工作。

C11草案6.7.3。 6

考虑以下代码:

#include <stdio.h>
int
main(int argc, char **argv)
{
        char a[] = "foo";
        const char *b = a;
        char *c = (char *)b;
        *c = 'a';
        printf("%s\n", b);
        return 0;
}

这在很大程度上相当于您在内部所做的事情。 理论上char *c = (char *)b; *c = 'a'; char *c = (char *)b; *c = 'a'; 是非法的,但实际上在这种情况下它确实起作用。

const视为API的编写者与该API的用户之间的一种契约,而不是由编译器和运行时严格执行的契约。 存在const的第二个原因是,它允许将字符串文字放入程序中的只读段中,以进行许多有用的优化(例如重复数据删除字符串),但是我认为const主要只是为了提醒程序员。

这就是为什么在大型项目中,我看到将const添加到函数参数有时被称为“ const毒害”的原因。 您毒害了在许多不同的API层之间传递的字符串或结构,以保证不会在这些层中的任何地方对其进行修改。 本身添加const的行为对于发现意外修改发生的位置非常有价值。 您总是可以很轻松地摆脱const并破坏合同,但是如果不这样做,您将更易于阅读和调试程序。 我什至已经做了一些实验,并且编译器没有进行优化,如果编译器期望const被尊重,那优化就不会合理地进行。

有时甚至出于完全正当的理由甚至有必要放弃const。 例如,当您有这样的事情:

struct foo {
    const char *string;
    int dynamic;
};
void
foo_dynamic(struct foo *f, int x)
{
    f->dynamic = 1;
    f->string = malloc(16);
    snprintf(&s->string, 16, "%d", x);
}
void
foo_static(struct foo *f, const char *x)
{
    f->dynamic = 0;
    f->string = x;
}
void
foo_free(struct foo *f)
{
    if (f->dynamic)
        free((void *)f->string);
    free(f);
}

在这种情况下,我们在API中承诺不会在foo的生存期内更改foo->string指向的内容,但是如果我们自己分配它,我们仍然需要能够释放它。 语言 原教旨主义者的 律师可能会说这是未定义的行为(确实如此),并且有解决方案来实现同一件事(有),但这在实践中非常普遍。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM