Помощь - Поиск - Пользователи - Календарь
Полная версия: Несоответсвие типов VAR-параметров подпрограмм(error 26).
Форум «Всё о Паскале» > Pascal, Object Pascal > Теоретические вопросы
Neznaika
Извиняюсь за возможное нарушение правил, но решил поместить пример программы,
чтобы сразу было понятно что к чему.

ПРОГРАММА:
Код

program VarStr;

uses
   Dos;

procedure Proc( var Dir : DirStr; var Name : NameStr; var Ext : ExtStr );
begin
end;

procedure Proc2( var B : Byte; var W : Word; var L : LongInt );
begin
end;

procedure Proc3( var I : Integer; var W : Word );
begin
end;

procedure Proc21( B : Byte; W : Word; L : LongInt );
begin
end;

var
   SD, SN, SE : String;
   SDir : DirStr;
   SName : NameStr;
   SExt : ExtStr;
   B : Byte;
   W : Word;
   L : LongInt;
   I : Integer;
begin
   (*Proc(SD,SN,SE);  { НЕЛЬЗЯ! Ошибка 26: Type mismatch(несовпадение типов). }*)
   Proc(SDir,SName,SExt);  { Всё в порядке! }
   (*Proc2(B,B{Error 26!},B);*)
   (*Proc2(W{Error 26!},W,W);*)
   (*Proc2(L{Error 26!},L,L);*)
   Proc2(B,W,L);  { Всё в порядке! }
   (*Proc3(W{Error 26!},W);*)
   (*Proc3(W{Error 26!},I);*)
   (*Proc3(I,I{Error 26!});*)
   Proc3(I,W);  { Всё в порядке! }
   Proc3(Integer(W),Word(I));  { Всё в порядке! }
   Proc21(B,W,L);  { Всё в порядке! }
   Proc21(L,B,W);  { Всё в порядке! }
   Proc21(W,L,B)  { Всё в порядке! }
end.


И сам ВОПРОС(недеюсь он теоретический(имеет отношение к синтаксису, семантике и т.д. и т.п)):
Почему типы фактических параметров переменных, задаваемых при вызове подпрограммы,
должны СОВПАДАТЬ(быть РАВНЫМИ) соответствующим формальным VAR-параметрам.
Почему здесь ошибка 26(Type mismatch) - НЕСООТВЕТСТВИЕ ТИПОВ превращается в ошибку
НЕСОВПАДЕНИЕ ТИПОВ.


Пожалуйста, если кто-нибудь знает, объясните.
compiler
procedure p( const s:string );
begin
writeLn(s);
end;
const
str='s';
begin
p('s');//печатает s
p(str);//печатает s
p();//что печатать?
end.
что б не возникало подобных вопросов сделано так как сделано...
volvo
Цитата
Почему здесь ошибка 26(Type mismatch) - НЕСООТВЕТСТВИЕ ТИПОВ превращается в ошибку
НЕСОВПАДЕНИЕ ТИПОВ.
Ничего никуда не превращается. Как всегда было Error 26 - Type Mismatch, так и есть Type Mismatch. То что тебе в одном случае хочется это перевести как "несоответствие", а в другом - как "несовпадение" - это не проблемы компилятора.

Теперь о том,
Цитата
Почему типы фактических параметров переменных, задаваемых при вызове подпрограммы, должны СОВПАДАТЬ(быть РАВНЫМИ) соответствующим формальным VAR-параметрам
Вот именно потому и должны совпадать, что процедуре будет передана не локальная копия переменной, с которой она будет работать, а адрес переменной, то есть, работа-то осуществляется непосредственно с той переменной, которая передана в качестве параметра. Отсюда следует что? Типы фактического и формального параметра в этом случае должны быть не просто совместимыми, они должны быть эквивалентны... И за этим компилятор следит.

Кстати, частично твоя проблема очень просто решается добавлением в первую строку директивы {$V-}, которая отключает подобный контроль (только для строковых типов) со стороны компилятора.
Neznaika
compiler:
Не смешно, мой пример СИНТАКСИЧЕСКИ КОРРЕКТЕН.

