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.
Rolf F. schrieb: > Das Problem müssten doch abermillionen andere Programmierer schon gehabt > habe welches Problem denn?
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 | int main() |
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.
:
Bearbeitet durch User
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?
:
Bearbeitet durch User
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.
#include <limits.h>
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.
:
Bearbeitet durch User
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 | > int main() |
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:
1 | $ echo | avr-gcc -x c - -E -dM | grep SIZE | sort |
2 | #define __SIZEOF_DOUBLE__ 4 |
3 | #define __SIZEOF_FLOAT__ 4 |
4 | #define __SIZEOF_INT__ 2 |
5 | #define __SIZEOF_LONG_DOUBLE__ 4 |
6 | #define __SIZEOF_LONG_LONG__ 8 |
7 | #define __SIZEOF_LONG__ 4 |
8 | #define __SIZEOF_POINTER__ 2 |
9 | #define __SIZEOF_PTRDIFF_T__ 2 |
10 | #define __SIZEOF_SHORT__ 2 |
11 | #define __SIZEOF_SIZE_T__ 2 |
12 | #define __SIZEOF_WCHAR_T__ 2 |
13 | #define __SIZEOF_WINT_T__ 2 |
14 | #define __SIZE_MAX__ 65535U |
15 | #define __SIZE_TYPE__ unsigned int |
...wie Basistypen definiert sind:
1 | $ echo | avr-gcc -x c - -E -dM | grep TYPE | sort |
2 | #define __CHAR16_TYPE__ short unsigned int |
3 | #define __CHAR32_TYPE__ long unsigned int |
4 | #define __INT16_TYPE__ short int |
5 | #define __INT32_TYPE__ long int |
6 | #define __INT64_TYPE__ long long int |
7 | #define __INT8_TYPE__ signed char |
8 | #define __INTMAX_TYPE__ long long int |
9 | #define __INTPTR_TYPE__ int |
10 | #define __INT_FAST16_TYPE__ int |
11 | #define __INT_FAST32_TYPE__ long int |
12 | #define __INT_FAST64_TYPE__ long long int |
13 | #define __INT_FAST8_TYPE__ signed char |
14 | #define __INT_LEAST16_TYPE__ short int |
15 | #define __INT_LEAST32_TYPE__ long int |
16 | #define __INT_LEAST64_TYPE__ long long int |
17 | #define __INT_LEAST8_TYPE__ signed char |
18 | #define __PTRDIFF_TYPE__ int |
19 | #define __SIG_ATOMIC_TYPE__ char |
20 | #define __SIZE_TYPE__ unsigned int |
21 | #define __UINT16_TYPE__ short unsigned int |
22 | #define __UINT32_TYPE__ long unsigned int |
23 | #define __UINT64_TYPE__ long long unsigned int |
24 | #define __UINT8_TYPE__ unsigned char |
25 | #define __UINTMAX_TYPE__ long long unsigned int |
...Endianess:
1 | $ echo | avr-gcc -x c - -E -dM | grep END | sort |
2 | #define __BYTE_ORDER__ __ORDER_LITTLE_ENDIAN__ |
3 | #define __FLOAT_WORD_ORDER__ __ORDER_LITTLE_ENDIAN__ |
4 | #define __ORDER_BIG_ENDIAN__ 4321 |
5 | #define __ORDER_LITTLE_ENDIAN__ 1234 |
6 | #define __ORDER_PDP_ENDIAN__ 3412 |
7 | D:\C\avrtest> |
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 | int a [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 | int a[sizeof (struct {char a; int b;}) == 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.
:
Bearbeitet durch User
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.
Mark Brandis schrieb: > Rolf F. schrieb: >> Endianess > > Das hier find ich gut: > > http://esr.ibiblio.org/?p=5095 Damit findet man aber PDP Endian und andere nicht. Besser ist:
1 | // Estimate the main endianess. |
2 | // Caution: There are more platform dependencies (implementation-dependent |
3 | // things), e. g. the alignment rules and the size of a Byte, which can be |
4 | // found as CHAR_BIT in limits.h. |
5 | # ifndef LITTLE_ENDIAN |
6 | # define LITTLE_ENDIAN 1 |
7 | # endif |
8 | # ifndef PDP_ENDIAN |
9 | # define PDP_ENDIAN 2 |
10 | # endif |
11 | # ifndef BIG_ENDIAN |
12 | # define BIG_ENDIAN 3 |
13 | # endif |
14 | # ifndef UNKNOWN_ENDIAN |
15 | # define UNKNOWN_ENDIAN 0 |
16 | # endif |
17 | uint16_t endianess () |
18 | { |
19 | union |
20 | { |
21 | uint64_t uli; |
22 | uint32_t ui[2]; |
23 | uint16_t usi[4]; |
24 | uint8_t uc[8]; |
25 | } u_end = |
26 | { |
27 | 1}; |
28 | if (1 == u_end.uc[0]) |
29 | return (LITTLE_ENDIAN); |
30 | else |
31 | { |
32 | if (1 == u_end.usi[1]) |
33 | return (PDP_ENDIAN); |
34 | else |
35 | { |
36 | if (1 == u_end.uc[7]) |
37 | return (BIG_ENDIAN); |
38 | } |
39 | } |
40 | return (UNKNOWN_ENDIAN); |
41 | } |
:
Bearbeitet durch User
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; }
Rolf F. schrieb: > Hier ist mal ein Anfang mit einem kleinen Testprogramm: Das ist nicht portabel da ungültiger Code :-)
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...
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.