Das Verständniss für die Speicherverwaltung, Memoryadressen und der Zugriff über Pointer ist für die C-Programmierung von elementarer Bedeutung.
Hier soll eine Einführung erfolgen, die Sie in die Lage versetzt, selbständig weiter an den Thema zu arbeiten.
Dazu werden in einem Programm einige Variablen, ein Pointer und 2 Tabellen definiert.
Diese Speicherplätze werden dann in unterschiedlichen Formaten ausgegeben.
Bei dieser Ausgabe werden auch die vom Programm verwendeten Speicheradressen sichtbar gemacht.
Dazu werden u.a. Pointer verwendet.
Ein Pointer ist eine Variable die als Inhalt einen Verweis (Adresse) einer andern Variablen (Memorylocation) hat. Mit dem Pointer ist ein Type verknüpft. Dieser sollte der Zielvariablen entsprechen.
Der Inhalt eines Pointers kann verändert werden und zeigt dann auf eine andere Variable (Speicheradresse).
So wie eine Adressangabe Holzweg 5 (auf einem Zettel) auf ein bestimmtes Haus verweist, verweist eben ein Pointer auf eine Speicheradresse.
Mit der Angabe 1 Haus weiter oder ein Haus zurück kann auf ein anderes Haus verwiesen werden.
Ebenso kann ein Pointer verändert werden und verweist dann auf eine andere Speicheradresse.
Natürlich hinkt diese Vergleich etwas, da beim Pointer auch noch der Speichertyp eine Rolle spielt.
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  8
  
Wie 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

  
  1. Spalte %3i der Laufindex i der for-Schleife wird als Integer mit 3 Stellen ausgegeben.
  2. Spalte, %11i hier wird der Inhalt des Tablellenplatzes itab[i] ausgegeben.
    Die grosse Spaltenbreite ist den Maximalwerten geschuldet.
  3. 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
  4. 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.
  5. 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.
  6. 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.
  7. 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

  
  1. Spalte, %3i gibt wieder den Laufindex der Schleife aus.
  2. 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).
  3. Spalte, %08X   gibt wieder nur die Speicheradressen aus. Wir sehen eine Float belegt 4 Bytes.
  4. 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 :
    Bit 3124231615870 Exponent(Bias)Mantisse Vorzeichen VEEEEEEEEMMMMMMMMMMMMMMMMMMMMMMM 00111111100000000000000000000000
    während -1.0f lediglich noch zusätzlich das Vorzeichenbit gesetzt hat BF800000
  5. 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.
  6. Spalte, %08X   gibt wieder nur die Speicheradressen aus. Dabei wird hier der Inhalt des Poiters ausgegeben, also der Verweis auf den Zielspeicher.
  7. 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]