8 Stimmen

Warum sind "long *" und "int *" im 32-Bit-Code nicht kompatibel?

Ich habe mich gefragt, warum der folgende Code nicht kompiliert:

void foo_int(int *a) { }
void foo_long(long *a) { }

int main()
{
    int i;
    long l;

    foo_long(&i);
    foo_int(&l);
}

Ich verwende GCC, und weder die Aufrufe funktionieren in C noch in C++. Da es sich um ein 32-Bit-System handelt, sind sowohl int als auch long vorzeichenbehaftete 32-Bit-Integer (was zur Compile-Zeit mit sizeof überprüft werden kann).

Der Grund, warum ich frage, ist, dass ich zwei separate Header-Dateien habe, die nicht unter meiner Kontrolle sind, und eine tut so etwas wie: typedef unsigned long u32; und die andere: typedef unsigned int uint32_t;. Die Deklarationen sind im Grunde kompatibel, außer wenn ich sie als Zeiger verwende wie im obigen Code-Schnipsel, muss ich explizit casten.

Irgendeine Idee, warum das so ist?

0 Stimmen

Gute Frage und interessante Antworten. Wahrscheinlich ist die technisch sinnvolle Lösung, eines der Header-Datei-Typdefinitionen zu ändern - obwohl dies möglicherweise aus anderen Gründen schwierig ist, wie Sie sagten.

2voto

Earlz Punkte 59611

Dies liegt daran, dass auf einigen Plattformen long und int unterschiedliche Größen haben.

16 bit:
long=32bits
int=16bits

32bit:
long=32bits
int=32bits

64bit(ILP64):
long=64bits
int=64bits

64bit(LP64):
long=64bits
int=32bits

64bit(LLP64): (was Windows aus irgendeinem Grund verwendet)
long long=64bits
long=32bits
int=32bits

Außerdem ist das Verwirrende, dass Sie zwar zwischen den beiden Typen umwandeln müssen, jedoch keine Funktionenüberladung wie folgt machen können, als ob es wirklich zwei separate Typen wären

long foo(int bar);
int foo(int bar);

2 Stimmen

Das ist keine Längen-/integer-Sache - man kann nie Überlastung des Rückgabetyps erreichen. Der Compiler könnte foo(int bar) und foo(long bar) als dasselbe behandeln, was falsch erscheint, aber nützlich ist, wenn man sich vorstellt, foo(1);

0 Stimmen

Integer-Konvertierung ist nicht, worum es in der Frage geht. Es geht um Zeigerkonvertierung. Eine implizite Konvertierung zwischen Integer-Typen ist in jedem Fall zulässig.

2voto

jkerian Punkte 15571

Int* und long* sind unterschiedliche Typen, die nicht unbedingt gleich sind. In jeder realen Implementierung denke ich, dass sie es sind, aber das ist weder hier noch da für einen standardkonformen Compiler.

Ich glaube, es war eine der frühen PDP-Maschinen, in der ein char* größer war als ein int*. Der Grund dafür war die ungerade Größe von ints auf dieser Architektur (36 Bits). Also packte das System mehrere 9-Bit-Zeichen in ein einziges int, so dass ein char* die Adresse im Format von (int*, Offset innerhalb des int) enthielt.

Der Standard schreibt vor, dass alle Zeiger als void* darstellbar sind und deutet darauf hin, dass char* gleich void* sein muss. Es gibt jedoch keine spezielle Anforderung, dass die anderen Zeigertypen konvertierbar sein müssen.

** Ich kann keine Referenzen dazu finden, daher könnte die Quelle dafür ein theoretisches (aber dennoch gültiges) Beispiel gewesen sein, anstelle einer tatsächlichen Implementierung.C++ FAQ Lite

1 Stimmen

Jede echte Implementierung? Berücksichtigen Sie die 8-Bit- und 16-Bit-Embedded-Welt. Oft ist Int 16 Bit lang, Long 32 Bit.

2 Stimmen

Ich spreche nicht über int und long, sondern über int* und long*. Mir ist keine Implementierung bekannt, in der diese POINTER-Typen nicht die gleiche Größe haben. Die Beobachtungen des Originalautors, dass int und long auf seiner Plattform die gleiche Größe haben, sind in diesem Fall eine völlige Ablenkung.

1voto

codymanix Punkte 26958

Int und long sind als verschiedene Typen definiert, damit Sie portabel programmieren können.

0 Stimmen

+1 für die Feststellung der Portabilität. Du kannst "Programm" durch "Überlastung" ersetzen.

1voto

Jack Aidley Punkte 18310

Ich glaube nicht, dass eine dieser Antworten zum Kern der Sache vordringt.

Die Antwort lautet wie folgt: Eine gültige Umwandlung zwischen Typen impliziert keine gültige Umwandlung zwischen Zeigern. Das ergibt Sinn, oder? Sie möchten, dass der folgende Code kompiliert

char a = 12;
int b = a;

Aber das Kompilieren dieses Codes wäre ein Rezept für ein Desaster:

void foo(int* x) { x = 0x7f8f9faf; }

// ...

char a = 12;
foo(&a);

Nur weil es eine Umwandlung zwischen long und int gibt, bedeutet das nicht, dass es eine Zeigerumwandlung geben sollte. Aber Sie könnten einwenden, dass long und int auf Ihrem Compiler genau die gleiche Darstellung haben! Und Sie hätten recht, aber das ändert nichts daran, dass sie nach dem Standard und Compiler unterschiedliche Typen sind. Und Sie können nicht implizit zwischen Zeigern zu Typen umwandeln, es sei denn, es besteht eine Vererbungsbeziehung.

Allgemeiner gesagt, obwohl C++ sich möglicherweise ändert, ob es basierend auf den lokalen Definitionen, wie groß int usw. sind, gültig ist oder nicht, ändert es nicht, ob es syntaktisch korrekt ist. Wenn man den Unterschied zwischen long und int komplett aufhebt, wird dies dazu führen.

0voto

sambowry Punkte 2386

Sie müssen den expliziten Cast nicht bei jedem Aufruf von Hand schreiben. Ein einfaches Makro kann dies erledigen:

#include  

void foo_int( int *a ){ printf("a: %i\n", *a ); }

#define foo_int(a)  foo_int( (int*)a ) 

int main(){
  long l = 12;
  foo_int( &l ); // keine Warnung, läuft wie erwartet
  foo_int(  l ); // keine Warnung, Segmentierungsfehler
}

0 Stimmen

Ich hoffe, Sie erkennen, dass dies nicht tragbar ist. Es entspricht einem reinterpret_cast. Falls int und long nicht den gleichen "zugrunde liegenden Typ" teilen, erhalten Sie seltsame Ergebnisse auf Big-Endian-Maschinen.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X