Ich suche ein Diagnose-Programm (im Quelltext) mit dem man die
Umgebungsparameter der Plattform angezeigt bekommt, beispielsweise
sizeof(int), Endianess, Alignment und anderes.
Können die Experten hier eines empfehlen?
Das Problem müssten doch abermillionen andere Programmierer schon gehabt
haben und entsprechend müsste es doch Standard-Programme dafür geben.
Peter II schrieb:> Rolf F. schrieb:>> Das Problem müssten doch abermillionen andere Programmierer schon gehabt>> habe>> welches Problem denn?
und wenn man wirklich die Parameter eines Compilers mal schnell braucht,
dann schreibt man sich den 4 Zeiler mal halt eben schnell
1
#include<stdio.h>
2
3
intmain()
4
{
5
printf("int : %d Bytes\n",(int)sizeof(int));
6
printf("long: %d Bytes\n",(int)sizeof(long));
7
8
printf("to be continued\n");
9
}
das geht schneller als das Durchsuchen der Tools Verzeichnisse und sich
daran erinnern, wie denn das Tool verflixt noch mal geheissen hat.
Eben. Selber machen ist angesagt. Endianess zu prüfen geht leicht mit
einer union. Alignement geht u.U. mit sizeof
(meine_geschickt_angelegte_struct).
Man kann aber auch einfach portabel programmieren. Dann braucht man
weder Wissen über Endianess noch über Alignments.
Es gibt Binärprotokolle die auch 2- oder 4 Byte Typen übertragen können.
Irgendwo wird man da zwangsläufig die Endianess der Zielhardware in
Betracht ziehen müssen, da gehts dann nicht mehr 100% portabel.
Haro schrieb:> Es gibt Binärprotokolle die auch 2- oder 4 Byte Typen übertragen können.> Irgendwo wird man da zwangsläufig die Endianess der Zielhardware in> Betracht ziehen müssen, da gehts dann nicht mehr 100% portabel.
Du kennst Funktionen wie htonl?
Haro schrieb:> Es gibt Binärprotokolle die auch 2- oder 4 Byte Typen übertragen können.> Irgendwo wird man da zwangsläufig die Endianess der Zielhardware in> Betracht ziehen müssen, da gehts dann nicht mehr 100% portabel.
warum nicht?
uint16_t x = byteH<<8 | byteL
A. K. schrieb:> Geht. Tempo spielt aber ab und zu doch noch eine Rolle.
wo sieht du hier ein Problem? So etwas bekommt wohl jeder aktuelle
Compiler sinnvoll hin.
Peter II schrieb:> wo sieht du hier ein Problem? So etwas bekommt wohl jeder aktuelle> Compiler sinnvoll hin.
Mag sein, dass manche aktuellen Compiler die entsprechende Byte-Orgie
auch bei 4 Bytes erkennen und optimieren. Aber da man dieses Problem
schon vor zig Jahren hatte gibt es überall dort, wo der Sockel-API
Einzug hielt, mindestens Funktionen wie htons/ntohs und htonl/ntohl, die
(hoffentlich) einigermassen optimiert implementiert sind (garnix, oder
Byteswap-Befehl).
Weshalb also das Rad neu erfinden?
A. K. schrieb:> Weshalb also das Rad neu erfinden?
will ich doch gar nicht.
Ich wollte nur fragen wo er ein Problem sieht
> Es gibt Binärprotokolle die auch 2- oder 4 Byte Typen übertragen können.> Irgendwo wird man da zwangsläufig die Endianess der Zielhardware in> Betracht ziehen müssen, da gehts dann nicht mehr 100% portabel.
wie man es löst, ist ja egal aber es geht portable.
Rolf F. schrieb:> Das Problem müssten doch abermillionen andere Programmierer schon gehabt> haben und entsprechend müsste es doch Standard-Programme dafür geben.
Ja, die gibt es:
* autoconf/automake
* cmake
Um mal die bekanntesten zu nennen. Mit solchen oder ähnlichen Tools wird
dieser ganze Themenkomplex mehr oder weniger automatisiert erschlagen,
die finden raus wie es auf der Zielplatform aussieht und konfigurieren
dein Makefile entsprechend.
Karl Heinz schrieb:> Peter II schrieb:>> Rolf F. schrieb:>>> Das Problem müssten doch abermillionen andere Programmierer>>> schon gehabt habe>>>> welches Problem denn?>> und wenn man wirklich die Parameter eines Compilers mal schnell braucht,> dann schreibt man sich den 4 Zeiler mal halt eben schnell>>
1
>#include<stdio.h>
2
>
3
>intmain()
4
>{
5
>printf("int : %d Bytes\n",(int)sizeof(int));
6
>printf("long: %d Bytes\n",(int)sizeof(long));
7
>
8
>printf("to be continued\n");
9
>}
10
>
Ok, das ist für den einfachen Fall, dass der Compiler, um den es geht,
ein nativer ist und man das Programm einfach ausführen kann.
Bei einem Crosscompiler ist das nicht mehr ganz so einfach... Was
hingegen auch mit einem Crosscompiler geht, ist Code präprozessieren zu
lassen.
Handelt es sich um einen foo-gcc, dann gibt es built-in Makros für die
meisten Typen; hier avr-gcc:
Weiß man nicht, um welchen Compiler es sich handelt, scannt man stderr
auf Ausgabe -- zumindest wenn sich der Compiler halbwegs an den Standard
hält:
Keine Diagnostic --> der Test trifft zu.
Diagnostic --> der Test trifft nicht zu.
Beispiel:
1
inta[sizeof(int)==2?1:-1];
Bei Fehler ist int nicht 2 Bytes groß, ansonsten ist es 2 Bytes groß;
wobei sizeof (Byte) = sizeof (char) sei. Das ist z.B. der Ansatz, den
configure bzw. autoconf wählt.
Für Alignment ist das alles schon was hakeliger. Evtl. geht dieser Test:
1
inta[sizeof(struct{chara;intb;})==8?1:-1];
D.h. unter der Annahme, dass das Alignment des int in einer Struktur
Rückschlüsse auf das Alignment von int zulässt. Hier ist man auch dann
nicht besser dran, wenn man das Programm ausführen kann, denn das
Ergebnis basiert auf der gleichen Annahme.
Das einzige was dann hilft, ist die Compiler-Doku bzw. (E)ABI zu lesen,
wo "Implementation Defined Behaviour" dokumentiert ist.
Ob es einen sicheren Compilezeit-Test auf Endianess gibt weiß ich nicht;
bisher ist mir keiner eingefallen. Ideen?
A. K. schrieb:> Haro schrieb:> Es gibt Binärprotokolle die auch 2- oder 4 Byte Typen übertragen können.> Irgendwo wird man da zwangsläufig die Endianess der Zielhardware in> Betracht ziehen müssen, da gehts dann nicht mehr 100% portabel.>> Du kennst Funktionen wie htonl?
Ja, die werden aber nicht vom C Standard definiert, stehen damit also
nicht automatisch auf jedem System mit C Compiler zur Verfügung. Die AVR
libc bringt sie beispielsweise nicht mit.
Selber schreiben ist zwar trivial, aber Code der darauf angewiesen ist
ist damit jedenfalls nicht mehr uneingeschränkt portabel.
> warum nicht?>> uint16_t x = byteH<<8 | byteL
In deinem Beispiel mit byteH und byteL natürlich klar.
Üblicherweise gelangen die Daten aber Byteweise ins System und liegen
erstmal irgendwo in einem Puffer myBuffer[MY_BUFFER_SIZE].
Das Protokoll enthält Header, Kommandos, und irgendwie auch die
Nutzdaten.
Kannst du nun für alle Systeme uneingeschränkt festlegen, dass immer
myBuffer[20] an die höherwertige Adresse und myBuffer[21] an die
niederwertige Adresse eines 16bit Typen muss?
Haro schrieb:> warum nicht?> uint16_t x = byteH<<8 | byteL>> In deinem Beispiel mit byteH und byteL natürlich klar.>> Üblicherweise gelangen die Daten aber Byteweise ins System und liegen> erstmal irgendwo in einem Puffer myBuffer[MY_BUFFER_SIZE].> Das Protokoll enthält Header, Kommandos, und irgendwie auch die> Nutzdaten.>> Kannst du nun für alle Systeme uneingeschränkt festlegen, dass immer> myBuffer[20] an die höherwertige Adresse und myBuffer[21] an die> niederwertige Adresse eines 16bit Typen muss?
Vergiss diesen Beitrag, es ist spät...
Gewöhnlich legt man für das Protokoll eine bestimmte Bytereihenfolge
fest, unabhängig davon wer mit wem redet. Bei Seiten passen sich dann
dieser Definition an.
Frank M. schrieb:> Man kann aber auch einfach portabel programmieren. Dann braucht man> weder Wissen über Endianess noch über Alignments.
Das ist aber ineffizient.
Beispielsweise kann mam uint64_t verwenden, aber allein damit kennt man
die Endianess nicht, die bei Unions wichtig ist. Bei vielen
ARM-Prozessoren kann man ja einstellen ob Big- oder Little Endian
(vielleicht auch PDP Endian).
Und es gibt Plattformen wie MSP430, bei denen die Operationen damit
deutlich langsamer als mit uint16_t sind.
Außerdem geht es hier um das Wissen über eine Plattform, wozu nicht nur
das Datenblatt der CPU gehört sondern auch das was Compiler, Assembler,
Linker usw. auch tatsächlich können.
Was man mit dem Wissen macht ist eine andere Sache. Mir geht es erstmal
um die Diagnose.
Hier ist mal ein Anfang mit einem kleinen Testprogramm:
[CODE]
// test program for platform dependent things like type sizes,
endianess, alignment, ...
#include <stdio.h>
#include <stdint.h>
#include <limits.h>
// Estimate the main endianess.
void endianess ()
{
union
{
uint64_t uli;
uint32_t ui[2];
uint16_t usi[4];
uint8_t uc[8];
} u_end =
{
1};
printf ("Endianess: ");
if (1 == u_end.uc[0])
{
printf ("LITTLE_ENDIAN\n");
return;
}
else
{
if (1 == u_end.usi[1])
{
printf ("PDP_ENDIAN\n");
return;
}
else
{
if (1 == u_end.uc[7])
{
printf ("BIG_ENDIAN\n");
return;
}
}
}
printf ("UNKNOWN_ENDIAN\n");
return;
}
int main ()
{
//clrscr();
endianess ();
printf ("Size of byte in bits is %d\n", CHAR_BIT);
printf ("Size of char (in bytes) is %d\n", sizeof (char));
printf ("Size of wide char is %d\n", sizeof (wchar_t));
printf ("Size of short int is %d\n", sizeof (short int));
printf ("Size of int is %d\n", sizeof (int));
printf ("Size of long int is %d\n", sizeof (long int));
printf ("Size of long long int is %d\n", sizeof (long long int));
printf ("Size of pointer is %d\n", sizeof (int *));
printf ("Size of long is %d\n", sizeof (float));
printf ("Size of double is %d\n", sizeof (double));
printf ("Size of long double is %d\n", sizeof (long double));
//sizeof struct test{char c, long long int lli;
return 0;
}
Johann L. schrieb:> Rolf F. schrieb:>> Hier ist mal ein Anfang mit einem kleinen Testprogramm:>> Das ist nicht portabel da ungültiger Code :-)
Klar, das ist ja nur der Quelltext.
Ein Programm wird daraus erst mit Präprozessor, Compiler, Assembler,
Linker usw..
Und das ist für eine hosted Implementation. Freestanding ist es
schwieriger, weil es da meist kein printf gibt.
Und schöner wäre es wenn schon der Präprozessor die Werte ausgeben
würde, über #warning.
Aber geht das überhaupt?
Hier gibt es unten auf der Seite ein Test-Programm und mit weniger
Umfang die Autoconf-Einträge:
https://wiki.debian.org/ArchitectureSpecificsMemo
Unter Ubuntu liefert mir das auf meinem PC:
Size of byte in bits is 8
Size of char (in bytes) is 1
Size of wide char is 4
sizeof(short) = 2
sizeof(int) = 4
sizeof(long) = 8
sizeof(long long) = 8
sizeof(float) = 4
sizeof(double) = 8
sizeof(long double) = 16
sizeof(void *) = 8
byte order = little endian
char signedness = signed
stack = grows down
Das long double war früher nur 10 Byte groß und wide Char nur 2.
Erwin Meyer schrieb:> Johann L. schrieb:>> Rolf F. schrieb:>>> Hier ist mal ein Anfang mit einem kleinen Testprogramm:>>>> Das ist nicht portabel da ungültiger Code :-)>> Klar, das ist ja nur der Quelltext.
...dessen Verhalten im C-Standard festgelegt ist. Diese bezeichnet das
Verhalten des Programms als "Undefined Behavior". Hier im Forum wurde
das schon X mal durchgekaut. Im Enfeffekt sid es ähnliche Regeln wie
sie auch für Strict Aliasing gelten.
GCC unterstützt deinen Code als Spracherweiterung und sichert das, was
du erwartest, auch in seiner Dokumentation zu. Der Code ist also nur
gültig, weil wir hier im GCC-Forum sind :-) Mit anderen Compilern geht
du aber durchaus baden...