Динамично разпределение на паметта в 'c' проблеми

Пишех код, използвайки malloc за нещо и след това се сблъсках с проблем, така че написах тестов код, който всъщност обобщава цялото объркване, което е по-долу::

# include <stdio.h>
# include <stdlib.h>
# include <error.h>

int main()     
{
     int *p = NULL;
     void *t = NULL;
     unsigned short *d = NULL;

     t = malloc(2);
     if(t == NULL) perror("\n ERROR:");
     printf("\nSHORT:%d\n",sizeof(short));
     d =t;
     (*d) = 65536;
     p = t; 
     *p = 65536;
     printf("\nP:%p: D:%p:\n",p,d);
     printf("\nVAL_P:%d ## VAL_D:%d\n",(*p),(*d));
     return 0;
  }
   Output:: abhi@ubuntu:~/Desktop/ad/A1/CC$ ./test

            SHORT:2
            P:0x9512008: D:0x9512008:
            VAL_P:65536 ## VAL_D:0

Разпределям 2 байта памет с помощта на malloc. Malloc, който връща празен * указател, се съхранява в void* указател 't'.

След това се декларират 2 указателя p - цял тип и d - от кратък тип. след това присвоих t и на двамата*(p =t и d=t)* това означава, че и d & p сочат към едно и също място на mem в купчина.

при опит да запазя 65536(2^16) в (*d) получавам предупреждение, че голямата int стойност е съкратена, което е както се очаква. Сега отново записах 65536(2^16) в (*p), което не предизвика никакво предупреждение.

*При отпечатването на двете (*p) и (d) получих различни стойности (въпреки че всяка е правилна за собствен дефиниран тип указател).

Въпросът ми е:

  1. Въпреки че съм разпределил 2 байта (т.е. 16 бита) от heap mem, използвайки malloc, как мога да запазя 65536 в тези два байта (като използвам (p), който е указател от тип цяло число).?? имам чувството, че причината за това е автоматичното преобразуване на типа на void в int* указател (в p =t), така че присвояването на t на p води до достъп до региони на паметта извън това, което е разпределено чрез malloc . ??.

  2. Въпреки че всичко това се случва, как, по дяволите, премахването на един и същ регион на паметта чрез (*p) и (*d) отпечатва два различни отговора (въпреки че това също може да се обясни, ако това, което си мисля, е причината за въпрос 1).

Може ли някой да хвърли малко светлина върху това, ще бъде наистина оценено..а също и ако някой може да обясни причините зад това..

Много благодаря


person abhi    schedule 21.04.2012    source източник
comment
Malloc може да закръгля искания размер към определен множител. Не знам за *nix, но Windows обича да го закръгля до кратни на 8 байта. Ако случаят е такъв, вие пишете извън зоната, която сте поискали, но се оказва в границата на безопасност и следователно не повреждате нищо друго.   -  person DCoder    schedule 21.04.2012
comment
Внимателно сте злоупотребили със силата на кастинг, за да получите недефинирано поведение. C ви дава голяма сила и гъвкавост да правите лоши неща. Трябва да поемете отговорността да не ги правите.   -  person dmckee --- ex-moderator kitten    schedule 21.04.2012
comment
@dmckee знам, че това, което правя, не трябва да се прави, но след като се натъкнах на това, бях любопитен да разбера точните причини зад това, което първоначално предположих, че е фактът, че автоматичното преобразуване на тип в int * на 2 байта разпределен void* указател води към int* указателят има достъп до повече памет от 2 байта, защото на моята машина int е от 4 байта. Това правилно ли е (мое предположение)   -  person abhi    schedule 21.04.2012
comment
Разбира се, и тогава започва истинското забавление. Прочетете за Big- и Little-endianness. След това прочетете за mixed-endian и бъдете готови да се свиете.   -  person dmckee --- ex-moderator kitten    schedule 21.04.2012


Отговори (3)


Първо отговарям на втория ви въпрос:

Обяснението е фактът, че int обикновено е 4 байта и най-значимите байтове могат да се съхраняват в първите две позиции. short, който е само 2 байта, също съхранява своите данни в първите две позиции. Тогава е ясно, че съхраняването на 65536 в int и short, но сочещо към едно и също място в паметта, ще накара данните да бъдат съхранени изместени с два байта за int по отношение на short, с двата най-малко значими байта на int съответстващ на хранилището за short.

Следователно, когато компилаторът отпечата *d, той интерпретира това като short и разглежда областта, съответстваща на паметта за short, която не е мястото, където компилаторът преди това е съхранявал 65536, когато *p е бил написан. Обърнете внимание, че писането на *p = 65536; презаписа предишното *d = 65536;, попълвайки двата най-малко значими байта с 0.

Относно първия въпрос: Компилаторът не съхранява 65536 за *p в рамките на 2 байта. Той просто излиза извън границите на паметта, която сте разпределили - което вероятно ще причини грешка в даден момент.

person Dan Nissenbaum    schedule 21.04.2012
comment
Така че фактът, че автоматичното преобразуване на тип в int * на 2-байтов разпределен void* указател води до int* указател за достъп до повече памет от 2 байта, защото на моята машина int е от 4 байта. Това правилно ли е (мое предположение) - person abhi; 21.04.2012
comment
и благодаря за ясния и точен отговор, който обяснява всичко :) - person abhi; 21.04.2012
comment
Концептуално това е. Бих казал, че от техническа гледна точка, може би не е най-добрата формулировка да кажете автоматично преобразуване на типа в изречението си, защото всъщност нищо не се преобразува - просто компилаторът има достъп до байтовете в паметта, сякаш съхраняват един тип данни , за разлика от достъпа до тях, сякаш съхраняват друг тип. - person Dan Nissenbaum; 22.04.2012
comment
оххх лошо .. още веднъж благодаря за отговора и подробното обяснение :) - person abhi; 22.04.2012

В C няма никаква защита за писане извън границите на разпределението. Просто не го правете, всичко може да се случи. Тук изглежда, че работи за вас, защото по някакво съвпадение пространството зад двата байта, които сте разпределили, не се използва за нещо друго.

person Jens Gustedt    schedule 21.04.2012
comment
Или поне не е направил нищо, което да демонстрира някаква друга употреба на тази памет. Мълчаливият отговор на този вид грешка е това, което им позволява да се промъкнат към вас. - person dmckee --- ex-moderator kitten; 21.04.2012

1) Детайлността на диспечера на паметта на ОС е 4K. Презаписването с един бит е малко вероятно да задейства AV/segfault, но ще повреди ли всички данни в съседното местоположение, водещо до:

2) Недефинирано поведение. Този набор от поведение включва „очевидно правилна операция“ (засега!).

person Martin James    schedule 21.04.2012