Header Dateien Verwirrung!

ocsme

Top Contributor
Guten Tag,

derzeit beschäftige ich mich etwas mit C und habe einige Fragezeichen im Kopf vor allem bezüglich den Header-Dateien.
Ich weiß das man so gesehen alles in eine Header Datei schreiben kann. So habe ich aus dem Buch Grundkurs C von Jürgen Wolf das Beispiel hier nach gebaut:

Hallo.h (hab diese Datei erst Kompiliert danach hatte ich in der main() im Terminal nur rosa Rauschen :D)
C:
#include <stdio.h>

int i;

void print_was() {
    printf("Hallo %d\n",i);
}

Main.c
C:
#include <stdio.h>
#include <stdlib.h>
#include "Hallo.h"

int main(void) {
    int j;
    for( j = 0; j < 10; j++,i++) {
        print_was();
    }
    return 0;
}

Also kann ich ganze C Programme in die Header Datei rein kopieren. Nun dachte ich mir mhhh... das erinnert mich doch sehr stark an die ganzen Standard Klassen von Java also schauen wir mal in den /usr/include Ordner rein und Ja gefunden Math.h z. b. Doch dort stehen ja nur Konstanten, Variablen und Direktiven drin.
Wo bekommt man aber die Funktionen sqrt,... her wenn man die Math.h includet?

Vielleicht kann das jemand für "doofies" Erklären.

Denn ich habe schon viel im Internet nachgelesen und nichts gefunden. Dachte auch erst mhhh... vielleicht liegt ja eine Bibliothek hinten dran das nichts anderes ist als ein Archivdatei aus Kompilierten Dateien.

Kann mir doch sicher jemand Helfen :)

LG
 

fhoffmann

Top Contributor
Uralte C-Compiler haben bei Auftreten eines #include den Inhalt der Datei, die included wird, an die Stelle des #include geschrieben und die c-Datei anschließend kompiliert. Das tun sie schon lange nicht mehr; aber mann kann sich den Vorgang dennoch so vorstellen, als täten sie es. Deshalb ist dein Beispiel auch kompilierbar.

Üblich ist es jedoch, nur die Deklaration einer Methode (Klasse) in die Header-Datei zu schreiben. Die Impementation gehört in die zugehörige c-Datei. Dann wird die Implementation nur einmal kompiliert. In allen anderen Dateien, die die Header-Datei includen, ist nur bekannt, dass die Methode existiert.
 

ocsme

Top Contributor
Vielen Dank für die schnelle Antwort.

Okay das leuchtet mir ein ich sehe die Header Datei so ein bisschen wie eine ART Interface bei Java. Doch was ich überhaupt nicht verstehe ist woher weiß nun mein Programm wenn ich die Standard Header Datei includiere oder eine von mir ausgedachte in welchem C Code die Funktion liegt.
Ist etwas doof geschrieben. Als Beispiel ganz einfach man schreibt immer #include <stdio.h> so nun ist das ja ein Standard Fall doch woher weiß nun das C Programm, dass ich schreibe, wo die Funktion zu finden ist? in stdio steht nur drin das es eine printf Funktion gibt aber WO????

Das Beispiel lässt sich ja nun auf sämtliche Datentypen zurück führen nehmen wir z. B. ich habe eine Stack.h dort steht so etwas drin wie pop(), push() ... <- die ganzen Funktionen von dem Stack // so in etwas wäre ja nun auch ein Interface in Java
nun include ich diese Headerdatei in meine MainStack.c. Dort wird nun der Stack ausprogrammiert. Wenn ich den Stack nun benutzen wollen würde in einem anderen Programm include ich ja sicherlich nicht Stack.h? wenn doch dann verstehe ich einfach nicht woher das Programm weiß wo MainStack dann ist?

Irgendwie verstehe ich immer noch nichts!

LG
 
K

kneitzel

Gast
Der C-Compiler hat keine Ahnung, wo die Funktion liegt. Er weiß nur, dass es die geben soll.

Verantwortlich ist für das zusammen stückeln dann der sogenannte Linker. Dem wird gesagt, was er alles zusammen packen soll. Und dabei gibt es dann bei jedem Part sowas wie ein Inhaltsverzeichnis. Eine Library sagt z.B. was sie alles an Funktionen hat (und wo diese in der Library zu finden sind) aber auch, was sie alles braucht. Der Linker setzt das dann zusammen. Da wo stand, was gebraucht wird, schreibt er dann, wo es zu finden ist. Sollte irgendwo etwas gebraucht werden, was der Linker nicht gefunden hat, dann meckert der Linker.

