Предаването на 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? Как напъхвате аргументи вътре в него? Защо предавате КОПИЕ на 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, да копирате списъка си с параметри в него и след това да зададете char * в BigStruct да сочи към съответния блок памет, както следва:

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

И след това отново извикайте вашата testdynamic функция. Опитайте това, все още ли получавате грешка по време на изпълнение?

редактиране: след като видях съдържанието на функцията 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 символа, тогава го локализирайте вътре в BigStruct, както други предложиха.

person Tony    schedule 05.08.2013