Передача char* в качестве параметра динамически вызываемой функции дает ошибку времени выполнения

У меня есть функция с именем testdynamic, которая вызывается динамически с помощью dlopen и dlsym. Теперь я создал структуру:

typedef struct BigStruct{
    char el[2000];
}BigStruct;

который используется для хранения параметров функции. Затем я выделяю место для переменной с именем:

void *cur = (void*)malloc(totalSize);

где totalSize — размер параметров. У меня есть эта информация заранее.

После этого копирую все параметры в cur.

Затем я передал его в BigStruct следующим образом:

BigStruct *bg;
bg = (BigStruct*)cur;

И запустить его так:

void* ch = (void*)testdynamic(*bg);

Теперь в функции testdynamic, когда я печатаю параметры, я получаю правильные значения для всех типов данных, таких как char**, int*, int и т. д.

Единственный тип данных, который не работает, это char*. Даже до вызова функции с *bg содержимое bg->el корректно даже для char*. Но, после звонка возникает ошибка.

В чем может быть проблема?

Вот код testdynamic

char* testdynamic(char* x, char* y){
    printf("%s %s\n", x, y);
    return "hello";
}

Я хочу передать параметры функции testdynamic из своего кода.
Эта testdynamic может быть любой функцией, которая может принимать любой параметр любого типа.
Я получаю информацию о функции во время выполнения. Поскольку размер char* равен 1, я привожу все к char*, а затем передаю его функции.
В этот момент я получаю ошибку времени выполнения, если печатаю внутри testdynamic что-либо типа char*.


person Rishi    schedule 05.08.2013    source источник
comment
Больше информации, пожалуйста!   -  person Nik Bougalis    schedule 05.08.2013
comment
Какая дополнительная информация необходима? Я отредактирую свой вопрос.   -  person Rishi    schedule 05.08.2013
comment
Какую ошибку вы получаете? Вы пробовали запускать код под отладчиком? Как вы печатаете параметры? Как выглядит объявление testdynamic? Как вы запихиваете туда аргументы? Почему вы передаете COPY BigStruct в стеке testdynamic? Как вы распаковываете аргументы внутри testdynamic? Что делать с этими аргументами? И Т. Д.   -  person Nik Bougalis    schedule 05.08.2013
comment
Приведение типов не требуется, так как все преобразования в void * или из него автоматически преобразуются в соответствующий тип.   -  person mohit    schedule 05.08.2013
comment
1) Дан пример неудачного вывода. 2) Поскольку *bg является хорошим до testdynamic(*bg) и плохим после него, укажите код testdynamic().   -  person chux - Reinstate Monica    schedule 05.08.2013
comment
Вам действительно нужно, чтобы мы сказали вам, что случилось? Вы вызываете функцию, которая принимает два указателя, и вместо двух указателей вы вставляете в нее 2000-байтовый буфер; это похоже на то, что вы пытаетесь вставить очень большой квадратный колышек в два маленьких круглых отверстия одновременно. Как это вообще компилируется?!?   -  person Nik Bougalis    schedule 05.08.2013


Ответы (6)


Вы предполагаете, что BigStruct выглядит точно так же, как массив из 2000 символов. Это зависит от компилятора и вряд ли будет правдой. Я предполагаю, что вы действительно хотите скопировать данные, на которые указывает cur, в массив el, а не записывать их по всему BigStruct, который будет иметь какой-то внутренний формат хранения, который вы не можете знать.

person David Elliman    schedule 05.08.2013
comment
Поскольку sizeof char* равен 1, поэтому я привожу тип к BigStruct. Он работает для всех других типов. - person Rishi; 05.08.2013
comment
Даже если это иногда срабатывает, Риши, это не способ написания кода, который копирует содержимое в структуры. Он делает слишком много предположений о том, как компилятор их хранит. Он компилируется только потому, что вы используете указатели void * и будете подвержены всевозможным ошибкам перезаписи памяти. - person David Elliman; 05.08.2013
comment
То, что это работает некоторое время, не означает, что это правильно. Употребление сырого мяса тоже хорошо работает... кроме тех случаев, когда вы едите курицу и случаются очень плохие вещи. - person Nik Bougalis; 05.08.2013

Если totalSize > sizeof(BigStruct), у вас есть проблемы, потому что вы не передаете полную копию своих данных в testdynamic(), вероятно, искажаете копию и, следовательно, неопределенное поведение.

Если totalSize < sizeof(BigStruct), у вас проблемы с чтением области памяти, которой вы не владеете, когда вы передаете *bg в testdynamic() - отсюда неопределенное поведение.

Вы намного безопаснее просто с

bg = malloc(*bg);

Здесь также происходят другие сомнительные проблемы с программой, но потребуется более полная публикация.

person chux - Reinstate Monica    schedule 05.08.2013

Почитав еще немного....

вы передаете один параметр для функции, которая требует 2 параметра.

