В C хранятся ли символы в массиве (то есть в строке) в отдельных регистрах или в каждом регистре четыре символа?
Я пишу программу на C (32 бита), в которой я вывожу строку (длиной от 15 до 40 символов). Я решил использовать указатели и calloc вместо формального объявления массива. Моя программа функционирует совершенно нормально, так что это не вопрос логики или функции, мне просто любопытно, что "происходит под капотом" моего C-кода.
Мое понимание: когда я использую calloc, я выделяю часть памяти в байтах. Переменные хранятся в ячейках памяти размером 32 бита (или 4 байта). В моей программе я пишу символы, используя мой указатель (т.е. *ptr = '!';), А затем увеличиваю точки (ptr++;), чтобы перейти к следующей ячейке памяти.
Мой вопрос: если ячейки памяти 32-разрядные, и я записываю только 8-разрядную в эту ячейку памяти, оставшиеся 24-разрядные не используются? Если нет, то указатели, которые я использую, указывают на какое-то 8-битноерасположение в под-памяти, указывая на 8-битные участки в памяти?
6 ответов
Использование регистров - и, технически, даже наличие регистров вообще - это характеристика реализации C и аппаратного обеспечения, на котором он работает. Поэтому нет определенного ответа на ваш вопрос на уровне его общности. Это по большей части верно для любого вопроса о "что происходит под капотом".
Говоря с точки зрения типичных реализаций для товарного оборудования, тем не менее,
Мое понимание: когда я использую calloc, я выделяю часть памяти в байтах.
Разумная характеристика.
Переменные хранятся в регистрах размером 32 бита (или 4 байта).
Нет. Значения хранятся в регистрах. Реализации обычно предоставляют хранилище для значений переменных в обычной памяти, хотя эти значения могут быть скопированы в регистры для вычисления.
При некоторых специфических для реализации обстоятельствах некоторые переменные могут не иметь связанной области памяти, а их значения поддерживаются только в регистрах. В целом, однако, это никогда не относится к переменным или выделенному пространству, на которое указывал указатель, или когда-либо на него можно было сослаться.
В моей программе я пишу символы, используя мой указатель (т.е. *ptr = '!';), А затем увеличиваю точки (ptr++;), чтобы перейти к следующему регистру.
Нет, абсолютно нет. Увеличение указателя заставляет его указывать на следующий элемент вашего динамического хранилища, измеряемый в единицах размера указанного типа. Это не имеет ничего общего с регистрами. Запись в объект, на который указывает указатель, вероятно, включает использование регистра (потому что так работают ЦП), но в конечном итоге записанный символ попадает в обычную память.
Мой вопрос: если регистры 32-разрядные, и я записываю только 8-разрядные в этот регистр, оставшиеся 24-разрядные не используются?
Как я уже объяснил, этот вопрос основан на неправильном представлении. Цель вашей записи не является регистром. В любом случае в памяти нет пробелов между элементами, которые вы пишете.
Вполне возможно, что при некоторых обстоятельствах умный компилятор может оптимизировать ваш код для минимизации операций записи в память, собирая байты в регистре и выполняя записи в виде фрагментов такого размера. Может ли это быть или будет, зависит от реализации и действующих вариантов.
Если нет, то указатели, которые я использую, указывают на какое-то 8-битное распределение подрегистров, указывая на 8-битные секции регистров?
Ваши указатели (логически) указывают на основную память, которая (логически) адресуется в байтовых единицах. Они не указывают на регистры.
Эти указатели не обязательно хранятся в регистрах, обычно они просто хранятся в стеке. Это результат оптимизации компилятора. В некоторых компиляторах вы можете использовать register
заявление для обеспечения использования реестра.
Также нет "следующих" регистров, регистры не имеют адресов. Регистровый файл - это специальное аппаратное устройство, интегрированное в процессор и обычно называемое определенным набором битов.
Я советую вам использовать ваш компилятор или инструмент разборки, чтобы увидеть, как именно он выглядит в сборке.
Нет, регистр не задействован, в общем, они - дефицитный ресурс.
На самом деле происходит запись значений в ячейки памяти, на которые указывает возвращенный указатель. Указатели и арифметика указателей относятся к типу данных, поэтому возвращаемый указатель, приведенный к правильному типу, заботится о доступе.
Я пишу символы, используя мой указатель (т.е.
*ptr = '!';
), а затем я увеличиваю точки (ptr++;
) перейти к следующему регистру.
Не совсем, вы говорите о ячейке памяти, на которую указывает указатель ptr
, В случае, ptr
определяется как char *
, ptr++
такой же как ptr = ptr + 1
, который увеличивает ptr
по размеру указывающего типа данных, char
, Итак, после выражения ptr
указывает на следующий элемент в ячейке памяти.
Вы можете указать в c, что переменная входит в регистр, и большинство компиляторов оптимизируют это, но то, куда идет переменная, зависит от того, какая это переменная. Локальные переменные попадут в стек, функции выделения памяти должны поместить его в кучу и дать вам адрес. Константы и строковые литералы войдут в сегмент данных только для чтения.
Как указывал Сурав, вы неправильно используете регистры. В памяти есть регистр и есть ключевое слово register
в C. Но это не имеет ничего общего с указателями.
Типичный размер для выровненного блока памяти составляет 16/32/64 бит в зависимости от вашей архитектуры. Вы думаете, что вы увеличиваете указатель на этот размер блока. Это не правильно. В зависимости от того, какой у вас тип указателя, размер шага приращения может отличаться. Это всегда размер вашего соответствующего типа данных в байтах.
*char
получает увеличение на 1 байт, если вы делаете ++
в то время как *(long long)
увеличивается на 8.
Поскольку в некоторых случаях массивы могут распадаться на указатели, механика очень похожа.
Что вы думаете о том, что произойдет, если вы объявите два char
(или char
и int
в struct
), их адреса отличаются кратным размеру блока, а остальная часть памяти "теряется". Но когда вы распределили память, которой вы можете управлять, вы можете упаковать ее как массив.
Кажется, есть путаница в том, что такое регистр. Регистр - это место хранения внутри процессора. Регистры имеют разные функции. Тем не менее, программисты, как правило, занимаются общими регистрами и регистром состояния процессов.
Общие регистры - это рабочие места для выполнения вычислений. В некоторых системах все операции выполняются в регистрах. Таким образом, если вы хотите добавить два значения, вы должны загрузить оба в регистры, а затем добавить их. В наши дни большинство систем без RISC позволяют выполнять операции непосредственно в памяти.
Мое понимание: когда я использую calloc, я выделяю часть памяти в байтах. Переменные хранятся в регистрах размером 32 бита (или 4 байта). В моей программе я пишу символы, используя мой указатель (т.е. *ptr = '!';), А затем увеличиваю точки (ptr++;), чтобы перейти к следующему регистру.
Ваш компилятор может назначать переменные для существования в регистрах, а не в памяти. Однако каждый раз, когда вы разыменовываете указатель (например, * ptr), вы должны получить доступ к памяти.
Если вы позвоните
char *ptr = calloc (...)
Переменная ptr может (или не может) быть помещена в регистр. Все зависит от вашего компилятора. Значение, возвращаемое calloc - это расположение памяти, а не регистров.
Чтобы узнать это, вам нужно сгенерировать код на ассемблере из вашего компилятора. У большинства компиляторов есть такая опция, и они обычно чередуют ваш C-код с сгенерированным ассемблерным кодом.
Если вы делаете:
В моей программе я пишу символы, используя мой указатель (т.е. *ptr = '!';), А затем увеличиваю точки (ptr++;), чтобы перейти к следующему регистру.
Ваш сгенерированный код может выглядеть так (при условии, что ptr сопоставлен с R1):
MOVB '!', (R0)+
Который в нескольких системах переносит значение '!' по адресу, указанному R0, затем увеличивает R0 на единицу.
Мой вопрос: если регистры 32-разрядные, и я записываю только 8-разрядные в этот регистр, оставшиеся 24-разрядные не используются? Если нет, то указатели, которые я использую, указывают на какое-то 8-битное распределение подрегистров, указывая на 8-битные секции регистров?
В вашем случае вы не читаете и не записываете байты в регистры. Тем не менее, многие системы имеют подразделение REGISTER.