Как значения r присваиваются значениям lvalues в сборке?

#c #assembly #stack #rvalue

Вопрос:

Первый вопрос здесь. Через несколько недель/месяцев мне нужно будет создать процедурный код, в котором будут функции, назначающие большие (я имею в виду действительно большие) наборы данных непосредственно указателям. Вот пример кода, который я буду выполнять :

 void MyFuntion(string* str)
{
     *str = "some data in a string";
}
 

Как это, безусловно, важно : я нахожусь в Windows 10, в visual-studio 2019, компилируюсь с компилятором c по умолчанию в выпуске x86.

Представьте себе что-то подобное, но со строками, которые могут содержать несколько миллионов символов, или с массивами int/float, также содержащими несколько миллионов элементов. Таким образом, это единственная операция, присваивающая значение r указателю, который, следовательно, находится в куче. Конечно, если я создам локальную переменную, содержащую данные, она будет больше 1 МБ и, следовательно, вызовет переполнение стека, верно ?

Как я понимаю, поскольку данные здесь существуют только как значение r, у них нет памяти, но я хотел бы знать : как значение r присваивается указателю ? Например, как это делается в сборке ? Я должен сказать, что я никогда не занимался сборкой, у меня есть несколько (очень мало) идей, но я хотел бы заняться этим, когда у меня будет время.

Является ли он временным, созданным в стеке или куче перед помещением в конечный адрес памяти ? Я предполагаю, что адрес памяти (указатель, в котором я назначаю данные) непосредственно заполняется данными, например, по частям, поэтому значения rvalue в памяти не существует.

Если я прав, единственными вещами, которые существуют в стеке здесь, являются : вызов функции, копирование указателя, затем инструкция, которая должна быть чем-то вроде «присвоить значение X значению Y», и размер инструкции не зависит от размера значения r и значения l, поэтому здесь не должно быть никаких проблем со стеком.

Итак, если я прав, этот код не должен вызывать никаких проблем, независимо от того, насколько велико значение r, но я все равно хотел бы знать, как это делается точно, с точки зрения сборки. Обратите внимание, что я не только ищу ответ, но и больше похож на некоторые ссылки, книги или документы, которые могли бы подробно объяснить. Я предполагаю, что то, что я ищу, не будет в книге на c , но больше похоже на книгу по сборке, это может быть хорошей отправной точкой для того, чтобы погрузиться в нее !

Комментарии:

1. Если string std::string да , то это не просто присвоение переменной, а вызов оператора присвоения строки. В сборке это выглядит иначе, чем присвоение примитивного типа.

2. @PeterCordes Да, но я не уверен, что эти детали (особенно возможная оптимизация) — это то, о чем на самом деле спрашивают. Когда я читаю вопрос, они предполагают, что в foo = huge_immediate_data; данных нигде не было данных до инструкции, и предполагают, что они будут переведены в большое количество mov инструкций с непосредственными значениями. Я пытался указать, что существует такая вещь, как статическое хранилище.

3. @olm: да, прочитав больше вопросов, согласился. Я точно знаю, какие неправильные представления есть у спрашивающего, но они включают идею о том, что сама инструкция может быть в стеке. (Этого не может быть.) Так что да, цикл, который копирует из статического хранилища, является ключевым моментом для того, что будет делать конструктор при передаче char* значения r в абстрактной машине C . Оптимизация с этого момента зависит от компилятора, но да, нет никаких причин для какого-либо использования стека вообще, за исключением обратного адреса. Пара скалярных вещей, таких как указатель, могут поместиться в регистрах.

4. Да, «статический класс хранения» языка Си-это место, где живут символы строковых литералов, а также глобальные и static переменные. Они обычно реализуются с помощью статического хранилища на языке ассемблера, которое да включает .data , .bss , .rodata (где вы найдете строковые литералы и другие константы) и .text раздел, в котором вы найдете машинный код. (Помните, что C и C предназначены для компиляции с опережением времени, нет необходимости в создании кода во время выполнения в куче или стеке. В стеке asm в обычной реализации хранятся только указатели функций и такие вещи, как адреса возврата).

5. только записывает значение rvalue непосредственно по адресу, указанному str — нет, в C он запускает std::string::operator=(const char*) функцию для std::string объекта с указанием delete на старую строку и создает новую из строки с указанием на C. Значение rvalue, указывающее на строковый литерал, становится функцией arg. Если бы вы выбрали пример с примитивным типом , например const char **str , тогда да , вы бы просто сохраняли указатель (на статическое хранилище) в объект указателя размером 4 или 8 байт, на который указывает str , где бы это ни было (стек, «куча»,. data или .bss).