В C символы в массиве (т.е. строка) хранятся в отдельных регистрах или в каждом регистре четыре символа?

Я пишу программу на C (32 бита), в которой я вывожу строку (длиной от 15 до 40 символов). Я решил использовать указатели и calloc вместо формального объявления массива. Моя программа работает совершенно нормально, так что это не вопрос логики или функции, мне просто любопытно, что «происходит под капотом» моего кода C.

Насколько я понимаю, когда я использую calloc, я выделяю раздел памяти в байтах. Переменные хранятся в ячейках памяти размером 32 бита (или 4 байта). В моей программе я пишу символы, используя свой указатель (например, *ptr = '!';), а затем увеличиваю количество точек (ptr++;), чтобы перейти к следующему участку памяти.

Мой вопрос: если ячейки памяти являются 32-битными, и я записываю только 8-битные в эту ячейку памяти, оставшиеся 24-битные неиспользованный? Если нет, то указатели, которые я использую, указывают на какую-то 8-битную суб-ячейку памяти, указывая на 8-битные разделы ячейки памяти?


person MCHatora    schedule 22.02.2017    source источник
comment
Под регистрами вы имеете в виду ячейки памяти?   -  person Eugene Sh.    schedule 22.02.2017
comment
Вы пишете Переменные хранятся в регистрах размером 32 бита (или 4 байта). Неправда, скорее Переменные могут быть сохранены в регистрах размером 32 бита ( или 4 байта).   -  person Jabberwocky    schedule 22.02.2017
comment
Если регистры 32-битные, и я записываю в этот регистр только 8-битные, оставшиеся 24-битные не используются? является проблемой сборки и не специфицируется и не контролируется C. Существует много возможностей. С точки зрения C это не имеет значения.   -  person chux - Reinstate Monica    schedule 22.02.2017
comment
Имейте также в виду, что в C '!' относится к типу int, а не к типу char, поэтому будет использоваться полная ширина регистра, не оставляя неиспользуемых битов.   -  person Weather Vane    schedule 22.02.2017
comment
Ты не хочешь знать. Потому что тебе должно быть все равно.   -  person Jens    schedule 22.02.2017
comment
Непонимание. В одном байте 8 бит. Если вы хотите выделить 16 байт, вы вызываете calloc(16,sizeof(char)) и получаете указатель (адрес), поэтому этот указатель сначала сохраняется в регистре (регистр имеет ширину 32 или 64 бита). Если вы хотите, вы можете сохранить его в памяти или в любом другом месте, чтобы вы могли получить к нему доступ позже. Когда вы вызываете *ptr++='a', вы просто записываете один байт (8 бит) в адрес, хранящийся в переменной 'ptr', и увеличиваете этот адрес на один байт (конечно, ptr должен иметь тип 8-битного целочисленного указателя -char - который это байт).   -  person ttdado    schedule 22.02.2017
comment
Чтобы было ясно, переменные хранятся в регистрах размером 32 бита (или 4 байта) неправильно. Переменные хранятся в какой-то памяти. Переменная может занимать только 1 байт, 15 байт, 42 байта, 8675309 байт и т. д. Это не зависит от размера регистров процессора. Запись в n-байт влияет на эти n-байты. Если запись влияет на другую память, эта память в это время не используется и не имеет значения.   -  person chux - Reinstate Monica    schedule 22.02.2017
comment
Ваш вопрос смешивает слишком много вещей. Вы должны сначала посмотреть, что на самом деле представляют собой аппаратные регистры. Затем вы должны найти ключевое слово register для C, что является чем-то совершенно другим.   -  person Jens Gustedt    schedule 22.02.2017
comment
Да, я по невнимательности употребил здесь неправильную терминологию: не должно быть ссылок на регистры. Что я действительно имею в виду, так это места в памяти. Прошу прощения за путаницу.   -  person MCHatora    schedule 23.02.2017


Ответы (6)


Использование регистров — и, технически, даже существование регистров вообще — это характеристика реализации C и оборудования, на котором она работает. Поэтому нет окончательного ответа на ваш вопрос на его уровне общности. Это по большей части верно для любого вопроса о том, «что происходит под капотом».

Однако, говоря о типичных реализациях для массового оборудования,

Мое понимание: когда я использую calloc, я выделяю раздел памяти в единицах байтов.

Разумная характеристика.

Переменные хранятся в регистрах размером 32 бита (или 4 байта).

Нет. Значения хранятся в регистрах. Реализации обычно обеспечивают хранение значений переменных в обычной памяти, хотя эти значения могут быть скопированы в регистры для вычислений.

При некоторых обстоятельствах, зависящих от реализации, некоторые переменные могут не иметь связанной ячейки памяти, а их значения будут храниться только в регистрах. Однако, вообще говоря, это никогда не относится к переменным или выделенному пространству, на которое есть, было или когда-либо могло быть обращение с помощью указателя.

В моей программе я пишу символы, используя свой указатель (например, *ptr = '!';), а затем увеличиваю точки (ptr++;), чтобы перейти к следующему регистру.

