Почему назначение большого числа в одном байте работает в C? - PullRequest
3 голосов
/ 13 ноября 2010
int *ptr = malloc(sizeof(char));

*ptr = 100000;

printf("%d\n", *ptr); // 100000

Разве это не должно выделять достаточно памяти для char, то есть 1 байт? Следовательно, не должно ли наибольшее число быть 255?

Как все еще печатать 100000?

Обновление

Спасибо за ответы. Если он перезаписывает следующие байты, откуда C узнает, что это число больше одного байта, а не просто выглядит в первом байте?

Ответы [ 6 ]

11 голосов
/ 13 ноября 2010

Потому что в C нет проверки диапазона памяти.Он выделяет байт, а затем ваше назначение через указатель перезаписывает его и следующие три байта.Если вы выделили другой бит памяти сразу после первого malloc, но перед назначением, вы могли бы перезаписать часть кучи (в зависимости от того, как работает ваш malloc).

Именно поэтому указатели могут быть очень опаснымив C.

% d в выражении формата (плюс тип переменной) сообщает компилятору, что вы смотрите на int, и получает доступ ко всем четырем байтам.

Обратите внимание, что если выдействительно присвоил значение char, например, char * ptr;* ptr = 100000;

, а затем с некоторыми компиляторами (и при условии, что обычный символ рассматривается как подписанный, но по умолчанию) он вывел бы -96, а не 255 (или 127).Это связано с тем, что компилятор не ограничивает автоматически максимальное значение, которое может поместиться (127 для знака со знаком, 255 для знака без знака), а просто переполняется.Большинство компиляторов будут жаловаться на то, что вы пытаетесь присвоить постоянное значение, которое переполняет переменную.

Причина, по которой он равен -96, состоит в том, что 100000% 256 равно 160, но как символ со знаком он выводится как256-160).

5 голосов
/ 13 ноября 2010

Краткий ответ: вы вызываете неопределенное поведение, записывая в память, которая не принадлежит вам, поэтому вы никогда не знаете, что можете получить.Это может просто сработать, может произойти сбой или может произойти множество других вещей.В этом случае вы помещаете int в позицию, на которую ссылается ptr, которая записывает первый байт int в выделенную 1-байтовую область и разбивает все, что было в следующих трех байтах.

Когда вы читаете значение обратно с помощью спецификатора формата %d, printf читает sizeof(int) байтов из памяти, чтобы распечатать их как int.Если вы хотите вывести только значение одного байта, вам нужно сделать что-то вроде:

printf("%d\n", *(char*)ptr);

То есть сообщить компилятору, что ptr относится к char, затем получите char значение, которое преобразуется в int в списке аргументов и впоследствии корректно выводится спецификатором %d.

3 голосов
/ 13 ноября 2010

Это переполнение 3 байтов. Подобное переполнение от логотипа stackoverflow, но в куче, а не в стеке.

2 голосов
/ 13 ноября 2010

Самый простой ответ будет: он не гарантированно работает.

По сути, вы повреждаете память вокруг указателя.

1 голос
/ 13 ноября 2010

Как правило, malloc выделяет память с большей детализацией, чем один байт, поэтому ваш запрос на один байт зарезервирует пространство памяти, округленное до ближайших 8 байтов или около того. Затем вы пишете в это пространство, беспечно проходя мимо того, что вы зарезервировали. Затем вы разыменовываете указатель, возвращая int, и передаете его printf, который радостно печатает его.

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

1 голос
/ 13 ноября 2010

ответить на ваш обновленный вопрос;C "видит" * ptr, то есть содержимое ptr, где ptr указывает на целое число.Таким образом, он читает целое число из этого ptr.Он уже забыл, что вы выделили только одного персонажа.Две части (выделение и последующий доступ) не связаны ничем из того, что вы указали в своем коде.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...