Fórum Root.cz
Hlavní témata => Vývoj => Téma založeno: 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:
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á:
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.
-
A co je na tom divneho? Funkce ma parametr typu ponter na char a ten ma velikost 8 byte.
-
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.
-
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.
-
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?
-
No a nemôžeš si zistiť veľkosť reťazca v tej funkcii volaním strlen?
-
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!
-
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 ...
-
zavisi aj od toho na co sa ta dlzka bude pouzivat. nemozes pouzit strdup() miesto memcpy()?
-
(==> 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).
-
(==> 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?
-
(==> 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.
-
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.
-
Děkuji všem za odpovědi. Nyní již mám funkční kód.
Zde je výsledek mého snažení.
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:
int len_MQCHAR48 = 48;
char strPutDate[49] = ""; //MQCHAR48
char *pstrPutDate;
pstrPutDate = toCharStrTrimR(strPutDate, pMsgDesc->PutDate, len_MQCHAR48);
-
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.
-
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'