Diskuze: magie v C
V předchozím kvízu, Online test znalostí C++, jsme si ověřili nabyté zkušenosti z kurzu.

Tvůrce

Zobrazeno 30 zpráv z 30.
//= Settings::TRACKING_CODE_B ?> //= Settings::TRACKING_CODE ?>
V předchozím kvízu, Online test znalostí C++, jsme si ověřili nabyté zkušenosti z kurzu.
Najprv som to testol na Code:Blocks a pracovalo to presne ako píšeš, ale
potom som zmenil hodnoty v *a *b na 9-miestne čísla a už to vypisovalo len
číslo v *b.
Potom som schválne nainštaloval MS Visual C++ (express 2010) a tam to hneď
vypisovalo hodnoty z *b.
oklamal si compilátor
neviem
int *b = (int *)realloc(a, sizeof(int));
pretože funkcia realloc() sa pokúša zmeniť veľkosť už
vopred alokovanej pamäti pomocou funkcie malloc alebo calloc,
čo v tomto prípade nie je splnené.
int main(int argc, const char **argv)
{
int *a = (int *)malloc(sizeof(int)); // vyčlenená pamäť pre 1 int
// *b nebol alokovaný
int *b = (int *)realloc(a, sizeof(int)); // pointer *b = pointer *a
*a = 0;
*b = 1; // obsah *b = obsah *a
if (a == b)
printf("a=%p b=%p => *a=%d *b=%d\n", a, b, *a, *b);
return 0;
}
Zkoušel jsem Code::Blocks, Dev-Cpp a VS2013, ve všech třech případech jsem dostal tohle:
a=004776B0 b=004776B0 => *a=1 *b=1
Tipnul bych si, že výše popsaný jev je způsoben nějakou optimalizací kompilátoru spojenou s cachováním paměti.
Ja si myslím, že je to chybou compilatoru. V mojom prípade najskôr pracoval ako písal coels, ale po zmene obsahu pointer sa optimalizovať. Asi to bolo tým, že *a = 0
Navyše hodnota 0 je asi NULL, preto je asi možné že bol compilátor oklamaný.
Tím to není, NULL je normální nula pouze přetypovaná na pointer a z výpisu je zřejmé, že pointer a hodnotu NULL nemá.
Proto je to puzzle
Zkus to ve Visual Studiu spustit s malou úpravou (VS2010 i VS2012)
int main(int argc, const char **argv)
{
int *a = (int *)malloc(sizeof(int));
int *b = (int *)realloc(a, sizeof(int));
*a = 0;
*b = 1;
if (a == b)
printf("*a=%d *b=%d\n", *a, *b);
return 0;
}
Trochu se divím, že nikdo nenašel v programu chybu, na to si ho nemusíte ani spouštět.
Libor Šimo (libcosenior) tvoje analýza programu není správně
Podle výpisu má stejnou hodnotu jako pointer b, což je logické i proto, že funkce realloc v případě "nezvětšení bloku paměti" data nepřesouvá, mělo by tedy platit že a == b, což zjevně platí, protože jinak by ta podmínka ani nebyla splněna a nic by se nevypsalo.
Opravdu? Můžeš tohle
tvrzení něčím podložitm, například dokumentací?
V dokumentaci je napsáno, že data můžou nebo nemusí být přesunuta. Přičemž mi přijde logické, že pokud nový blok má stejnou velikost jako ten původní, data se nepřesouvají, protože k tomu není důvod. Pokud by se data přesunula, pak by nemohlo platit, že a == b a nic by se nevypsalo.
Tá malá zmena ale vlastne nič nerobí a výpis je rovnaký, nie ako tvoj.
Po té změně jsem ve VS již dostal stejný výsledek, ale pouze v release módu. Z toho soudím, že je to opravdu způsobeno optimalizací kompilátoru, kde při přiřazení *b = 1 pravděpodobně program přepisuje data někde v cache a ne přímo v paměti na kterou pointer ukazuje. Pak při výpisu přes pointer b program přistupuje do cache ale přes pointer a přistupuje do paměti na adresu, na kterou ukazuje.
Takže jsi přišel na otázku (a), ale co otázka (c)?
Opravdu nikdo nevidí tu chybu v programu?
Chyba je imho v tom, že přiřazujeme data tam, kam ukazuje pointer a, i přesto, že paměť, na kterou ukazuje byla realokována, tedy obecně již může být uvolněná a nemělo by se k ní takto přistupvat. Kompilátor s tím očividně při optimalizacích počítá a proto nastal takovýto zvláští jev.
free(a);
... což vede optimalizátor k tomu, že 'a' a 'b' nemají nic společného a
přesun instrukcí provede nezávisle.
Jedna z variant, co vidí kompilátor.
a = (int *)malloc(sizeof(int))
b = (int *)realloc(a, sizeof(int));
/* a = undefined */
/* assign to undef, optimize value */ *a = 0;
*b = 1;
if (a == b)
printf("*a=%d *b=%d\n", 0, 1); /* a, b independent */
Důkaz, že to tak je, je jednoduchý, s malou úpravou už bude výstup zcela korektní:
a = (int *)malloc(sizeof(int))
b = (int *)realloc(a, sizeof(int));
a = b
// ...
Chtělo by to víc takových programátorských hříček, člověk se
alespoň odreaguje a zapřemýšlí nad něčím zajímavým.
Libco, alokace paměti v C je plná triků:
int *a, *b;
a = (int *)malloc(sizeof(int));
b = (int *)realloc(a, sizeof(int));
Zkus si odpovědět podle specifikace C bez použití debuggeru.
A hlavně se poučí, že logické předpoklady jsou sice nutné, ale s
referenční dokumentací mají málo společného.
Včera jsem hodinu honil jeden pointer, který způsoboval segfault na
místě, kde se nic nedělo.
Nakonec se ukázalo, že mi tam kompilátor z definice typu proměné cpe free()
navíc, o který jsem nestál a uvolňuje pamět, která není moje.
tie, ktoré tam boli pred alokáciou
4 bajty
4 bajty
tie, ktoré tam boli pred alokáciou
Release v 2012 už fungoval.
Koukal jsem na to v asm a je to jak píšeš - kompilátor tam do výpisu
rovnou posílal hodnoty 0 a 1.
Já nedávno narazil na naprosto nepochopitelnou chybu v gcc. Měl jsem funkci, která přebírala pointer typu void*. Do dat předaných pointerem nic nezapisovala, pouze z nich četla. Tu funkci jsem zavolal v jiném souboru a předával jí adresu floatové proměnné. K mému údivu, se tato proměnná z naprosto nepochopitelných důvodů vždy vynulovala (i přesto, že funkce na tu adresu nic nezapisovala). Dlouho jsem hledal chybu, až jsem přišel na to, že jsem zapomněl naincludovat header, který danou funkci obsahoval. Doteď absolutně nechápu, jak je možné, že kompilátor nehlásil chybu a že tu funkci dokázal zavolat i přesto, že v daném souboru o ní nemohl vědět, ale dělala očividně něco uplně jiného než měla. Po naincludování příslušného souboru chyba zmizela.
Všechny odpovědi jsou, bohužel, špatně. Takhle C opravdu nefunguje.
Fajn, ako to teda je?
namiesto 4 bašty som mal napísať sizeof(int)?
bajty
Zobrazeno 30 zpráv z 30.