volvo:
Есть
a) НЕСОВМЕСТИМОСТЬ ТИПОВ:
например WORD := STRING или ARRAY := RECORD
б) СОВМЕСТИМОСТЬ(НО НЕ РАВЕНСТВО) ТИПОВ:
BYTE := WORD или REAL := LONGINT
в) РАВЕНСТВО ТИПОВ:
SHORTINT := SHORTINT, BYTE := BYTE, WORD := WORD и т.д.

В операторе присваивания с совместимостью всё в порядке, в параметрах,
передаваемых по значению, тоже.
Но почему для

procedure Proc3( var I : Integer; var W : Word );
begin
end;

var
I : Integer;
W : Word;



сработает это
Код

Proc3(I,W);

и это
Код

Proc3(Integer(W),Word(I));

но не это
Код

Proc3(W,I);

???
volvo
Если ты не понял того, что написано в предыдущем посте, то чего бы я опять что-то тебе объяснял? Результат будет один и тот же. Тебе ясно сказано: для того, чтобы корректно работать с адресом передаваемым в качестве фактического параметра, типы должны быть ЭКВИВАЛЕНТНЫ, нет, ты опять начинаешь бодягу про совместимость типов... Совместимость - это по присваиванию, у тебя совсем другой вариант.

Что, действительно заняться нечем? Тогда иди, почитай доки, что-ли...

Цитата
сработает это
...
Код
Proc3(Integer(W),Word(I));
То, что оно компилируется в Турбо Паскале - еще не значит, что оно ДОЛЖНО там компилироваться, вот мой компилятор такого не пропускает, чтоб неповадно было. Проходит только эквивалентный тип. Смени компилятор, и убедись...
Neznaika
volvo:
Я не хочу ругаться, я разобраться хочу.
Просто ошибка в этом месте у меня несколько раз появлялась.

Если TP не устраивает, тогда на Delphi(Win32):
Код

procedure Proc1( var I : Integer; var W : Word );
begin
end;

procedure Proc2( var I : SmallInt; var W : Word );
begin
end;

var
   I : Integer;  { 4 bit }
   W : Word;   { 2 bit }
   SI : SmallInt;  { 2 bit }
   D : DWord;  { 4 bit }

begin
   Proc1(I,W);
   Proc1(D,Word(D));  { Proc1(D,D) - не работает! }
   Proc2(SI,W);
   Proc2(SmallInt(W),Word(SI));
end.


Например есть число, которое будет обрабатываться как 32-разрядное,
а инициализируется оно например вызовом некоторой процедуры как 16-разрядное.
ИЛИ числа со знаком и без знака, но их SizeOf(Type1) = SizeOf(Type2).

Эквивалентность и равенство - это одно и то же или нет?
Если по-вашему выходит, что ЭКВИВАЛЕНТНОСТЬ = РАВЕНСТВО, то как Delphi пропускает
Proc1(D,Word(D));
???
Я понимаю, что здесь в процедуру передаётся адрес ячейки памяти, названной D(на этапе
объявления переменных компилятор отвёл под неё 4 байта, записал в своих таблицах, что
её тип DWord, ещё что-то там сделал). Ещё здесь(при генерации вызова процедуры) компилятор
проверяет типы фактических параметров. Но при этом никакого ПРЕОБРАЗОВАНИЯ/ПРИВЕДЕНИЯ
ТИПОВ не происходит, компилятор просто генерирует
push VarAddr
call Proc
или
mov reg, VarAddr
call Proc
Тоже самое он сгенерирует для переменной правильного типа(как у VAR-параметра).
Сама процедура просто тупо скопирует значение результата в ячейку по переданному ей адресу.
Мне непонятно, почему нельзя Proc1(D,D), а нужно обязательно Proc1(D,Word(D))???
Neznaika
Вот например, что написано у Епанешниковых(Программирование в среде Turbo Pascal 7.0):

10.3.2. Параметры переменные
Цитата

При вызове подпрограммы на месте параметра-переменной в качестве фактического параметра
должна использоваться переменная идентичного типа(см п 9.1).

Смотрим пункт 9.1:
Цитата

