Hier zunächst einmal das komplette Programm. In der Folge wird dann auf die einzelnen Anweisungen eingegangen
001 |
002 | #include <stdio.h>
003 | #include <stdlib.h>
004 |
005 | //#include "../include/myheader.h"
006 |
007 | #define MAX 10
008 |
009 |
010 | int x = 0; // Variable mit Anfangsinhalt
011 | int i; // Nur Speicherreservierung ohne Inhalt
012 | int j,n=1;
013 |
014 | int itab[MAX]= {1,-1, 100,-100,10000, -10000,32767,-32768,2147483647,-2147483648};
015 | int *ptr_itab; // Pointer zeigt noch ins Nirvana
016 |
017 | float ftab[15]= {0.0f, 1.0f, -1.0f, 5.0f ,-5.0f,0.4f, -0.4f, 0.25f, -0.25f, 0.00001f,-0.00001f,0.00
| 00001234567f,100.0f,-100.0f,100.123567f, 1234.98765432f};
018 | float *flt_ptr;
019 | // Gleitkommaliterale ohne Suffix sind stets vom Typ double
020 | float flt_1=123456E30F; // überschreitet Signifikanzbereich f,F ist Float
021 | float flt_2=-123456E30f; // überschreitet Signifikanzbereich
022 |
023 | double dbl_1=1.0F; // Innerhalb Signifikanzbereich L
024 | double dbl_2=-1.0F; // Innerhalb Signifikanzbereich L
025 | double dbl_3=123456789012E14;
026 | double dbl_4=123456789012E-14;
027 |
028 | unsigned int *p1;
029 | unsigned int *p2;
030 |
031 | const char fmt[]=" %s %08X %12.2lf\n";
032 |
033 | unsigned char *ptr, *end;
034 |
035 | char endstring[]=" **** ENDE **** \n";
036 | int dummy=0Xcfcfcfcf;
037 |
038 | /* wie kann ich das extern machen */
039 | void memory_dump(unsigned char *anf, unsigned char *ende ){
040 | unsigned char *ptr, *end;
041 | char ascii[81];
042 | ptr=anf;
043 | end=ende;
044 | int i=0;
045 | int n=0;
046 | int ind=0;
047 | int val=0;
048 | char c=' ';
049 | // printf(" Memorydump \n Startadress %08X Endadresse %08X\n\n",ptr,end);
050 |
051 | while(ptr < end){
052 |
053 | char *asc_ptr=&ascii[12];
054 | for(i=0;i<79;i++){
055 | ascii[i]=' ';
056 | }
057 | ascii[79]='\0';
058 |
059 | sprintf(ascii," %08X | ", ptr);
060 |
061 | for(i=0;i<16;i+=4) {
062 |
063 | for(n=0;n<4;n++){
064 | sprintf(asc_ptr,"%02X",*ptr);
065 | asc_ptr+=2;
066 | val= *ptr ; // Zeichen holen
067 | ind=48+i+n;
068 | // printf("%s %i %08X %08X\n",ascii,i ,ptr,asc_ptr);
069 | // printf("%s %i\n",ascii, val);
070 |
071 | if((val > 31) && (val < 127)) {
072 | c=val;
073 | // printf( "%i %c ",val,c );
074 | ascii[ind] = c;
075 | }
076 | else { ascii[ind] = '.';}
077 | ptr++;
078 | }
079 | *asc_ptr=' ';
080 | asc_ptr++;
081 | }
082 |
083 | ascii[70]='\0';
084 | printf ("%s\n",ascii);
085 |
086 | }
087 | return ;
088 | }
089 |
090 |
091 |
092 | int main(int argc, char **argv)
093 | {
094 | printf("Grösse einer Integer Variablen %i \n",(unsigned long) sizeof(int ));
095 | printf("Grösse einer Float Variablen %i \n",(unsigned long) sizeof(float ));
096 | printf("Grösse einer Double Variablen %i \n\n",(unsigned long) sizeof(double ));
097 |
098 | printf("Grösse eines int Pointers %i \n",(unsigned long) sizeof(int *));
099 | printf("Grösse eines double Pointers %i \n",(unsigned long) sizeof(double *));
100 | printf("\n ===========================================================================\n\n");
101 |
102 | ptr_itab=itab;
103 | printf(" Ausgabe der Tabelle itab in unterschiedlichen Formaten\n");
104 | printf("Index Inhalt Inhalt &itab[i] &itab[i] Pointer *Pointer \n");
105 | printf( "%%3i %%11i HEX-%%08X %%10i %%08X %%08X %%11i\n");
106 | for(i=0;i<MAX;i++){
107 | printf ("%3i %11i %08X %08X %10i %08X %11i\n",i,itab[i],itab[i], &itab[i], &it
| ab[i], ptr_itab, *ptr_itab);
108 | ptr_itab++;
109 | }
110 |
111 |
112 | printf("\n ===========================================================================\n\n");
113 | printf(" Ausgabe der Tabelle ftab in unterschiedlichen Formaten\n");
114 | printf(" Index Inhalt HEX-Adr. Inhalt Inhalt Adresse Adresse HEX\n");
115 | printf("%%3i %%12.5f %%08X %%08X %%15.10f %%08X %%18.13f\n");
116 | flt_ptr=ftab;
117 | for(i=0;i<15;i++){
118 | p1=(int *) &ftab[i];
119 | printf("%3i%12.5f %08X %08X %15.10f %08X %18.13f\n",i,ftab[i], &ftab[i], *p1, ftab[i], flt_
| ptr, *&ftab[i]);
120 | flt_ptr++;
121 | }
122 | printf("\n ===========================================================================\n\n");
123 | printf(fmt,"flt_1" ,flt_1, flt_1);
124 | printf(fmt,"flt_1" , &flt_1, *&flt_1);
125 | printf("\n");
126 |
127 | printf(" %s %08X %08X %18.12lf \n", "flt_2" , &flt_2, *&flt_2, flt_2);
128 | printf(" %s %08X %08X %lf \n\n", "flt_2" , &flt_2, *&flt_2, flt_2);
129 |
130 | p1=(int *) &dbl_1; // cast nach intpointer (böse trickserei)
131 | printf(" %s %08X %16X %18.12lf \n", "dbl_1" ,&dbl_1, *p1, dbl_1);
132 |
133 |
134 |
135 | p2=p1; // p2
136 | p2++;
137 |
138 | printf(" %s %08X %08X %08X %18.12f \n", "dbl_2",(int *) &dbl_1, *p1, *p2, dbl_1);
139 |
140 | p1+=2;
141 | p2+=2;
142 | printf(" %s %08X %08X %08X %18.12f \n", "dbl_2",(int *) &dbl_2, *p1, *p2, dbl_2);
143 |
144 | p1+=2;
145 | p2+=2;
146 | printf(" %s %08X %08X %08X %18.12f \n", "dbl_3",(int *) &dbl_3, *p1, *p2, dbl_3);
147 |
148 | p1++;
149 | p2++;
150 | printf(" %s %08X %08X %08X %18.12f \n", "dbl_4",(int *) &dbl_4, *p1, *p2, dbl_4);
151 |
152 |
153 | ptr_itab=itab;
154 | printf("\n ===========================================================================\n\n");
155 | printf(" Ausgabe der variablen x, i, ptr_itab in unterschiedlichen Formaten\n");
156 | printf(" %%3 %%8i %%08X %%8i\t %%08X %%08X \n");
157 | printf(" x %3i\t %8i\t %08X \t %8i\t%08X\n",x,x,x,&x,&x);
158 | printf(" i %3i\t %8i\t %08X \t %8i\t%08X\n",i,i,i,&i,&i);
159 | printf(" i %3i\t %8i\t %08X \t %8i\t%08X \t%08X \n",ptr_itab, ptr_itab, *ptr_itab, *ptr_itab, &am
| p;ptr_itab, &ptr_itab);
160 |
161 |
162 | printf("\n ===========================================================================\n\n");
163 | printf(" Stackdump\n\n");
164 | ptr= (unsigned char*) itab;
165 | end= (unsigned char*) &dummy+4;
166 | n=1;
167 | while(ptr < end){
168 | printf("%08X | ", ptr);
169 | for(i=0;i<4;i++){
170 | printf("%02X ",*ptr++);
171 | // n++;
172 | }
173 | printf("\n");
174 | }
175 | printf("\n Ende Stackdump \n");
176 | printf("\n ===========================================================================\n\n");
177 |
178 | ptr= (unsigned char*) itab;
179 |
180 | memory_dump(ptr,end);
181 | return 0;
182 |
183 |
184 | }
185 |
186 |
Die gesamte Ausgabe des Programms
detlef@i7-ssd-graf:/data/htdocs/arduino/arduino/src$ ./hex_adr Grösse einer Integer Variablen 4 Grösse einer Float Variablen 4 Grösse einer Double Variablen 8 Grösse eines int Pointers 8 Grösse eines double Pointers 8 =========================================================================== Ausgabe der Tabelle itab in unterschiedlichen Formaten Index Inhalt Inhalt &itab[i] &itab[i] Pointer *Pointer %3i %11i HEX-%08X %10i %08X %08X %11i 0 1 00000001 5A526080 1515348096 5A526080 1 1 -1 FFFFFFFF 5A526084 1515348100 5A526084 -1 2 100 00000064 5A526088 1515348104 5A526088 100 3 -100 FFFFFF9C 5A52608C 1515348108 5A52608C -100 4 10000 00002710 5A526090 1515348112 5A526090 10000 5 -10000 FFFFD8F0 5A526094 1515348116 5A526094 -10000 6 32767 00007FFF 5A526098 1515348120 5A526098 32767 7 -32768 FFFF8000 5A52609C 1515348124 5A52609C -32768 8 2147483647 7FFFFFFF 5A5260A0 1515348128 5A5260A0 2147483647 9 -2147483648 80000000 5A5260A4 1515348132 5A5260A4 -2147483648 =========================================================================== Ausgabe der Tabelle ftab in unterschiedlichen Formaten Index Inhalt HEX-Adr. Inhalt Inhalt Adresse Adresse HEX %3i %11i HEX-%08X %10u %08X %08X %11i 0 1 00000001 09AD5080 162353280 09AD5080 1 1 -1 FFFFFFFF 09AD5084 162353284 09AD5084 -1 2 100 00000064 09AD5088 162353288 09AD5088 100 3 -100 FFFFFF9C 09AD508C 162353292 09AD508C -100 4 10000 00002710 09AD5090 162353296 09AD5090 10000 5 -10000 FFFFD8F0 09AD5094 162353300 09AD5094 -10000 6 32767 00007FFF 09AD5098 162353304 09AD5098 32767 7 -32768 FFFF8000 09AD509C 162353308 09AD509C -32768 8 2147483647 7FFFFFFF 09AD50A0 162353312 09AD50A0 2147483647 9 -2147483648 80000000 09AD50A4 162353316 09AD50A4 -2147483648 =========================================================================== flt_1 19871760 123455996780211547259653965521879040.00 flt_1 5A5260E8 123455996780211547259653965521879040.00 flt_2 5A5260EC 5BE959AB -123455996780211547259653965521879040.000000000000 flt_2 5A5260EC 7FFFFFAF -123455996780211547259653965521879040.000000 dbl_1 5A5260F0 0 1.000000000000 dbl_2 5A5260F0 00000000 3FF00000 1.000000000000 dbl_2 5A5260F8 00000000 BFF00000 -1.000000000000 dbl_3 5A526100 3044BE74 45246C99 12345678901200000200474624.000000000000 dbl_4 5A526108 45246C99 2D9DD00C 0.001234567890 =========================================================================== flt_1 19871760 123455996780211547259653965521879040.00 flt_1 5A5260E8 123455996780211547259653965521879040.00 flt_2 5A5260EC 5BE959AB -123455996780211547259653965521879040.000000000000 flt_2 5A5260EC 7FFFFFAF -123455996780211547259653965521879040.000000 dbl_1 5A5260F0 0 1.000000000000 dbl_2 5A5260F0 00000000 3FF00000 1.000000000000 dbl_2 5A5260F8 00000000 BFF00000 -1.000000000000 dbl_3 5A526100 3044BE74 45246C99 12345678901200000200474624.000000000000 dbl_4 5A526108 45246C99 2D9DD00C 0.001234567890 =========================================================================== Ausgabe der variablen x, i, ptr_itab in unterschiedlichen Formaten %3 %8i %08X %8i %08X %08X x 0 0 00000000 1515348268 5A52612C i 10 10 0000000A 1515348312 5A526158 i 1515348096 1515348096 00000001 1 5A526168 5A526168 =========================================================================== Stackdump 5A526080 | 01 00 00 00 5A526084 | FF FF FF FF 5A526088 | 64 00 00 00 5A52608C | 9C FF FF FF 5A526090 | 10 27 00 00 5A526094 | F0 D8 FF FF 5A526098 | FF 7F 00 00 5A52609C | 00 80 FF FF 5A5260A0 | FF FF FF 7F 5A5260A4 | 00 00 00 80 5A5260A8 | 00 00 00 00 5A5260AC | 00 00 00 00 5A5260B0 | 00 00 00 00 5A5260B4 | 00 00 00 00 5A5260B8 | 00 00 00 00 5A5260BC | 00 00 00 00 5A5260C0 | 00 00 00 00 5A5260C4 | 00 00 80 3F 5A5260C8 | 00 00 80 BF 5A5260CC | CD CC CC 3E 5A5260D0 | CD CC CC BE 5A5260D4 | 00 00 80 3E 5A5260D8 | 00 00 80 BE 5A5260DC | 85 8F 04 34 5A5260E0 | 44 3F C8 42 5A5260E4 | 9B 5F 9A 44 5A5260E8 | CE 36 BE 79 5A5260EC | CE 36 BE F9 5A5260F0 | 00 00 00 00 5A5260F4 | 00 00 F0 3F 5A5260F8 | 00 00 00 00 5A5260FC | 00 00 F0 BF 5A526100 | 74 BE 44 30 5A526104 | 99 6C 24 45 5A526108 | 0C D0 9D 2D 5A52610C | 27 3A 54 3F 5A526110 | 20 2A 2A 2A 5A526114 | 2A 20 20 20 5A526118 | 45 4E 44 45 5A52611C | 20 2A 2A 2A 5A526120 | 2A 20 0A 00 5A526124 | CF CF CF CF Ende Stackdump =========================================================================== =========================================================================== 5A526080 | 01000000 FFFFFFFF 64000000 9CFFFFFF ........d....... 5A526090 | 10270000 F0D8FFFF FF7F0000 0080FFFF .'.............. 5A5260A0 | FFFFFF7F 00000080 00000000 00000000 ................ 5A5260B0 | 00000000 00000000 00000000 00000000 ................ 5A5260C0 | 00000000 0000803F 000080BF CDCCCC3E .......?.......> 5A5260D0 | CDCCCCBE 0000803E 000080BE 858F0434 .......>.......4 5A5260E0 | 443FC842 9B5F9A44 CE36BE79 CE36BEF9 D?.B._.D.6.y.6.. 5A5260F0 | 00000000 0000F03F 00000000 0000F0BF .......?........ 5A526100 | 74BE4430 996C2445 0CD09D2D 273A543F t.D0.l$E...-':T? 5A526110 | 202A2A2A 2A202020 454E4445 202A2A2A **** ENDE *** 5A526120 | 2A200A00 CFCFCFCF 00000000 00000000 * ..............
Detailanalyse
Schauen wir uns hier das Programm im Detail an.Zunächst werden einige Variablen und Pointer definiert. Wir wissen eine int belegt 4 Bytes. Wir wissen aber nicht, an welcher Speicheradresse eine Variable jeweils im Memory erzeugt wird. Das wollen wir später in der ersten Ausgabeschleife herausfinden.
int itab[MAX]={1,-1,100,-100,10000,-10000,32767,-32768,2147483647,-2147483648};Die Tabelle enthält u.a. am vorletzten Tabellenplatz die grösste positive Integer die sich in 32 Bit darstellen lässt, sowie am letzten Tabellenplatz die kleinste negative Integer, für 32 Bit.
Im Main werden zuerst die Speichergrössen des verwendeten Systems ermittelt und ausgegeben, dazu wird die Funktion sizeof() aus stdlib.h verwendet
printf("Grösse einer Integer Variablen %i \n",(unsigned long) sizeof(int )); printf("Grösse einer Float Variablen %i \n",(unsigned long) sizeof(float )); printf("Grösse einer Double Variablen %i \n\n",(unsigned long) sizeof(double )); printf("Grösse eines int Pointers %i \n",(unsigned long) sizeof(int *)); printf("Grösse eines double Pointers %i \n",(unsigned long) sizeof(double *));Wie wir sehen ist eine Integer hier 32 Bit, während sie beim Arduino nur 16 Bit hat. Der Programmierer muss also wissen, für welches System er programiert, um die Spreichergrössen zu berücksichtigen.
Grösse einer Integer Variablen 4 Grösse einer Float Variablen 4 Grösse einer Double Variablen 8 Grösse eines int Pointers 8 Grösse eines double Pointers 8Wie erwartet belegen Integer und Float jeweils 4 Bytes und Double 8 Bytes.
Dagegen belegen die Pointer jeweils 8 Bytes. Das ist so viel wie nötig ist, um eine Adresse abzubilden.
Mein Rechner ist ein I7 (64-Bit Maschine) mit 64GB Real Memory. Auf einem 32-Bit Rechner werden Sie für den Pointer nur 4 Bytes benötigen.
Für den Umgang mit Pointern existieren 2 Operatoren.
- * Dieser Operator wird bei der Definition eines Pointers verwendet. z.B
int *ptr_itab; // Pointer zeigt noch ins Nirvana
- & Dieser Operator erzeugt eine Adresse. Sie haben diesen Operator sicher schon mehrfach bei Leseoperationen verwendet.
scanf("%i",&celsius);
Hier übergeben Sie mit &celsius die Adresse der Variablen Celsius an den scanf, damit dieser den gelesenen Wert in der Variablen ablegen kann.
Hier wird dem Pointer ptr_itab die Anfangsadress der Tabelle itab zugewiesen. ptr_itab=itab;
Falls sie sich wundern, warum dabei nicht der Operator & verwendet wird, so liegt das daran, dass der Name einer Tabelle automatisch die Adresse des ersten Tabellenelementes ist. Es ist also ein Sonderfall
In dieser Schleife geben wir in einer Schleife mit folgender Anweisung nacheinander die Tabelle itab aus. Mit der Formatspezifikation %08X erfolgt die Ausgabe als Hexa mit 8 Stellen. U.U mault der Compiler den falschen Typ an, führt es aber aus. warning: format ‘%X’ expects argument of type ‘unsigned int’
for(i=0;i < MAX;i++){ printf (" %3i %11i %08X %08X %08X %11i\n", i,itab[i],itab[i], &itab[i],ptr_itab, *ptr_itab); ptr_itab++; // Pointer auf den nächsten Tabellenplatz weiterstellen }Bei jedem Schleifendurchlauf wird der Pointer ptr_itab auf den nächsten Tabellenplatz weitergestellt.
Für die Ausgabe wird nachfolgende Formatspezifikation verwendet.
%3i %11i HEX-%08X %10u %08X %08X %11i
Da Adressen immer positiv sind, wird sie in Spale 3 mit %10u ausgegeben.
<
Ausgabe der Tabelle itab in unterschiedlichen Formaten Index Inhalt Inhalt &itab[i] &itab[i] Pointer *Pointer %3i %11i HEX-%08X %10u %08X %08X %11i 0 1 00000001 09AD5080 162353280 09AD5080 1 1 -1 FFFFFFFF 09AD5084 162353284 09AD5084 -1 2 100 00000064 09AD5088 162353288 09AD5088 100 3 -100 FFFFFF9C 09AD508C 162353292 09AD508C -100 4 10000 00002710 09AD5090 162353296 09AD5090 10000 5 -10000 FFFFD8F0 09AD5094 162353300 09AD5094 -10000 6 32767 00007FFF 09AD5098 162353304 09AD5098 32767 7 -32768 FFFF8000 09AD509C 162353308 09AD509C -32768 8 2147483647 7FFFFFFF 09AD50A0 162353312 09AD50A0 2147483647 9 -2147483648 80000000 09AD50A4 162353316 09AD50A4 -2147483648
- Spalte %3i der Laufindex i der for-Schleife wird als Integer mit 3 Stellen ausgegeben.
- Spalte, %11i hier wird der Inhalt des Tablellenplatzes itab[i] ausgegeben.
Die grosse Spaltenbreite ist den Maximalwerten geschuldet. - Spalte, %08X mit der Formatspezifikation wird itab[i] in HEXA-Darstellung ausgegeben. Dabei werden führende Nullen erzeugt.
Wir sehen sehr schön, wie die negativen Zahlen als 2-Kompliment gespeichert sind.
- 1 = 00000001
- -1 = FFFFFFFF
- 32767 = 00007FFF grösste 16 Bit Integer (z.B. Microprocessor Arduino )
- -32768 = FFFF8000 kleinste 16 Bit Integer (z.B. Microprocessor Arduino )
- 2147483647 = 7FFFFFFF grösste 32 Bit Integer. Wenn wir dazu eine 1 Addieren
bekommen wir einen Überlauf zu -2147483648 - -2147483648 = 80000000 kleinste negative 32 Bit Integer
- Spalte, itab[i]%10u hier geben wir die Speicheradresse unsigned &itab[ i ] als Dezimalzahl aus. Wie wir sehen, belegt jede Integer 4 Bytes.
Damit wir auf die Adresse und nicht auf den Inhalt zugreifen, wird der Adressoperator itab[i]&itab[i] verwendet.
Die Speicheradressen sind unterschiedlich, wenn wir das Programm mehrfach laufen lassen. Das liegt daran, dass das Programm in einem Multiprogramming/Multiprocessing-System immer an eine andere Stellen geladen wird. I.d.R. arbeiten wir aber mit Hexa-Speicheradressen wie es in den beiden nächsten Spalten gezeigt wird.
Beim arduino (der nur 2KB Datenspeicher hat) ist das nicht der Fall. - Spalte, %08X auch hier wird der Adressoperator & verwendet, aber die Ausgabe als Hex-Wert ausgegeben.Auch hier sehen wird, dass jede Integer 4 Bytes belegt.
- Spalte, %08X hier wird der Inhalt des Pointers ptr_itab als Hexwert ausgegeben. Dieser Pointer wurde am Anfang auf ptr_itab=itab; gesetzt. Wobei der Name einer Tabelle selbst ein Pointer auf den Anfang der Tabelle ist. Bei jeden Schleifendurchgang wird der Pointer um 1 erhöht. Da der Pointer als int *ptr_itab definiert wurde, also mit dem Typ Integer verknüpft ist, wird mit der logischen Erhöhung um einen Speicherplatz, der reale interne Wert um die definierte Länge (4) des Speicherplatzes erhöht. Dies kann man sehr schön an den Speicheradressen sehen.
- Spalte, %11i Um auf den Inhalt der Speicherstelle zuzugreifen, auf die der Pointer zeigt, muss der Pointer dereferenziert werden. Der Operator dafür ist der *.
Mittels *ptr_itab im printf, greifen wir auf den Inhalt der Speicherstelle zu, auf die der Pointer gerade zeigt (verweist).
Es liegt in der Verantwortung des Programmierers, dass das eine sinnvolle Adresse ist. Viele Probleme bei der C-Programmierung liegen an Pointern, die ins Nirwana zeigen oder nicht richtig initialisiert wurden.
Es ist duchaus möglich ausserhalb einer Tablle zuzugreifen. Dabei werden evtl. wichtige Daten zustört.
Hacker können so evtl den Speicher und das Programm manipulieren. Das ist ein Schwachstelle von C
Detailanalyse Fortsetzung
In diesem Teil geht es um die Floatingpoint. Es wird eine Tabelle mit 10 Werten definiert.float ftab[MAX]= {0.0, 1.0, -1.0 ,0.4, -0.4, 0.25, -0.25, 0.0000001234567,100.123567, 1234.98765432};Wir werden sehen, dass Gleitkommazahlen ungenau sind und nur einen beschränkten Signifikanzbereich haben.
Wenn Sie genauer wissen wollen wie eine Gleitkommazahl aufgebaut ist und warum sie ungenau ist siehe IEEE_754
Die Ausgabe erfolgt wieder in einen Schleife mit folgender Anweisung:
printf(" %3i %12.5f %08X %15.10f %08X %19.13f\n", i,ftab[i], &ftab[i], ftab[i], flt_ptr, *&ftab[i]);
Ausgabe der Tabelle ftab in unterschiedlichen Formaten Index Inhalt HEX-Adr. Inhalt Inhalt Adresse Adresse HEX %3i %12.5f %08X %08X %15.10f %08X %18.13f 0 0.00000 5A5260C0 00000000 0.0000000000 5A5260C0 0.0000000000000 1 1.00000 5A5260C4 3F800000 1.0000000000 5A5260C4 1.0000000000000 2 -1.00000 5A5260C8 BF800000 -1.0000000000 5A5260C8 -1.0000000000000 3 0.40000 5A5260CC 3ECCCCCD 0.4000000060 5A5260CC 0.4000000059605 4 -0.40000 5A5260D0 BECCCCCD -0.4000000060 5A5260D0 -0.4000000059605 5 0.25000 5A5260D4 3E800000 0.2500000000 5A5260D4 0.2500000000000 6 -0.25000 5A5260D8 BE800000 -0.2500000000 5A5260D8 -0.2500000000000 7 0.00000 5A5260DC 34048F85 0.0000001235 5A5260DC 0.0000001234567 8 100.12357 5A5260E0 42C83F44 100.1235656738 5A5260E0 100.1235656738281 9 1234.98767 5A5260E4 449A5F9B 1234.9876708984 5A5260E4 1234.9876708984375
- Spalte, %3i gibt wieder den Laufindex der Schleife aus.
- Spalte, %12.5fgibt die Werte der Tabelle aus. Auf den ersten Blick scheint alles OK zu sein.
Doch die Ausgabe 100.12357 stimmt nicht mit der Definition in der Taballe 100.123567 überein. Wir haben eine Differenz von -0.000003 Das könnte natürlich daran liegen, dass bei der Ausgabe nur 5 Nachkommastellen ausgegeben werden sollten und deshalb gerundet wurde.
Tatsächlich wurde aber auch der Signifikanzbereich überschritten. Unsere gesamte Zahl 100.123567 hat 9 Stellen. Eine float hat ingesamt nur 32 Bits. 1 Bit Vorzeichen, 8 Bit Exponent und 23 Bits für die Mantissse.
Für jede Dezimalstelle werden etwa 3,3 Bits benötigt. 23 / 3.3 =6.98989697 . Unsere Zahl mit 9 Ziffern pass also nicht in die Mantisse. Es muss also etwas abgeschnitten werden.wodurch die Zahl ungenau wird. Diese Ungenauigkeit kann sogar im ganzzahligen Bereich der Zahl liegen.
Die Zahl 0.4 wird richtig ausgegeben. Wir werden aber in der Folge sehen, dass diese Zahl auch ungenau gespeichert ist (Spalte 4). - Spalte, %08X gibt wieder nur die Speicheradressen aus. Wir sehen eine Float belegt 4 Bytes.
- Spalte, %08X wird der Inhalt der Gleitkommazahl (single Precision) ausgegeben.
Da 08X ein Argument vom Typ 'unsigned int' erwartet, wurde etwas getrickst. Durch einen Typecast in der Schleife p1=(int *) &ftab[i]; wird der Ausgabe der Zugriff auf eine Integer vorgegaukelt deren Inhalt dann mit %08X ausgegeben wird.
Wir sehen in der Zeile mit dem Index 0, dass eine Float 0.0 auch nur 00000000 enthält.
1.0f dagegen 3F800000 das ergibt folgende interne Darstellung :
während -1.0f lediglich noch zusätzlich das Vorzeichenbit gesetzt hat BF800000
- Spalte, %15.10f hier wird mit vielen Nachkommastellen ausgegeben. Dabei wird sichtbar, dass z.B. 0.4 nicht exakt gespeichert ist.
Alle gebrochenen Zahlen, die keine Potenz von 2 sind, sind nicht exakt speicherbar. Beim Abschneiden der Mantisse wird das letzte Bit evtl aufgerundet (wie bei 0.4) dann ist der interne Wert gösser als die Ausgangszahl oder es wird einfach abgeschnitten, dann ist die gespeichert Zahl kleiner als die Ausgangszahl
0.25 ist dagegen eine Potenz von 2 und wird exakt gespeichert. Auch bei 100.123567 und 1234.98765432 sehen wir eine Ungenauigkeit. Diese Ungenaugikeit geht auch in jede Rechenoperation mit ein und vergrössert damit evtl den Fehler. Aus diesem Grund wird i.d.R. mit double gearbeitet. Der systembedingte Fehler ist damit zwar nicht aus der Welt, die Ungenauigkeit beginnt aber später. - Spalte, %08X gibt wieder nur die Speicheradressen aus. Dabei wird hier der Inhalt des Poiters ausgegeben, also der Verweis auf den Zielspeicher.
- Spalte, %19.13f hier wird über *&ftab[i] auf den Inhalt der Tabelle zugegriffen. Eine Konstruktion, die so nicht erforderlich wäre,
da erst über &ftab[i] eine Adresse erzeugt wird und dann über den vorangestellten * wieder auf den Inhalt referenziert wird.
Es wäre auch *flt_ptr möglich oder einfach nur ftab[i]