Wenn Du eine Library baust, dann musst Du also die Header Datei weiter geben und die Library selbst. Das kann dann eine .lib Datei sein. Die Header Datei nutzt der Compiler, der Linker nutzt dann die .lib Datei.
 

ocsme

Top Contributor
Wenn Du eine Library baust, dann musst Du also die Header Datei weiter geben und die Library selbst. Das kann dann eine .lib Datei sein. Die Header Datei nutzt der Compiler, der Linker nutzt dann die .lib Datei.

Hier den Link habe ich gefunden schon vor ein paar Tagen und ich glaube jetzt habe ich auch das hier verstanden :)

Mehr Spaß mit Libraries
Bevor wir die Gefilde von gcc verlassen, wollen wir noch ein paar Worte zum Thema Linken und Libraries sagen. Es ist gar nicht so schwierig, eigene Bibliotheken zu erzeugen. Wenn Sie eine Reihe von Routinen geschrieben haben, die Sie häufig aufrufen, sollten Sie diese vielleicht zu einer Gruppe von Quelltextdateien zusammenfassen, aus jeder Quelldatei eine Objektdatei erzeugen und dann aus diesen Objektdateien eine Bibliothek erstellen. Anschließend brauchen Sie diese Routinen nicht mit jedem Programm, das darauf zugreift, erneut zu kompilieren.

Nehmen wir an, daß Sie einige Quelldateien haben, in denen häufig benutzte Routinen wie zum Beispiel

float square(float x) {
/* Code für square()... */
}

int factorial(int x, int n) {
/* Code für factorial()... */
}


usw. stehen. (Natürlich sind in den Standardbibliotheken von gcc
solche gebräuchlichen Routinen bereits enthalten; lassen Sie sich also
durch dieses Beispiel nicht verwirren.) Der Einfachheit halber wollen
wir weiterhin annehmen, daß der Code für square() in der Datei square.c steht und der Code für factorial() in factorial.c.


Um eine Bibliothek zu erzeugen, die diese Routinen enthält, müssen Sie nur diese Quelldateien kompilieren:

papaya$ gcc -c square.c factorial.c

Damit erhalten Sie square.o und factorial.o. Als
nächstes erzeugen Sie aus diesen Objektdateien die Bibliothek. Dabei
zeigt sich, daß eine Bibliothek einfach eine Archivdatei ist, die mit
dem Befehl ar erzeugt wird (einem engen Verwandten von tar). Wir wollen unsere Bibliothek libstuff.a nennen, und so gehen wir dabei vor:


papaya$ ar r libstuff.a square.o factorial.o


Wenn Sie eine solche Bibliothek aktualisieren möchten, sollten Sie vielleicht vorher die alte libstuff.a
löschen. Als letzten Schritt erstellen wir einen Index zu dieser
Bibliothek, damit der Linker die Routinen darin finden kann. Benutzen
Sie dazu den Befehl ranlib, etwa so:

papaya$ ranlib libstuff.a

Dieser Befehl fügt zusätzliche Informationen in die Bibliothek
selbst ein; es wird keine eigenständige Indexdatei erzeugt. Sie könnten
die letzten beiden Schritte mit ar und ranlib auch kombinieren, indem Sie bei ar den Schalter s angeben:

papaya$ ar rs libstuff.a square.o factorial.o

Mit libstuff.a haben Sie jetzt eine statische Library, die
Ihre Routinen enthält. Bevor Sie Programme mit dieser Bibliothek binden
können, müssen Sie noch eine Header-Datei schreiben, die den Inhalt der
Bibliothek beschreibt. Wir könnten zum Beispiel die Datei libstuff.h mit folgendem Inhalt erstellen:


/* libstuff.h: routines in libstuff.a */
extern float square(float);
extern int factorial(int, int);

In jede Quellcodedatei, die Routinen aus libstuff.a aufruft, sollten Sie die Zeile #include "libstuff.h" einfügen, wie Sie das mit den Standard-Header-Dateien auch tun würden.

Wie kompilieren wir Programme, die auf die soeben fertiggestellte
Bibliothek samt Header-Datei zugreifen? Zunächst müssen wir beide an
einer Stelle abspeichern, wo der Compiler sie finden kann. Viele
Programmierer legen eigene Bibliotheken im Unterverzeichnis lib ihres Home-Verzeichnisses ab und eigene Include-Dateien unter include.