Используете ли вы -Wall для предупреждения об ошибках во время компиляции? Второй параметр не передается, т. е. аргумент y равен нулю. Доступ к null вызовет проблему.

person Tony    schedule 05.08.2013

Итак, чтобы обобщить то, что я понимаю, вы делаете:

  • Обработка cur как char* и копирование в него списка параметров
  • Приведение cur к типу BigStruct* и последующая передача его в функцию testdynamic.

Мне кажется, что это странный способ сделать это, и, не заглядывая внутрь функции testdynamic, я бы предположил, что именно это вызывает ошибку. Я бы рекомендовал изменить ваше определение BigStruct:

typedef struct {
    char * el;
} BigStruct;

И таким образом, вы можете выделить пространство для своего массива символов, скопировать в него список параметров, а затем установить char * в BigStruct, чтобы он указывал на соответствующий блок памяти следующим образом:

char * cur = malloc(totalSize);
// Copy parameters over into cur
BigStruct * bg = malloc(sizeof(BigStruct));
bg->el = cur;

А затем снова вызовите свою тестовую динамическую функцию. Попробуйте это, вы все еще получаете ошибку времени выполнения?

редактировать: увидев содержимое функции testdynamic, я вижу несколько проблем с ней

char* testdynamic(char* x, char* y){
    printf("%s %s\n", x, y);
    return "hello";
}
  1. Функция принимает 2 символа *, но вы, кажется, передаете только 1 в своем коде. Что он должен напечатать для y, если вы дадите ему только аргумент для x?
  2. Предположим, вы теперь передаете аргументы как для x, так и для y. Вы уверены, что обе строки заканчиваются нулем? printf работает, печатая символы, пока не найдет символ '\0'.
  3. Вы не можете просто вернуть строковый литерал из такой функции. Этот строковый литерал принадлежит функции, и после возврата функции вы не можете гарантировать, что память, в которой хранится строка, будет по-прежнему безопасно доступна для повторного доступа. Если вы хотите вернуть такую ​​строку, вы должны сначала использовать malloc() для строки и вернуть адрес в выделенный блок памяти.
person WhoBuntu    schedule 05.08.2013
comment
Ваш пункт 3 неверен. return "hello" совершенно допустимо. Возвращать его как char* технически неправильно, поскольку строковый литерал имеет тип const char *, но это не фатальная ошибка. - person Nik Bougalis; 05.08.2013
comment
Спасибо, что указали мне на это @NikBougalis, я перепроверил и нашел эту тему, которая разъяснила это дальше для меня. - person WhoBuntu; 05.08.2013
comment
@NikBougalis Неверно говорить, что строковые литералы имеют тип const char *. По историческим причинам строковые литералы не имеют типа const в C. Их изменение по-прежнему не определено. - person This isn't my real name; 06.08.2013
comment
@ElchononEdelson Спасибо, что указали на это; еще одно тонкое различие между C и C++, о котором следует помнить. - person Nik Bougalis; 06.08.2013

Чтобы предоставить точные, непредвзятые комментарии, требуется копия кода. Таким образом, я принял ваши комментарии и создал следующий код. Вероятно, это НЕ то, что вы хотите сделать. Однако к проблеме программирования следует подходить поэтапно или поэтапно.

То есть заставьте что-то простое работать, а затем сделайте его немного более сложным, но все еще работающим, и работайте над своей окончательной версией. Так многие из нас работают. Никто не в состоянии правильно написать сложную программу с первого раза, особенно если он новичок в языке!

#include <stdio.h>
#include <stdlib.h>
char *testdynamic(char *x, char* y){

        printf("%s %s\n", x, y);
        return "hello";
    }

main()
{
    typedef struct BigStruct{
        char el[2000];
    } BigStruct;

    char      *ret_char_ptr;

    BigStruct *bg;

    char x[] = "abcd";
    char y[] = "23456";

    bg = malloc(sizeof(BigStruct));

    // invoke the testdynamic function
    ret_char_ptr = testdynamic(x, y);


    printf("%s\n", ret_char_ptr);

}

Я запустил этот код на компиляторе Eclipse/Microsoft C и получил следующий результат:

abcd 23456
hello

Примечание. BigStruct еще не использовался в этом коде. Не уверен, каково ваше намерение, но C предоставляет способ передачи списков параметров, которые различаются по длине и данным из нескольких разных вызовов одной и той же функции.

person JackCColeman    schedule 05.08.2013
comment
Функция testdynamic загружается динамически. Почему вы передаете ей x, y? Пожалуйста, посмотрите, что произойдет, когда вы передадите ему bg. - person Rishi; 05.08.2013

Это работает, только если размер sizeof(BigStruct)

Вы не можете предполагать, что структура - это размер ее содержимого, поскольку компиляторы могут делать предположения о выравнивании и т. д.

Если вам просто нужно 20 000 символов, тогда malloc внутри BigStruct, как предлагали другие.

person Tony    schedule 05.08.2013