2014 dxdy logo

Научный форум dxdy

Математика, Физика, Computer Science, Machine Learning, LaTeX, Механика и Техника, Химия,
Биология и Медицина, Экономика и Финансовая Математика, Гуманитарные науки




Начать новую тему Ответить на тему На страницу 1, 2, 3, 4, 5  След.
 
 Вопрос по работе компилятора C++
Сообщение28.02.2017, 13:11 
Аватара пользователя


05/08/11
36
Калининград
Как я понимаю, необходимое количество памяти под автоматические переменные подсчитывается на этапе компиляции. А именно: компилятор смотрит на то, какие функции вызываются, смотрит на локальные переменные, которые имеются в определении этих функций и суммирует их. Но как он может подсчитать память в случае, когда определение функции ему не доступно? Например находится в другой единице трансляции.

 Профиль  
                  
 
 Re: Вопрос по работе компилятора C++
Сообщение28.02.2017, 13:16 
Заслуженный участник


02/08/11
6874
Alex Fox в сообщении #1195949 писал(а):
компилятор смотрит на то, какие функции вызываются
Функции-то тут при чём?
Alex Fox в сообщении #1195949 писал(а):
смотрит на локальные переменные, которые имеются в определении этих функций
В определении функции нет переменных, локальных или иных.

Alex Fox в сообщении #1195949 писал(а):
необходимое количество памяти под автоматические переменные подсчитывается на этапе компиляции
Да. Для каждой такой переменной компилятор знает тип, по типу он определяет размер (и ругается, если не может определить, что случается, если тип неполный) каждой локальной переменной и суммирует их.

 Профиль  
                  
 
 Re: Вопрос по работе компилятора C++
Сообщение28.02.2017, 13:24 
Заслуженный участник
Аватара пользователя


16/07/14
8347
Цюрих
warlock66613 в сообщении #1195951 писал(а):
В определении функции нет переменных, локальных или иных.
Вот у нас есть определение функции, в нем есть определение переменной - это не называется "в определении функции есть переменная"?

Alex Fox, для того, чтобы скомпилировать вызов функции, ее определение не нужно - достаточно объявления. И, соответственно, не нужно знать, что внутри функции.

 Профиль  
                  
 
 Re: Вопрос по работе компилятора C++
Сообщение28.02.2017, 13:24 
Аватара пользователя


05/08/11
36
Калининград
Цитата:
В определении функции нет переменных, локальных или иных.


Ну как же? например:
Используется синтаксис C++
void foo(char ch)
{
    int a(0);
    char b(0);
    {
         double c(0.0);
    }
}
 

может вы имели ввиду объявление?

 Профиль  
                  
 
 Re: Вопрос по работе компилятора C++
Сообщение28.02.2017, 13:24 
Заслуженный участник


04/03/09
906
Alex Fox в сообщении #1195949 писал(а):
Но как он может подсчитать память в случае, когда определение функции ему не доступно? Например находится в другой единице трансляции.

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

 Профиль  
                  
 
 Re: Вопрос по работе компилятора C++
Сообщение28.02.2017, 13:28 
Аватара пользователя


05/08/11
36
Калининград
12d3 в сообщении #1195955 писал(а):
В скомпилированном коде, сначала происходит вызов функции, а потом только сдвигается указатель стека, чтобы хватило для всех переменных внутри этой функции. Таким образом, "снаружи" этой функции совершенно необязательно знать, сколько и каких внутри нее переменных.


Как указатель узнает на какую величину ему нужно сдвинуться, чтобы хватило места под все переменные? Это ведь было подсчитано каким-то образом на этапе компиляции.

 Профиль  
                  
 
 Re: Вопрос по работе компилятора C++
Сообщение28.02.2017, 13:36 
Заслуженный участник


04/03/09
906
Alex Fox в сообщении #1195957 писал(а):
Как указатель узнает на какую величину ему нужно сдвинуться, чтобы хватило места под все переменные?

Alex Fox в сообщении #1195949 писал(а):
смотрит на локальные переменные, которые имеются в определении этих функций и суммирует их.


Естественно, это происходит на этапе компиляции этой самой функции, а не на этапе компиляции других функций, в которых вызывается первая.

 Профиль  
                  
 
 Re: Вопрос по работе компилятора C++
Сообщение28.02.2017, 13:45 
Заслуженный участник
Аватара пользователя


16/07/14
8347
Цюрих
Alex Fox в сообщении #1195957 писал(а):
Это ведь было подсчитано каким-то образом на этапе компиляции.
На этапе компиляции функции, но не вызова функции.
Посмотрите, как выглядит вызов: https://godbolt.org/g/ush0B4 (строчки 10-15). Попробуйте поменять что-то внутри f, не меняя ее сигнатуру - увидите, что в вызове функции ничего не поменялось.

 Профиль  
                  
 
 Re: Вопрос по работе компилятора C++
Сообщение28.02.2017, 13:46 
Заслуженный участник


02/08/11
6874
Alex Fox в сообщении #1195954 писал(а):
может вы имели ввиду объявление?
Да. Вечно путаю эти термины.

 Профиль  
                  
 
 Re: Вопрос по работе компилятора C++
Сообщение28.02.2017, 14:08 
Аватара пользователя


05/08/11
36
Калининград
Вот у меня есть два .cpp файла:
код: [ скачать ] [ спрятать ]
Используется синтаксис C++
fibonacci.cpp

int Fib(int num)
{
    int prevVal = 0;
    int val= 1;
    int temp;

    for(int n = 1; n < num; n++)
    {
        temp = val;
        val += prevVal ;
        prevVal = temp;
    }
    return(val);
}
 