Wir gehen davon aus, daß dies bereits geschehen ist, und können dann das ominöse Programm wibble.c mit folgendem Befehl kompilieren:


papaya$ gcc -Iinclude -Llib -o wibble wibble.c -lstuff
Mit der Option -I weisen Sie gcc an, das Verzeichnis include in den Include-Pfad einzufügen, in dem gcc nach Include-Dateien sucht. Die Option -L funktioniert ganz ähnlich, indem sie gcc anweist, das Verzeichnis lib in den Library-Pfad einzutragen.


Das letzte Argument auf der Befehlszeile ist -lstuff; damit wird der Linker angewiesen, die Bibliothek libstuff.a einzubinden (solange sie irgendwo im Library-Pfad zu finden ist). Das lib am Anfang des Dateinamens wird für Bibliotheken automatisch angenommen.

Das probiere ich später mal aus und gebe Bescheid :)
Dann habe ich das ganze nun auch verstanden Danke an euch zwei :)
 

ocsme

Top Contributor
So ich habe nun das ganze versucht doch bekomme es nicht hin da ich auch nicht genau weiß in welchen Ordern die Daten hin müssen bzw. wollte die ganzen Daten auf dem Desktop lassen :D <- da ich nicht weiß in welche lib oder include Verzeichnis dort die Rede ist. usr/include und /usr/lib/gcc/x86_64-linux-gnu/7.4.0???

Deswegen habe ich folgendes gemacht was bis jetzt auch geklappt hat. Alle Dateien auf dem Deskopt(Schreibtisch) gespeichert:

min.c
C:
int min(int a, int b) {
    return a > b ? b : a;
}

max.c
C:
int min(int a, int b) {
    return a > b ? a : b;
}

Natürlich sind die zwei Dateien nicht sehr sinnhaft es geht mir ja nur darum das ich das mit den Libraries und Header Dateien besser verstehe :)

Als nächstes habe ich die Zwei .c Dateien übersetzt mit:
gcc -c max.c min.c

Danach noch die Librarie erstellt mit:
ar rs libstuff.a max.o min.o

zu guter Letzt habe ich die Header Datei erstellt mit den Einträgen:
libstuff.h
C:
extern int min(int,int);
extern int max(int,int);

main.c
C:
#include <stdio.h>
#include "/home/test/Schreibtisch/libstuff.h"

int main(void) {
    
    printf("%d %d\n", max(3,4),min(3,4));
    
    return 0;
}

Sobald ich nun versuche die Datei mit gcc -o main main.c zu übersetzen bekomme ich folgende Fehlermeldung:
gcc -o Main Main.c
/tmp/cc2Wbeyn.o: In Funktion »main«:
Main.c:(.text+0x14): Warnung: undefinierter Verweis auf »min«
Main.c:(.text+0x25): Warnung: undefinierter Verweis auf »max«
collect2: error: ld returned 1 exit status

Da ich keine Ahnung habe wo die Liebarie hin muss den diese ist ja nun auch auf meinem Desktop wie die Header Datei! Denke ich das er diese Liebarie eben nicht findet und dann sagt min und max gibt es nicht.

Kann mir dabei vielleicht nochmal jemand weiter Helfen?
Denn ich habe auch von meinem Link oben nicht so ganz verstanden wie das mit diesen Schaltern hier gemeint ist:
Mit der Option -I weisen Sie gcc an, das Verzeichnis include in den Include-Pfad einzufügen, in dem gcc nach Include-Dateien sucht. Die Option -L funktioniert ganz ähnlich, indem sie gcc anweist, das Verzeichnis lib in den Library-Pfad einzutragen.

Das letzte Argument auf der Befehlszeile ist -lstuff; damit wird der Linker angewiesen, die Bibliothek libstuff.a einzubinden (solange sie irgendwo im Library-Pfad zu finden ist). Das lib am Anfang des Dateinamens wird für Bibliotheken automatisch angenommen.

Sie sollten den Schalter -l auf der Befehlszeile jedesmal benutzen, wenn Sie andere als die Standardbibliotheken einbinden wollen. Wenn Sie beispielsweise mathematische Routinen aus math.h benutzen möchten, sollten Sie am Ende der gcc-Befehlszeile -lm anhängen, womit libm eingebunden wird. Bedenken Sie aber, daß die Reihenfolge der -l-Optionen von Bedeutung ist. Ein Beispiel: Wenn Ihre Bibliothek libstuff Routinen aus libm aufruft, dann muß in der Befehlszeile -lm hinter -lstuff stehen:

LG
 
K

kneitzel

Gast
Also die genannten Verzeichnisse sind nicht für von Dir erstellte Include und Library Dateien. Die kannst Du irgendwo hin legen und dann gibt Du den Ort lediglich mit an.

Du kannst die Dateien aber auch bei dir im Verzeichnis ablegen, das ist auch ok.

Beim übersetzen musst Du dann aber natürlich angeben, dass er die library mit einbinden soll. Das wäre dann z.B. ein -l stuff
Wenn die libstuff.a woanders liegt, dann kannst Du auch noch den Ort angeben mit -L
Also gcc -o main -l stuff main.c

Ist aber bei mir schon sehr lange her, dass ich sowas gemacht habe, daher bitte nicht böse sein, wenn ich das etwas falsch in Erinnerung haben sollte. (Mein Wechsel weg von C++ war 2003 :) )
 

ocsme

Top Contributor
Leider bekomme ich das Kompilieren einfach nicht hin.
Die ganzen Dateien liegen auf dem Schreibtisch, habe die Datei libstuff.a mal nach /usr/lib/gcc/x86_64-linux-gnu/7.4.0 verschoben und versucht zu Kompilieren.

Immer noch der selbe Fehler :(

Keine Ahnung am liebsten hätte ich ja so etwas wie z. B.
gcc -o main main.c libstuff.a benutzen :D

Was ich nur nicht verstehe ist das es auch nicht in C von A - Z drin steht z. B. :( zumindest habe ich unter den Begriffen Librarie, Libraries, Bibliothek und Header nichts gefunden was zum Thema passt :( Kann doch nicht wahr sein :(

Naja wenn nicht lass ich es einfach denn so habe ich es ja nun verstanden denn das erklärt dann auch das man z. B. unter der Header Datei math.h die Funktionen so nicht findet diese sind dann schon Kompiliert in einer Librarie drin :) Das habe ich ja nun alles verstanden :)

Dann geht es morgen mal an die Arrays und Strings :D

LG
 

mihe7

Top Contributor
gcc -o main main.c libstuff.a
Das sollte auch funktionieren, wenn libstuff.a sich im gleichen Verzeichnis befindet. Alternativ gcc -o main main.c /home/test/Schreibtisch/libstuff.a

Mach einfach nochmal von vorne: leg Dir erstmal ein Verzeichnis für das ganze Zeug an. Darunter erstellst Du nochmal zwei Verzeichnisse, namentlich stuff und app. Dabei simuliert stuff ein Library-Projekt und app eine Anwendung.

In stuff legst Du die Dateien libstuff.h, min.c und max.c ab. Innerhalb von stuff erzeugst Du auch die lib (gcc und ar, wie Du es ja schon gemacht hast). Danach solltest Du die Dateien
Code:
stuff/libstuff.h
stuff/min.c
stuff/max.c
stuff/min.o
stuff/max.o
stuff/libstuff.h
haben.

Unter app legst Du Dir erstmal die main.c:
C:
#include <stdio.h>
#include "libstuff.h"

int main(void) {
    
    printf("%d %d\n", max(3,4),min(3,4));
    
    return 0;
}
Beachte das angepasst include (Deines würde auch funktionieren, aber so hast Du keine absoluten Pfadangaben in der Datei).

Du kannst Deine main kompilieren:
Code:
gcc -c main.c -I ../stuff
und linken
Code:
gcc -o main main.o -lstuff -L ../stuff
oder auch beides zusammen
Code:
gcc -o main main.c -I ../stuff -L ../stuff -lstuff

Mit -I (großes Ida) gibst Du ein (zusätzliches) Verzeichnis an, in dem der Compiler nach header-Files suchen soll. Du kannst gcc auch mitteilen, dass er dort nur nach Headerfiles suchen soll, die mit Anführungszeichen inkludiert wurden. Dazu wird dann -iquote statt -I verwendet.

Mit -l (kleines Ludwig) gibst Du an, welche Lib (der Linker sucht bei Angabe von -lstuff nach libstuff.a) verwendet werden soll, mit -L dagegen, wo der Linker (zusätzlich) nach Libs suchen soll.
 
K

kneitzel

Gast
gcc -o main main.c libstuff.a benutzen :D

Also da fehlt das -l und bei einer Library wird das .a und das führende lib nicht mit angegeben.
Wo mihe7 aber Recht hat: Da war damals kein Leerzeichen, also -lstuff war die Ausgabe, die notwendig ist.

Du kannst auch noch einmal https://access.redhat.com/documenta...7/html/developer_guide/creating-libraries-gcc durchgehen. Wichtige Anmerkung: Du baust keine shared LIbrary sondern eine static library.

Du könntest z.B. einmal die library prüfen wie dort angegeben: nm libstuff.a
Mir fällt z.B. auf, dass Du da ar rs .... angegeben hast aber ar rcs ... als Aufruf in der verlinkten Dokumentation angegeben wurde.

Also ich habe es auf meinem Linux jetzt einmal nachvollzogen. Zwei Verzeichnisse: lib und test:

in lib: min.c, max.c
Aufrufe:
gcc -c min.c max.c
==> min.o, max.o wurden erzeugt
ar rcs libstuff.a min.o max.o
==> libstuff.a wurde erzeugt
nm libstuff.a:

min.o:
0000000000000000 T min

max.o:
0000000000000000 T max

cp libstuff.a ../test
==> Ergebnis kommt ins test Verzeichnis.

Nun geht es weiter in test:
- libstuff.a ist schon da, also kommt noch libstuff.h und main.c dazu.

Nun versuche ich das übersetzen mit:
gcc -o main -L. -lstuff main.c
/tmp/cccjjueo_O: In function `main':
main.c:(.text+0x14): undefined reference to `min'
main.c:(.text+0x25): undefined reference to `max'
collect2: error: ld returned 1 exit status

Hmm, unerwartetes Ergebnis ... Im Augenblick verstehe ich das nicht muss ich gestehen. Die Library findet er, denn lasse ich -L. weg, dann meckert er -lstuff an.

Versuchen wir was anderes:
gcc -c main.o main.c
==> main.o wird erzeugt

gcc -o main main.o libstuff.a
==> main wird erzeugt und kann aufgerufen werden ...

rm main main.o && gcc -o main main.c libstuff.a
=> funktioniert auch

Und dann schaut man sich das etwas an und siehe da: Ich habe etwas vergessen:
gcc <source files> <Options>
Also nächster Versuch

gcc main.c -L. -lstuff -o main
==> Yeah, wieso nicht gleich so :)

Gegenprobe: das main.c wieder ans Ende gesetzt:
konrad@mba:~/Projects/c++/test$ gcc -o main -L. -lstuff main.c
/tmp/ccxcwucN.o: In function `main':
main.c:(.text+0x14): undefined reference to `min'
main.c:(.text+0x25): undefined reference to `max'
collect2: error: ld returned 1 exit status
konrad@mba:~/Projects/c++/test$

Nunja - sowas passiert, wenn man sich einige Jahre nicht mit sowas beschäftigt hat :)
 
K

kneitzel

Gast
Also wieso das -lstuff am Ende sein muss verstehe ich immer noch nicht. (Die anderen Optionen können auch vor dem main.c kommen in meinen Tests.)

Ich habe da jetzt diverse Seiten überflogen aber habe dafür keine Erklärung gefunden. Aber evtl. hat da ja noch jemand eine Erläuterung für.

Ich hoffe aber, dass Du die Schritte, die ich beschrieben habe, auch nachvollziehen kannst.

Ach ja: Eine Änderung hatte ich an Deiner main.c: Ich habe die .h Datei ohne Pfad angegeben - im aktuellen Verzeichnis wird die ja gefunden.
 

httpdigest

Top Contributor
Also wieso das -lstuff am Ende sein muss verstehe ich immer noch nicht. (Die anderen Optionen können auch vor dem main.c kommen in meinen Tests.)
Diese Stackoverflow-Antwort erklärt es eigentlich ganz gut: https://stackoverflow.com/questions...der-of-l-option-in-gcc-matter#answer-11894098
When you add a library using the -loption, the linker does not unconditionally take all object files from the library. It only takes those object files that are currently needed, i.e. files that resolve some currently unresolved (pending) symbols. After that, the linker completely forgets about that library.
 
K

kneitzel

Gast
Ahh, super. Wobei ich mich gerade Frage, ob das ein neues Verhalten ist oder ob ich das von 2003 noch hätte kennen müssen (und ich schlicht nur nicht drüber gestolpert bin auf Grund von Makefiles, die immer erst objectfiles erzeugt haben und am Ende alles verlinkt hatten ...)

Aber bin erst einmal unterwegs und auf dem Handy lässt sich sowas nicht gut recherchieren.
 
K

kneitzel

Gast
Ahh, in dem Stackoverflow-Thread ist die Antwort auf meine Frage schon drin. Das ist eine Posix / GCC Sache. Ein User merkt da an: "Other compiler tools like msvc and borland doesn't follow this approach and it works just fine. "

==> Meine damalige Entwicklung lief fast ausschließlich unter Windows und ich habe so 88/89 mit Turbo C 2.0 angefangen und bin da sehr lange eben auf diesem Compiler geblieben (über Turbo c++ / Borland C++) ehe ich dann 2000 auf Visual Studio 6 gewechselt bin...)