Идентичность типов
Идентичность типов требуется от формальных параметров процедур и функций и соответствующих им
фактических параметров во время вызова.
Два типа T1 и T2 идентичны в следующих случаях:
1) T1 и T2 - один и тот же идентификатор типа(например, Integer, Real и т.д.);
2) один тип объявляется эквивалентным другому.
Пример
type
T1 = Boolean;
T2 = T1;
T3 = Boolean;
Все типы T1, T2, T3 и Boolean - идентичные типы.
type
M1 = array[1..5] of Integer;
M2 = array[1..5] of Integer;
Типы M1 и M2 - не идентичные типы. Однако V1 и V2 - переменные идентичных типов:
var
V1, V2 : array[1..5] of Integer;


Получается, что типы данных формальных и фактических VAR-параметров просто должны совпадать,
но это не так! И Turbo Pascal и Delphi нормально компилируют
Type1(VarType2)
(здесь VAR-параметр имеет тип Type1, а переменная VarType2 - соответственно Type2)
если Type1 является подмножеством Type2. mega_chok.gif
compiler
Цитата(Neznaika @ 14.07.2007 15:49) *
Получается, что типы данных формальных и фактических VAR-параметров просто должны совпадать, но это не так! И Turbo Pascal и Delphi нормально компилируют
тебе же написали
Цитата
То, что оно компилируется в Турбо Паскале - еще не значит, что оно ДОЛЖНО там компилироваться


а еще многие ошибки вообще не отлавливаются во время компиляции..
Neznaika
Цитата(compiler @ 14.07.2007 17:05) *

тебе же написали

а еще многие ошибки вообще не отлавливаются во время компиляции..




Я так не думаю.
По-моему использование в качестве фактического VAR-параметра переменной совместимого
(расширенного) типа - это не ошибка, а вполне нормальное явление. Не могу привести никаких ссылок,
но я где-то встречал примеры такого способа передачи параметров.
Повторяю:
И Turbo Pascal и Delphi нормально компилируют
Type1(VarType2)
(здесь VAR-параметр имеет тип Type1, а переменная VarType2 - соответственно Type2)
если Type1 является подмножеством Type2.

Компилятор пишут одни люди, документацию другие.
Например в моей версии Delphi в помощи на синтаксических диаграммах для CASE указано,
что перед ELSE ";"(точку с запятой) ставить нельзя. На самом же деле её можно и ставить и не ставить!!!
Проблема не в компиляторе, а в ДОКУМЕНТАЦИИ!
volvo
Цитата
И Turbo Pascal и Delphi нормально компилируют
Дельфи? Сорри, только не Дельфи:

Нажмите для просмотра прикрепленного файла

Что я делаю не так?

Цитата
Получается, что типы данных формальных и фактических VAR-параметров просто должны совпадать
Это некорректное определение эквивалентности. Вот эти типы - эквивалентны, хотя в первом случае они "одинаковы", а во втором - совсем нет:

type
t1 = integer;
t2 = t1;

procedure p(var v: t1);
begin
end;

type
obj1 = object
end;
obj2 = object(obj1)
end;

procedure p2(var v: obj1);
begin
end;

var
i1: integer;
i2 : t2;

o1: obj1;
o2: obj2;

begin
p(i1);
p(i2);

p2(o1);
p2(o2);
end.

Вот эта программа действительно компилируется как в FPC, так и в Delphi... Потому, что компилироваться должна.

Добавлено через 1 мин.
P.S. Повторяю, Дельфи - НЕ компилирует... Еще раз повторить?
Neznaika

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

var
W : Word;
DW : DWord;

procedure Proc( W : Word; DW : DWord );
begin
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Proc(W,DW);
Proc(Word(DW),DW);
Application.MessageBox('OK','OK',MB_OK)
end;

end.




Только что скомпилировал. Я не вру.
мисс_граффити
Естественно.
А где в этом примере несоответствие может возникнуть?
volvo
Цитата
Только что скомпилировал.
Разговор был про Var-параметры... Попробуй добавить

procedure Proc(VAR W : Word; DW : DWord ); // <--- Вот этот Var

и теперь откомпилировать...
Neznaika
Тьфу, чёрт. Теперь вроде всё "правильно". Delphi 2.0 транслирует.

unit Unit1;

interface

uses
Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs,
StdCtrls;

type
TForm1 = class(TForm)
Button1: TButton;
procedure Button1Click(Sender: TObject);
private
{ Private declarations }
public
{ Public declarations }
end;

var
Form1: TForm1;

implementation

{$R *.DFM}

var
W : Word;
DW : DWord;

