Předávání dat do funkce v C a jiná velikost

Jardaa

Předávání dat do funkce v C a jiná velikost
« kdy: 26. 11. 2012, 15:49:20 »
Dobrý den všem,

Narazil jsem v jazyce C na pro mě zatím nepochopitelné chování při předávání dat do funkce.

Zde je zdrojový kód funkce:
Kód: [Vybrat]
char* toCharStr(char *str, char *ptr, unsigned long size) {
   
    char log1[500] = "";
    char log2[500] = "";
    sprintf(log1, "echo Size before:str%u: ptr:%u: >> $MQ_DEBUG_LOG", sizeof (str), sizeof (ptr));
    //memcpy(str, ptr, size);
    memcpy(str, ptr, sizeof(ptr));
    sprintf(log2, "echo Size after:str%u: ptr:%u: >> $MQ_DEBUG_LOG", sizeof (str), sizeof (ptr));
    str[sizeof (str)] = '\0';

    system(log1);
    system(log2);

    return str;
}

Zde je zdrojový kód, odkud se volá:

Kód: [Vybrat]
void addLogEntry(ExitTypeEnum Type, PMQAXP pExitParms, PMQAXC pExitContext, PPMQMD ppMsgDesc, PPMQGMO ppGetMsgOpts, PPMQPMO ppPutMsgOpts, PMQLONG pBufferLength) {

    int i;
    char logdata[1000] = "";
    char logdata1[1000] = "";
    char logdata2[1000] = "";
    char logdata3[1000] = "";
    char logdata4[1000] = "";

    char strResolvedQName[49] = "";
    char strResolvedQNameTMP[49] = "";

    MQMD* pMsgDesc = *ppMsgDesc;

    MQGMO* pGetMsgOpts;
    MQPMO* pPutMsgOpts;

    switch (Type) {
        case Type_GetAfter:
            break;

        case Type_PutAfter:
        case Type_Put1After:

            pPutMsgOpts = *ppPutMsgOpts;

            memcpy(strResolvedQNameTMP, pPutMsgOpts->ResolvedQName, sizeof (MQCHAR48));
            strResolvedQNameTMP[sizeof (strResolvedQNameTMP)] = '\0';
           
           
            sprintf(logdata4, ";ResolvedQName_%s_;", toCharTrim(strResolvedQName, pPutMsgOpts->ResolvedQName, sizeof (MQCHAR48)));
           
            strcat(logdata, logdata4);
            strcat(logdata, rtrim(strResolvedQNameTMP));

            break;
    };

    PutToLoggingQueue(logdata, pExitParms, pExitContext);

    return;
}

Pokud si zobrazím sizeof strResolvedQName je přesně 49 bajtů ale pokud jej předám do funkce pak má sizeof pouze 8 bajtů. Stejná situace nastane i pro ptr.
Můj problém tedy je, že nerozumím, co dělám špatně při předávání dat do funkce toCharStr
Pokud použiju zdrojový kód mé funkce přímo, pak je vše v pořádku.

Pokud zde nejsou uvedeny všechny důležité věci, pak rád doplním.
« Poslední změna: 26. 11. 2012, 16:11:47 od Petr Krčmář »


kuka

Re:Předávání dat do vlastní funkce v jazyce C, jiná velikost?
« Odpověď #1 kdy: 26. 11. 2012, 15:55:20 »
A co je na tom divneho? Funkce ma parametr typu ponter na char a ten ma velikost 8 byte.

me vakérav

Re:Předávání dat do vlastní funkce v jazyce C, jiná velikost?
« Odpověď #2 kdy: 26. 11. 2012, 15:56:16 »
To je v pořádku. Protože v prvním případě děláš sizeof celého řetězce alokovaného na zásobníku, do funkce však nepředáváš celý řetězec, ale jen ukazatel na něj, což je na 64bitovém systému 64 bitů = 8 bajtů.

Obecně předat v C předat celý řetězec nejde, prostě musíš předat ukazatel a pak *někde bokem* délku toho bufferu. Anebo použít něco vyššího, třeba C++kové stringy.

me vakérav

