Страница 4 из 6

Re: А что, на функцию может быть не только указатель, но и ссылка?

Добавлено: 07 дек 2015, 19:36
Absurd
Указатель - это данное, хранящее адрес. Он не может быть синонимом чего либо за одним исключением - можно объявить одну или несколько ссылок и на указатель, тогда несколько указателей будут синонимичны друг другу.
Для компилятора все переменные int* это синонимы. То есть компилятор считает что где-то в программе есть один единственный int и все переменные типа int* ссылаются на него, то есть являются синонимами одного единственного инта. Об этом дальше и идет речь.

Re: А что, на функцию может быть не только указатель, но и ссылка?

Добавлено: 07 дек 2015, 19:38
Сионист
int *a = foo();
int *b = bar();
*a += 10;
return *a + *b;
}

Если ты попытаешься написать коспилятор Си (*), то ты поймешь что в общем случае невозможно определить a или b во время выполнения будут равны или не равны. Поэтому компилятор С всегда исходит из пессимистического предположения что они равны.
При том, что они заведомо не равны? Однако! Кстати, если она вдруг окажутся равны, а ты используешь их оба, то сума будет правильной, а вот если наоборот, то ты удвоишь одно слагаемое.

Код: Выделить всё

int x=2;
int y=3;
return x+y;
. Если компилятор к третьей строке забыл о равенстве и исходит из того, что они равны, то выполнено будет или

Код: Выделить всё

return x+x;
, или

Код: Выделить всё

return y+y;
, в первом случае результат будет 4, во втором случае результат будет 6, а правильный результат 5.

Код: Выделить всё

int x=2;
int y=2;
return x+y;
Что x+x, что y+y, что x+y равны 4, ошибиться не возможно. Так какой вариант пессимистический? Адреса здесь точно также заведомо не равны. Но вот другой текст.

Код: Выделить всё

int x=2;
int &y=x;
return x+y;
. Видим

Код: Выделить всё

int &y=x;
и заменяем все последующие упоминания y на x:

Код: Выделить всё

int x=2;
int &y=x;
return x+x;
. Если предположить, что y реализован на указателе на x, то значение этого указателя измениться уже не может, а во второй строчке сказано, что он равен адресу x, значит там всегда будет адрес x, а при разыменовании всегда будет получать синоним икса, замена допустима. Ещё один текст.

Код: Выделить всё

int x=2;
int *y=&x;
return x+*y;
. А вот здесь есть одна особенность: значение игрека может измениться, а адрес икса - нет, значит их идентичность не гарантирована. Хотя именно в этом случае, когда y фактически не меняется, можно оптимизировать до ссылки, а дальше поступать, как в предыдущем случае. А если между второй и третьей строкой будет ещё какой то текст? Здесь:

Код: Выделить всё

int x=2;
int &y=x;
return x+x;
вставка текста между второй и третьей строкой не влияет на то, что игрек всегда будет иксом, здесь:

Код: Выделить всё

int x=2;
int *y=&x;
return x+*y;
- нет, вот здесь как раз можно вставить текст, меняющий значение y, то есть адрес *y. Анализировать, как именно меняется игрек и выяснять, восстанавливает ли он своё значение? Ну иногда это возможно. А если

Код: Выделить всё

int x=2;
int *y=&x;
int d;
std::cin>d;
y+=d;
y-=4;
return x+*y;
? Первое изменение y известно только на этапе исполнения, равно ли оно четырём - тоже.

Re: А что, на функцию может быть не только указатель, но и ссылка?

Добавлено: 07 дек 2015, 19:42
Absurd
При том, что они заведомо не равны?
Почему заведомо? Даже если foo() и bar() это не extern функции, чтобы понять что они возвращают в общем случае надо решить т.н. "проблему останова". Алан Тьюринг в 1936 году доказал что она неразрешима.

Re: А что, на функцию может быть не только указатель, но и ссылка?

Добавлено: 07 дек 2015, 20:12
Сионист
Почему заведомо?
А ты свой текст читал? Ну хорошо:

Код: Выделить всё

int x=2;
int y=2;
int *z=&x;
int *t=&y;
. Ну ка как z и t могут оказаться уже равны, если им присвоены адреса двух заведомо разных сущностей? Равны *z и *t, но ни как не z и t.

Re: А что, на функцию может быть не только указатель, но и ссылка?

Добавлено: 07 дек 2015, 20:44
Absurd

Код: Выделить всё