procedure Proc( var W : Word; var DW : DWord );
begin
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Proc(W,DW);
Proc(Word(DW),DW);
Application.MessageBox('OK','OK',MB_OK)
end;

end.

Neznaika
BDS2006(компилятор Delphi для Win32):

...
var
W : Word;
DW : DWord;

procedure Proc( var W : Word; var DW : DWord );
begin
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Proc(W,DW);
Proc(Word((@DW)^),DW);
{Proc(Word(DW),DW);} {[Pascal Error] Unit1.pas(39): E2197 Constant object cannot be passed as var parameter }
Application.MessageBox('OK','OK',MB_OK)
end;
...



Delphi 2.0:

...
var
B : Byte;
W : Word;
DW : DWord;

procedure Proc( var W : Word; var DW : DWord );
begin
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
Proc(W,DW);
Proc(Word(DW),DW);
{Proc(B,B); // Error: Types of actual and formal var parameters must be identical.}
{Proc(Word(B),DW); // Error: Constant object cannot be passed as var parameter.}
{Proc(@Word(B),DW); // Variable requared.}
Proc(Word((@B)^),DWord((@B)^)); { !!! }
Application.MessageBox('OK','OK',MB_OK)
end;
...



Уважаемые эксперты, объясните пожалуйста
1) Что означает ошибка "Constant object cannot be passed as var parameter"
в случае BDS2006 и в случае Delphi2 ???
2) Если типы VAR-параметров должны быть "идентичны", почему тогда компилятор пропускает
Proc(Word((@B)^),DWord((@B)^)); ???
3) Дайте пожалуйста определение эквивалентности(как написал volvo) типов формальных и фактических
VAR-параметров.

Спасибо заранее.
hardcase
Цитата(Neznaika @ 18.07.2007 17:42) *

Уважаемые эксперты, объясните пожалуйста
1) Что означает ошибка "Constant object cannot be passed as var parameter"
в случае BDS2006 и в случае Delphi2 ???

Приведение типа приводит к созданию временной локальной копии объекта, которая трактуется копилятором как константа приводимого типа.

Цитата(Neznaika @ 18.07.2007 17:42) *

2) Если типы VAR-параметров должны быть "идентичны", почему тогда компилятор пропускает
Proc(Word((@B)^),DWord((@B)^)); ???

Дело в том, что по умолчанию операция получения адреса @ "теряет" тип объекта. Разыменовывая указатель операцией ^ мы получаем некий объект, который не имеет типа; для того чтобы с ним работать компилятору требуется явно указать тип адресованного объекта - в этом случае операция приведения типа не создает локальной копии, ибо выполняется на этапе компиляции.

Neznaika
Цитата

Приведение типа приводит к созданию временной локальной копии объекта, которая трактуется копилятором как константа приводимого типа.

hardcase, спасибо.

Но всё равно, тёмное дело. Во второй версии Delphi вызов с Type1(VariableOfType2) производит только проверку
совместимости(как я написал раньше вместо W можно указать Word(DW), но не Word(B)). В Delphi 6
(судя по картинке volvo) и в BDS2006 компилятор похоже действительно пытается создать локальную
копию объекта.

Цитата

Дело в том, что по умолчанию операция получения адреса @ "теряет" тип объекта. Разыменовывая указатель операцией ^ мы получаем некий объект, который не имеет типа; для того чтобы с ним работать компилятору требуется явно указать тип адресованного объекта - в этом случае операция приведения типа не создает локальной копии, ибо выполняется на этапе компиляции.


Мне кажется, что оба действия - и Type1(Type2Variable) и Type1((@Type2Variable)^) - выполняются на этапе
компиляции. Но почему в первом случае создаётся локальная копия(для VAR-параметров), по какому синтаксическому правилу?
Можно где-нибудь об этом узнать?
volvo
Neznaika, во-первых, будет ли твоя программа компилироваться в Дельфи (в частности, в D6 - ибо из BDS2006 у меня установлен только Builder, так что проверить не могу), зависит напрямую от установок директив компиляции. К примеру, при добавлении первой строкой модуля: {$T+} твое утверждение:
Цитата
компилятор пропускает
Proc(Word((@B)^),DWord((@B)^)); ???
становится ложным, хотя при {$T-} все компилируется...

