简体   繁体   English

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

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

Consider I have the following function in a dynamic external library libExternal.dylib : 考虑一下我在动态外部库libExternal.dylib具有以下功能:

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

Next, I have an executable that loads this external library and calls the function (error checking omitted): 接下来,我有一个可执行文件,可加载此外部库并调用函数(省略了错误检查):

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;
}

As you can see, the function defined in the library takes a char* as parameter. 如您所见,库中定义的函数将char*作为参数。 When using dlsym , I made it get a function that takes a const char* . 使用dlsym ,我获得了一个采用const char*const char* And it works! 而且有效!

My question is, how is this possible? 我的问题是,这怎么可能? The dynamic loader ignores const types? 动态加载器会忽略const类型吗? I really couldn't find an answer anywhere, so please help me! 我真的在任何地方都找不到答案,所以请帮助我! :) :)

EDIT: I know this code is wrong, I'm just trying to understand how is this possible. 编辑:我知道此代码是错误的,我只是想了解这怎么可能。

It works but it doesn't mean it's correct. 它有效,但这并不意味着它是正确的。

It doesn't ignore const types, you're casting the external function to a function which accepts const types: 它不会忽略const类型,您正在将外部函数转换为接受const类型的函数:

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

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

And try to use right function signature to avoid undefined behavior due to modifying a const value. 并尝试使用正确的函数签名来避免由于修改const值而导致的未定义行为

Passing const char * to foo(char *str) is undefined behavior (UB). const char *传递给foo(char *str)是未定义的行为(UB)。

It could work, it might not. 它可以工作,可能不行。 It certainly will not work on systems that prevent writing to const memory. 它肯定不能在阻止写入const内存的系统上工作。 Other systems are lenient and subsequent operations may / may not work as expected. 其他系统宽松,后续操作可能/可能无法按预期工作。

C11 draft 6.7.3. C11草案6.7.3。 6 6

Consider this code: 考虑以下代码:

#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;
}

That's on a high level equivalent to what you're doing internally. 这在很大程度上相当于您在内部所做的事情。 Theoretically char *c = (char *)b; *c = 'a'; 理论上char *c = (char *)b; *c = 'a'; char *c = (char *)b; *c = 'a'; is illegal, but in practice it happens to work in this particular case. 是非法的,但实际上在这种情况下它确实起作用。

See const as a kind of contract between the writer of the API and the user of that API rather than something that is strictly enforced by the compiler and runtime. const视为API的编写者与该API的用户之间的一种契约,而不是由编译器和运行时严格执行的契约。 A secondary reason for const to exist is that it allows string literals to be put into read-only segments in programs opening up for many useful optimizations (like deduplication of strings), but I'd argue that primarily const is a just reminder for the programmer. 存在const的第二个原因是,它允许将字符串文字放入程序中的只读段中,以进行许多有用的优化(例如重复数据删除字符串),但是我认为const主要只是为了提醒程序员。

This is why in large projects I've seen adding const to function arguments is sometimes called "const poisoning". 这就是为什么在大型项目中,我看到将const添加到函数参数有时被称为“ const毒害”的原因。 You poison some string or struct that is being passed around many different layers of APIs to make a promise that it isn't modified anywhere through the layers. 您毒害了在许多不同的API层之间传递的字符串或结构,以保证不会在这些层中的任何地方对其进行修改。 The act of adding const by itself is very valuable to discover where unintended modifications happen. 本身添加const的行为对于发现意外修改发生的位置非常有价值。 You can always get rid of the const quite easily and break the contract, but by not doing that you make it easier to read and debug your program. 您总是可以很轻松地摆脱const并破坏合同,但是如果不这样做,您将更易于阅读和调试程序。 I've even done some experiments and compilers don't do optimizations that would be reasonable to do if the compiler expected const to be respected. 我什至已经做了一些实验,并且编译器没有进行优化,如果编译器期望const被尊重,那优化就不会合理地进行。

Sometimes it's even necessary to cast away const for completely legitimate reasons. 有时甚至出于完全正当的理由甚至有必要放弃const。 For example when you have something like this: 例如,当您有这样的事情:

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);
}

In this case we make the promise in our API that we won't change the contents of what foo->string points to during the life time of foo , but we still need to be able to free it if we allocated it ourselves. 在这种情况下,我们在API中承诺不会在foo的生存期内更改foo->string指向的内容,但是如果我们自己分配它,我们仍然需要能够释放它。 A language fundamentalist lawyer might say that this is undefined behavior (it is) and there are solutions to achieve the same thing (there are), but this is pretty common in practice. 语言 原教旨主义者的 律师可能会说这是未定义的行为(确实如此),并且有解决方案来实现同一件事(有),但这在实践中非常普遍。

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

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