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

Модераторы: Hawk, Romeo, Absurd, DeeJayC, WinMain

Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Указатель - это данное, хранящее адрес. Он не может быть синонимом чего либо за одним исключением - можно объявить одну или несколько ссылок и на указатель, тогда несколько указателей будут синонимичны друг другу.
Для компилятора все переменные int* это синонимы. То есть компилятор считает что где-то в программе есть один единственный int и все переменные типа int* ссылаются на него, то есть являются синонимами одного единственного инта. Об этом дальше и идет речь.
2B OR NOT(2B) = FF
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

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 известно только на этапе исполнения, равно ли оно четырём - тоже.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

При том, что они заведомо не равны?
Почему заведомо? Даже если foo() и bar() это не extern функции, чтобы понять что они возвращают в общем случае надо решить т.н. "проблему останова". Алан Тьюринг в 1936 году доказал что она неразрешима.
2B OR NOT(2B) = FF
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

Почему заведомо?
А ты свой текст читал? Ну хорошо:

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

int x=2;
int y=2;
int *z=&x;
int *t=&y;
. Ну ка как z и t могут оказаться уже равны, если им присвоены адреса двух заведомо разных сущностей? Равны *z и *t, но ни как не z и t.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

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

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 никогда не равны ничему кроме себя самих. Иногда это позволяет сгенерировать в два раза более оптимальный код.
2B OR NOT(2B) = FF
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

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

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

int x;
int y;
int &f1()
{
 return x;
}
int &f2()
{
 return y;
}
...
int &z=f1();
int &t=f2();
, но это можно обработать как особый случай, отдельно от декларации ссылки как синонима переменной.
Компилятор должен работать быстро, иначе люди будут сердиться и плеваться.
Знал бы ты, как медленно он у меня работает. Я и поиграть успеваю в две игры, и японский кросворд разгадать, и кино посмотреть за время компиляции. И время компиляции - не повод утверждать, что все вдруг занялись её оптимизацией в ущерб времени исполнения. Вот приложения должны работать быстро, иначе люди будут кидаться. Молотками по компам.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

Знал бы ты, как медленно он у меня работает. Я и поиграть успеваю в две игры, и японский кросворд разгадать, и кино посмотреть за время компиляции.
Да, у нас на форуме один Сионист имеет опыт компиляции С++ проектов.
И время компиляции - не повод утверждать, что все вдруг занялись её оптимизацией в ущерб времени исполнения.
Фортран компилируется быстро и проблем с синонимами не имеет, отчего генерирует очень быстрый код для работы с многомерными массивами. Поэтому в научных вычислениях обычно используют именно его. По крайней мере, использовали до недавнего времени.
2B OR NOT(2B) = FF
Аватара пользователя
Сионист
Сообщения: 1211
Зарегистрирован: 31 мар 2014, 06:18

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

Может быть любая ссылка - надстройка над указателем. Стандарт требует, чтоб любая ссылка вела себя, как если бы была надстройкой над указателем (хотя чёрт его знает, может и другими словами это формулирует). Но стандарт не требует именно такой реализации абсолютно всех ссылок.
Писать можно на чём угодно, но зачем же так себя ограничивать? Пиши на c.
Absurd
Сообщения: 1228
Зарегистрирован: 26 фев 2004, 13:24
Откуда: Pietari, Venäjä
Контактная информация:

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

Да, и чтение-запись в регистры не дает 50-кратного ускорения над ячейками памяти в стеке. Часто используемые ячейки памяти хранятся в кеше L0, и вершина стека с локальными переменными попадает именно туда. У меня есть виртуальная машинка, в ней в режиме интерпретации каждому регистру соответствует ячейка в массиве. Виртуалка умеет выделять функции по тому куда часто делают call и компилирует их в нативный код. При этом ячейки памяти с псевдорегистрами заменяются на настоящие нативные регистры. Ускорения в 50 раз не происходит. В Debug компиляции ускорение в 18 раз, в Release режиме интерпретатор по видимому оптимизируется очень хорошо, и ускорение всего в 6 раз. Проблема спектрумовского бейсика скорее всего в том что на Z80 не было аппаратного ускорителя плавающей точки, и для каждого инкремента переменной вызывалась довольно нетривиальная подпрограмма из ПЗУ, которая сначала денормализовала экспоненту, потом прибавляла к мантиссе единичку, а потом нормализовала экспоненту обратно. Хотя нет, кажется припоминаю что там был union в котором либо int16 либо плавающая точка.
Стандарт требует, чтоб любая ссылка вела себя, как если бы была надстройкой над указателем
Когда функция или метод класса с внешней линковкой возваращают ссылку не существует иного способа кроме ее реализовать кроме как при помощи передачи адреса. Если же возможно ее заинлайнить, то можно промежуточную ссылку выкинуть и работать непосредственно со значением на которое она ссылается. Но и указатель тоже можно выкинуть в таком случае.
2B OR NOT(2B) = FF
Аватара пользователя
Romeo
Сообщения: 3126
Зарегистрирован: 02 мар 2004, 17:25
Откуда: Крым, Севастополь
Контактная информация:

Очень здраво рассуждаешь, Absurd. Причём один комментарий лучше другого. Давно уже хотел репутацию апнуть тебе. Вот добрались руки :)
Entites should not be multiplied beyond necessity @ William Occam
---
Для выделения С++ кода используйте конструкцию [ code=cpp ] Код [ /code ] (без пробелов)
---
Сообщение "Спасибо" малоинформативно. Благодарность правильнее высказать, воспользовавшись кнопкой "Reputation" в виде звёздочки, расположенной в левом нижнем углу рамки сообщения.
Ответить