Und alle Entwicklungen auf Linux liefen rein über (generierte) Makefiles, so dass ich da nicht drüber gestolpert bin.
 

mihe7

Top Contributor
Also da fehlt das -l und bei einer Library wird das .a und das führende lib nicht mit angegeben.
Du kannst auch direkt die .a-Datei angeben. Von der man-Page
Code:
           The only difference between using an -l option and
           specifying a file name is that -l surrounds library with lib and .a
           and searches several directories.
 
K

kneitzel

Gast
Du kannst auch direkt die .a-Datei angeben. Von der man-Page
Code:
           The only difference between using an -l option and
           specifying a file name is that -l surrounds library with lib and .a
           and searches several directories.
Ja, ist richtig. Interessant ist sogar, dass ich mich im Rahmen meiner Versuche dann auch selbst erinnert habe, denn als mein gcc -o main -L. -lstuff main.c ja auf einen Fehler gelaufen ist, habe ich ja auch alles noch einmal in "Einzelschritten" gemacht incl. einem gcc -o main main.o libstuff.a

Dass ich vorher aber Quatsch geschrieben hatte, ist mir da nicht aufgefallen. Daher auf jeden Fall Danke für die Korrektur und die klare Erläuterung.
 

ocsme

Top Contributor
WOW :)