int x=2;
int y=2;
int *z=&x;
int *t=&y;
z и t это переменные, они могут измениться. Никто не будет ходить по control flow graph'у чтобы при помощи автоматического доказательства теорем доказать что они всегда не равны. Компилятор должен работать быстро, а иначе люди будут сердиться и плеваться.

В С99 можно сказать явно что они никогда не равны, а если равны то это ошибка и undefined behavior.

Код: Выделить всё

int x=2;
int y=2;
int * __restrict z=&x;
int * __restrict t=&y;
Тут явно указывано что z и t никогда не равны ничему кроме себя самих. Иногда это позволяет сгенерировать в два раза более оптимальный код.

Добавлено: 07 дек 2015, 20:44
Сионист
А ну да, прогнал, проглядев функции. Но в случае указателей это не имеет значения, так как они могут быть не равны. И хотя поддерживается и

Код: Выделить всё

int x;
int y;
int &f1()
{
 return x;
}
int &f2()
{
 return y;
}
...
int &z=f1();
int &t=f2();
, но это можно обработать как особый случай, отдельно от декларации ссылки как синонима переменной.
Компилятор должен работать быстро, иначе люди будут сердиться и плеваться.
Знал бы ты, как медленно он у меня работает. Я и поиграть успеваю в две игры, и японский кросворд разгадать, и кино посмотреть за время компиляции. И время компиляции - не повод утверждать, что все вдруг занялись её оптимизацией в ущерб времени исполнения. Вот приложения должны работать быстро, иначе люди будут кидаться. Молотками по компам.

Re: А что, на функцию может быть не только указатель, но и ссылка?

Добавлено: 07 дек 2015, 20:58
Absurd
Знал бы ты, как медленно он у меня работает. Я и поиграть успеваю в две игры, и японский кросворд разгадать, и кино посмотреть за время компиляции.
Да, у нас на форуме один Сионист имеет опыт компиляции С++ проектов.
И время компиляции - не повод утверждать, что все вдруг занялись её оптимизацией в ущерб времени исполнения.
Фортран компилируется быстро и проблем с синонимами не имеет, отчего генерирует очень быстрый код для работы с многомерными массивами. Поэтому в научных вычислениях обычно используют именно его. По крайней мере, использовали до недавнего времени.

Добавлено: 09 дек 2015, 08:53
Сионист
Оказывается я фортран в серьёзных проектах использовал. Интересно как, если я его толком не знаю? А если серьёзно, то у меня пока нет завершённых проектов на c++, имеющих какое либо иное назначение кроме научных вычислений. Для остального бейсик, паскаль и внимание... фортран! Спектрумовскеий бейсик проблем с синонимами не имеет, но даже в мануэле к нему пишут, что бейсик-программы исполняются в 50 раз медленнее, чем компилированные и даже не уточняется, с какого языка компилированные, видимо имеется ввиду некая усреднённая скорость исполнения программ. Не мифическая проблема синонимов влияет на скорость исполнения, а совсем другие факторы. Во-первых интерпретация текста медленнее исполнения натива. Но даже в мануале к балсту, а это оптимизирующий компилятор бейсика, обещано ускорение по сравнению со встроенным интерпретатором всего в 36 раз, а не в 50. Тормоза также появляются, когда до рантайма доживает таблица переменных. Плохое решение, но возможное, если кто задастся целью сделать пессимизирующий компилятор, то может быть именно так компилируемые им программы и будут тормозить. Но хорошие компиляторы так не делают, не зависимо от языка. Следующие факторы: избыточные проверки и не только на выход за пределы массива и переувлечение вариэнтом и подобными ему типами. Дальше идёт переувлечение действительными типами. На спектрумовском бейсике нет целого типа, так что даже счётчики всех циклов приходится юзать действительные. Инкрмент быстрее, чем , так как икремент выполняется за одну операцию, а - это сначала копирование i в буфер, потом увеличение его на единицу операцией ADD, требующей к тому же загрузки лишнего операнда, а потому могущей выполняться медленнее, чем INC, а потом копирование суммы в I. И здесь приходим к вопросу о качестве оптимизатора в компиляторе, если он способен заменить всю цепочку на INC, то скорость исполнения от отсутствия в языке инкремента не пострадает, а если нет, то пострадает. На c++ же уже есть готовый инкремент. тоже быстрей, чем , так как - это просто ADD, а - ADD с двумя копированиями. Но это различие проявляется только если x уже хранится в регистре, так как регистр - тот самый буфер. И опять таки одно может быть оптимизировано до другого компилятором. То же самое относится к декременту и составному оператору вычитания и присваивания. также быстрей, чем , а - чем , а если x и o целые, то даже чем

