Мастера DELPHI, Delphi programming community Рейтинг@Mail.ru Титульная страница Поиск, карта сайта Написать письмо 
| Новости |
Новости сайта
Поиск |
Поиск по лучшим сайтам о Delphi
FAQ |
Огромная база часто задаваемых вопросов и, конечно же, ответы к ним ;)
Статьи |
Подборка статей на самые разные темы. Все о DELPHI
Книги |
Новинки книжного рынка
Новости VCL
Обзор свежих компонент со всего мира, по-русски!
|
| Форумы
Здесь вы можете задать свой вопрос и наверняка получите ответ
| ЧАТ |
Место для общения :)
Орешник |
Коллекция курьезных вопросов из форумов
KOL и MCK |
KOL и MCK - Компактные программы на Delphi
Основная («Начинающим»)/ Базы / WinAPI / Компоненты / Сети / Media / Игры / Corba и COM / KOL / FreePascal / .Net / Прочее / rsdn.org

 
Чтобы не потерять эту дискуссию, сделайте закладку « предыдущая ветвь | форум | следующая ветвь »

Что выдаст функция


Pavia ©   (31.05.17 22:35

Какой результат у вас? 20 или 30?

var
 t1,t2:Integer;
function foo(var a,b:Integer):Integer;
begin
a:=10;
b:=20;
a:=a+10;
b:=a;
Result:=b;
end;
begin
t2:=foo(t1,t1);
WriteLn(t2);
end.


ПС. Интересуют разные компиляторы и их версии в том числе питон и Си диез.


Игорь Шевченко ©   (31.05.17 23:12[1]


> в том числе питон и Си диез.


Ошибка компиляции.


Dimka Maslov ©   (31.05.17 23:26[2]

С какой радости может быть 20?


DayGaykin ©   (31.05.17 23:43[3]

Тоже самое, если сделать

var
 t1: Integer;
 t2: Integer absolute t1;


dmk ©   (31.05.17 23:55[4]

У меня 30.
Delphi XE6 Prof. Win64.


Vons ©   (01.06.17 02:21[5]

Поддерживаю, из-за var будет 30, а как может быть по другому? Если по значению, то будет 20.

Вроде очевидно, что во всех языках. Если там поддерживается передача по ссылке, то будет 30. Если не поддерживается, то пример транслировать нельзя и скорее всего будет 20. В чем смысл вопроса?


Pavia ©   (01.06.17 06:15[6]

В том что var это не должен быть передачью по ссылке. Это должно быть передача по значению с возвратом результата.


Inovet ©   (01.06.17 06:38[7]

> [6] Pavia ©   (01.06.17 06:15)
> Это должно быть передача по значению с возвратом результата

Это как?


Pavia ©   (01.06.17 07:36[8]

Всмысле как? То как описывал Вирт. Он специально отказался от передачи по ссылке и по указателю. Введя понятия var и const. И мне обсолютно не прнятно почему  Delphi не следует стандарту.


Юрий Зотов ©   (01.06.17 08:26[9]

>  Pavia ©   (01.06.17 06:15) [6]

> var это не должен быть передачью по ссылке. Это должно быть
> передача по значению с возвратом результата.


Допустим, что так. Но тогда сразу возникают вопросы:
- по какому адресу надо этот результат разместить?
- как вызывающая сторона узнает этот адрес?


Inovet ©   (01.06.17 08:38[10]

> [8] Pavia ©   (01.06.17 07:36)
> То как описывал Вирт.

Напомни точную цитату, я наверное и не читал Вирта, но как уже сказал ЮЗ, фактически компилятор должен генерировать 2 параметра - один для значения, второй для возврата, что нарушает саму идею о возможности модификации передаваемого параметра в вызываемой функции.


Юрий Зотов ©   (01.06.17 08:49[11]

> Pavia ©   (01.06.17 07:36) [8]

> ... Вирт ... специально отказался от передачи по ссылке и по указателю.
>  Введя понятия var и const.


Насколько помню, при передаче параметров в Паскале Вирт ни от чего не отказывался и атрибут const для параметров не вводил. Этот атрибут появился только в Delphi, причем даже и не в ранних версиях. А у Вирта - как у Вирта, все просто и четко:

- есть var - передается адрес;
- нет var - передается значение.


Pavia ©   (01.06.17 09:00[12]


> Допустим, что так. Но тогда сразу возникают вопросы:- по
> какому адресу надо этот результат разместить?- как вызывающая
> сторона узнает этот адрес?

Для результата функции с типом record они это сделали.
Если функция возвращает запись. То в стеке формируется переменная под эту запись. В параметры функции добавляется ещё одно поле с указателем на это переменную. По выходу из функции вызывающая сторона копирует данные из временной переменной в ту которой должна присвоить по коду.

Для var можно поступить так же. Для оптимизации всёже будем использовать ссылки. Если в функцию передаются разные переменные они передаются по ссылке. Если одна переменная передаётся 2 и более раз. Для них заводятся локальные переменные по числу дублей минус оригинал. Размножаем исходную переменную. И вызываем функцию как обычно.
Только вначале теневые копии, а в конце оригинальная переменная.
Тогда выходные данные сразу попадут в оригинальную переменную без лишнего копирования. На изменения будет влиять крайний правый параметр.

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

Важно, что в результате алгоритм-функции будет предсказуем. Изменение b не будет приводить к изменению переменной a.

Насколько слышал в Питоне как-то это же реализовано. По крайней мере переменные не ведут себя подобно запутанным-фотонам.


Kerk ©   (01.06.17 09:17[13]


> Если одна переменная передаётся 2 и более раз. Для них заводятся
> локальные переменные по числу дублей минус оригинал. Размножаем
> исходную переменную. И вызываем функцию как обычно.

Ну допустим. А что тогда должен вывести на консоль такой код:
var
 t1:Integer;

function foo(var a,b:Integer):Integer;
begin
 a:=10;
 b:=20;
end;

begin
 foo(t1,t1);
 WriteLn(t1);
end.


Юрий Зотов ©   (01.06.17 09:47[14]

> переменные не ведут себя подобно запутанным-фотонам

Есть var - передается адрес;
Нет var - передается значение.

Где тут путаница?


Юрий Зотов ©   (01.06.17 09:53[15]

> Kerk ©   (01.06.17 09:17) [13]

> что тогда должен вывести на консоль такой код


Такой код должен вывести: "Аффтар, убей сибя ап стену".
:o)


Игорь Шевченко ©   (01.06.17 10:35[16]

Юрий Зотов ©   (01.06.17 09:53) [15]

"Еще не хотелось бы видеть открытого коверканья русского языка, использования уличного сленга. Это конечно не наказуемо, но помните, что Ваши слова будут читать люди, которые с ними могут быть незнакомы, или они им просто неприятны.
"

Мне - просто неприятно


Pavia ©   (01.06.17 13:25[17]


> Kerk ©   (01.06.17 09:17) [13]

Я уже написал. Крайнее правое (var a,b:Integer). То бишь значение b.
Это логичнее, чем передавать по ссылке. И наводить путаницу.


Kerk ©   (01.06.17 17:40[18]


> Pavia ©   (01.06.17 13:25) [17]
>
>
> > Kerk ©   (01.06.17 09:17) [13]
>
> Я уже написал. Крайнее правое (var a,b:Integer). То бишь
> значение b.
> Это логичнее, чем передавать по ссылке. И наводить путаницу.

Ну вот вообще ни разу не логичнее


Тимохов Дима ©   (02.06.17 14:15[19]

Ну 30 должно быть, очевидно же?

С этими var'ами много чудес. Особенно с неявными.
Ниже код.
Без теста, кто скажет, Button1 и Button2 полностью ли идентичны?


type
  TPerson = record
     FIO: String;
     Age: Integer;
     // Потенциально могут добавляться члены.
  end;

function InitPerson(const FIO: String; Age: Integer): TPerson;
begin
  // Дабы не оставить возможные новые члены
  // не инициализированными инициализируем нулям.
  FillChar(Result, SizeOf(Result), 0);
  Result.FIO := FIO;
  Result.Age := Age;
end;

procedure TForm_Tests.Button1Click(Sender: TObject);
var
  Person: TPerson;
  Persons: array of TPerson;
begin
  SetLength(Persons, 3);

  Person := InitPerson('И.И. Иванов', 40);
  Persons[0] := Person;

  Person := InitPerson('И.И. Сидоров', 42);
  Persons[1] := Person;

  Person := InitPerson('И.И. Петров', 45);
  Persons[2] := Person;
end;

procedure TForm_Tests.Button2Click(Sender: TObject);
var
  Persons: array of TPerson;
begin
  SetLength(Persons, 3);
  Persons[0] := InitPerson('И.И. Иванов', 40);
  Persons[1] := InitPerson('И.И. Сидоров', 42);
  Persons[2] := InitPerson('И.И. Петров', 45);
end;


Sha ©   (02.06.17 14:30[20]

> Тимохов Дима ©   (02.06.17 14:15) [19]

интересно другое, FillChar зачем?


Тимохов Дима ©   (02.06.17 14:37[21]


> Sha ©   (02.06.17 14:30) [20]
> > Тимохов Дима ©   (02.06.17 14:15) [19]
> интересно другое, FillChar зачем?


// Дабы не оставить возможные новые члены
// не инициализированными инициализируем нулям.


ибо // Потенциально могут добавляться члены.


Sha ©   (02.06.17 14:41[22]

> Тимохов Дима ©   (02.06.17 14:37) [21]

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


Тимохов Дима ©   (02.06.17 14:48[23]


> Sha ©   (02.06.17 14:41) [22]
> > Тимохов Дима ©   (02.06.17 14:37) [21]
> так лучше не делать,
> ибо потенциально это может привести к затиранию нулем указателя
> на строку и, как следствие, к утечке памяти

Конечно, ты прав.

Вопрос, в другом, что код то в общем-то невинный.
Зачастую что-то похожее может прийти еще из старого кода, а строка была добавлена позднее. И вот тебе и утечка.
К тому же Button1 приводит к ошибке, Button2 - нет. Опять же, понятно почему.

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

Если уж очень мне надо сохранить функционал FillChar (т.е. умолчательную инициализацию), но не получить мемлик, то пишу обычно так:

function InitPerson(const FIO: String; Age: Integer): TPerson;
var
  R: TPerson;
begin
 // Дабы не оставить возможные новые члены
 // не инициализированными инициализируем нулям.
 FillChar(R, SizeOf(R), 0);
 R.FIO := FIO;
 R.Age := Age;
 Result := R;
end;


Sha ©   (02.06.17 16:12[24]

> Тимохов Дима ©   (02.06.17 14:48) [23]
> Если уж очень мне надо сохранить функционал FillChar

а тут тоже FillChar не нужен,
т.к. благодаря магии компилятора R будет проинициализирована


Тимохов Дима ©   (02.06.17 16:23[25]


> Sha ©   (02.06.17 16:12) [24]
> > Тимохов Дима ©   (02.06.17 14:48) [23]
> > Если уж очень мне надо сохранить функционал FillChar
>
> а тут тоже FillChar не нужен,
> т.к. благодаря магии компилятора R будет проинициализирована

и даже Integer? )))


Sha ©   (02.06.17 16:29[26]

а нафига его инициализировать, если это никогда не приведет к ошибке?


Тимохов Дима ©   (02.06.17 16:45[27]


> Sha ©   (02.06.17 16:29) [26]
> а нафига его инициализировать, если это никогда не приведет
> к ошибке?

Предпочитаю не оставлять неинициализированные переменные.


Игорь Шевченко ©   (03.06.17 10:35[28]

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

В свое время бывший коллега (ныне покойный) писал такой код:

A := 0;
A := FieldByName('B').AsInteger;

Когда его спросили, зачем первое присваивание, ответ был: "А вдруг поле не найдется"


Тимохов Дима ©   (03.06.17 12:57[29]


> Игорь Шевченко ©   (03.06.17 10:35) [28]
> FillChar не нужен более чем совсем, во-первых, он не делает
> ничего полезного, во-вторых затрудняет понимание кода.


Иногда - нужен, иногда - не нужен. Зависит от ситуации.

В VCL пример есть. Вот, например DBTables.pas -> TDatabase.Execute, или StdCtrls.pas -> TCustomListBox.ItemRect.
Факт - есть примеры использования FillChar для очистки результата типа record.

Еще раз: FillChar(Result,...) - это способ дать умолчательное значение для результата типа Record. Но этот способ таит в себе тонкости, которые надо знать начинающим программистам, которые еще не сталкивались с подобными тонкостями. Собственно, я для этого и написал свой комментарий выше.


Pavia ©   (03.06.17 21:20[30]

Вообще-то FillChar как раз и нужен
Если его не сделать то в первом коде в первом рекорде будет инвалидный указатель.


Pavia ©   (03.06.17 21:26[31]


> Когда его спросили, зачем первое присваивание, ответ был:
>  "А вдруг поле не найдется"

Это просто скорее всего человек джавист.

Кстати навеяло ещё вопрос.
Нужно ли тут оставить OutInt:=0; ? Или обнулять должен Slot принимающий эмитированный сигнал?


procedure ReadInt(var OutInt:Integer);
var str:String;
begin
repeat
Read(str);
until (IsNumber(str) or IsEndOfRead or IsTermitateChar(Ch) or IsFullString(OutStr));  
if IsNumber(str) then
  OutInt:=StrToInt(Str)
  else
    begin
    OutInt:=0;
    emitFirstSignal(@ReadInt, @OutInt, nil);
    end;
end;


Тимохов Дима ©   (03.06.17 21:56[32]


> Нужно ли тут оставить OutInt:=0; ? Или обнулять должен Slot
> принимающий эмитированный сигнал?

Это как по контракту. Т.е. по спецификации слота.


Pavia ©   (03.06.17 22:39[33]


> Это как по контракту. Т.е. по спецификации слота.

О точно. У эмитора есть контракт. Спасибо, переписал так.

    Goal:=0;
    emitFirstSignal(@ReadInt, @OutInt, @Goal);
    OutInt:=Goal;


Игорь Шевченко ©   (03.06.17 22:39[34]

Тимохов Дима ©   (03.06.17 12:57) [29]


> Иногда - нужен, иногда - не нужен. Зависит от ситуации.
>
>
> В VCL пример есть. Вот, например DBTables.pas -> TDatabase.
> Execute, или StdCtrls.pas -> TCustomListBox.ItemRect.
> Факт - есть примеры использования FillChar для очистки результата
> типа record.


2006:

function TCustomListBox.ItemRect(Index: Integer): TRect;
var
 Count: Integer;
begin
 Count := Items.Count;
 if (Index = 0) or (Index < Count) then
   Perform(LB_GETITEMRECT, Index, Longint(@Result))
 else if Index = Count then
 begin
   Perform(LB_GETITEMRECT, Index - 1, Longint(@Result));
   OffsetRect(Result, 0, Result.Bottom - Result.Top);
 end else FillChar(Result, SizeOf(Result), 0);
end;


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

Keep it simple, Syndey!

Вот в XE5 написали гораздо понятнее:

function TCustomListBox.ItemRect(Index: Integer): TRect;
var
 Count: Integer;
begin
 Count := Items.Count;
 if (Index = 0) or (Index < Count) then
   Result := GetItemRect(Index)
 else if Index = Count then
 begin
   Result := GetItemRect(Index - 1);
   OffsetRect(Result, 0, Result.Bottom - Result.Top);
 end
 else
   Result := TRect.Empty;
end;


Игорь Шевченко ©   (03.06.17 22:40[35]


> procedure ReadInt(var OutInt:Integer);


Дальше пост можно не читать. Калверта зубрить наизусть!


Pavia ©   (03.06.17 22:49[36]


> Калверта зубрить наизусть!

У меня нет его книг. Не могли бы уточнить какую именно книгу вы имеете в виду?


Pavia ©   (03.06.17 23:16[37]


> Игорь Шевченко ©   (03.06.17 22:40) [35]

и вообще не надо принимать всё так близко к сердцу. На самом деле это черновик. OutInt элементарно заменяется на value просто это я себе напоминалку оставил. Теперь когда разобрался что есть var и какой контракт у эммитора можно смело заменить на value.


Игорь Шевченко ©   (04.06.17 10:44[38]

Pavia ©   (03.06.17 22:49) [36]

https://edn.embarcadero.com/article/10280


версия для печати

Написать ответ

Ваше имя (регистрация  E-mail 







Разрешается использование тегов форматирования текста:
<b>жирный</b> <i>наклонный</i> <u>подчеркнутый</u>,
а для выделения текста программ, используйте <code> ... </code>
и не забывайте закрывать теги! </b></i></u></code> :)


Наверх

  Рейтинг@Mail.ru     Титульная страница Поиск, карта сайта Написать письмо