Vielen Lieben Dank an euch :) es hat geklappt und verstanden habe ich es nun auch so weit.
Wieso diese ganzen Schalter und wozu die sind etc. damit befasse ich mich jetzt erst einmal nicht weiter.
Hab ja mit dem C und C++ Stoff mehr als genug zu tun!

Bei mir hat er auch immer gemekert wenn -lstuff nicht am ende steht! Dann kommt das hier:
gcc -o main main.o -lstuff -L stuff
/usr/bin/ld: -lstuff kann nicht gefunden werden
collect2: error: ld returned 1 exit status

Da kann man froh sein so nette Hilfe zu haben :) @kneitzel , @mihe7 und @httpdigest

LG
 
X

Xyz1

Gast
Nun dachte ich mir mhhh... das erinnert mich doch sehr stark an die ganzen Standard Klassen von Java also schauen wir mal
Math ist in Java partiell nativ implementiert, damit gewisse Funktionen schnell sind.

Das soll heißen es wird auf Betriebssystem Funktionen zurückgegriffen.

Sin und cos sind zum Bleistift solche Kandidaten. Ist damit Deine Frage gelöst?
 

temi

Top Contributor
wobei das aber auch mit anderen C/C++ - Resourcen passieren kann

Wohl wahr, aber bei JW sind sich offenbar alle sehr einig. Im von dir verlinkten Thread werden ja auch ein paar Bücher empfohlen. Ich selbst habe den "Breymann" und zwei Bücher von Scott Meyers, die ich alle empfehlen kann, wobei die von Scott Meyers eher für Fortgeschrittene und Profis sind.
 

ocsme

