Wenn die Größe von „long“ und „int“ auf einer Plattform gleich ist – unterscheiden sich „long“ und „int“ in irgendeiner Weise?

Lesezeit: 5 Minuten

Vilhelms Benutzeravatar
Wilhelm

Wenn die Darstellung von a long int und ein int sind auf einer Plattform gleich, sind sie genau gleich? Verhalten sich die Typen auf der Plattform nach dem C-Standard in irgendeiner Weise anders?

Z.B. geht das immer:

int int_var;
long long_var;

void long_bar(long *l);
void int_bar(int *i);

void foo() 
{
    long_bar(&int_var); /* Always OK? */
    int_bar(&long_var);
}

Ich denke, die gleiche Frage gilt für short und int, wenn sie zufällig dieselbe Darstellung sind.

Die Frage tauchte auf, als diskutiert wurde, wie man a definiert int32_t-like typedef für einen eingebetteten C89-Compiler ohne stdint.h, dh as int oder long und ob es wichtig wäre.

  • Unterschiedliche Typen sind unterschiedlich, auch wenn ihre bitweise Darstellung gleich ist. Was Sie tun, bricht striktes Aliasing.

    – Irgendein Programmierer-Typ

    29. März 2021 um 7:47 Uhr


  • Stellen Sie Ihre Frage aus sprachjuristischer Sicht (lesen Sie dann n1570 oder besser ….) oder interessiert es Sie in der Praxis – dann mit welchem ​​Compiler und Zielprozessor? Wenn Ihr Compiler ist GCC benutze es als gcc -Wall -Wextra

    – Basile Starynkevitch

    29. März 2021 um 7:55 Uhr


  • selbst wenn ihre Größen gleich sind, können sie dennoch einen unterschiedlichen Bereich haben, da C zulässt, dass Typen Trap-Darstellungen und Füllbits haben. Zum Beispiel int kann aber 0x80000000 als Trap haben long kann normal gelagert werden. Oder int kann aber 2 Füllbits haben long hat nur 1

    – phuklv

    29. März 2021 um 12:16 Uhr

  • ein weiteres Beispiel ist char, signed char, unsigned char sind 3 verschiedene Typen mit der gleichen Größe, aber ihre Reichweiten sind auch unterschiedlich _Bool das ist ein anderer Typ, der typischerweise die gleiche Größe hat

    – phuklv

    29. März 2021 um 12:26 Uhr

  • Mit GCC können Sie Ihr eigenes GCC-Plugin codieren, um sein Verhalten zu ändern. Und sowohl GCC als auch Clang sind Open-Source-Compiler, die Sie verbessern könnten

    – Basile Starynkevitch

    30. März 2021 um 5:10 Uhr


Benutzeravatar von Lundin
Lundin

Sie sind nicht kompatible Typen, was Sie an einem einfachen Beispiel sehen können:

int* iptr;
long* lptr = iptr; // compiler error here

Daher ist es am wichtigsten, wenn es um Zeiger auf diese Typen geht. Ebenso gibt es die “strenge Aliasing-Regel”, die diesen Code zu einem undefinierten Verhalten macht:

int i;
long* lptr = (long*)&i;
*lptr = ...;  // undefined behavior

Ein weiteres subtiles Problem ist die implizite Beförderung. Falls ja some_int + some_long dann ist der resultierende Typ dieses Ausdrucks long. Oder falls einer der Parameter unsigned ist, unsigned long. Dies liegt an der ganzzahligen Beförderung durch die üblichen arithmetischen Umrechnungen, siehe Heraufstufungsregeln für implizite Typen. Sollte die meiste Zeit keine Rolle spielen, aber Code wie dieser wird fehlschlagen: _Generic(some_int + some_long, int: stuff() ) da es keine gibt long Klausel im Ausdruck.