Нет, абсолютно нет. Увеличение указателя приводит к тому, что он указывает на следующий элемент вашей динамической памяти, измеряемый в единицах размера указываемого типа. Это не имеет никакого отношения к реестрам. Запись в указанный объект, вероятно, требует использования регистра (поскольку именно так работают ЦП), но в конечном итоге записанный символ оказывается в обычной памяти.

Мой вопрос: если регистры 32-битные и я записываю в этот регистр только 8-битные, оставшиеся 24-битные не используются?

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

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

Если нет, то указатели, которые я использую, указывают на какое-то 8-битное выделение подрегистров, указывая на 8-битные разделы регистров?

Ваши указатели (логически) указывают на основную память, которая (логически) адресуется в единицах размера байта. Они не указывают на регистры.

person John Bollinger    schedule 22.02.2017
comment
Что ж, мой ответ забылся до того, как я его отправил^^. +1 за этот очень подробный ответ - person Kami Kaze; 22.02.2017
comment
Спасибо за подробный ответ. Я полностью использовал здесь неправильную терминологию, потому что я невежественен. Все ссылки на регистр терминов должны читать адрес памяти. Таким образом, ячейки памяти адресуют байты, а не слова? - person MCHatora; 23.02.2017
comment
@MCHatora, как видите, правильная терминология необходима для точного общения. Однако, даже заменяя место в памяти регистром, у вас все еще есть недоразумение, указанное в самом конце моего ответа: в C ячейки памяти имеют (логически) размер один байт, а не четыре. Это не имеет ничего общего с собственным размером слова базовой архитектуры. - person John Bollinger; 23.02.2017
comment
Да, это был настоящий вопрос, который у меня возник: сколько бит содержит адрес памяти (то есть 8). Спасибо за этот ответ! - person MCHatora; 23.02.2017

Нет, реестр тут ни при чем, в общем, это дефицитный ресурс.

На самом деле происходит следующее: вы записываете значения в области памяти, на которые указывает возвращенный указатель. Указатели и арифметика указателей относятся к типу данных, поэтому возвращаемый указатель, приведенный к правильному типу, обеспечивает доступ.

Я пишу символы, используя свой указатель (например, *ptr = '!';), а затем увеличиваю точки (ptr++;), чтобы перейти к следующему регистру.

Не совсем так, вы говорите об области памяти, на которую указывает указатель ptr. В случае, если ptr определяется как char *, ptr++ совпадает с ptr = ptr + 1, что увеличивает ptr на размер указывающего типа данных, char. Итак, после выражения ptr указывает на следующий элемент в ячейке памяти.

person Sourav Ghosh    schedule 22.02.2017

Эти указатели не обязательно будут храниться в регистрах, обычно они просто хранятся в стеке. Это результат оптимизации компилятора. В некоторых компиляторах вы можете использовать оператор register, чтобы обеспечить использование регистра.

Также нет «следующих» регистров, у регистров нет адресов. Регистровый файл — это специальный аппаратный модуль, встроенный в процессор и обычно именуемый определенным набором битов.

Я советую вам использовать ваш компилятор или инструмент для дизассемблирования, чтобы точно увидеть, как это выглядит на ассемблере.

person Uriel Zilberberg    schedule 22.02.2017
comment
Ключевое слово register в настоящее время обычно игнорируется. См. этот вопрос SO - person Jabberwocky; 22.02.2017
comment
to ensure-- нет, совсем нет, компилятор может свободно и, вероятно, полностью игнорировать ключевое слово register. - person Sourav Ghosh; 22.02.2017
comment
normally they will be just stored on the stack., к сожалению, в стандарте C нет понятия stack, это до реализации. - person Sourav Ghosh; 22.02.2017

Вы можете указать в c, что var помещается в регистр, и большинство компиляторов оптимизируют это, но куда помещается var, зависит от типа переменной. Локальные переменные будут помещаться в стек, функции распределения памяти должны помещать их в кучу и давать вам адрес. Константы и строковые литералы перейдут в сегмент данных только для чтения.

person NathanAck    schedule 22.02.2017

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

Типичный размер выровненного блока памяти составляет 16/32/64 бита в зависимости от вашей архитектуры. Вы думаете, что увеличиваете свой указатель на этот размер блока. Это неправильно. В зависимости от того, какой тип указателя у вас есть, ваш размер шага приращения различается. Это всегда размер вашего соответствующего типа данных в байтах.

*char увеличивается на 1 байт, если вы выполняете ++, а *(long long) увеличивается на 8.

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

Вы думаете о том, что произойдет, если вы объявите два char (или char и int в struct), их адреса различаются на кратное размеру блока, а остальная часть памяти «тратится впустую». Но поскольку вы выделили память, которой вы можете управлять, вы можете упаковать ее подобно массиву.

person Kami Kaze    schedule 22.02.2017

Кажется, существует путаница в отношении того, что такое регистр. Регистр — это место хранения внутри процессора. Регистры имеют разные функции. Однако программисты обычно интересуются ОБЩИМИ РЕГИСТРАМИ и регистром статуса процесса.

Общие регистры — это рабочие места для выполнения вычислений. В некоторых системах все операции выполняются в регистрах. Таким образом, если вы хотите добавить два значения, вы должны загрузить оба в регистры, а затем добавить их. В настоящее время большинство не-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.

person user3344003    schedule 22.02.2017