Код: Выделить всё

x:=x div o;
и тоже из-за двух копирований, но оптимизируется до CODE]x:=x*n;[/CODE] оптимизируется до , а и

Код: Выделить всё

x:=x div o;
- до . Опять таки если в языке уже есть составные операторы, то можно их сразу использовать, а если их нет, то остаётся полагаться на оптимизатор неизвестного качества. В последнюю очередь назову переувлечение указателями, из перечисленного оно влияет меньше всего. Ив последнюю - не возможность изменения локальных копий параметров и операднов, которые не были объявлены как изменяемые, то есть через которые нельзя вернуть значения. Тот же факториал

Код: Выделить всё

unsigned long long int f(unsigned short int n)
{
 unsigned long long int r;
 for (r=1; n>0; --n)
 {
  r*=n;
 }
 return r;
}
на одну операцию быстрей, чем

Код: Выделить всё

unsigned long long int f(unsigned short int n)
{
 unsigned long long int r;
 unsigned short int i;
 for (r=1, i=n; i>0; --i)
 {
  r*=n;
 }
 return r;
}
. А если язык не допускает таких вольностей в отношении локальных копий? Если любой параметр эквивалентен или переданному по обычной ссылке, или переданному по константной ссылке? И это только то, валяется на "поверхности". А ведь есть и нюансы, известные только разработчикам оптимизаторов и не факт, что оптимизатор в компиляторе того же фортрана одного качества с плюсовым, это разные программы, писанные разными программистами. Кстати, явные именованные ссылки могут быть хоть как то полезны ровно в двух случаях:
1. Параметры и операнды.
2. Присваивание переменной значения функции, возвращающей ссылку, или такого же операнда в тех случаях, когда требуется зафиксировать адрес, к которому происходит обращение, но функция/оператор может возвращать ссылку на новую функцию/переменную при каждом обращении.
В остальных случаях они безтолку усложняют текст.

Может быть любая ссылка - надстройка над указателем. Стандарт требует, чтоб любая ссылка вела себя, как если бы была надстройкой над указателем (хотя чёрт его знает, может и другими словами это формулирует). Но стандарт не требует именно такой реализации абсолютно всех ссылок.

Re: А что, на функцию может быть не только указатель, но и ссылка?

Добавлено: 09 дек 2015, 17:22
Absurd
Мог бы просто в одну строчку написать "Я читал про спектрумовский бейсик, но не читал про static single assignment form", короче бы вышло.

Да, и чтение-запись в регистры не дает 50-кратного ускорения над ячейками памяти в стеке. Часто используемые ячейки памяти хранятся в кеше L0, и вершина стека с локальными переменными попадает именно туда. У меня есть виртуальная машинка, в ней в режиме интерпретации каждому регистру соответствует ячейка в массиве. Виртуалка умеет выделять функции по тому куда часто делают call и компилирует их в нативный код. При этом ячейки памяти с псевдорегистрами заменяются на настоящие нативные регистры. Ускорения в 50 раз не происходит. В Debug компиляции ускорение в 18 раз, в Release режиме интерпретатор по видимому оптимизируется очень хорошо, и ускорение всего в 6 раз. Проблема спектрумовского бейсика скорее всего в том что на Z80 не было аппаратного ускорителя плавающей точки, и для каждого инкремента переменной вызывалась довольно нетривиальная подпрограмма из ПЗУ, которая сначала денормализовала экспоненту, потом прибавляла к мантиссе единичку, а потом нормализовала экспоненту обратно. Хотя нет, кажется припоминаю что там был union в котором либо int16 либо плавающая точка.
Стандарт требует, чтоб любая ссылка вела себя, как если бы была надстройкой над указателем
Когда функция или метод класса с внешней линковкой возваращают ссылку не существует иного способа кроме ее реализовать кроме как при помощи передачи адреса. Если же возможно ее заинлайнить, то можно промежуточную ссылку выкинуть и работать непосредственно со значением на которое она ссылается. Но и указатель тоже можно выкинуть в таком случае.

Re: А что, на функцию может быть не только указатель, но и ссылка?

Добавлено: 09 дек 2015, 18:34
Romeo
Очень здраво рассуждаешь, Absurd. Причём один комментарий лучше другого. Давно уже хотел репутацию апнуть тебе. Вот добрались руки :)