Hallo ich habe eine Funktion geschrieben die ein Zeiger der auf eine int
Variable zeigt zurück geben soll. Leider bekomme ich immer einen Fehler
beim Versuch den Code zu compilieren.
error: conflicting types for 'firstEight'|
Ich nutze einen gcc Compiler und die Entwicklungsumgebung Code::Blocks
10.05
Ich habe schon einiges ausprobiert aber leider hilft nichts, ich
verstehe das Problem nicht.
Vllt kann mir ja jmd auf die Sprünge helfen.
Ich könnte natürlich einfach einen Zeiger übergeben mittels
Dereferenzierungsoperator den inhalt verändern wie beim "index" zeiger
in "firstEight". Allerdings helfe ich einen Freund (versuche es) und er
hat die Aufgabe das eben so zu machen.
Wo ist die forward-Deklaration für firstEight? Klingt für mich, dass die
von der tatsächlichen Definition abweicht.
In deinem Beispiel fehlt diese komplett. Es ist also schonmal nicht das
Bsp, welches du getestet hast, denn hier würde der Compiler einen
anderen Fehler melden....
Ich glaube, der Compiler interpretiert deine Funktionsaufrufe als
Deklarationen, weil die da noch nicht definiert sind. Schieb' mal die
Main-Funktion ganz an den Schluss, dann sollte es gehen.
Oh, und benenne bitte die Funktionen um, bei diesem Denglisch dreht's
einem ja den Magen um. ;D
Unfassbar, das ist die Rätselslösung.
Ich dachte schon ich bin total depperlt :D
Einfach die Funktionen über die "main" schieben. C/C++ ist bei mir ewig
her, ich komme aus der Java ecke da gibt es dieses Problem nicht.
Vielen Dank
Dunkel erinnere ich mich an die Header Geschichte von C.
Ich glaube ich beschäftige mich mal wieder ernsthafter mit C, ich finde
die Pointer "Geschichte" Spannend. :D
Der Code dient hauptsächlich dazu mir klar zumachen was es damit auf
sich hat, also der Übung. Danke euch :-)
Kai schrieb:> Der Code dient hauptsächlich dazu mir klar zumachen was es damit auf> sich hat, also der Übung. Danke euch :-)
auf jedem Fall gehört hinter einem malloc eine Abfrage ob du Speicher
bekommen hast. Und ansonsten ist es auch sehr unsauber, soetwas sollte
man auf keinem Fall im Programm machen.
Mik schrieb:> auf jedem Fall gehört hinter einem malloc eine Abfrage ob du Speicher> bekommen hast.
Was das genau nützen soll in $durchschnitts-userspace-programm habe ich
ja auch noch nie verstanden. Wenn das Betriebssystem keinen Speicher
mehr zur Verfügung hat, ist halt Ende. Daran lässt sich durch die
Abfrage im Endeffekt eh nix ändern... davon abgesehen, dass es auf
modernen Systemen sowieso so gut wie nie passiert.
Das verursacht imo nur eine Menge unnötigen "Boilerplate"-Code überall.
Mik schrieb:> Und ansonsten ist es auch sehr unsauber, soetwas sollte> man auf keinem Fall im Programm machen.
Was genau gefällt dir denn nicht?
Wie gesagt ich habe mich einfach mal ausgetobt.
Mir ist auch "noch nicht" klar warum ich überhaupt malloc genutzt habe,
es steht halt in der Aufgabenstellung.
Ich hätte einfach ein Array mit n Einträgen definiert, was ja
anscheinend auch nichts anderes als ein Zeiger ist.
Der Code hat garnicht den Anspruch Perfekt zusein, ich bin ein C Noob,
zu testen ob der Speicher reserviert wurden konnte wäre mir nichtmal
eingefallen. Meine C/C++ Erfahrungen beschränken sich auf Vorlesungen in
den man lächerliche Progrämmchen geschrieben hat oder etwas mit MFC
zusammen geklickt hat :-)
Sven B. schrieb:> Mik schrieb:>> auf jedem Fall gehört hinter einem malloc eine Abfrage ob du Speicher>> bekommen hast.> Was das genau nützen soll in $durchschnitts-userspace-programm habe ich> ja auch noch nie verstanden. Wenn das Betriebssystem keinen Speicher> mehr zur Verfügung hat, ist halt Ende.
Weil man als guter verantwortungsvoller Programmierer grundsätzlich
dafür sorgt das ein Programm sauber terminiert und nicht einfach das
Programm Amok laufen läßt und darauf wartet das das System das Programm
abbricht.
> Daran lässt sich durch die> Abfrage im Endeffekt eh nix ändern... davon abgesehen, dass es auf> modernen Systemen sowieso so gut wie nie passiert.
Moderne Systeme sind auch z.B. XMegas. Hier überschreibst du den
Speicher was dazu führen kann das gefährliche Dinge passieren. z.B. ein
Brotbackautomat der irgendwann anfängt einfach nur noch zu heizen, bis
es zum Brand führt. Nur weil so ein Feld und Wiesen Anfänger glaubte man
brauche so etwas nicht abzufragen.
> Das verursacht imo nur eine Menge unnötigen "Boilerplate"-Code überall.
Der ist bestimmt nicht auf sinnvolle Abfragen zurückzuführen, sondern
das Anfänger, z.B. die, die glauben das solche Abfragen überflüssig
sind, keinerlei Ahnung haben von dem was sie machen und sinnlos massig
Code einfügen der nicht gebraucht wird und schon gar nicht in der Lage
sind das Programm sinnvoll so zu strukturieren das es mit wesentlich
weniger Ressourcen auskommt.
Kai schrieb:> int main()> {> int n = 30;> int index = -1;> int * zeiger = (int *) malloc(n * sizeof(int));> randomZeiger(zeiger, n);> print_memory(zeiger, n);> int * index_zeiger = &index;>> firstEight(zeiger, n, index_zeiger);
// du benutzt weder den Rückgabewert noch den allokierten Speicher (der
eh nie zurückgegeben wird)
> printf("index: %i", index);> }>> void print_memory(int * memory, int lenght) {> int i;> for(i = 0; i < lenght; i++) {> printf("%i: %i\n", i, memory[i] );> }> }>> void randomZeiger(int * zeiger, int n) {> int i;> for(i = 0; i < n; i++) {> zeiger[i] = i;> }> }>> int *firstEight(int * memory, int size, int * index) {> int i;> int *return_z = (int *) malloc(sizeof(int));
// bei jede Aufruf wird Speicher angefordert der nie wieder freigegeben
wird
// wobei der malloc absolut sinnlos ist, da der Speicher noch nicht mal
gebraucht wird.
> for(i = 0; i < size; i++) {> if( memory[i] == 8 ) {> *index = i;> return_z = &memory[i];
// auch hier benutz du den allokierten Speicher nicht sondern erzeugst
ein Memoryhole indem du den einzigen Zeiger darauf überschreibst.
> return(return_z);> }> }> return(NULL);
// auch hier keinerlei Bedarf an dem verschwendeten Speicher
> }
==4136== 4 bytes in 1 blocks are definitely lost in loss record 1 of 2
8
==4136== at 0x4C2C04B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
9
==4136== by 0x4005E3: firstEight (test.c:21)
10
==4136== by 0x4006AF: main (test.c:40)
11
==4136==
12
==4136== 120 bytes in 1 blocks are definitely lost in loss record 2 of 2
13
==4136== at 0x4C2C04B: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so)
14
==4136== by 0x40066C: main (test.c:35)
15
==4136==
16
==4136== LEAK SUMMARY:
17
==4136== definitely lost: 124 bytes in 2 blocks
18
==4136== indirectly lost: 0 bytes in 0 blocks
19
==4136== possibly lost: 0 bytes in 0 blocks
20
==4136== still reachable: 0 bytes in 0 blocks
21
==4136== suppressed: 0 bytes in 0 blocks
22
==4136==
23
==4136== For counts of detected and suppressed errors, rerun with: -v
24
==4136== ERROR SUMMARY: 2 errors from 2 contexts (suppressed: 2 from 2)
Dieses Tool findet übrigens auch fast jeden anderen Fehler, der mit
kaputtem Speicher zu tun hat, in Sekunden (wenn z.B. unerklärliche
Abstürze auftreten o.ä.).
Danke Mik,
Das ich malloc im firstEight nutze war einfach ein Versuch, mitlerweile
ist das wieder rausgeflogen.
Der Rückgabewert wird im jetzt "Fertigen Code" benutzt.
1
int*irgendeinPointer=firstEioght(...);
>>// auch hier benutz du den allokierten Speicher nicht sondern erzeugst>>ein Memoryhole indem du den einzigen Zeiger darauf überschreibst.
Leider vrestehe ich nicht ganz wo das Problem liegt. Memoryhole weil ich
einen Zeigererzeuge der den Speicher belegt?
Uhrsprünglich sah es so aus
1
return(&memory[i]);
würde es auch immer wieder so machen. (Stand Jetzt ^^)
Dieses "valgrind" werde ich mir mal angucken, google wird mir helfen :-)
> Leider vrestehe ich nicht ganz wo das Problem liegt. Memoryhole weil ich> einen Zeigererzeuge der den Speicher belegt?
Nein, weil du den Speicher nicht wieder freigibst.
Zu jedem malloc() muss später im Programm ein passender free()-Aufruf
stattfinden, zu jedem new ein delete. Ist das nicht der Fall, so
reserviert dein Programm nach und nach immer mehr Speicher, den es
eigentlich gar nicht benutzt.
Insbesondere: Wenn du mit malloc Speicher reservierst, speicherst du ja
den Pointer auf den Speicher irgendwo. Wenn der Pointer aber in keiner
Variable (oder sonstwo) mehr steht, sollte der Speicher freigegeben
werden (mit free). Sonst ist der nämlich quasi weg (denn wie willst du
darauf zugreifen, wenn du den Pointer "vergessen" hast).
Klar kein Problem, die namens gebung ist nicht ausschließlich mein Mist,
dem jenigen dem ich helfe wurde eine Funktion mitgeliefert die random
Zahlen ausgibt. Die habe ich nicht, habe sie nur Simmuliert. (wenn man
das so sagen will)
Deshalb habe ich sie einfach so genannt. (im Orginal soll sie das tun)
firstEight (oder first_eight) steht klar so in der Aufgaben Definition
drinne :)
Mein Wording ist aber so oder so bescheiden, das ist mir bewusst, von
daher passt das.
>>/// evtl. return (memory+i)>>..>>///! i, memory[i], (memory+i)
"memory+i" stellt das selbe da wie "&memory[i]"?
Probiere ich gleich mal aus.
>>///! int* memory ist const, also auch angeben!
Guter Hinweis, so noch nie drüber nachgedacht lässt sich sicher auch auf
Java übertragen.
>>//! for ( int i = 0; ... )
Würde ich immer so machen, aber das akzeptiert mein Compiler leider
nicht.
Vllt weis es C ist und nicht C++?
>>///! inkonsistente Formatierung
Magst du mir ein Beispiel nennen.
>> ///! Formatierung
z.B.fehlendes Leerzeichen nach dem if, unnötiges vor ")"
Sei ruhig pingelig ich habe damit keine Probleme, im Gegenteil! :-D
Kai schrieb:>>>//! for ( int i = 0; ... )>> Würde ich immer so machen, aber das akzeptiert mein Compiler leider> nicht.> Vllt weis es C ist und nicht C++?
C99. Da sind "on the fly" Deklarationen von Variablen erlaubt. Die sind
nur im lokalen Block gültig. Der gnu compiler muss wissen, wenn du C99
machst (-std=c99 oder so, rtfm). :)
Ah ok, ich wusste nicht, dass Du den alten C-Standard programmierst.
Dann ist das ok mit der for-Schleife.
Wegen der Formattierung, ich will Dir nicht sagen wo Du die Leerzeichen
setzen sollst, weil das in großem Umfang Ansichtssache ist. int * x
schreibt allerdings niemand, entweder int* x (finde ich besser) oder int
*x, aber auf jeden Fall überall gleich. Meines Wissens nach ist die
erste Variante in C++ üblicher, die zweite in C.
Dasselbe gilt für die Klammern, mach' es wie Du willst, aber mach' es
überall gleich, nicht mal so und mal anders. Das macht den Code viel
leichter zu lesen und vermeidet auch Fehler.
Grüße,
Sven
Kai schrieb:> Der Vollständigkeit halber, hier das soweit fertige Stück.> Mit Optimierungs Potenzial. :D> #include <stdio.h>> #include <stdlib.h>>
// auf das Einrücken mag ich nicht eingehen, das ist auch etwas
//Geschmacksache. Bei mir stehen '{' und '}' untereinander.
//
> void randomPointer(int * pointer, int n) {
// Diese zeile sähe bei mit so aus
// void RandomPointer(int *pointer, int n) {
// Der Stern gehört bei mir zum Variablenamen als da rangerückt
// denn bei
// int *p1, *p2, i; Stände der Stern sonst alleine da.
// den Namen Pointer gibt es bei mir nicht, jeder Pointer fängt mit
einem // kleinen p an, gefolgt von eine Namen der Großgeschrieben wird.
// Also hier z.B. pInt oder pInteger.
> int i;> for(i = 0; i < n; i++) {> pointer[i] = i;> }> }>> int *firstEight(int * memory, int size, int * index) {
// Dieses sähe bei mir so aus
// int *pFirstEight(int *pMemory, int size, int *pIndex) {
// wobei ich aber vermutlich andere Namen genommen hätte, z.B. Buffer
statt Memory
> int i;> for(i = 0; i < size; i++) {> if( memory[i] == 8 ) {> *index = i;> return(&memory[i]);> }> }
// Sinnvoll wäre an dieser Stelle noch
*index=-1; // damit index immer einen Sinnvollen wert
zurückliefert.
> return(NULL);> }>> void printMemory(int * memory, int lenght) {> int i;> for(i = 0; i < lenght; i++) {> printf("%i: %i adresse: %i \n", i, memory[i], &memory[i] );> }> }>> int main(int argc, int* argv[])> {
// Sowas mag ich z.B. gar nicht
> if(argc <= 1) {> printf("Parameter fehlt!");> return 1;> }> int n = atoi(argv[1]);> int index = -1;> int * pointer = (int *) malloc(n * sizeof(int));
// am anfang kommen die Deklarationen mit Kommentar
> int n; // Anzahl Feldelemente> int index = -1; // Index auf die 8> int * pointer = // Zeiger auf den allocierten Speicher
// die überprüfung ob ein Parameter vorliegt
> if(argc <= 1) {> printf("Parameter fehlt!");
// warum hier eine so nichtssagende Fehlermeldung?
// warum nicht direkt sagen wie es richtig geht, und was für ein
parameter benötigt wird?
> return 1;> }
n = atoi(argv[1]);
// hier kommt Abfrage ob gültig hin, was ist wenn "hut" als parameter
übergeben wird?
// also Abfrage ob n im erlaubten Bereich ist
if (n > .... n < ... )
// Jetzt Speicher holen
pointer = (int *) malloc(n * sizeof(int));
// Was wenn soviel Speicher nicht da ist?
if ( pointer == NULL) ...
> randomPointer(pointer, n);> printMemory(pointer, n);> //int * index_pointer = &index;>> int * eightPointer = firstEight(pointer, n, &index);
// Jetzt hier mitten im Code noch eine Variable anlegen? sowas machen
nur Schmierfinken, gehört nicht zu einem sauberen Code
//
> printf("index: %i\n", index);> if(index != -1 && eightPointer != NULL) {
// warum doppel abfragen?
> printf("inhalt: %i, adresse: %i", *eightPointer, eightPointer );
// Pointer auszugeben ist immer so eine Sache ist sehr
Machienenabhängig.
// meistens ist man mit %ld besser.
> free(pointer);> return 0;> } else {> free(pointer);> return 1;
// warum der gleiche Rückgabewert bei "nicht gefunden" und "das Programm
konnte gar nicht ausgeführt werden"?
> }> }
Mik schrieb:> // Dieses sähe bei mir so aus> // int *pFirstEight(int *pMemory, int size, int *pIndex) {
Eeeew. Das sieht man doch schon an der Deklaration. Und was machst Du
bei einem Smart Pointer? Oder bei einem opaken "Handle", das eigentlich
z.B. ein void * ist? Was passiert mit dem Präfix, wenn im Laufe der
Codeevolution aus dem Pointer etwas anderes wird oder andersrum? Okay,
in Bezug auf http://www.joelonsoftware.com/articles/Wrong.html ist das
reine "p" wohl gerade noch auf der Grenze zwischen "kind" und "type".
Aber hart an der Grenze ;)
Mik schrieb:> // Jetzt hier mitten im Code noch eine Variable anlegen? sowas machen> nur Schmierfinken, gehört nicht zu einem sauberen Code
Nein, das gehört in C++ und AFAIK auch in C99 zum guten Ton. Declare
Near Use. Nicht zuletzt vermindert das den Bedarf an Ungarischer
Notation wie mit dem "p"-Präfix. Wenn die Variable da deklariert ist, wo
sie auch erstmals verwendet wird und dann auch noch der Code sauber
strukturiert und entsprechend der Bereich der Verwendung sehr lokal ist,
dann erübrigt sich das und man kann einfache, sprechende Namen ohne
Typen-Warzen nehmen.
Mik schrieb:> // Pointer auszugeben ist immer so eine Sache ist sehr> Machienenabhängig.> // meistens ist man mit %ld besser.
Stimmt. Und noch besser ist %p, dann passt es immer. Und wird i.d.R.
auch gleich hexadezimal ausgegeben, was an sich dann schon ein Hinweis
darauf ist, dass Normaluser das nicht unbedingt als blanke Zahl
verstehen sollte.
Vielen Dank für eure mühe, cih werde die meisten eure "Ratschläge"
versuchen zu beherzigen.
Es ist jedenfalls sehr nice von euch das ihr euch die mühe gemacht habe,
vielen Dank.:-)