Im Allgemeinen sollte es beim Zuweisen von Werten zwischen Typen keine Probleme geben. Im Falle von uint32_tes spielt keine Rolle, welcher Art es entspricht, denn Sie sollten behandeln uint32_t sowieso als eigenständige Art. Ich würde wählen long für die Kompatibilität mit kleinen Mikrocontrollern, wo typedef unsigned int uint32_t; wird brechen. (Und offensichtlich typedef signed long int32_t; für das vorzeichenbehaftete Äquivalent.)

  • @chux-ReinstateMonica: Es gibt keine long Eintrag ein _Generic(some_int + some_long, int: stuff() ).

    – Benutzer2357112

    29. März 2021 um 12:15 Uhr

  • @chux-ReinstateMonica Was hat Signierbarkeit damit zu tun? unsigned int ist 16 Bit auf kleinen MCUs. Also, wenn Sie wählen unsigned int zum uint32_t du definierst ein uint32_t mit 16bit.

    – Ludin

    29. März 2021 um 13:06 Uhr

  • Ich bin wahrscheinlich stumpfsinnig, aber – es scheint, als ob es bei Ihrem Beispiel “implizite Werbung” nicht wirklich um implizite Werbung geht. Sie würden das gleiche Problem mit just bekommen _Generic(some_long, int: stuff() ) oder _Generic(some_int, long: stuff() ).

    – ruach

    29. März 2021 um 18:45 Uhr

  • @ruakh Der Punkt ist das _Generic(some_int + some_int, int: stuff() ) hätte funktioniert. Durch die Einführung von a long In einem Ausdruck, der der üblichen arithmetischen Konvertierung unterliegt, habe ich den Typ geändert in longselbst wenn long hat zufällig die gleiche Größe wie int.

    – Ludin

    30. März 2021 um 13:56 Uhr

  • Ebenso überlegen _Generic( ... , int: stuff(), int32_t: stuff) vs _Generic( ... , long: stuff(), int: stuff). Ersteres führt zu einem Compiler-Fehler, wenn int32_t ist eigentlich der gleiche Typ wie int. Letzteres jedoch nicht, da es sich um charakteristische Typen derselben Größe handelt.

    – Ludin

    30. März 2021 um 14:02 Uhr

Vlad aus Moskaus Benutzer-Avatar
Vlad aus Moskau

Die Typen long und int haben verschiedene Ränge. Der Rang des Typs long ist höher als der Rang des Typs int. Also in einem binären Ausdruck, wo ein Objekt des Typs verwendet wird long und ein Objekt des Typs int der letzte wird immer in den Typ konvertiert long.

Vergleichen Sie die folgenden Codeausschnitte.

int x = 0;
unsigned int y = 0;

die Art des Ausdrucks x + y ist unsigned int.

long x = 0;
unsigned int y = 0;

die Art des Ausdrucks x + y ist unsigned long (aufgrund der üblichen arithmetischen Umrechnungen) vorausgesetzt sizeof( int ) ist gleich sizeof( long).

Dies ist in C++ sehr wichtig als in C, wo das Überladen von Funktionen erlaubt ist.

In C müssen Sie dies beispielsweise berücksichtigen, wenn Sie I/O-Funktionen wie zum Beispiel verwenden printf um einen korrekten Konvertierungsbezeichner anzugeben.

Auch auf Plattformen wo long und int die gleiche Darstellung haben, würde der Standard es Compilern ermöglichen, absichtlich blind gegenüber der Möglichkeit zu sein, dass der Vorgang des Speicherns eines Werts in a long* könnte den Wert von an beeinflussen int* oder umgekehrt. Angesichts so etwas wie:

#include <stdint.h>

void store_to_int32(void *p, int index)
{
    ((int32_t*)p)[index] = 2;
}
int array1[10];
int test1(int index)
{
    array1[0] = 1;
    store_to_int32(array1, index);
    return array1[0];
}
long array2[10];
long test2(int index)
{
    array2[0] = 1;
    store_to_int32(array2, index);
    return array2[0];
}

Die 32-Bit-ARM-Version von gcc wird behandelt int32_t wie synonym mit long und ignorieren Sie die Möglichkeit, dass die Adresse von an übergeben wird array1 zu store_to_int32 könnte dazu führen, dass das erste Element dieses Arrays geschrieben wird, und die 32-Bit-Version von clang behandelt int32_t wie synonym mit int und ignorieren Sie die Möglichkeit, dass die Adresse übergeben wird array2 zu store_to_int32 kann dazu führen, dass das erste Element dieses Arrays geschrieben wird.

Natürlich würde nichts im Standard Compilern verbieten, sich auf diese Weise zu verhalten, aber ich denke, das Versäumnis des Standards, eine solche Blindheit zu verbieten, ergibt sich aus dem Grundsatz “je dümmer etwas wäre, desto weniger Notwendigkeit sollte es verbieten”.

1409350cookie-checkWenn die Größe von „long“ und „int“ auf einer Plattform gleich ist – unterscheiden sich „long“ und „int“ in irgendeiner Weise?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy