Wskaźniki – błąd "incompatible types" podczas przypisania

0

Dlaczego kompilator rzuca błąd Incompatible types przy przypisaniu temp do TopElement?

type
    TElement = record
        Data: integer;
        Next: ^TElement;
    end;

var
    TopElement : ^TElement;

procedure Push(data: integer);
var
    temp : ^TElement;
begin
    temp := TopElement;
    NEW(TopElement);
    TopElement^.Data := data;
    TopElement^.Next := temp;
end;
2

Szczerze mówiąc nie wyjaśnię Ci, dlaczego Delphi się tak zachowuje (żeby było śmieszniej - w Lazarusie, zarówno w trybie {$mode delphi} jak i {$mode objfpc} nie pojawiają się błędy), niemniej po wprowadzeniu małych poprawek udało mi się to skompilować ;)

type
  WskElement = ^TElement;
  TElement = record
        Data: integer;
        Next: WskElement;
    end;

var
  TopElement : WskElement;

procedure TForm1.Button1Click(Sender: TObject);
var
    data: integer;
    temp : WskElement;
begin
   temp := TopElement;
    NEW(TopElement);
    TopElement^.Data := data;
    TopElement^.Next := temp;
end;

1

Dla potomnych: trzeba utworzyć nowy typ PElement : ^TElement. Taki kod działa:

type
    TElement = record
        Data: integer;
        Next: ^TElement;
    end;

type
    PElement = ^TElement;

var
    First : PElement;
    Last : PElement;

begin
  new(First);
  First^.Data := 5;

  new(Last);
  Last^.Data := 3;

  writeln(First^.Data);
  writeln(Last^.Data);

  First := Last;

  writeln(First^.Data);

  writeln('Hello, world!');
  readln;
end.

Ogólnie nie mam pojęcia o Delphi, więc mogę się mylić, ale jak dla mnie to jakiś WTF. Mógłby ktoś wyjaśnić, dlaczego tak trzeba robić?

0

jak dla mnie to jakiś WTF. Mógłby ktoś wyjaśnić, dlaczego tak trzeba robić?

Sam chętnie też się dowiem, z czego to wynika. @wloochacz, @Mr.YaHooo, @karpov, @Patryk27, @woolfik, @abrakadaber, @Clarc - czy ktoś z Was jest w stanie rzucić nowe światło na ten temat i wyjaśnić, czemu Delphi tak się zachowuje? Podejrzewam, że to z czegoś wynika/ma jakieś uzasadnienie, niemniej sam nie czaję, czemu przy wykorzystywaniu daszka mamy problem, ale tworząc osobny typ wszystko jest OK.

Tym bardziej mi to wygląda na jakieś "wymysły" Delphi, że jak napisałem we wcześniejszym poście - Lazarus to łyknął bez jakiegokolwiek zająknięcia.

1

Wskaźniki nigdy nie były moją mocną stroną ale dla mnie komunikat wygląda ok. Podobnie jest w przypadku deklaracji tablic:

var
  tab1 : array[0..1] of array [0..1] of integer;

procedure copy;
var
  tab2 : array[0..1, 0..1] of integer;
begin
  tab1[0] := tab2[0];   // <- Incompatible types
end;

Typy musza sie zgadzać, nie tylko pod względem 'zawartości' ale i nazwy. W przypadku wskaźników określenie typu jako ^Typ jest pewnie interpretowane przez Delphi jako nie jednoznaczne. Typ ma być typem a nie wskaźnikiem (jeśli porównujemy później dwa takie twory). Dokładnie tego nie wyjaśnię. Przyjmuję że tak jest i tyle. Takie silne typowanie nie powinno dziwić @cerrato. Sam w rozmowie na temat nowości w Rio wskazywałeś po co tworzyć zmiennie inline i psuć poukładany schemat w Delphi z deklaracją zmiennych na początku. Ja bym tutaj upatrywał podobnego zachowania z tym typowaniem jak z przyjętą deklaracją zmiennych na początku - chociaż z czasem jak widać coś się może zmienić.

0

Wow, czuję się zaszczycony - ktoś pamięta rzeczy, które pisałem pewien czas temu (i teraz jeszcze wyciąga je przeciw mnie samemu). Jeszcze trochę i będę drugim @jarekr000000 ;)

A tak poważnie - masz rację, ja nie lubię takich rozwiązań. Zawsze jak działam z "daszkami" to robię sobie do tego osobny typ, którego potem używam - dlatego nie zetknąłem się wcześniej z opisaną przez OP sytuacją/problemem. Niemniej zastanawiam się, z czego to wynika.

Owszem, wprawdzie daszek nie jest osobnym typem, ale przecież kompilator może sobie zobaczyć, do czego ten daszek się odnosi, w związku z czym już na etapie kompilacji może ocenić, czy dany kod ma sens, czy jest coś pomieszane.

Dziwne wydaje się także to, że Delphi, które wprowadza nowości (chociażby wspomniane przez Ciebie deklaracje inline) sobie nie radzi z podanym przez OP zapisem, ale za to bardziej konserwatywny FPC bez kłopotu go przełyka.

1

Na forum Lazarusa znalazłem coś takiego:

Use {$mode delphi} or {$mode tp} for this. FPC and OBJFPC strives to be stricter than Delphi/TP, I think because it may cause confusion when you need both the pointer and the pointed value.

Może to jest właściwa odpowiedź na pytanie czemu. A skoro są osobne dyrektywy dla konkretnej kompilacji to jakieś różnice muszą być.
A tutaj jeszcze komentarz z 4p odnośnie wskaźników :