Re:Předávání dat do vlastní funkce v jazyce C, jiná velikost?
« Odpověď #3 kdy: 26. 11. 2012, 15:59:42 »
Ještě bych dodal, že sizeof() je makro a vyhodnocuje se už v době překladu, ne za běhu! V době překladu není zjevné, s jakými různými vstupy (délkami řetězců) se může funkce volat, takže překladač ani nemůže doplnit správnou velikost.

Jardaa

Re:Předávání dat do vlastní funkce v jazyce C, jiná velikost?
« Odpověď #4 kdy: 26. 11. 2012, 16:08:45 »
To je v pořádku. Protože v prvním případě děláš sizeof celého řetězce alokovaného na zásobníku, do funkce však nepředáváš celý řetězec, ale jen ukazatel na něj, což je na 64bitovém systému 64 bitů = 8 bajtů.

Obecně předat v C předat celý řetězec nejde, prostě musíš předat ukazatel a pak *někde bokem* délku toho bufferu. Anebo použít něco vyššího, třeba C++kové stringy.

Bohužel musím zůstat u základního ANSI C. Předávat velikost "bokem" jsem již zkoušel (zakomentované memcpy). Mám tedy předávat ještě velikost str? Případně použít while cyklus místo memcpy?


kagro

Re:Předávání dat do funkce v C a jiná velikost
« Odpověď #5 kdy: 26. 11. 2012, 16:27:27 »
No a nemôžeš si zistiť veľkosť reťazca v tej funkcii volaním strlen?

Náhodný kolemjdoucí

Re:Předávání dat do funkce v C a jiná velikost
« Odpověď #6 kdy: 26. 11. 2012, 16:37:46 »
No a nemôžeš si zistiť veľkosť reťazca v tej funkcii volaním strlen?

To bude jistě velmi efektivní, obzvláště v případě častého volání funkce s dlouhými řetězci!

Re:Předávání dat do funkce v C a jiná velikost
« Odpověď #7 kdy: 26. 11. 2012, 17:05:31 »
Pokud se nepletu, tak sizeof je operátor (je to jedno z klíčových slov jazyka C), není to funkce.
Aplikován na pointer (deklarovaný jako parametr funkce) vrátí velikost toho pointru v bajtech.
(==> proto bacha na toto: str[sizeof (str)] = '\0';, memcpy(str, ptr, sizeof(ptr)); )

Pokud je velikost těch řetězců známá v okolí té volané funkce, určitě bude efektivnější do ní předávat i velikosti jednotlivých řetězců.
Pokud ne a jde o normální (= řetzce ukončené znakem '\0' ) řetězce, asi nemá smysl nejdříve počítat velikost pomocí strlen() a pak tu velikost použít v dalším cyklu ...

aaa158

  • ***
  • 248
    • Zobrazit profil
    • E-mail
Re:Předávání dat do funkce v C a jiná velikost
« Odpověď #8 kdy: 26. 11. 2012, 17:16:25 »
zavisi aj od toho na co sa ta dlzka bude pouzivat. nemozes pouzit strdup() miesto memcpy()?

poiu

Re:Předávání dat do funkce v C a jiná velikost
« Odpověď #9 kdy: 26. 11. 2012, 17:29:03 »
(==> proto bacha na toto: str[sizeof (str)] = '\0';, memcpy(str, ptr, sizeof(ptr)); )
IMHO na taketo kopirovanie kazdy normalny aspon trochu skuseny clovek pouzije strncpy - a ked taku funkciu nema od libiek, tak si ju implementuje (u nej je pri implementacii kazdemu jasne, co robi).

jardaa

Re:Předávání dat do funkce v C a jiná velikost
« Odpověď #10 kdy: 26. 11. 2012, 18:02:40 »
(==> proto bacha na toto: str[sizeof (str)] = '\0';, memcpy(str, ptr, sizeof(ptr)); )
IMHO na taketo kopirovanie kazdy normalny aspon trochu skuseny clovek pouzije strncpy - a ked taku funkciu nema od libiek, tak si ju implementuje (u nej je pri implementacii kazdemu jasne, co robi).

Děkuji za reakce - takový fofr jsem nečekal.

