Hallo,
ich bin Student der Elektrotechnik und beschäftige mich zur Zeit während
meiner Semesterferien mit C-Programmierung. Jetzt bin ich auf ein
Problem gestoßen, mit dem ich überhaupt nicht klar komme - ich hoffe Ihr
könnt mir helfen.
Folgende Funktion first erzeugt ein array[4]. Jedes element des array
enthält eine Zufallszahl von 0-5. Es darf keine der Zahlen 0-5 in mehr
als einem Arrayelement stehen.
Diese Funktion rufe ich in der main 2 mal auf und gebe das array danach
jeweils testweise aus.
Das Problem ist, dass 2 mal das gleiche Array ausgegeben wird. Warum ist
das so? Eigentlich müsste doch bei jedem Funktionsaufruf neu "gewürfelt"
werden. Bzw. was muss ich ändern, dass es so ist? Danke schonmal für
Eure Hilfe!
gox schrieb:> Diese Funktion rufe ich in der main 2 mal auf und gebe das array danach> jeweils testweise aus.> Das Problem ist, dass 2 mal das gleiche Array ausgegeben wird. Warum ist> das so?
Dein Problem ist, dass der Rechner zu schnell ist. Dein time(0) ändert
sich zwischen den Aufrufen nicht. Damit startet der
Zufallszahlengenerator immer mit der gleichen Zahl und liefert dann auch
immer die gleiche Folge.
das srand stellt man sinnigerweise ganz an den Anfang in main() und ruft
es auch nur ein einziges mal auf. Das ist sogar sehr wichtig, dass man
diesen Aufruf nur einmal macht. Denn Zufallszahlen definieren sich durch
statistische Eigenschaften. Die kann ein Generator wie rand() aber nur
dann aufbauen, wenn du ihn in Ruhe arbeiten lässt. Jedesmal wenn du
srand() einsetzt, lässt du den Generator nicht mehr arbeiten, sondern
zwingst ihm etwas auf - deine statistische Verteilung der Zahlen ist
hinüber und die Zahlen sind nicht mehr pseudo-zufällig, sondern es gibt
dubiose statistische Zusammenhänge zwischen den einzelnen Zufallsreihen.
Und deine Arraybehandlung. Lies noch mal nach, wie Argument passing in C
funktioniert. Speziell bei Arrays. Da ist die Sache nämlich ein wenig
anders als bei normalen Variablen.
Edit: zu langsam
Danke für eure Antworten,
@Marcus B.: ich hab mir den Wert von time(0) mal ausgeben lassen, indem
ich nach
1
srand(time(0));
mal time(0) mit printf ausgegeben habe
Das Resultat ist, dass es tatsächlich in beiden Durchläufen gleich ist.
Das Ausgeben von srand(time(0)) bringt das gleiche Ergebnis.
@Volker Zabe: Ich hab hier das Buch "C-Programmierung unter Linux, UNIX
und Windows" von den Autoren Herold und Arndt.
An der Übergabe des Arrays liegt das Problem nicht, weil ich mir das
Array auch testweise schonmal innerhalb der Funktion und außerhalb der
funktion ausgeben hab lassen, und hab das gleiche rausbekommen.
@ Karl Heinz Buchegger: Ich habs gleich mal probiert und es funktioniert
mit dem srand() in der main()!
Seid ihr sicher, dass ich das array falsch übergebe? Das Programm tut
jetzt nämlich was es soll. Oder ist das unsauberer Stil?
@Volker Zabe: Ich hab die von dir vorgeschlagenen Änderungen mal
ausprobiert. Aber dann meckert CodeBlocks beim Kompilieren immer schon
rum und das Programm wird nicht ausgeführt.
gox schrieb:> Ich hab die von dir vorgeschlagenen Änderungen mal> ausprobiert. Aber dann meckert CodeBlocks beim Kompilieren immer schon> rum und das Programm wird nicht ausgeführt.
Bei mir (MSC) ist es umgekehrt.
Deine Version: 6 Fehler
C:\projects_svn\winkel\test_atan\test_atan.cpp(146) : error C2440: '=' :
cannot convert from 'int' to 'int *'
Conversion from integral type to pointer type requires
reinterpret_cast, C-style cast or function-style cast
C:\projects_svn\winkel\test_atan\test_atan.cpp(157) : error C2664:
'first' : cannot convert parameter 1 from 'int (*)[4]' to 'int *[]'
Types pointed to are unrelated; conversion requires
reinterpret_cast, C-style cast or function-style cast
Änderung : 0 Fehler, 0 Warnings
ciao Volker
gox schrieb:
^
> Seid ihr sicher, dass ich das array falsch übergebe?
Ja, sind wir.
> Das Programm tut> jetzt nämlich was es soll. Oder ist das unsauberer Stil?
Genau genommen ist es falsches C.
Schon alleine deswegen, weil deine Funktion einen Pointer auf ein Array
bekommt, welches aus lauter Pointern besteht.
Was dich rettet ist die Tatsache, dass auf deinem System offenbar
sizeof(int) == sizeof( void*)
Und natürlich, dass der Compiler einen & bei einem Array-Argument
ignoriert. Das entspricht zwar nicht den C-Vorgaben, manche Compiler tun
das aber trotzdem.
Das das trotzdem funktioniert ist reines Glueck.
int * begin[4] ist ein [4]-fach-Array von Pointern auf ein int
array
wenn Du schreibst
if( begin[1]==begin[0]) blah ..
vergleichst Du zwei Pointer , nicht zwei Integer.
Nun ist witzigerweise beides das Gleiche, also funktioniert das
Programm.
Lass einfach den * und das & weg, und alles ist gut.
Die eckigen Klammern bei der Funktionsdefinition sind nur syntaktischer
Zucker. Ob du das so schreibst
1
voidfirst(int*begin)
oder so
1
voidfirst(intbegin[])
ist beides dasselbe (die 4 da drinnen spielen sowieso keine Rolle). In
beiden Fällen wird der Funktion die Startadresse des Arrays übergeben.
Im zweiten Fall sieht man vielleicht noch etwas eher, dass der
Programmierer der Funktion mit einem Array rechnet, aber aus C-Sicht
sind es nur unterschiedliche Schreibweisen desselben Sachverhaltes.
Und beim Funktionsaufruf
first( testar );
wird der & weggelassen, weil ein Arrayname für sich alleine (also ohne
Index, wie bei testar[0]) an dieser Stelle sowieso automatisch für die
Startadresse des Arrays steht. Ein Arrayname alleine IST gleichbedeutend
mit der Startadresse eines Arrays. Dies folgt aus der Art und Weise, wie
der Indexoperator in C definiert ist. Der ist nämlich auch nur eine
andere Schreibweise für etwas anderes :-)
array[i] <==> *(array + i)
und hier steht der Name des Arrays wieder alleine und fungiert somit als
Startadresse der Daten im Speicher. Zu dieser Startadresse wird dann
noch der Offset zum i-ten Element dazugezählt. Das Ergebnis ist dann
wieder eine Adresse, nämlich die des i-ten Elements und der *
schlussendlich ist dann dafür zuständig die Daten von dort zu lesen oder
zu schreiben.
gox schrieb:> Danke für die Mühe und auch die Erklärungen unten finde ich echt klasse> und gut verständlich.> Bei mir funktionierts nur mit
1
voidfirst(intbegin[])
> ich darf die [] also nicht weglassen.
Die [] darfst du natürlich nicht einfach weglassen. Wenn du sie
weglässt, musst du sie durch einen * beim Datentyp ersetzen und
umgekehrt!
Also entweder
1
voidfirst(intbegin[])
2
{
3
....
4
}
oder
1
voidfirst(int*begin)
2
{
3
...
4
}
aber eines von beiden muss es schon sein.
(Oder dein Compiler kocht da sein eigenes Süppchen. Aber in dem Fall ist
das dann kein C-Compiler mehr)
> andernfalls meldet mir der compiler "error: conflicting types of> 'first'" und markiert die eben zitierte zeile
Hast du noch irgendwo einen Prototypen, den du nicht gezeigt hast?
Es ist bei solchen Grundlagenfragen immer gut, wenn du immer dein
komplettes Programm zeigst und nicht nur Ausschnitte. Im schlimmsten
Fall zeigst du dann Dinge her, die nicht notwendig gewesen wären. Aber
andersrum ist es schlimmer. Nämlich dann wenn wir Annahmen treffen
müssen und raten müssen, was wohl in deinem Programmtext noch so alles
steht, was wir nicht sehen können.
Im Zweifelsfall einfach das komplette C-File, so wie es ist, als
Attachment anhängen und wir sehen alles was wir brauchen.
>ich darf die [] also nicht weglassen.
Das hat auch nie jemand behauptet.
Du musst zwischen der Deklaration bzw. Definition einer Funktion auf der
einen Seite und der Verwendung/dem Aufruf auf der anderen Seite
unterscheiden.
Bei der Deklaration musst Du entweder eckige Klammern oder * verwenden
um klarzustellen, das ein Zeiger auf ein Array übergeben wird.
Beim Aufruf hingegen ist der Name eines Arrays gleichbedeutend mit einem
Zeiger darauf. Der Aufruf darf also kein zusätzliches * oder die eckigen
Klammern enthalten.
Vielleicht hat Dich ja das hier irregeführt:
>Die eckigen Klammern bei der Funktionsdefinition sind nur syntaktischer>Zucker.
Das heisst nicht, dass die eckigen Klammern nach Belieben weggelassen
werden können. Vielmehr bedeutet es, das die eckigen Klammern eine
alternative Schreibweise zu * sind. Siehe auch:
http://de.wikipedia.org/wiki/Syntaktischer_Zucker
@Kali: Das wars, ich konnte mit dem Begriff "Syntaktischer Zucker"
nichts anfangen - wieder was gelernt!
@Karl Heinz Buchegger: Ich hab zum testen dieser Funktion nichts anderes
in der main stehen, als das, was ich gepostet hab. Ich bau mir erst die
einzelnen Funktionen und teste, ob diese funktionieren und erst zum
Schluss bau ich daraus dann das fertige Programm.
Ist das keine übliche Vorgehensweise?
gox schrieb:> @Karl Heinz Buchegger: Ich hab zum testen dieser Funktion nichts anderes> in der main stehen, als das, was ich gepostet hab. Ich bau mir erst die> einzelnen Funktionen und teste, ob diese funktionieren und erst zum> Schluss bau ich daraus dann das fertige Programm.> Ist das keine übliche Vorgehensweise?
Doch doch. Das ist schon ok.
Ganz im Gegenteil. Das ist das, was dir viele erfahrene Programmierer
auch empfehlen würden: Nicht zuviel auf einmal. Sieh zu, dass du schnell
was zum Testen hast. Kleine Schritte. Aufgabenteilung. Ruhig auch mal
ein Testprogramm mit dem man einzelne Aspekte losgelöst von der
eigentlichen Problemstellung für sich alleine testet.
Volker Zabe schrieb:> C:\projects_svn\winkel\test_atan\test_atan.cpp(146)
.
.
.
> Types pointed to are unrelated; conversion requires> reinterpret_cast, C-style cast or function-style cast
Wenn man ein Programm in C schreibt, sollte man es auch mit einem
C-Compiler übersetzen und nicht mit einem C++-Compiler.
Karl Heinz Buchegger schreibt:
> (Oder dein Compiler kocht da sein eigenes Süppchen. Aber in dem Fall ist> das dann kein C-Compiler mehr)
Richtig erkannt. ;-)
>void first(int* begin)>oder so>void first(int begin[])>ist beides dasselbe (die 4 da drinnen spielen sowieso keine Rolle).
als pascal programmierer dreht es einem da aber sowas von den magen
um...
;-)