Используется синтаксис C++
main.cpp
#include <....>

int fib(int num);

int main()
{
    int n = fib(5);
    return 0;
}
 

Компилятор ведь не может видеть одновременно оба .cpp. Он отдельно смотрит main.cpp видит вызов fib(5), и должен как-то узнать на какую величину нужно будет передвинуть указатель стека, чтобы выполнить эту функцию. Затем компилируется fibonacci.cpp, здесь есть определение функции, из которого он может узнать сколько нужно памяти под вызов этой функции, как он может связать эту информацию вместе с вызовом, или этим уже не компилятор занимается, а компоновщик?

 Профиль  
                  
 
 Re: Вопрос по работе компилятора C++
Сообщение28.02.2017, 14:11 


11/12/14
893
В первых редакциях языка C, которые еще от Кернигана и Ричи, коду вызывающему функцию вообще не надо было ничего знать о функции.
Ни количество и состав аргументов, ни тип возвращаемого значения.
Соглашение о вызове функций было устроено таким образом, что передача большего числа значений в функцию не могла ничего поломать, потому что стек от переданных параметров очищал вызывающий код, а параметры передавались так, чтобы вершина стека всегда "смотрела" на первый из них. Возвращаемое же значение возвращалось в регистре-аккумуляторе, и если функция ничего не возвращала, то всё равно возвращала какой то мусор в этом регистре, оставшийся от последних действий, просто вызывающий код может отбрасывать ненужное ему значение, ну а если захочет использовать то чего нет, то просто получит мусор, но получит без ошибки времени выполнения.
Т.к. все примитивные типы были в то время довольно ограниченными, укладывающимися при передаче всегда машинное слово, то можно было без ошибок вызвать функцию даже не зная заранее её сигнатуры, причём без ошибок даже вызвать с большим числом параметров, и даже использовать несуществующее возвращаемое значение, но тут уже по ситуации.

Этим, собственно, до сих пор пользуется любой C-runtime, когда большой спектр вариантов точки входа: void main(), int main(void) и int main( int argc, char *argv[] ) безропотно проглатываются компилятором и системой линковки. Ну и до сих пор соглашение об очистке стека от параметров вызывающим кодом лежит в основе работы эллипсиса (printf(...))

 Профиль  
                  
 
 Re: Вопрос по работе компилятора C++
Сообщение28.02.2017, 14:11 
Аватара пользователя


05/08/11
36
Калининград
mihaild в сообщении #1195964 писал(а):
На этапе компиляции функции, но не вызова функции.
Посмотрите, как выглядит вызов: https://godbolt.org/g/ush0B4 (строчки 10-15). Попробуйте поменять что-то внутри f, не меняя ее сигнатуру - увидите, что в вызове функции ничего не поменялось.


Не могу прочитать ассемблерный код, но любопытно.

 Профиль  
                  
 
 Re: Вопрос по работе компилятора C++
Сообщение28.02.2017, 14:22 


11/12/14
893
Alex Fox в сообщении #1195976 писал(а):
...


Код в main.cpp знает (исходя из доступной декларации функции), что у неё 1 параметр типа int.
Он ложит его в стек и выполняет инструкцию call по переходу на адрес функции (при этом в стек дополнительно ложится адрес возврата).
Функция уже в своём теле знает, что у неё 3 локальных переменных типа int - она просто берет и (сама) добавляет их к стеку.
При возврате функция убирает свои локальные переменные со стека сама.
Далее возможны варианты - если это "классический си", то функция выполняет инструкцию ret, которая убирает со стека адрес возврата и переходит по нему, чтобы продолжить выполнение вызвавшего функцию кода, а уже этот код еще раз убирает со стека параметр функции и продолжает работу.
Если же мы имеем дело с соглашениями по вызову функций адаптированными под архитектуру Intel 386, то сама функция может выполнить инструкцию ret X, где X это размер параметров, которые уберутся при возврате из функции автоматически, это меньше инструкций в ассемблере получится.

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

 Профиль  
                  
 
 Re: Вопрос по работе компилятора C++
Сообщение28.02.2017, 14:31 
Аватара пользователя


05/08/11
36
Калининград
Как компилятор тогда высчитывает сколько всего нужно памяти выделить под стек? Или размер стека фиксирован и не зависит от компилятора?

 Профиль  
                  
 
 Re: Вопрос по работе компилятора C++
Сообщение28.02.2017, 14:38 


10/04/12
704
В результате компиляции образуется объектный код, в котором перечислены глобальные объекты. Память уже подсчитывает линкер, у которого есть доступ ко всем объектным файлам. Он обладает всей полнотой информации и решает, какие секции будут в исполняемом файле, и что по какому смещению будет располягаться. Что надо поместить в глобальную память, что в константную, если такая есть, что забито нулями а что будет проинициализировано при старте программы, ...

Например, если у нас проект состоит из двух файлов a.c и b.c, то обычно его можно скомпилировать командами:
Используется синтаксис Bash
gcc -c a.c -o a.o # Компиляция первого файла, на выходе создаёться файл a.o
gcc -c b.c -o b.o # Компиляция второго файла, на выходе создаёться файл b.o
gcc a.o b.o -o my-prog # Компоновка исполняемого файла, на выходе создаёться файл my-prog
 


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

 Профиль  
                  
Показать сообщения за:  Поле сортировки  
Начать новую тему Ответить на тему  [ Сообщений: 61 ]  На страницу 1, 2, 3, 4, 5  След.

Модераторы: Karan, Toucan, PAV, maxal, Супермодераторы



Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей


Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете добавлять вложения

Найти:
Powered by phpBB © 2000, 2002, 2005, 2007 phpBB Group