Top Contributor
Danke werde ich mich mal zu schlau machen. Ich habe nur die zwei kleinen Bücher mir damals mal gekauft Grundkurs C und C++ von Jürgen Wolf und bin echt froh das ich erst mit Java angefangen habe. Ohne Grundwissen wäre ich damit überhaupt nicht klar gekommen :(

Ich hab hier mal noch ein Beispiel vielleicht kann mir dabei jemand Helfen zu den Pointern in Verbindung mit Arrays.
C:
int feld[2][3] = {{1,2,3},{4,5,6}};
int *px;
px = (int *) feld;
printf("%d\n",*px);
px = feld[0];
printf("%d\n",px[1]);
px = feld[1];
printf("%d\n",*px);
px = &feld[1][2];
printf("%d\n",px[0]);

feld ist eine Referenz und *px ein int Pointer. Diesem Pointer wird der Anfang des Arrays also dies Referenz mit gegeben. So weit so unklar :D
In meinem Beispiel hier ist das Feld zweidimensional und ich kann mit (int *) feld nichts anfangen :( was bedeutet das? ich dachte man könnte dann einen **px Pointer z. B. nutzen doch auch dabei kommen nur Warrnings :(

Wenn nicht erstelle ich ein neues Thema denn hier gehört es ja eh nicht hin :D

lg
 

mihe7

Top Contributor
bin echt froh das ich erst mit Java angefangen habe. Ohne Grundwissen wäre ich damit überhaupt nicht klar gekommen :(
Die Voraussetzungen (und damit auch die Zielsetzungen) bei der Entwicklung von C waren ganz andere als bei Java. Dazwischen liegen etwa 20 Jahre und nicht umsonst hat C den Ruf eines besseren Assemblers. Bei C brauchst Du etwas mehr technisches Verständnis, Du musst mehr "in RAM" denken.

Ich hab hier mal noch ein Beispiel vielleicht kann mir dabei jemand Helfen zu den Pointern in Verbindung mit Arrays.

Nehmen wir mal an, Du hast folgende Bytes im Speicher ab Adresse 1000 stehen:
Code:
0 0 0 1 0 0 0 2 0 0 0 3 0 0 0 4 0 0 0 5 0 0 0 6

Die Bytes alleine sind erstmal nur Bytes. Ohne weitere Informationen, kannst Du dazu nichts sagen.

Man könnte jetzt mitteilen: das sind ganzzahlige 32-Bit-Werte, wobei das höherwertige Byte zuerst kommt. Daraus kannst Du ableiten, dass an Adresse 1000 die Zahl 1, an Adresse 1004 die Zahl 2, an Adresse 1008 die Zahl 3 usw. gespeichert sind.

Das sind jetzt einfach mal sechs 32-Bit-Werte, die nacheinander im Speicher stehen. Das könnten also z. B. die Werte von sechs Variablen sein. Ich könnte Dir aber auch sagen, dass die Werte zusammengehören, und ein eindimensionales Array der Länge 6 bilden. Damit könntest Du sofort beantworten, was das fünfte Element dieses Arrays ist.

Genauso gut könnte es aber auch ein zweidimensionales Array sein, z. B. der Größe 2x3, bei dem der Spaß zeilenweise abgelegt ist (erst alle Elemente der ersten Zeile, dann alle Elemente der zweiten Zeile usw.) Dann kannst Du beantworten, was das erste Element der zweiten Zeile ist.

Kurz: es dreht sich eigentlich nur darum, wie die Daten interpretiert werden. In C sagst Du dem Compiler, wie er was zu interpretieren hat.

Die Folge oben könnte z. B. Deinem Array "feld" entsprechen (auf einem System, bei dem int 32-Bit-Werte sind und in Big Endian abgelegt werden). D. h. feld wäre einfach ein Bezeichner für die Adresse 1000. Durch die Angabe int[2][3] sagst Du dem Compiler, dass es 2x3 int-Werte sind, die zeilenweise nacheinander im Speicher liegen sollen. Dadurch weiß der Compiler auch, wie er feld[0][1] zu interpretieren hat.

Ein Pointer ist dagegen eine Variable, die eine Speicheradresse aufnimmt. Durch Dereferenzierung greifst Du auf den Speicher an eben dieser Adresse zu.

Dein px ist also die Variable, die eine Adresse speichert. Du kannst px verändern; damit änderst Du nur die gespeicherte Speicheradresse. Wenn Du auf die gespeicherte Adresse zugreifen willst, musst Du den Pointer dereferenzieren. Dafür gibt es den "dereference operator", auch "value at" genannt. Das ist das Sternchen vor dem Pointer.

D. h. mit px ist die Adresse gemeint, die in px gespeichert ist, mit *px ist der Wert an der Adresse gemeint, die in px gespeichert ist ("value at px").

Mit Zeigern (also den gespeicherten Adressen) kannst Du in C rechnen (Pointer-Arithmetik). Der Clou ist, dass dabei die Größe des beim Pointer angegebenen Datentyps berücksichtigt wird.

Nehmen wir an, in px wäre die 1000 (erstes Element des Arrays) gespeichert. Dann wäre px+1 nicht etwa 1001 sondern 1004 (wenn ein int 32-Bit breit ist) :)

Code:
Var  Addr  Wert
feld 1000  1 <----+ *px
     1004  2      | *px+1
     1008  3      | *px+2
     ....         |
px   2000  1000  -+

Auch möglich ist es, den subscript-operator zu verwenden: px[i] ist nichts anderes als *(px+i). Auf die in px gespeicherte Adresse wird also i-mal die Größe eines ints addiert und anschließend wird der so berechnete Zeiger dereferenziert.

Was sind nun feld[0] bzw. feld[1]? Da Du ein 2D-Array hast, handelt es sich um die Adressen der ersten Elemente der ersten bzw. zweiten Zeile.

ich dachte man könnte dann einen **px Pointer z. B. nutzen doch auch dabei kommen nur Warrnings :(
Nur Warnungen? Da könnte auch ein Fehler kommen.

Wäre px eine Variable vom Typ int **, dann würdest Du dadurch zweimal dereferenzieren. Im Beispiel in den Code-Tags oben: ist px 1000. Damit ist *px der Wert an Adresse 1000, also die 1. Damit ist **px der Wert an Adresse 1... Da solltest Du bei der Ausführung ein segfault bekommen.

Zum Abschluss noch ein Hinweis: es ist gut möglich, dass das alles nicht 100 %-ig korrekt ist. Versteh es einfach als mentale Stütze.
 

ocsme

Top Contributor
Danke :)

Doch leider verstehe ich immer noch nicht das hier :(
Also nehmen wir an ich habe ein Array dann Funktioniert die Zuweisung eines Pointers so:
Code:
int feld[2] = {1,2,3,4,5,6};
int *px;
px = feld;

ist das Array nun zweidimensional geht es komischerweise so:
C:
int feld[2][3] = {{1,2,3},{4,5,6}};
int *px;
px = (int *) feld;

Da ich nicht verstehe wieso das an dieser Stelle so gemacht wird also px = (int *) <--- ??? feld
komme ich auch nicht drauf wie ich ein mehrdiminsionales Array als 3,4, etc Dimensionen Referenzieren könnte mit einem Pointer.

Vielleicht weiß da jemand eine Idee wenn nicht lasse ich es erst einmal.
Ich habe auch zu den Büchern noch einmal nach geschaut und die hier gefunden sind zwar schon etwas älter doch vielleicht immer noch zu gebrauchen?


Bei C++ bin ich noch nicht da habe ich aber auch eine alte Version von Jürgen Wolf :(:D Doch vielleicht steht da ja die Sachen verständlicher drin?


LG

Vielleicht versteht man so meine Frage etwas besser:
Code:
int feld[2] = {1,2,3,4,5,6};
int *px;
px = feld;

//äquivalent dazu wäre:
px = &feld[0]

//mehrdimensional:
int feld[2][3] = {{1,2,3},{4,5,6}};
int *px;
px = (int *) feld; //wie kommt man auf den Cast mit dem Int-Zeiger?

//äquivalent dazu wäre:
px = &feld[0][0]
 
Zuletzt bearbeitet:
K

kneitzel

Gast
Was passiert denn, wenn Du das weg lässt?

==> Du bekommst eine Warnung. Incompatible pointer types werden angemeckert.

Ein Array ist ein Zeiger auf das erste Element.
Also bei int feld[2] ist feld ein zeiger auf ein int.
Aber bei int feld[2][3] ist feld kein zeiger auf ein int sondern auf ein Array von int (auf ein int[3], denn davon hast Du ja zwei).

Diese Warnung soll besagen: Pass auf, was Du da machst. Denn wenn Du Dir die Erklärung von mihe anschaust:
px+1 geht, da es jetzt nur ein int ist, eben 4 Bytes weiter. Wenn Du aber als Typ ein int[3] hast, dann ist das nicht 4 sondern 12 Bytes lang. ==> Das "nächste" Element wäre also ggf. etwas anderes, als Du jetzt erhälst. Daher die Warnung.

==> Als Entwickler solltest Du Dir genau überlegen, was Du machst. In diesem Fall musst Du explizit sagen: Ich will das als einzelne int behandeln.
 

ocsme

Top Contributor
Nochmals Danke.

Vielleicht hat ja noch jemand letzte Worte zu den Büchern da oben.
Derzeit habe ich mir das Buch von Kirsch, Schmitt besorgt Programmieren in C, Eine mathematikorientierte Einfuehrung.

Mal schauen vielleicht wird dadurch mehr Klarheit geschaffen?

LG
 

ocsme

Top Contributor
Ja über das Buch bin ich auch gestolpert doch das PROBLEM dabei ist leider das ich nicht so gut Englisch kann :(
Ich weiß in der Informatiker Szene kommt man ohne Englisch nicht zurecht! Doch dafür habe ich noch etwas Zeit hoffe ich :) :p

LG
 

Ähnliche Java Themen

Neue Themen


Oben