2012-04-21 1 views
5

Я писал код, используя таНос для чего-то, а затем столкнулся с вопросом, поэтому я написал тестовый код, который фактически суммирует всю путаницу, которая находится ниже ::Динамического распределения памяти в «с» Проблемы

# 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:: [email protected]:~/Desktop/ad/A1/CC$ ./test 

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

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

Затем после этого указывается 2 указателя p - целочисленный тип и d - короткого типа. то я назначил t каждому из них * (p = t и d = t) * , что означает, что оба d & p указывают на то же место mem в куче.

при попытке сэкономить 65536 (2^16) до (* d) я получаю предупреждение о том, что большое значение int усечено, что и ожидалось. Теперь я снова сохранил 65536 (2^16) до (* p), который не вызвал никаких предупреждений.

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

Мой вопрос являются:

  1. Хотя я выделил 2 байта (т.е. 16 бит) кучи мем с помощью таНос Как я смог сохранить 65536 в этих двух байтах (при использовании (р) который является указателем целочисленного типа). У меня такое ощущение, что причиной этого является автоматическое преобразование типа void в int * pointer (в p = t), так что присвоение t p приводит к доступу к областям памяти за пределами того, что выделяется через malloc. ??.

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

Может кто-нибудь положил некоторый свет на это, он будет действительно appreciated..and также, если кто-нибудь может объяснить причины этого ..

Большое спасибо

+0

Malloc может округлять запрошенный размер до определенного множителя. Не знаю о * nix, но Windows любит округлять его до кратных 8 байтам. Если это так, вы пишете вне области, которую вы просили, но она находится в пределах запаса прочности, и поэтому вы не развращаете что-либо еще. – DCoder

+0

Вы тщательно злоупотребляли силой каста для получения неопределенного поведения. C дает вам большую силу и гибкость, чтобы делать * плохие вещи *. Вы должны взять на себя ответственность за ***, не делайте их ***. – dmckee

+0

@dmckee Я знаю, что я делаю, не следует делать, но после спотыкания на этом мне было любопытно узнать точные причины, по которым я изначально догадался, был тот факт, что автоматическое преобразование типа в int * выделенного 2-байтового указателя void * приводит к тому, что int * указатель получает больше памяти, а затем 2 байта, потому что на моей машине int имеет 4 байта. Правильно ли это (мое предположение) – abhi

ответ

2

Отвечая на Ваш второй вопрос:

Объяснение заключается в том, что int обычно 4 байта, а наиболее значимые байты могут быть сохранены в первых двух позициях.A short, который составляет всего 2 байта, также сохраняет свои данные в первых двух позициях. Очевидно, что после хранения 65536 в int и short, но указывая на то же место памяти, данные будут сохранены смещенными на два байта для int по отношению к short с двумя наименее значимыми байтами int соответствующий хранению для short.

Поэтому, когда компилятор печатает *d, он интерпретирует это как short и смотрит на области, соответствующей для хранения short, который не является где компилятор ранее сохраненным в 65536, когда *p был написан. Обратите внимание, что запись *p = 65536; переписывала предыдущие *d = 65536;, заполняя два младших значащих байта 0.

Что касается первого вопроса: Компилятор делает не МАГАЗИНУ 65536 для *p в 2 байта. Он просто выходит за пределы выделенной памяти - что может вызвать ошибку в какой-то момент.

+0

Итак, тот факт, что автоматическое преобразование типа в int * выделенного 2-байтового указателя void * приводит к тому, что int * указатель получает больше памяти, а затем 2 байта, потому что на моей машине int составляет 4 байта. Это правильно (мое предположение) – abhi

+0

и спасибо за четкий и четкий ответ, который объясняет все :) – abhi

+1

Концептуально, вот и все. Я бы сказал, что технически говоря, возможно, это не лучшая формулировка, чтобы сказать «автоматическое преобразование типов» в вашем предложении, потому что ничто фактически не преобразуется - просто компилятор обращается к байтам в памяти, как будто они хранят один тип данных, в отличие от доступа к ним, как если бы они сохраняли другой тип. –

1

В C есть вообще не является защитой для записи за пределы выделения. Просто не делай этого, все может случиться. Здесь это работает для вас, потому что по некоторым совпадениям пространство за двумя выделенными вами байтами не используется для чего-то другого.

+0

Или, по крайней мере, он не сделал ничего, что демонстрирует другое использование этой памяти. Ответ молчания на эту ошибку - это то, что позволяет им подкрасться к вам. – dmckee

0

1) Гранулярность диспетчера памяти ОС составляет 4 КБ. Оверить на один бит вряд ли вызовет AV/segfault, но он повредит любые данные в соседнем местоположении, что приведет к:

2) Неопределенное поведение. Этот набор правил включает «непринужденно правильную операцию» (сейчас!).

 Смежные вопросы

  • Нет связанных вопросов^_^