Problém je v tom, že strlen určite použít nepůjde - otestováno a jak zde již bylo zmíněno uvedené řetězce nepoužívají \0 terminator proto předpokládám, nepůjde použít ani strcpy.

Velikost řetězců je malá cca 50 bajtů, takže s výkonem by problém být neměl.

Ta má funkce právě slouží k tomu, že by měla zkopírovat vstupní data jako pole "char []" do nového pole char []  ukončeného znakem \0 abych mohl použít strlen pro ořezání whitespace znaků zprava. Nové char [] používám proto, protože objekt, z kterého potřebuji vytáhnout určitá data bych si přepsal.

str[sizeof (str)] = ! '\0' ale v mém případě vždy 8 dle výpisu hodnoty do logu :)

Ještě tedy dotaz. Bude lepší (efektivnější) použít jinou ne-str funkci na překopírování pole typu char než memcpy?

kuka

Re:Předávání dat do funkce v C a jiná velikost
« Odpověď #11 kdy: 26. 11. 2012, 18:06:48 »
(==> proto bacha na toto: str[sizeof (str)] = '\0';, memcpy(str, ptr, sizeof(ptr)); )
IMHO na taketo kopirovanie kazdy normalny aspon trochu skuseny clovek pouzije strncpy - a ked taku funkciu nema od libiek, tak si ju implementuje (u nej je pri implementacii kazdemu jasne, co robi).

Tam muze byt problem s vykonem, memcpy je mnohem rychlejsi. Samozrejme ve vetsine pripadu jde o povestnou predcasnou optimalizaci, ale vzdy to tak byt nemusi. str[sizeof (str)] = '\0' je ovsem humus. Funguje to pouze tam, kde je deklarace pole, jak se uz presvedcil mistni tazatel, a i kdyz to je tak, mela by byt velikost uvedena jako konstanta a s tou by se melo vsude pracovat - v deklaraci, v parametrech memcpy atd. Ne ze si nekam napisu natvrdo 49 a pak se to snazim zpatky rozklicovat pres sizeof, coz je necitelne a extremne nachylne k chybam.

kuka

Re:Předávání dat do funkce v C a jiná velikost
« Odpověď #12 kdy: 26. 11. 2012, 18:10:58 »
Citace
Ještě tedy dotaz. Bude lepší (efektivnější) použít jinou ne-str funkci na překopírování pole typu char než memcpy?

Ne nebude, ale proste v te funkci musis vedet, kolik toho chces kopirovat. sizeof ke zjisteni velikosti retezce v zadnem pripade neslouzi. Posli si do funkce delku nebo pokud je to nejaka obecnejsi vlastnost v celem programu, napr. max velikost logovaneho retezce, tak velikost bufferu vytahni jako konstantu do vhodneho hlavickoveho souboru.

Jardaa

Re:Předávání dat do funkce v C a jiná velikost
« Odpověď #13 kdy: 27. 11. 2012, 15:29:43 »
Děkuji všem za odpovědi. Nyní již mám funkční kód.

Zde je výsledek mého snažení.

Kód: [Vybrat]
char* toCharStrTrimR(char *str, char *ptr, unsigned long ptrSize) {

    char *end;

    memcpy(str, ptr, ptrSize);

    end = str + ptrSize;
    while ((end > str && isspace(*end)) || (end > str && isprint(*end) == 0)) end--;
    // Write new null terminator
    *(end + 1) = '\0';

    return str;

}

A zde část kódu:

Kód: [Vybrat]
int len_MQCHAR48 = 48;

char strPutDate[49] = ""; //MQCHAR48
char *pstrPutDate;

pstrPutDate = toCharStrTrimR(strPutDate, pMsgDesc->PutDate, len_MQCHAR48);

Rax

Re:Předávání dat do funkce v C a jiná velikost
« Odpověď #14 kdy: 27. 11. 2012, 16:06:33 »
    end = str + ptrSize;
    while ((end > str && isspace(*end)) || (end > str && isprint(*end) == 0)) end--;

Tohle ti už od pohledu bude číst char mimo pMsgDesc->PutDate, kterýžto má mít 48 byte takže můžeš přistupovat jenom k bytům od 0 do 47 a né 48.