Hey, ich experimentiere gerade ein wenig mit Functions, Pointern und
Void-Pointern.
Das unten aufgeführte Script läuft und gibt das richtige Ergebnis,
jedoch bekomme ich auf die Zeile
1
b=*(int*)a;
folgende Warnung: "warning: incompatible integer to pointer conversion
assigning to 'void *' from 'int'"
Ich versuche verzweifelt den Hintergrund zu verstehen, komme aber leider
nicht dahinter :/
Rechts vom = steht ein int (der gesamte Ausdruck)
Ein Pointer ist keine Ganzzahl. Auch wenn es manchmal so aussieht.
Verlasse dich bei diesen Experimenten auf Nichts.
Sie haben auch keine nachvollziehbare Aussage.
Es kann mit einem anderen Compiler/Version/Optimierungsstufe/System
anders aussehen
In C schreibt man keine Scripte sondern Programme. C ist schließlich
keine Scriptsprache. Dein Programm enthält mehrere Fehler und
funktioniert daher nicht; es hat nur zufällig den Anschein zu
funktionieren. Durch Änderung der Umgebung (Compiler-Version, -Optionen,
Betriebssystem, Prozessor) kann das schon ganz anders aussehen.
Dennis schrieb:> return (void*)nr;
Irgendeinen long-Wert nach void* casten ist fehlerhaft. Es können Daten
verloren gehen (der long ist ggf. nicht wiederherstellbar) und beim
Dereferenzieren des Pointers kann beliebiger Unfug passieren. Es
"funktioniert" nur weil du den Rückgabewert nirgends verwendest.
Dennis schrieb:> if (b==(int*)2)
Was soll das sein? Einen Integer vom Wert "2" in einen Pointer-Typ zu
casten ergibt Unsinn. Der Compiler warnt zu recht.
Dennis schrieb:> return &b;
Hier fehlt ein cast. &b ist vom Typ void**.
Dennis schrieb:> f=*(long*)stage2(&f);
Hier dereferenzierst du einen Pointer vom Typ "long*" welcher aber auf
ein *b zeigt und schneidest dann noch ggf. Bits ab (cast nach int). Es
wird also der ggf. gekürzte Wert in "b" ausgegeben, d.h. die Adresse von
f.
Würde einer der if-Blöcke ausgeführt, wäre diese Adresse inkrementiert
und damit ungültig.
So ein Code ist natürlich nicht nur fehlerhaft, sondern auch unlesbar
und unwartbar. Auf die Nutzung von void*, das Casten zwischen
verschiedenen Pointer-Typen und insbesondere das Casten zwischen
Pointern und Integern sollte man so weit wie irgend möglich vermeiden
(das geht in C++ übrigens besser als in C dank templates). Falls man
doch zwischen Pointer und Integer casten muss, sollte man das nur mit
dem Integer-Typ "uintptr_t" machen, auf keinen Fall mit "long" oder
"int". Auf AMD64 z.B. ist ein Zeiger 64bit-lang, "long" und "int" aber
meist (je nach Compiler) nur 32bit. Somit gehen 32bits verloren.
Umgekehrt ist auf x86 ein Pointer nur 32bit groß, ein "long" könnte je
nach Compiler 64bit sein wodurch wieder Daten verloren gehen wenn man
den "long" auf den Zeiger umwandelt. uintptr_t ist immer richtig groß.
Irgendwelche Zahlen (2, 3) nach Pointer casten ist zwar möglich, aber
hässlich - diesen Zeiger darf man nicht dereferenzieren, sondern nur
wieder zurück auf den Integer casten.
Stelle deinen Compiler auf einen strikten Sprachstandard (ohne
Erweiterungen) und aktiviere alle Warnungen. So lernt man am Besten.
Beim GCC geht das z.B. mit den Optionen "-std=c11 -Wall -Wextra
-Wconversion -pedantic -Werror". Code der damit anstandslos übersetzt
wird hat eine deutlich höhere Wahrscheinlichkeit, dass er korrekt ist.
Hier wird der void-Pointer a also zu einem int-Pointer gecastet:
1
(int*)a
dieser int-Pointer dann dereferenziert
1
*((int*)a)
und das Ergebis --ein int-- einem void-Pointer zugewiesen:
1
b=*((int*)a)
(Die zusätzlichen Klammern dienen dem Verständnis, worauf sich der
jeweilige Operator bezieht)
Und damit sollte die Warnung des Compilers verständlich sein:
"Zuweisung eines ints zu einem Pointer"
Man könnte jetzt noch einen zusätzlichen Typecast einführen
Rufus Τ. F. schrieb:> Man könnte jetzt noch einen zusätzlichen Typecast einführen>>
1
>b=(void*)(*((int*)a))
2
>
>> dann wäre die Warnung weg.
Und der ganze syntaktische Aufwand nur, um eine aus Sicht der Maschine
völlig legale Operation auch für den Compiler zu legalisieren...
Wenn man das dann noch unter besonderer Berücksichtigung des
Sachverhalts betrachtet, dass bei Maschinen, wo diese Operation nicht
legal ist, das Geschwalle zwar den Compiler ruhig stellt, aber letztlich
trotzdem nix funktionieren wird, sollten dann doch erste Zweifel am
Nutzen dieses ganzen Geschwalles aufkommen...
Aber natürlich: wenn C die Bibel ist, dann ist es von Gott gegeben, dass
C alles löst und überhaupt...
42
c-hater schrieb:> Aber natürlich: wenn C die Bibel ist, dann ist es von Gott gegeben, dass> C alles löst und überhaupt...
Du schliesst von extrem schlechten, unleserlichen und sinnfreien Code
zum Experimentieren auf die Sprache um deinen Mist abzuladen.
leo
Wir sollten den Anfängern besser den Rat geben: Datenstrukturen so
anlegen, das keine Casts erforderlich sind. Casts nur in einigen
Sonderfällen wie malloc() oder memcpy() nutzen. Vor allem, niemals von
Const-Pointer auf Nicht-Const casten. Das gibt immer undurchschaubare
Probleme.
Dr. Sommer schrieb:> Irgendwelche Zahlen (2, 3) nach Pointer casten ist zwar möglich, aber> hässlich - diesen Zeiger darf man nicht dereferenzieren, sondern nur> wieder zurück auf den Integer casten.
Für memory mapped Register, IO und bestimmte Speicherbereiche ist das
doch völlig normal. Daß man das in defines kapselt, ist ja bloß
Textersetzung.
Besser gar keinen Cast benutzen schrieb:> Casts nur in einigen Sonderfällen wie malloc() oder memcpy() nutzen.
In C sind da keine Cast nötig.
In C++ verwendet man sie (zumindest malloc) nicht.
A. K. schrieb:> Wieder auf Streit aus?Dirk B. schrieb:> Besser gar keinen Cast benutzen schrieb:>> Casts nur in einigen Sonderfällen wie malloc() oder memcpy() nutzen.>> In C sind da keine Cast nötig.> In C++ verwendet man sie (zumindest malloc) nicht.
Und bei memcpy ist auch in C++ kein Cast nötig.
Dirk B. schrieb:> Besser gar keinen Cast benutzen schrieb:>> Casts nur in einigen Sonderfällen wie malloc() oder memcpy() nutzen.>> In C sind da keine Cast nötig.> In C++ verwendet man sie (zumindest malloc) nicht.
Bitte was?
malloc() liefert doch einen Zeiger vom Typ void* zurück. Seit wann kann
man sowas verwenden ohne zu casten?
Mark B. schrieb:> malloc() liefer doch einen Zeiger vom Typ void* zurück. Seit wann kann> man sowas verwenden ohne zu casten?
C erlaubt die implizite Konvertierung von void* auf andere Zeiger-Typen,
damit die armen C-Programmierer nicht ständig casten müssen (ala "int* f
= (int*) malloc (sizeof(int));"). In C++ schreibt man halt einfach "new
int".
Dr. Sommer schrieb:> C erlaubt die implizite Konvertierung von void* auf andere Zeiger-Typen,> damit die armen C-Programmierer nicht ständig casten müssen (ala "int* f> = (int*) malloc (sizeof(int));"). In C++ schreibt man halt einfach "new> int".
Oh, okay. Ich habe bei malloc() immer gecastet... hat immer funktioniert
;-)
Mark B. schrieb:> Oh, okay. Ich habe bei malloc() immer gecastet... hat immer funktioniert> ;-)
Ist auch sinnvoll, damit der Code auch unter C++ funktioniert.
Dr. Sommer schrieb:> Mark B. schrieb:>> Oh, okay. Ich habe bei malloc() immer gecastet... hat immer funktioniert>> ;-)>> Ist auch sinnvoll, damit der Code auch unter C++ funktioniert.
C Programme sollte man mit einem C-Compiler übersetzen.
Den bieten die allermeisten C++-Compiler auch an.
Dirk B. schrieb:> Dr. Sommer schrieb:>> Mark B. schrieb:>>> Oh, okay. Ich habe bei malloc() immer gecastet... hat immer funktioniert>>> ;-)>>>> Ist auch sinnvoll, damit der Code auch unter C++ funktioniert.>> C Programme sollte man mit einem C-Compiler übersetzen.>> Den bieten die allermeisten C++-Compiler auch an.
Vielleicht hat man aber ein Stück C-Code, das man in eine C++-Source
einfügen will. Das hat Dr.Sommer wohl sagen wollen.
Mark B. schrieb:> Dr. Sommer schrieb:>> C erlaubt die implizite Konvertierung von void* auf andere Zeiger-Typen,>> damit die armen C-Programmierer nicht ständig casten müssen (ala "int* f>> = (int*) malloc (sizeof(int));"). In C++ schreibt man halt einfach "new>> int".>> Oh, okay. Ich habe bei malloc() immer gecastet... hat immer funktioniert> ;-)
Das sollte man in C aber besser nicht tun.
Carl D. schrieb:>> C Programme sollte man mit einem C-Compiler übersetzen.>>>> Den bieten die allermeisten C++-Compiler auch an.>> Vielleicht hat man aber ein Stück C-Code, das man in eine C++-Source> einfügen will. Das hat Dr.Sommer wohl sagen wollen.
Davon würde ich abraten. Wenn man das braucht, steckt man den C-Code in
eine C-Datei. Die Funktionen lassen sich problemlos auch von C++-Code
aus aufrufen. Es gibt doch ein paar subtile Unterschiede, durch die ich
nicht einfach blind darauf vertrauen würde, dass C-Code als C++ noch
genau gleich funktioniert.
Rolf M. schrieb:> Mark B. schrieb:>> Oh, okay. Ich habe bei malloc() immer gecastet... hat immer funktioniert>> ;-)>> Das sollte man in C aber besser nicht tun.
Warum nicht?
Mark B. schrieb:>> Das sollte man in C aber besser nicht tun.>> Warum nicht?
Falls du mal das dazugehörige #include <stdlib.h> vergisst, fehlt eine
Deklaration für malloc. Standard-Verhalten von C ist dann, int als
Return-Typ anzunhemen. Das geht spätestens dann schief, wenn du auf
einem System bist, wo int und Zeiger nicht zufällig die gleiche Größe
haben.
Ohne Cast kommt dann die Meldung, dass du versuchst, einen int in einen
Zeiger umzuwandeln. Mit Cast sagst du dem Compiler, dass du das so
willst und unterdrückst damit die Meldung.
Rolf M. schrieb:> Falls du mal das dazugehörige #include <stdlib.h> vergisst, fehlt eine> Deklaration für malloc. Standard-Verhalten von C ist dann, int als> Return-Typ anzunhemen. Das geht spätestens dann schief, wenn du auf> einem System bist, wo int und Zeiger nicht zufällig die gleiche Größe> haben.> Ohne Cast kommt dann die Meldung, dass du versuchst, einen int in einen> Zeiger umzuwandeln. Mit Cast sagst du dem Compiler, dass du das so> willst und unterdrückst damit die Meldung.
Na ja, den Fall halte ich nun nicht gerade für sehr wahrscheinlich.
Edit: Außerdem erhalte ich mit -Wall sowieso eine Warnung:
1
warning: implicit declaration of function 'malloc' [-Wimplicit-function-declaration]
Ja, die Compiler geben da heute deutlich bessere Warnungen aus als
früher, daher mag das nicht mehr so von Bedeutung sein.
Ich würde einen nicht nötigen Cast aber abgesehen davon sowieso lieber
weglassen.
c-hater schrieb:> Und der ganze syntaktische Aufwand nur, um eine aus Sicht der Maschine> völlig legale Operation auch für den Compiler zu legalisieren...
Nö der ganze syntaktische Aufwand ist eigentlich völlig unnötig, aber
der TS will halt herumspielen.
Aber du hast halt keine Ahnung von C, deswegen glaubst du dass das nötig
wäre.