А какие там значения директив по умолчанию на твоем компьютере выставлены - это я не знаю, и догадываться тоже не особо хочется...
Neznaika
Да, с {$T+} Proc(Word((@B)^),DWord((@B)^)); не компилируется в любой версии Delphi
(даже во второй, хотя в ней проходит Proc(Word(DW),DW); для procedure Proc( var W : Word; var DW : DWord );).

А можно ли вообще использовать в качестве фактических параметров процедур для VAR-параметров ВЫРАЖЕНИЯ? Для W типа Word конструкция Byte(W) - это выражение.

P.S.
Эх. Наверное надо было с самого начала так ставить вопрос...
hardcase
Есть такое понятие в программировании - безопасный код.
Помимо всего прочего (про exceptionы и потокобезопасность здесь речь не пойдет) оно включает в себя стиль написания программы, который не допускает работу с указателями в явной форме - получение, разыменование, арифметика на ним, также не приветствуются операции приведения типов.

Вместо указателей настоятельно рекомендуется использовать ссылки, а типы преобразовывать в явном виде - через операции присваивания, встроенные функции и т.п.

Т.е. нужно как можно больше работы сбрасывать на компилятор - статический контроль типов, управление памятью и прочую рутину. Он умеет это лучше чем человек.

Теперь по поводу совместимости и эквивалентности.

Совместимость типов - это такое отношение между двумя типами, которое доступно компилятору на этапе синтаксического анализа исходника. Т.е. компилятор ЗНАЕТ как перевести тип А в тип Б.
Например real совместим с integer (переменной типа real можно присвоить значение переменной типа integer), но не наоборот.

Эквивалентность типов - имеется в виду двоичная эквивалентность, это отношение между типами, выстроенное на основе размера памяти в байтах, который занимает переменная того или иного типа.
Например у нас есть запись вида:
Код

TMyDWord = packed record
    HiWord: Word;
    LoWord: Word;
end;

Если мы имеем переменную типа Dword то мы вполне можем привести её к типу TMyDWord. Это приведение допустимо потому что размер занимаемый TMyDword равен размеру DWord. Обратное приведение также возможно.
Эквивалентность типов может зависеть от настроек компилятора. Если мы поставим выравнивание полей записи по 4 байтам (это сильно ускорит их обработку) приведенный выше пример не будет работать, так как размеры перестанут совпадать и отношение двоичной эквивалентности между типами пропадет.

Операция приведения типа оперирует с понятием эквивалентности типов. Мы можем привести тип А к типу Б в том случае, когда size(А) >= size(Б). Это ограничение не позволит обратиться к, возможно, невыделенной области памяти, что приведет к AV-исключению.

При передаче параметров в подпрограммы или методы, для компилятора очень важен ТИП подставляемх фактических параметров, именно на основе их типов, он будет вызывать нужную перегруженную версию (если они есть) сабрутины. Отсюда правило - нужно ЗНАТЬ что ты хочешь сделать, чтобы небыло недопонимания с компилером.

Цитата
Мне кажется, что оба действия - и Type1(Type2Variable) и Type1((@Type2Variable)^) - выполняются на этапе
компиляции. Но почему в первом случае создаётся локальная копия(для VAR-параметров), по какому синтаксическому правилу?
Я уже объяснял..... Первый вариант приводит к копированию данных во временную переменную. Второй вариант компилятор рассматривает как некое переобъявление переменной. Разыменованный указатель - это переменная, операция приведения типа выступает в качестве декларирования типа переменной. Именно поэтому такая запись пригодна для передачи параметра по ссылке.


Добавлено через 6 мин.
Ваял предыдущий креатив.....
Цитата(Neznaika @ 11.08.2007 18:41) *

А можно ли вообще использовать в качестве фактических параметров процедур для VAR-параметров ВЫРАЖЕНИЯ? Для W типа Word конструкция Byte(W) - это выражение.

В принципе - можно. Если выражение своим результатом возвращает указатель, то используя технику дзен
Код

Type1((PType1PointerExpression)^)

Мы можем передавать данные по ссылке.
Другое дело, что нам потребуется написать выражение, результатом которого будет некий указаталь (с точки зрения безопасного кода подобные действия никуда не годятся).
Это текстовая версия — только основной контент. Для просмотра полной версии этой страницы, пожалуйста, нажмите сюда.