Fórum Root.cz

Hlavní témata => Vývoj => Téma založeno: Jardaa 26. 11. 2012, 15:49:20

Název: Předávání dat do funkce v C a jiná velikost
Přispěvatel: Jardaa 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.
Název: Re:Předávání dat do vlastní funkce v jazyce C, jiná velikost?
Přispěvatel: kuka 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.
Název: Re:Předávání dat do vlastní funkce v jazyce C, jiná velikost?
Přispěvatel: me vakérav 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.
Název: Re:Předávání dat do vlastní funkce v jazyce C, jiná velikost?
Přispěvatel: me vakérav 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.
Název: Re:Předávání dat do vlastní funkce v jazyce C, jiná velikost?
Přispěvatel: Jardaa 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?
Název: Re:Předávání dat do funkce v C a jiná velikost
Přispěvatel: kagro 26. 11. 2012, 16:27:27
No a nemôžeš si zistiť veľkosť reťazca v tej funkcii volaním strlen?
Název: Re:Předávání dat do funkce v C a jiná velikost
Přispěvatel: Náhodný kolemjdoucí 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!
Název: Re:Předávání dat do funkce v C a jiná velikost
Přispěvatel: hawran diskuse 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 ...
Název: Re:Předávání dat do funkce v C a jiná velikost
Přispěvatel: aaa158 26. 11. 2012, 17:16:25
zavisi aj od toho na co sa ta dlzka bude pouzivat. nemozes pouzit strdup() miesto memcpy()?
Název: Re:Předávání dat do funkce v C a jiná velikost
Přispěvatel: poiu 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).
Název: Re:Předávání dat do funkce v C a jiná velikost
Přispěvatel: jardaa 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?
Název: Re:Předávání dat do funkce v C a jiná velikost
Přispěvatel: kuka 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.
Název: Re:Předávání dat do funkce v C a jiná velikost
Přispěvatel: kuka 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.
Název: Re:Předávání dat do funkce v C a jiná velikost
Přispěvatel: Jardaa 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);
Název: Re:Předávání dat do funkce v C a jiná velikost
Přispěvatel: Rax 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.
Název: Re:Předávání dat do funkce v C a jiná velikost
Přispěvatel: Jardaa 27. 11. 2012, 16:47:37
    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.

Děkuji za upozornění, kontroloval jsem to a řetězec je zde celý (příklad 123456789012345678901234567890123456789012345678 = 48 znaků).

pMsgDesc->PutDate a ostatní řetězce, které zpracovávám přes ptr nejsou v pravém slova smyslu řetězce - chybý jim na konci znak '\0', který zpracovávám v str a v něm je zaručeno, že poslední znak bude vždy '\0' (pokud ptr neobsahuje prázdné znaky zprava v tom případě bude znak '\0' umístěn na jiný index.

Pokud existuje nějaký příklad, ve kterém toto selže byl bych rád, abych o tom věděl.

Příklad vstupních dat (záměrně neuvádím na konci dat '\0', protože 100% tam není).

't' + 'e' + 's' + 't' mi vrátí do str 't' + 'e' + 's' + 't' + '\0'

't' 'e' 's' 't' '\20' '\20' '\20' mi vrátí do str 't' + 'e' + 's' + 't' + '\0'