Jest typ Pointer - predefiniowany typ wskaźnikowy. Zmienne typu Pointer nie wskazują danych żadnego określonego typu, są jednak zgodne z dowolnym innym typem wskaźnikowym.

6

No dobra, jeszcze na chwilkę wpadłem – ”na długość kawki”. :]

nobody01 napisał(a):

Dlaczego kompilator rzuca błąd Incompatible types przy przypisaniu temp do TopElement?

Dlatego że korzystasz z dwóch różnych typów danych. Nieważne, że oba są wskaźnikami typowanymi na taką samą strukturkę – dla kompilatora to dwa różne typy danych.

Dla potomnych: trzeba utworzyć nowy typ PElement : ^TElement.

Do takich rzeczy utworzono wyjątkową konstrukcję, która pozwala wykorzystać typ danych, który jest zadeklarowany niżej (połączenie zwykłej deklaracji z forwardowaniem). W ten sposób można wykorzystać wskaźnik na strukturę do deklaracji pola w tejże strukturze:

type
  PElement = ^TElement;
  TElement = record
    Data: integer;
    Next: PElement;
  end;

cerrato napisał(a):

[…] czy ktoś z Was jest w stanie rzucić nowe światło na ten temat i wyjaśnić, czemu Delphi tak się zachowuje?

Ale co tu wyjaśniać? Dla kompilatora to dwa różne typy danych, dlatego rzuca błąd. Taka jest specyfika tego dialektu i trzeba mieć to na uwadze.

A tak poważnie - masz rację, ja nie lubię takich rozwiązań. Zawsze jak działam z "daszkami" to robię sobie do tego osobny typ, którego potem używam - dlatego nie zetknąłem się wcześniej z opisaną przez OP sytuacją/problemem.

Ale osobnego typu listy generycznej toś nie chciał deklarować… ;)

Zadeklarowanie osobnego typu danych nic nie kosztuje – jedna linijka i tyle, można używać wszędzie. Przy czym z ”daszków” można korzystać tylko do deklaracji, a w kodzie już nie. Automatyczna dereferencja wskaźników typowanych jest wspierana domyślnie przez Delphi, we Free Pascalu nie, ale można to zmienić dyrektywą {$MODESWITCH AUTODEREF}.


Clarc napisał(a):

Może to jest właściwa odpowiedź na pytanie czemu.

Odpowiedź jest prosta – bo takie było założenie, nie jest to bug czy WTF. Jeśli ktoś chce wiedzieć kto wpadł na taki pomysł i dlaczego taka decyzja padła to już musi pytać embarcadero.

A skoro są osobne dyrektywy dla konkretnej kompilacji to jakieś różnice muszą być.

Różnic pomiędzy trybami OBJFPC a DELPHI jest mnóstwo, tak samo jak pomiędzy trybami OBJFPC a TP czy pomiędzy dowolną inną parą. Każdy dialekt się czymś wyróżnia. Wsparcie trybów zostało wprowadzone po to, aby móc używać tego samego kompilatora do pisania kodu w różnych dialektach.

Przy czym dany tryb zdefiniowany za pomocą dyrektywy {$MODE} obowiązuje tylko dla danego modułu, więc każdy moduł w danym projekcie może zawierać kod napisany w innym dialekcie (jeden w FPC, inny w OBJFPC, a jeszcze inny w DELPHI). O ile jest taka możliwość, to raczej nie polecam takich mieszanin, bo może się to zemścić.

0

@furious programming: Trzy uwagi zanim znowu znikniesz ;)

1) szkoda, że się nie odniosłeś do fragmentu "wprawdzie daszek nie jest osobnym typem, ale przecież kompilator może sobie zobaczyć, do czego ten daszek się odnosi, w związku z czym już na etapie kompilacji może ocenić, czy dany kod ma sens, czy jest coś pomieszane". Da się sprawdzić z poziomu kompilatora, co jest za danym daszkiem schowane. Delphi strzela focha, ale Lazarus to łyka jak ... (tutaj chciałem wstawić pewne porównanie, ale że byłoby ono lekko niestosowne, to sobie odpuściłem ;) ). Podobnie jak przykład podany przez @Clarc - https://4programmers.net/Forum/1561118, Wprawdzie obie tablice są inaczej zadeklarowane, ale de facto są takie same, więc inteligentny kompilator chwilę się podrapał po procesorze i uznał, że tak może być ;)

2) W temacie nawiązania do wcześniejszego wątku z tablicą - żebyśmy się rozumieli: nie twierdzę, że opcja z przeleceniem tablicy w pętli jest lepsza, ale biorąc pod uwagę poziom pytającego, moim zdaniem jest łatwiejsza dla niego do ogarnięcia. Jednocześnie nie jest niepoprawna, co najwyżej trochę oldskulowa ;)

3) Pisałem Ci, że jak się pojawiłeś na chwilę, to zaraz wpadniesz znowu w nałóg. Miałem rację :P

1

A jeszcze w temacie daszków - rzućcie okiem na wspaniały wpis @somekind w innym wątku - https://4programmers.net/Forum/1560749 :D

1
furious programming napisał(a):

Dlatego że korzystasz z dwóch różnych typów danych. Nieważne, że oba są wskaźnikami typowanymi na taką samą strukturkę – dla kompilatora to dwa różne typy danych.

I w zasadzie nie wiem co mógłbym jeszcze od siebie dodać :) Ot taka ideologia embarcadero :)

1 użytkowników online, w tym zalogowanych: 0, gości: 1