Предположим, у меня есть следующая функция в динамической внешней библиотеке 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*
, И это работает!
У меня вопрос, как это возможно? Динамический загрузчик игнорирует константные типы? Я действительно нигде не мог найти ответ, поэтому, пожалуйста, помогите мне! 🙂
РЕДАКТИРОВАТЬ:
Я знаю, что этот код неправильный, я просто пытаюсь понять, как это возможно.
Это работает, но это не значит, что это правильно.
Он не игнорирует константные типы, вы приводите внешнюю функцию к функции, которая принимает константные типы:
typedef void(*printFunc)(const char*);
^^^^^^^^^^^
printFunc func = (printFunc)dlsym(hLib, "print");
^^^^^^^^^^^
И попробуйте использовать правильную функцию подписи, чтобы избежать неопределенное поведение из-за изменения значения const.
Переходя const char *
в foo(char *str)
является неопределенным поведением (UB).
Это может сработать, а может и нет. Это, конечно, не будет работать в системах, которые мешают записи в const
объем памяти. Другие системы снисходительны, и последующие операции могут / не могут работать должным образом.
С11 проект 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';
Это незаконно, но на практике это работает в данном конкретном случае.
Увидеть 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->string
указывает на в течение жизни foo
, но мы все еще должны быть в состоянии освободить это, если мы выделили это сами. Язык фундаменталистАдвокат может сказать, что это неопределенное поведение (оно есть), и есть решения для достижения того же (есть), но это довольно распространено на практике.