Hallo an alle (besonders C-Programmierer),
ich möchte gerne ein kleines Programm schreiben (um ein wenig C zu lernen). Jenes soll physikalische Gleichungen ausrechnen können. Allerdings gibt es ein Problem. Versucht mal das Weg-Zeit Gesetz bei einer gleichförmigen Bewegung. Wenn man dann die erste gesuchte Variable (s) eingeben soll, wars das, ende. Mit einem ddd habe ich einen exit-code 16 festgestellt. Wenn ich allerdings dir Variable gvar als integer initialisiere, klappt es! Warum denn das nun? Der Compiler meldet keinen Fehler auch beim compilieren mit gvar=char. Da bin ich doch schon etwas ratlos. Kennt sich einer aus?
Bye, Sebastian
P.S.: Das Proggi ist mal mit als Anhang (als C-Datei) , hoffe das fördert statt nervt :-)
Hallo Sebastian,
On Thu, May 31, 2001 at 13:13:07 +0200, Sebastian Roth wrote:
Bewegung. Wenn man dann die erste gesuchte Variable (s) eingeben soll, wars das, ende. Mit einem ddd habe ich einen exit-code 16 festgestellt. Wenn ich
Aus eigenger Erfahrung kann ich sagen: scanf() is a pain in the ass. Ich habe mir irgendwann mal ne eigene Funktion zum Einlesen eines Strings geschrieben. Sicher auch nicht das Gelbe vom Ei, aber fehlertoleranter als scanf() allemal.
/* Textzeile einlesen Stoppt bei LF oder CR/LF Fehler bei mehr als n gelesenen Zeichen oder EOF */ getline(FILE *fdesc, unsigned char *buf, int n) { unsigned char ch = 0; int cnt = 0;
while ((ch != '\n') && (ch != '\r')) { ch = getc(fdesc); *(buf++) = ch; cnt++; if ((cnt > n) || (feof(fdesc))) { fprintf(stderr, "getline(): read error\n"); return 1; } } /* CR ueberspringen */ if (ch == '\r') ch = getc(fdesc); /* String terminieren */ *(--buf) = 0; return 0; }
Ein aufrufendes Codefragment dafuer koennte so aussehen:
char line[200];
...
if (getline(stdin, line, 199) != 0) { fprintf(stderr, "shit happens.\n"); exit(1); } else { /* irgendwas mit line anstellen */ ... }
Wenn Du damit Zahlen einliest, musst Du natuerlich noch atoi() oder aehnliche Hilfen bemuehen.
bye, Chris
HAllo Sebastian und der ganze Rest,
On Thursday 31 May 2001 13:13, Sebastian Roth wrote:
Hallo an alle (besonders C-Programmierer),
ich möchte gerne ein kleines Programm schreiben (um ein wenig C zu lernen). Jenes soll physikalische Gleichungen ausrechnen können. Allerdings gibt es ein Problem. Versucht mal das Weg-Zeit Gesetz bei einer gleichförmigen Bewegung. Wenn man dann die erste gesuchte Variable (s) eingeben soll, wars das, ende. Mit einem ddd habe ich einen exit-code 16 festgestellt. Wenn ich allerdings dir Variable gvar als integer initialisiere, klappt es! Warum denn das nun? Der Compiler meldet keinen Fehler auch beim compilieren mit gvar=char. Da bin ich doch schon etwas ratlos. Kennt sich einer aus?
So vom kurz-mal-reinschauen kann ich zumindest sagen: Der Code hat noch ein paar andere Probleme (z.B. eine recht interessante Rekursion).
Ich schick Dir mal im Laufe des Abends eine veränderte Version mit _viel_ Kommentaren privat zu (oder will das auch noch jemand anderes lesen? Dann geht's an die Liste.).
Konrad
Konrad Rosenbaum wrote:
HAllo Sebastian und der ganze Rest,
On Thursday 31 May 2001 13:13, Sebastian Roth wrote:
Hallo an alle (besonders C-Programmierer),
ich möchte gerne ein kleines Programm schreiben (um ein wenig C zu lernen). Jenes soll physikalische Gleichungen ausrechnen können. Allerdings gibt es ein Problem. Versucht mal das Weg-Zeit Gesetz bei einer gleichförmigen Bewegung. Wenn man dann die erste gesuchte Variable (s) eingeben soll, wars das, ende. Mit einem ddd habe ich einen exit-code 16 festgestellt. Wenn ich allerdings dir Variable gvar als integer initialisiere, klappt es! Warum denn das nun? Der Compiler meldet keinen Fehler auch beim compilieren mit gvar=char. Da bin ich doch schon etwas ratlos. Kennt sich einer aus?
So vom kurz-mal-reinschauen kann ich zumindest sagen: Der Code hat noch ein paar andere Probleme (z.B. eine recht interessante Rekursion).
Ich schick Dir mal im Laufe des Abends eine veränderte Version mit _viel_ Kommentaren privat zu (oder will das auch noch jemand anderes lesen? Dann geht's an die Liste.).
Konrad
Ja, Ich
Tobias
Hallo Konrad,
So vom kurz-mal-reinschauen kann ich zumindest sagen: Der Code hat noch ein paar andere Probleme (z.B. eine recht interessante Rekursion).
Rekursion? *ins Buch guck* ahaa, bei mir rufen sich Funktionen selber auf. Gut zu wissen möcht ich dazu sagen. :-) Aber duie Funktionen rufen sich doch gegenseitig auf. also ruft main() physicmain() auf und weiter das main() main() aufruft habe ich net gefunden oder ist eben dieses aufrufen einer Funktion durch eine andere Rekursion?
Konrad
Ich habe die main.c mal mit hierrein geschrieben. Als Anlage wäre jene ja genau so gross oder?
< main.c >
/*************************************************************************** main.c - description ------------------- begin : Mon Mai 28 18:35:13 CEST 2001 copyright : (C) 2001 by Sebastian Roth email : sebbi@claranet.de ***************************************************************************/
/*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/
/*************************************************************************** * Corrections and advices by Konrad Rosenbaum konrad.rosenbaum@gmx.net * ***************************************************************************/
/*KR: Hinweis 1: Fragen weisen nicht immer auf Fehler hin, sondern oft auch auf Stellen, über die man einfach nachdenken sollte.*/
S: Ok, das kapiere ich
/*KR: warum denn config.h? Willst Du das Teil später via autoconf anpassen?*/
#ifdef HAVE_CONFIG_H #include <config.h> #endif
S: Hmm, das liegt daran, das ich das Prog mit KDevelop (1.4 en) erstellt S: habe. Da war das alles mit drin schon am Anfang. Es sah meiner Meinung S: nach nicht schlimm aus, weil ja in der config.h auch die Versionsnummer S: mit drinnesteht.
#include <stdio.h> #include <stdlib.h>
/*KR: warum static?*/ //static int eingabe; /*KR: ^^^^^^ das macht sich lokal besser als global! Grund: die Funktionen können sich dann nicht gegenseitig die Variablen verhunzen.*/ //static float v,s,t; /*KR: das gilt auch für diese. Noch ein Grund: wenn sie lokal sind wird der Speicher nur solange belegt, wie nötig. Das mag bei Größenordnungen von 16Byte (wie hier) noch lächerlich erscheinen, aber bei einigen hundert Funktionen mit ihrerseits dutzenden von Variablen, die teilweise im kilobyte-Bereich liegen fällt das schon ins Gewicht.*/
S: Nun ja, es war so. Ich wollte nicht immer wieder in jeder Funktion, in S: der man etwas auswählen kann immer wieder von vorn eine Variable S: definieren, die das repräsentiert, was man eben ausgewählt hat (1,2,3...). S: Die Variable hatte ja nix weiters zu bedeuten, sollte nur den Eingabewert S : darstellen. Bei den anderen (s.v,t) muss ich dir wohl Recht geben.
/*KR: Funktionen, die schon genutzt aber erst später definiert (=implementiert=ausprogrammiert) werden, sollten schonmal vorher deklariert (=bekanntgegeben) werden. Man nennt das Forward-Deklaration. Und genau das tun übrigens auch die meisten Header-Dateien, wie stdio.h*/
S: Hmm, da habe ich in dem Buch gelesen, das man die Funktionen vor die main S: schreiben soll, da brauch man sie auch nicht deklarieren. Da werde ich S: aber nochmal genau gucken
void physicmain();/*KR: Unterschied: statt {...} kommt ein Semikolon ; */ void geschwzeitausw(); void wegzeitgleich(); void wegzeitngleich(); void main2();
S: Ok bis jetzt.
int main(int argc, char *argv[]) { /*KR: der viele printf und switch-Code muss wirklich nur 1x existieren, also lagern wir ihn in eine Funktion aus (main2).*/ /*KR: Hauptschleife (sowas haben alle Ereignisorientierten Programme - Du bist in bester Gesellschaft.*/ for(;;)main2();
S: Ja, das verstehe ich. Die zweite main hatte ich deswegen genommen, weil S: der User, wenn er zurück ins Hauptmenü will, nicht erst wieder den S: Begrüßungstext sehen soll. Das hat mich genervt, wusste aber nicht, wie S: man es sonst anders machen könnte.
/*KR: Du kommst jetzt in den Genuß meiner Endlosschleifen-sammlung. Besser lesbar wäre sicherlich die gekommen: while(1) main2(); noch besser: while(1){ main2(); } andererseits ginge auch das: do{ main2(); }while(1);
S: aha! :-)
Oder etwas esotherischer: int i; for(i=1;i==1;i=1){ main2(); } was i auf 1 initialisiert, es auf 1 prüft und es wieder und wieder auf 1 setzt. Etwas redundant, also lassen wir das zweite setzen weg. Und die {} auch. ;-) int i; for(i=1;i==1;) main2(); wozu eigentlich i auf 1 prüfen? das Ergebnis des Ausdrucks ist schließlich wieder 1! int i; for(i=1;1;) main2(); wozu noch i definieren und setzen? for(;1;) main2(); also eigentlich sollte er wissen, was ich will!! for(;;) main2();
S: Wenn du mich damit meinst. Ja das habe ich gerafft. :-)
quod erat demonstrantum!
S: Hat jemand ein Latein-Deutsch(Sächsisch) Wörterbuch. S: Vermutung q.e.d Was zu beweisen war? Aus Mathematik?
*/ }/*Ende der Hauptfunktion*/
void main2(void) { int eingabe;/*KR: was sach' ich? Ist besser! ;-) */
/*KR: netter Sprachmix. Entscheide Dich bitte für eine Sprache. ;-) */
S: Ja da hast du schon wieder Recht. Also werde ich mich für tatatataa S: Deutsch entscheiden :-)
printf("Willkommen bei Physics for Linux!\n\n"); printf("Hauptmenü:\n"); printf("Eine physikalische Gleichung errechnen --> 1\n"); printf("Hilfe --> 2\n"); printf("Beenden --> 3\n");
/*Jetzt muss der User auswählen*/
printf("Auswahl: "); scanf("%i",&eingabe); switch(eingabe) { /*KR: es ist i.A. übersichtlicher, wenn die öffnende { hinter oder unter switch steht und der ":" hinter dem case xy und das darunter eingerückt wird....*/
S: Ok stimmt schon wieder. Sieht evtl. auch besser aus. Damals hatte ich ein S : wenig experimentiert mit dem Code(aussehen)... weniger erfolgreich , wie S : sich fetstellen lässt
/*KR: WICHTIG: bitte bitte lies Dir die Torvalds-Styles durch: /usr/src/linux/Documentation/CodingStyle */
S: *gefunden* *zumlesenvorgemerkt*
case 1: printf("Jetzt geht zur Physik!"); physicmain(); break;
case 2: printf("Noch nicht fertig"); break;
case 3: printf("cu...\n"); /*KR: bitte auch bei Funktionsnamen für exakt eine (1) Sprache entscheiden - möglichst die selbe, wie in den Strings.*/
S: Ok, nochmal Deutsch
//beenden(); /*KR: beenden ist recht kurz, also kann man es auch gleich von hier aus ausführen*/
S: Ja ok, stimmt.
exit(EXIT_SUCCESS); break;
default: printf("Bitte eine Zahl auswählen!\n"); /*KR: ohne extra Aufruf kehrt es zu main() zurück das geht in eine Schleife, die wieder main2 aufruft ------ das spart uns eine Menge Stack! Der Aufruf von main2 an dieser Stelle bringt Dich in eine Endlosschleife, die jedesmal Speicher verbraucht, bis Du beendest.*/
S: *insbuchguckundbegriffstacksuch* Ist mit Stack ein Teil des Speichers S: gemeint? Sorry, Beschreibung kenn ich nicht
//main2(); break; }/*Switch beenden*/ }/*Ende der Neben-Hauptfunktion*/
/*KR: wird nicht mehr gebraucht, siehe main2 void beenden(void) // Zuletzt editiert am 29.05.01 { exit(EXIT_SUCCESS); }/*Programm beenden*/
S: Gebongt! Weg damit..
void physicmain(void) // Zuletzt editiert am 30.05.01 { int end=0;/*KR: unterscheidet zwischen Ende und Schleife*/ int eingabe; do{ /*Da Rekursion (=Selbstaufruf) immer Ressourcen verbraucht ist eine Iteration (=Schleife) immer vorzuziehen, wenn der Code dadurch nicht übermäßig schwieriger wird. Erst recht, wenn er (wie hier) übersichtlicher wird.*/
S: gut printf("\nMit welcher Gleichung, welchem Gesetz soll gerechnet werden?\n"); printf("Weg-Zeit Gesetz --> 1\n"); printf("Beenden... --> 2\n"); printf("Auswahl: "); scanf("%i",&eingabe); switch(eingabe) { case 1: geschwzeitausw(); break;
case 2: /*KR: die selbe Kritik, wie in main2: ein einfaches beenden bringt uns in die Schleife zurück... ...versuch Dir mal zu überlegen, was passiert, wenn...*/ //main2(); end=1; break; default: printf("\nBitte eine gültige Zahl eingeben!\n"); /*die Schleife geht weiter*/ break; } }while(!end);/*KR: mach weiter, bis der Nutzer beenden will*/ /*KR: Hinweis "!end" heißt "not end" also ergibt dieser Ausdruck 1 (wahr, true), wenn end gleich false (in C: 0); ist end irgendwas ungleich 0 (< oder > 0) dann ergibt der Ausdruck 0 (false)*/
S: Ja das lässt sich verstehen.
}
void geschwzeitausw(void) { int eingabe; printf("Gleichförmig --> 1\n"); printf("Ungleichförmig --> 2\n"); printf("Auswahl: "); scanf("%i",&eingabe); switch(eingabe) { case 1: wegzeitgleich(); break; case 2: wegzeitngleich(); break;
default: printf("Bitte gültige Zahl eingeben"); break; } }
void wegzeitgleich(void) { /*KR: Insider Tipp: Einzel-chars machen sich mit scanf echt blöd. Also: String einlesen (a'ka ganze Zeile) und dann nur den ersten Character nehmen. Noch so ein Tipp: ein Array kann auch wie ein Pointer behandelt werden (und umgekehrt) die Ausdrücke x[0] und *x sind also immer identisch.*/
S: Erklär mir bitte das x[0] nochmal! Mir Zeigern und Vektoren komme ich bis S : jetzt noch net so recht aus. das *x ein Zeiger auf x ist, verstehe ich.
/*KR: mehr als 100 wird er wohl net tippen, auch wenn diese Schätzung noch riskant ist...*/
S: Wieso sollte er? Jemand, der das Prog richtig nutzen will, wird schon S: nicht so einen Käse machen oder was denkst du?
char gvar[100]; float s,v,t;
printf("Grundgleichung: s=v*t\n"); printf("Bitte die gesuchte Variable eingeben:"); scanf("%s",gvar);/*KR: Strings ohne & übergeben, sie sind schon pointer*/
S: Ok (schonwieder)
switch(*gvar)/*KR: das erste Element nehmen*/ { case 's': printf("Gesucht ist > s <\n"); printf("Bitte einen Wert für v (in m/s) eingeben: "); scanf("%f",&v); printf("Bitte einen Wert für t (in s) eingeben: "); scanf("%f",&t); s = v * t; printf("Geschwindigkeit: %f m/s\n",v); printf("Zeit: %f s\n",t); printf("Weg: %f m\n",s); /*KR: wie "böse", wir kommen bei Rücksprung doch sowieso dorthin zurück!*/
S: *an alle* Sorry, wenn der Quelltext tödlich für einige zu lesen ist. Bin S: C-Anfänger
//physicmain(); break; default: /*KR: man sollte immer Fehler und Fehleingaben behandeln*/
S: Stimmt printf("Sorry, andere Werte als 's' sind noch nicht implementiert.\n"); break;/*KR: break sollte man auch immer machen, Fehler passieren zu schnell!*/
S: Ok. } }
void wegzeitngleich(void) { }
</ main.c >
Also, ich danke dir erstmal ganz toll. Mal sehen, ob alles so funktioniert wie es soll. *1Minute später* Es klappt. Ich werd verrückt. Juhuu! Es war sehr nett von dir, das du Zeit gefunden hast für dieses Problem, jetzt bin ich um einiges schlauer geworden in Sachen C-Programmierung. Noch eine Frage: Welche Kommentare kann man drinne lassen im Quelltext? Wird das Prog zu groß mit Kommentaren?
Ebenfalls auch ein großes Danke an Christian für seinen Tipp mit scanf. Jene Funktion werde ich auch noch ausprobieren.
Ok, das war´s erstmal von mir. Machts gut.
Bye, Sebastian
On Thursday 31 May 2001 21:47, Sebastian Roth wrote:
Hallo Konrad,
So vom kurz-mal-reinschauen kann ich zumindest sagen: Der Code hat noch ein paar andere Probleme (z.B. eine recht interessante Rekursion).
Rekursion? *ins Buch guck* ahaa, bei mir rufen sich Funktionen selber auf. Gut zu wissen möcht ich dazu sagen. :-) Aber duie Funktionen rufen sich doch gegenseitig auf. also ruft main() physicmain() auf und weiter das main() main() aufruft habe ich net gefunden oder ist eben dieses aufrufen einer Funktion durch eine andere Rekursion?
Es gibt auch indirekte Rekursion: a() ruft b() auf und b() ruft ihrerseits a() auf. Das kann man auch noch komplizierter über x Ebenen und y Verzweigungen machen, aber entscheidend ist, dass irgendwann eine Funktion aufgerufen wird, die schon läuft, was immer die Gefahr birgt, dass es in einer Endlosschleife endet und damit den Speicher aufbraucht.
Ich habe die main.c mal mit hierrein geschrieben. Als Anlage wäre jene ja genau so gross oder?
genau, aber dann könnte man sie einfacher speichern. ;-)
[cut]
/*KR: warum denn config.h? Willst Du das Teil später via autoconf anpassen?*/
#ifdef HAVE_CONFIG_H #include <config.h> #endif
S: Hmm, das liegt daran, das ich das Prog mit KDevelop (1.4 en) erstellt S: habe. Da war das alles mit drin schon am Anfang. Es sah meiner Meinung S: nach nicht schlimm aus, weil ja in der config.h auch die Versionsnummer S: mit drinnesteht.
Hmm, am besten Du benutzt KDevelop nur zum editieren der C-Dateien (falls das geht) und erstellst Dir alles andere selbst. Zu viel Automagie verhindert sehr effektiv das Begreifen.
[cut]
S: Nun ja, es war so. Ich wollte nicht immer wieder in jeder Funktion, in S: der man etwas auswählen kann immer wieder von vorn eine Variable S: definieren, die das repräsentiert, was man eben ausgewählt hat (1,2,3...). S: Die Variable hatte ja nix weiters zu bedeuten, sollte nur den Eingabewert S
: darstellen. Bei den anderen (s.v,t) muss ich dir wohl Recht geben.
Noch ein Grund: je weniger Du global definierst, umso weniger kann Dir durcheinander geraten und umso unwahrscheinlicher ist es, dass Du in zwei Dateien die selbe Variable definierst (wird so ab 10000 Zeilen akut).
Merke: bei jeder globalen Variable nachdenken, ob es wirklich nötig ist.
/*KR: Funktionen, die schon genutzt aber erst später definiert (=implementiert=ausprogrammiert) werden, sollten schonmal vorher deklariert (=bekanntgegeben) werden. Man nennt das Forward-Deklaration. Und genau das tun übrigens auch die meisten Header-Dateien, wie stdio.h*/
S: Hmm, da habe ich in dem Buch gelesen, das man die Funktionen vor die main S: schreiben soll, da brauch man sie auch nicht deklarieren. Da werde ich S: aber nochmal genau gucken
Beides ist möglich. Die einen bevorzugen main() ans Ende zu schreiben, die anderen schreiben main() an den Anfang. Geschmackssache.
int main(int argc, char *argv[]) { /*KR: der viele printf und switch-Code muss wirklich nur 1x existieren, also lagern wir ihn in eine Funktion aus (main2).*/ /*KR: Hauptschleife (sowas haben alle Ereignisorientierten Programme - Du bist in bester Gesellschaft.*/ for(;;)main2();
S: Ja, das verstehe ich. Die zweite main hatte ich deswegen genommen, weil S: der User, wenn er zurück ins Hauptmenü will, nicht erst wieder den S: Begrüßungstext sehen soll. Das hat mich genervt, wusste aber nicht, wie S: man es sonst anders machen könnte.
Einfach: gib den Begrüßungstext _vor_ der Hauptschleife aus und lass ihn in main2() weg.
also eigentlich sollte er wissen, was ich will!! for(;;) main2();
S: Wenn du mich damit meinst. Ja das habe ich gerafft. :-)
Ich meinte den Rechner ;-) C hat da ein Gedanken-Lese-Feature (zumindest, wenn man den richtigen Gedanken hat) ;-)
quod erat demonstrantum!
S: Hat jemand ein Latein-Deutsch(Sächsisch) Wörterbuch. S: Vermutung q.e.d Was zu beweisen war? Aus Mathematik?
Genau. Und da ausserden gilt "de gustibus non est disputantum" (über Geschmack läßt sich (nicht) streiten) hätte ich eigentlich auch "w.z.b.w." schreiben können ;-)
[cut]
/*KR: WICHTIG: bitte bitte lies Dir die Torvalds-Styles durch: /usr/src/linux/Documentation/CodingStyle */
S: *gefunden* *zumlesenvorgemerkt*
Bitte bald. ;-)
[cut]
printf("Bitte eine Zahl auswählen!\n"); /*KR: ohne extra Aufruf kehrt es zu main() zurück das geht in eine Schleife, die wieder main2
aufruft ------ das spart uns eine Menge Stack! Der Aufruf von main2 an dieser Stelle bringt Dich in eine Endlosschleife, die jedesmal Speicher verbraucht, bis Du beendest.*/
S: *insbuchguckundbegriffstacksuch* Ist mit Stack ein Teil des Speichers S: gemeint? Sorry, Beschreibung kenn ich nicht
Stack, def.: [Vorsicht: langsam und mehrfach lesen, am besten ausprobieren]
Stapelspeicher. Der Stack funktioniert mit Werten genauso, wie ein Stapel Papier mit Blättern: Du kannst oben drauflegen und auch oben wieder wegnehmen (weil alles andere durcheinander bringt ist es bei Computern einfach nicht erlaubt). Wenn Du eine Funktion aufrufst werden die Parameter und die Rücksprungadresse (die Position wohin es zurückgeht, wenn Du die Funktion verläßt) auf den Stack gelegt, dann wird die Funktion aufgerufen, sie legt ihrerseits alle lokalen Variablen drauf und arbeitet damit. Wird von dort eine weitere Funktion aufgerufen wird wieder all das draufgelegt (deswegen verschwenden Rekursionen Speicher). Verläßt Du die Funktion werden erst die lokalen Variablen weggeworfen, der Return-Wert (siehe Stichwort return) draufgelegt und zu der Rücksprungadresse gesprungen, ab da übernimmt wieder die übergeordnete Funktion, wirft die Parameter weg und übernimmt den Rückgabewert (oder verwirft ihn, je nach dem, ob er gebraucht wird).
/*Da Rekursion (=Selbstaufruf) immer Ressourcen verbraucht ist eine Iteration (=Schleife) immer vorzuziehen, wenn der Code dadurch nicht übermäßig schwieriger wird. Erst recht, wenn er (wie hier) übersichtlicher wird.*/
S: gut
[cut]
void wegzeitgleich(void) { /*KR: Insider Tipp: Einzel-chars machen sich mit scanf echt blöd. Also: String einlesen (a'ka ganze Zeile) und dann nur den ersten Character nehmen. Noch so ein Tipp: ein Array kann auch wie ein Pointer behandelt werden (und umgekehrt) die Ausdrücke x[0] und *x sind also immer identisch.*/
S: Erklär mir bitte das x[0] nochmal! Mir Zeigern und Vektoren komme ich bis S
: jetzt noch net so recht aus. das *x ein Zeiger auf x ist, verstehe ich.
Ok, definieren wir mal ein Array:
char bsp[10];
bsp ist 10 char's gross. Ganz nebenbei bekommst Du 11 offensichtliche und legal anwendbare Ausdrücke:
bsp -> ein Zeiger auf das erste Element bsp[0] -> das erste Element .... bsp[9] -> das 10. Element
---------- Notiz am Rande: bsp[99] wird nicht vom Compiler angemeckert, führt meistens aber zu sehr seltsamem Verhalten, da es eine Speicherstelle weit ausserhalb von bsp bezeichnet (90 Byte weiter oben). ----------
jetzt einen Pointer: char *ptr;
Wir wissen, dass bsp ein Pointer auf ein char-Array ist, also ist das hier legal:
ptr=bsp;
damit zeigt ptr ebenso wie bsp auf das erste Element des Arrays bsp. Im Gegensatz zu bsp kannst Du ptr aber auch woanders hinzeigen lassen.
Um den Speicherinhalt der Variable zu nutzen, die hinter einem Pointer steht benutzt man *. Also:
*ptr='a';
weist dem Speicher hinter ptr, also dem ersten Element von bsp, ein 'a' zu. Sprich bsp[0]=='a'.
Da ptr und bsp sowieso auf den selben Speicher zeigen können wir sie also auch gleich behandeln:
*bsp ist identisch zu bsp[0], also sind diese Anweisungen:
*bsp='b'; bsp[0]='b';
absolut identisch. Das selbe gilt für ptr:
*ptr='c'; ptr[0]='c';
Das geht noch weiter:
bsp[4]='d'; ptr[4]='d';
tun (bei uns, da ptr==bsp) exakt das selbe.
Zeiger sind letztendlich nichts weiter als Zahlen, die irgendeine Stelle im Speicher bezeichnen, also kann man in C auch damit rechnen.
*(bsp+2) ist identisch zu bsp[2] und bsp+2 ist identisch zu &(bsp[2]) (& ergibt die Adresse der Variable dahinter)
Da man Pointer (im Gegensatz zu Array-Namen) auch verändern kann, kann man ihnen auch das Ergebnis dieser Pointer-Arithmetik zuweisen:
ptr=bsp+2;
Ab jetzt ist *ptr identisch zu bsp[2] ....
Oder man zählt sie schrittweise durch die Elemente durch: ptr++ - läßt ptr exakt ein Element weiter hoch zeigen (bei uns jetzt bsp[3]).
usw.....
Erfahrungsgemäß verbraucht man viele Tage mit Programmabstürzen, bis sich einem diese Logik vollkommen erschließt, also verzweifel nicht gleich ;-)
/*KR: mehr als 100 wird er wohl net tippen, auch wenn diese Schätzung noch riskant ist...*/
S: Wieso sollte er? Jemand, der das Prog richtig nutzen will, wird schon S: nicht so einen Käse machen oder was denkst du?
Normal würde er wohl net. Aber wichtig ist das, wenn Dein Programm irgendwas im System macht (auch wenn nur die Chance besteht, dass es von einem Skript aus aufgerufen wird, dass für einen Nutzer etwas mit root-Rechten macht), weil dann kann man mit gezielten Überläufen den Rücksprungpointer überschreiben und eigenen (bösen) Code ausführen, der wer weis was mit Deinem System macht.
/*KR: wie "böse", wir kommen bei Rücksprung doch sowieso dorthin zurück!*/
S: *an alle* Sorry, wenn der Quelltext tödlich für einige zu lesen ist. Bin S: C-Anfänger
Deswegen die Gänsefüßchen "".... ;-) Ich war auch mal Anfänger.
Also, ich danke dir erstmal ganz toll. Mal sehen, ob alles so funktioniert wie es soll. *1Minute später* Es klappt. Ich werd verrückt. Juhuu! Es war sehr nett von dir, das du Zeit gefunden hast für dieses Problem, jetzt bin ich um einiges schlauer geworden in Sachen C-Programmierung. Noch eine Frage: Welche Kommentare kann man drinne lassen im Quelltext? Wird das Prog zu groß mit Kommentaren?
Kommentare werden nicht einkompiliert. Sie verschwinden schon nach dem ersten von 4 Schritten (Präprozessor, Compiler, Assembler, Linker).
Konrad (der jetzt aber wirklich ins Bett geht)
Hallo Konrad,
Es gibt auch indirekte Rekursion: a() ruft b() auf und b() ruft ihrerseits a() auf. Das kann man auch noch komplizierter über x Ebenen und y Verzweigungen machen, aber entscheidend ist, dass irgendwann eine Funktion aufgerufen wird, die schon läuft, was immer die Gefahr birgt, dass es in einer Endlosschleife endet und damit den Speicher aufbraucht.
kapiert
Hmm, am besten Du benutzt KDevelop nur zum editieren der C-Dateien (falls das geht) und erstellst Dir alles andere selbst. Zu viel Automagie verhindert sehr effektiv das Begreifen.
Wie schon als Antwort auf die Mail von Ulf gesagt: ich werde mich bessern
Noch ein Grund: je weniger Du global definierst, umso weniger kann Dir durcheinander geraten und umso unwahrscheinlicher ist es, dass Du in zwei Dateien die selbe Variable definierst (wird so ab 10000 Zeilen akut).
Merke: bei jeder globalen Variable nachdenken, ob es wirklich nötig ist.
ok, bin überzeugt.
Einfach: gib den Begrüßungstext _vor_ der Hauptschleife aus und lass ihn in main2() weg.
Da hatte ich wieder nich weiter gedacht. Na klar! Manchmal liegt das einfachste am nächsten...
Ich meinte den Rechner ;-)
oh!
C hat da ein Gedanken-Lese-Feature (zumindest, wenn man den richtigen Gedanken hat) ;-)
*leichtgrins*
/*KR: WICHTIG: bitte bitte lies Dir die Torvalds-Styles durch: /usr/src/linux/Documentation/CodingStyle */
S: *gefunden* *zumlesenvorgemerkt*
Bitte bald. ;-)
habe es augedruckt. Liegt jetzt neben Monitor und ist meine neue Abendlektüre :-)
Stack, def.: [Vorsicht: langsam und mehrfach lesen, am besten ausprobieren]
Stapelspeicher. Der Stack funktioniert mit Werten genauso, wie ein Stapel Papier mit Blättern: Du kannst oben drauflegen und auch oben wieder wegnehmen (weil alles andere durcheinander bringt ist es bei Computern einfach nicht erlaubt). Wenn Du eine Funktion aufrufst werden die Parameter und die Rücksprungadresse (die Position wohin es zurückgeht, wenn Du die Funktion verläßt) auf den Stack gelegt, dann wird die Funktion aufgerufen, sie legt ihrerseits alle lokalen Variablen drauf und arbeitet damit. Wird von dort eine weitere Funktion aufgerufen wird wieder all das draufgelegt (deswegen verschwenden Rekursionen Speicher). Verläßt Du die Funktion werden erst die lokalen Variablen weggeworfen, der Return-Wert (siehe Stichwort return) draufgelegt und zu der Rücksprungadresse gesprungen, ab da übernimmt wieder die übergeordnete Funktion, wirft die Parameter weg und übernimmt den Rückgabewert (oder verwirft ihn, je nach dem, ob er gebraucht wird). /*Da Rekursion (=Selbstaufruf) immer Ressourcen verbraucht ist eine Iteration (=Schleife) immer vorzuziehen, wenn der Code dadurch nicht übermäßig schwieriger wird. Erst recht, wenn er (wie hier) übersichtlicher wird.*/
Ich hoffe, das habe ich kapiert.
S: Erklär mir bitte das x[0] nochmal! Mir Zeigern und Vektoren komme ich S: bis jetzt noch net so recht aus. das *x ein Zeiger auf x ist, verstehe S: ich.
Ok, definieren wir mal ein Array:
char bsp[10];
bsp ist 10 char's gross. Ganz nebenbei bekommst Du 11 offensichtliche und legal anwendbare Ausdrücke:
bsp -> ein Zeiger auf das erste Element bsp[0] -> das erste Element .... bsp[9] -> das 10. Element
Ok, das finde ich noch recht leicht zu verstehen.
Notiz am Rande: bsp[99] wird nicht vom Compiler angemeckert, führt meistens aber zu sehr seltsamem Verhalten, da es eine Speicherstelle weit ausserhalb von bsp bezeichnet (90 Byte weiter oben).
jetzt einen Pointer: char *ptr;
Wir wissen, dass bsp ein Pointer auf ein char-Array ist, also ist das hier legal:
ptr=bsp;
damit zeigt ptr ebenso wie bsp auf das erste Element des Arrays bsp. Im Gegensatz zu bsp kannst Du ptr aber auch woanders hinzeigen lassen.
Um den Speicherinhalt der Variable zu nutzen, die hinter einem Pointer steht benutzt man *. Also:
*ptr='a';
weist dem Speicher hinter ptr, also dem ersten Element von bsp, ein 'a' zu. Sprich bsp[0]=='a'.
Da ptr und bsp sowieso auf den selben Speicher zeigen können wir sie also auch gleich behandeln:
*bsp ist identisch zu bsp[0], also sind diese Anweisungen:
*bsp='b'; bsp[0]='b';
absolut identisch. Das selbe gilt für ptr:
*ptr='c'; ptr[0]='c';
Das geht noch weiter:
bsp[4]='d'; ptr[4]='d';
tun (bei uns, da ptr==bsp) exakt das selbe.
Zeiger sind letztendlich nichts weiter als Zahlen, die irgendeine Stelle im Speicher bezeichnen, also kann man in C auch damit rechnen.
*(bsp+2) ist identisch zu bsp[2] und bsp+2 ist identisch zu &(bsp[2]) (& ergibt die Adresse der Variable dahinter)
Da man Pointer (im Gegensatz zu Array-Namen) auch verändern kann, kann man ihnen auch das Ergebnis dieser Pointer-Arithmetik zuweisen:
ptr=bsp+2;
Ab jetzt ist *ptr identisch zu bsp[2] ....
Oder man zählt sie schrittweise durch die Elemente durch: ptr++ - läßt ptr exakt ein Element weiter hoch zeigen (bei uns jetzt bsp[3]).
usw.....
Erfahrungsgemäß verbraucht man viele Tage mit Programmabstürzen, bis sich einem diese Logik vollkommen erschließt, also verzweifel nicht gleich ;-)
Ok, ich werde es allerdings trotzdem versuchen zu verstehen. Wir haben ja jetzt Pfingsten.
S: Wieso sollte er? Jemand, der das Prog richtig nutzen will, wird schon S: nicht so einen Käse machen oder was denkst du?
Normal würde er wohl net. Aber wichtig ist das, wenn Dein Programm irgendwas im System macht (auch wenn nur die Chance besteht, dass es von einem Skript aus aufgerufen wird, dass für einen Nutzer etwas mit root-Rechten macht), weil dann kann man mit gezielten Überläufen den Rücksprungpointer überschreiben und eigenen (bösen) Code ausführen, der wer weis was mit Deinem System macht.
Kann denn ein C-Programm am System etwas verändern? Im Buch steht etwas über die Arbeit mit Textdateien, aber nix über Systembefehle oder so... Das diese Sachen eine Sicherheitslücke darstellen, finde ich erschreckend.
S: *an alle* Sorry, wenn der Quelltext tödlich für einige zu lesen ist. Bin S: C-Anfänger
Deswegen die Gänsefüßchen "".... ;-) Ich war auch mal Anfänger.
Es ist trotzdem nett (von allen), wenn mir hier geholfen wird.
Kommentare werden nicht einkompiliert. Sie verschwinden schon nach dem ersten von 4 Schritten (Präprozessor, Compiler, Assembler, Linker).
Wie schon im Antwort auf Mail von Ulf: Ich lasse ie Kommentare also drin.
Konrad (der jetzt aber wirklich ins Bett geht)
Bye, Sebastian, der jetzt noch etwas rumprobiert.
On Friday 01 June 2001 15:52, Sebastian Roth wrote:
Hmm, am besten Du benutzt KDevelop nur zum editieren der C-Dateien (falls das geht) und erstellst Dir alles andere selbst. Zu viel Automagie verhindert sehr effektiv das Begreifen.
Wie schon als Antwort auf die Mail von Ulf gesagt: ich werde mich bessern
Tipps: kwrite, xwpe, emacs und vim können Syntax-Highlighting, sind für Programmierer also höchstgradig geeignet. Trotz Kopfschütteln anderer Profis benutze ich immernoch xwpe - ich bin halt B*rl*nd-versaut... ;-)
[text über pointer]
Ok, ich werde es allerdings trotzdem versuchen zu verstehen. Wir haben ja jetzt Pfingsten.
lass Dir ruhig Zeit damit.... Du wirst die Zeit brauchen ;-)
S: Wieso sollte er? Jemand, der das Prog richtig nutzen will, wird schon S: nicht so einen Käse machen oder was denkst du?
Normal würde er wohl net. Aber wichtig ist das, wenn Dein Programm irgendwas im System macht (auch wenn nur die Chance besteht, dass es von einem Skript aus aufgerufen wird, dass für einen Nutzer etwas mit root-Rechten macht), weil dann kann man mit gezielten Überläufen den Rücksprungpointer überschreiben und eigenen (bösen) Code ausführen, der wer weis was mit Deinem System macht.
Kann denn ein C-Programm am System etwas verändern? Im Buch steht etwas über die Arbeit mit Textdateien, aber nix über Systembefehle oder so... Das diese Sachen eine Sicherheitslücke darstellen, finde ich erschreckend.
Wenn es mit den richtigen Rechten läuft kann ein C Programm grundsätzlich alles. z.B. alle System-Calls (die Schnittstellen zum Kern) haben C-Interfaces, alle anderen Sprachen setzen dann erst auf C auf.
Wenn Dein Programm als root läuft (aus welchem seltsamen Grund auch immer) kann ein Angreifer alles machen, was root auch kann - er braucht sich nur eine Shell zu starten....
Konrad
On Thu, May 31, 2001 at 09:47:34PM +0200, Sebastian Roth wrote:
Hallo Konrad,
So vom kurz-mal-reinschauen kann ich zumindest sagen: Der Code hat noch ein paar andere Probleme (z.B. eine recht interessante Rekursion).
Rekursion? *ins Buch guck* ahaa, bei mir rufen sich Funktionen selber auf. Gut zu wissen möcht ich dazu sagen. :-) Aber duie Funktionen rufen sich doch gegenseitig auf. also ruft main() physicmain() auf und weiter das main() main() aufruft habe ich net gefunden oder ist eben dieses aufrufen einer Funktion durch eine andere Rekursion?
Verallgemeinere das doch mal. nehmen wir an, wir haben n Funktionen... :) Im Ernst, stell dir vor, du hast z.B. 3 Funktionen a(), b() und c(). a ruft b auf, b ruft c auf und c wieder a. Damit sprengst du nach kurzer Zeit jeden Speicher. So etwas passiert jetzt dauernd in deinem Programm.
Ich habe die main.c mal mit hierrein geschrieben. Als Anlage wäre jene ja genau so gross oder?
Abgesehen von ein paar Headerbits, ja.
[schnipp]
/*KR: Hinweis 1: Fragen weisen nicht immer auf Fehler hin, sondern oft auch auf Stellen, über die man einfach nachdenken sollte.*/
S: Ok, das kapiere ich
/*KR: warum denn config.h? Willst Du das Teil später via autoconf anpassen?*/
#ifdef HAVE_CONFIG_H #include <config.h> #endif
S: Hmm, das liegt daran, das ich das Prog mit KDevelop (1.4 en) erstellt S: habe. Da war das alles mit drin schon am Anfang. Es sah meiner Meinung S: nach nicht schlimm aus, weil ja in der config.h auch die Versionsnummer S: mit drinnesteht.
Iiiihhhh, eine IDE :). Versuch's mal auf die ganz grausame Art mit Editor und Kommandozeile. Ist am Anfag zwar abschreckender, aber wenn du das kannst, weißt du wenigstens, was deine IDE macht und bist nicht so ein <F9>-Drücker :).
#include <stdio.h> #include <stdlib.h>
/*KR: warum static?*/ //static int eingabe; /*KR: ^^^^^^ das macht sich lokal besser als global! Grund: die Funktionen können sich dann nicht gegenseitig die Variablen verhunzen.*/ //static float v,s,t; /*KR: das gilt auch für diese. Noch ein Grund: wenn sie lokal sind wird der Speicher nur solange belegt, wie nötig. Das mag bei Größenordnungen von 16Byte (wie hier) noch lächerlich erscheinen, aber bei einigen hundert Funktionen mit ihrerseits dutzenden von Variablen, die teilweise im kilobyte-Bereich liegen fällt das schon ins Gewicht.*/
S: Nun ja, es war so. Ich wollte nicht immer wieder in jeder Funktion, in S: der man etwas auswählen kann immer wieder von vorn eine Variable S: definieren, die das repräsentiert, was man eben ausgewählt hat (1,2,3...). S: Die Variable hatte ja nix weiters zu bedeuten, sollte nur den Eingabewert S : darstellen. Bei den anderen (s.v,t) muss ich dir wohl Recht geben.
Da das ein hingekleckstes Trivialprogramm ist, mag das mit den globalen Variablen noch gehen, aber gwöhn dir das für halbwegs ernsthaftes Programmieren unbedingt ab. Es kommen sonst noch andere probleme ala Überschatten von variablen dazu.
/*KR: Funktionen, die schon genutzt aber erst später definiert (=implementiert=ausprogrammiert) werden, sollten schonmal vorher deklariert (=bekanntgegeben) werden. Man nennt das Forward-Deklaration. Und genau das tun übrigens auch die meisten Header-Dateien, wie stdio.h*/
S: Hmm, da habe ich in dem Buch gelesen, das man die Funktionen vor die main S: schreiben soll, da brauch man sie auch nicht deklarieren. Da werde ich S: aber nochmal genau gucken
Sobald du Headerfiles nutzt, wird das allerdings sowieso überflüssig.
int main(int argc, char *argv[]) { /*KR: der viele printf und switch-Code muss wirklich nur 1x existieren, also lagern wir ihn in eine Funktion aus (main2).*/ /*KR: Hauptschleife (sowas haben alle Ereignisorientierten Programme - Du bist in bester Gesellschaft.*/ for(;;)main2();
S: Ja, das verstehe ich. Die zweite main hatte ich deswegen genommen, weil S: der User, wenn er zurück ins Hauptmenü will, nicht erst wieder den S: Begrüßungstext sehen soll. Das hat mich genervt, wusste aber nicht, wie S: man es sonst anders machen könnte.
Dafür nimmst du eine Variable, die nur beim ertsen Durchlauf der Schleife einen bestimmten Wert hat.
int myflag = 1; for (...) { ... if (myflag) zeigeBegruessungstext(); myflag=0; ... }
[schnipp Endlosschleifen]
S: Wenn du mich damit meinst. Ja das habe ich gerafft. :-)
quod erat demonstrantum!
S: Hat jemand ein Latein-Deutsch(Sächsisch) Wörterbuch. S: Vermutung q.e.d Was zu beweisen war? Aus Mathematik?
richtig.
*/
}/*Ende der Hauptfunktion*/
void main2(void) { int eingabe;/*KR: was sach' ich? Ist besser! ;-) */
/*KR: netter Sprachmix. Entscheide Dich bitte für eine Sprache. ;-) */
S: Ja da hast du schon wieder Recht. Also werde ich mich für tatatataa S: Deutsch entscheiden :-)
Du darfst aber keine Umlaute benutzen, also nimm lieber Englisch :). Das hat den Vorteil, daß du später keine Probleme beim Umstellen bekommst, wenn du mal mit nicht-Deutschen programmierst (geht ziemlich schnell :))
[schnipp]
S: *insbuchguckundbegriffstacksuch* Ist mit Stack ein Teil des Speichers S: gemeint? Sorry, Beschreibung kenn ich nicht
Stack ist ein LIFO (Last In First Out) Speicher. Der Prozessor kann Werte mittels push und pop auf den Stack legen/vom Stack einlesen. Wenn du eine Funktion aufrufst, wird immer ein bißchen Stack verbraucht, nämlich für Parameter, Rücksprungaddresse, lokale Variablen etc. Ein sinnloser Funktionsaufruf verbraucht also immer Speicher. Mit gedankenlos eingesetzten rekursiven Aufrufen kannst du auch ganz hinterhältig dein System abschießen. Es wird immer mehr Stack belegt, bis einfach kein Speicher mehr da ist. Pech für die anderen Proggies.
[schnipp]
void wegzeitgleich(void) { /*KR: Insider Tipp: Einzel-chars machen sich mit scanf echt blöd. Also: String einlesen (a'ka ganze Zeile) und dann nur den ersten Character nehmen. Noch so ein Tipp: ein Array kann auch wie ein Pointer behandelt werden (und umgekehrt) die Ausdrücke x[0] und *x sind also immer identisch.*/
S: Erklär mir bitte das x[0] nochmal! Mir Zeigern und Vektoren komme ich bis S : jetzt noch net so recht aus. das *x ein Zeiger auf x ist, verstehe ich.
Also (irgendwann hatte ich das schonmal jemandem erklärt), wenn du nicht einen einzelnen char in deinen Speicher packst, sondern einen Array, so liegen die einzelnen Elemente linear hintereinander im Speicher. Wenn also z.B. vector ein Array von float ist, so liegen die einzelnen Elemente (d.h. die einzelnen floats) hintereinander im Speicher. Auf sie kann man dann mit dem []-Operator zugreifen. Liegt also z.B. vector[0] auf der Adresse 5, so ist vector[3] auf Addresse 8 zu finden. Eine Besonderheit von C ist nun, daß der Arrayname immer ein Zeiger auf den Datentyp darstellt. Wieder das Vectorbeispiel: Nach einer Deklaration, z.B. "float vector[10]", ist "vector" (ohne eine eckige Klammer) immer der konstante Zeiger auf das erste Element des Arrays, d.h. die folgenden Codezeilen sind äquivalent:
vector[0]=5.7; *vector=5.7;
vector[1]=1.7; *(vector+1)=1.7; //ungetestet, aber es müßte klappen
Laß von Speicherarithmetik aber lieber erst mal die Finger, bis du damit vertraut bist.
/*KR: mehr als 100 wird er wohl net tippen, auch wenn diese Schätzung noch riskant ist...*/
S: Wieso sollte er? Jemand, der das Prog richtig nutzen will, wird schon S: nicht so einen Käse machen oder was denkst du?
Jetzt mal was längeres zur Erklärung. Um auf den Stack zurückzukommen, ist bei i386-kompatiblem CPUs der Stack immer so ausgelegt, daß er VON OBEN NACH UNTEN gefüllt wird, d.h. wenn ich ein "push ax; push bx; push cx" (ax, bx, cx sind Register, für den Anfang also beliebige Werte), so sieht der Stack so aus (links sind niedrigere Speicherbereiche)
cx bx ax
Wenn eine Funktion aufgebaut wird, wird ein sogenannter stack frame aufgebaut, d.h. der Stack wird so gefüllt, daß die Funktion ein abgeschlossenes Stückchen Stack bekommt, aus dem sie nie herausmuß, d.h. angenommen a() ruft b() auf, sieht der Stack in etwa aus:
stack frame von b() | stack frame von a()
Alles was b jemals braucht, befindet sich in seinem Stackbereich. Ein einzelner stack frame sieht jetzt rudimentär etwa so aus
lokale Variablen| Sonstiges | Rücksprungadresse
Irgendwo in deinen lokalen Variablen steht jetzt der Puffer gvar. Was macht jetzt scanf? Es schreibt solange in gvar, bis ein newline kommt. Läuft wunderbar, solange der Benutzer bei <100 Zeichen bleibt. Was passiert aber jetzt, wenn der Benutzer mehr als 100 Zeichen eingibt? Dann wird der gesamte Stackbereich hinter gvar überschrieben (für den Prozessor ist dein Array lediglich ein großer Byteblock, also ist ihm das egal wenn er groß den Stack übermalt). Irgendwo steht dann die Rücksprungadresse. Jetzt stell dir einen ganz bösen Benutzer vor. Der nimmt dein programm im Debugger auseinander, stellt fest, wo die Rücksprungadresse liegt, überschreibt dann bei der Eingabe den stack frame so, daß die Funktion auf jeden Fall zurückkehrt, schreibt hinter die Rücksprungaddresse ein bißchen eigenen Code und überpinselt die Rücksprungaddresse, so daß sie auf seinen eigenen Code im Stack zeigt. Das Ganze nennt man dann buffer overflow oder auch stack smashing. Und so werden dann Server gehackt :). Solange dein Programm keine besonderen Privilegien hat, ist das nur halb so wild, aber wenn es sich um ein root-Proggie handelt...
[schnipp]
Noch eine Frage: Welche Kommentare kann man drinne lassen im Quelltext? Wird das Prog zu groß mit Kommentaren?
Vorm Compiler läuft noch der Präcompiler (gpp). Seine Aufgabe ist es u.a. Kommentare zu entfernen und Makros zu expandieren. Dann erst wird die Datei an den Compiler weitergereicht.
Ok, das war´s erstmal von mir. Machts gut.
Bye, Sebastian
cu, Ulf
Hi (nochmal) Ulf,
Verallgemeinere das doch mal. nehmen wir an, wir haben n Funktionen... :) Im Ernst, stell dir vor, du hast z.B. 3 Funktionen a(), b() und c(). a ruft b auf, b ruft c auf und c wieder a. Damit sprengst du nach kurzer Zeit jeden Speicher. So etwas passiert jetzt dauernd in deinem Programm.
*schluck* So kann also aus einem kleinen Programm ein Speicherfresser werden... wie fürchterlich.
Iiiihhhh, eine IDE :). Versuch's mal auf die ganz grausame Art mit Editor und Kommandozeile. Ist am Anfag zwar abschreckender, aber wenn du das kannst, weißt du wenigstens, was deine IDE macht und bist nicht so ein <F9>-Drücker :).
Ok, ich steige um. KDevelop war ja sowieso nur in Englisch (was ich nicht verstehe!)
Da das ein hingekleckstes Trivialprogramm ist, mag das mit den globalen Variablen noch gehen, aber gwöhn dir das für halbwegs ernsthaftes Programmieren unbedingt ab. Es kommen sonst noch andere probleme ala Überschatten von variablen dazu.
ok
S: Ja, das verstehe ich. Die zweite main hatte ich deswegen genommen, weil S: der User, wenn er zurück ins Hauptmenü will, nicht erst wieder den S: Begrüßungstext sehen soll. Das hat mich genervt, wusste aber nicht, wie S: man es sonst anders machen könnte.
Dafür nimmst du eine Variable, die nur beim ertsen Durchlauf der Schleife einen bestimmten Wert hat.
int myflag = 1; for (...) { ... if (myflag) zeigeBegruessungstext(); myflag=0; ... }
ok auch hier. Ich wusste garnicht, was es für nette Anwendungen gibt für Schleifen.
S: Ja da hast du schon wieder Recht. Also werde ich mich für tatatataa S: Deutsch entscheiden :-)
Du darfst aber keine Umlaute benutzen, also nimm lieber Englisch :). Das hat den Vorteil, daß du später keine Probleme beim Umstellen bekommst, wenn du mal mit nicht-Deutschen programmierst (geht ziemlich schnell :))
Ok, aber da kann ich nicht für grammatische Richtigkeit garantieren :-))
S: *insbuchguckundbegriffstacksuch* Ist mit Stack ein Teil des Speichers S: gemeint? Sorry, Beschreibung kenn ich nicht
Stack ist ein LIFO (Last In First Out) Speicher. Der Prozessor kann Werte mittels push und pop auf den Stack legen/vom Stack einlesen. Wenn du eine Funktion aufrufst, wird immer ein bißchen Stack verbraucht, nämlich für Parameter, Rücksprungaddresse, lokale Variablen etc. Ein sinnloser Funktionsaufruf verbraucht also immer Speicher. Mit gedankenlos eingesetzten rekursiven Aufrufen kannst du auch ganz hinterhältig dein System abschießen. Es wird immer mehr Stack belegt, bis einfach kein Speicher mehr da ist. Pech für die anderen Proggies.
Ok, das habe ich verstanden. BTW: ich sollte das C-Buch dringend weiterlesen (Kapitel 30-Rekursion)
Also (irgendwann hatte ich das schonmal jemandem erklärt), wenn du nicht einen einzelnen char in deinen Speicher packst, sondern einen Array, so liegen die einzelnen Elemente linear hintereinander im Speicher. Wenn also z.B. vector ein Array von float ist, so liegen die einzelnen Elemente (d.h. die einzelnen floats) hintereinander im Speicher. Auf sie kann man dann mit dem []-Operator zugreifen. Liegt also z.B. vector[0] auf der Adresse 5, so ist vector[3] auf Addresse 8 zu finden. Eine Besonderheit von C ist nun, daß der Arrayname immer ein Zeiger auf den Datentyp darstellt. Wieder das Vectorbeispiel: Nach einer Deklaration, z.B. "float vector[10]", ist "vector" (ohne eine eckige Klammer) immer der konstante Zeiger auf das erste Element des Arrays, d.h. die folgenden Codezeilen sind äquivalent:
vector[0]=5.7; *vector=5.7;
vector[1]=1.7; *(vector+1)=1.7; //ungetestet, aber es müßte klappen
Laß von Speicherarithmetik aber lieber erst mal die Finger, bis du damit vertraut bist.
Das werde ich lieber tun. Das ist doch schon ein wenig kompliziert.
Jetzt mal was längeres zur Erklärung. Um auf den Stack zurückzukommen, ist bei i386-kompatiblem CPUs der Stack immer so ausgelegt, daß er VON OBEN NACH UNTEN gefüllt wird, d.h. wenn ich ein "push ax; push bx; push cx" (ax, bx, cx sind Register, für den Anfang also beliebige Werte), so sieht der Stack so aus (links sind niedrigere Speicherbereiche)
cx bx ax
Wenn eine Funktion aufgebaut wird, wird ein sogenannter stack frame aufgebaut, d.h. der Stack wird so gefüllt, daß die Funktion ein abgeschlossenes Stückchen Stack bekommt, aus dem sie nie herausmuß, d.h. angenommen a() ruft b() auf, sieht der Stack in etwa aus:
stack frame von b() | stack frame von a()
Alles was b jemals braucht, befindet sich in seinem Stackbereich. Ein einzelner stack frame sieht jetzt rudimentär etwa so aus
lokale Variablen| Sonstiges | Rücksprungadresse
Irgendwo in deinen lokalen Variablen steht jetzt der Puffer gvar. Was macht jetzt scanf? Es schreibt solange in gvar, bis ein newline kommt. Läuft wunderbar, solange der Benutzer bei <100 Zeichen bleibt. Was passiert aber jetzt, wenn der Benutzer mehr als 100 Zeichen eingibt? Dann wird der gesamte Stackbereich hinter gvar überschrieben (für den Prozessor ist dein Array lediglich ein großer Byteblock, also ist ihm das egal wenn er groß den Stack übermalt). Irgendwo steht dann die Rücksprungadresse. Jetzt stell dir einen ganz bösen Benutzer vor. Der nimmt dein programm im Debugger auseinander, stellt fest, wo die Rücksprungadresse liegt, überschreibt dann bei der Eingabe den stack frame so, daß die Funktion auf jeden Fall zurückkehrt, schreibt hinter die Rücksprungaddresse ein bißchen eigenen Code und überpinselt die Rücksprungaddresse, so daß sie auf seinen eigenen Code im Stack zeigt. Das Ganze nennt man dann buffer overflow oder auch stack smashing. Und so werden dann Server gehackt :).
Tatsache? wow! Server knacken hatte ich mir anders vorgestellt. Aber wenn ich ehrlich bin ist das (sorry) sehr kompliziert, zumindest aus meinen Augen. Trotzdem ist das , was ich noch verstanden habe, sehr lehrreich. Danke für die Erklärung.
Solange dein Programm keine besonderen Privilegien hat, ist das nur halb so wild, aber wenn es sich um ein root-Proggie handelt...
...kann es sehr böse ausgehen, klar.
Vorm Compiler läuft noch der Präcompiler (gpp). Seine Aufgabe ist es u.a. Kommentare zu entfernen und Makros zu expandieren. Dann erst wird die Datei an den Compiler weitergereicht.
Gut, dann kann ich die Kommentare also drinlassen, wie schön!
cu, Ulf
Danke und Bye, Sebastian
On 31.05.01 Sebastian Roth (xsebbi@gmx.de) wrote:
Aloahea!
quod erat demonstrantum!
S: Hat jemand ein Latein-Deutsch(Sächsisch) Wörterbuch. S: Vermutung q.e.d Was zu beweisen war? Aus Mathematik?
Ich hab jetzt mal alle vier Schreibweisen "quod era(d|t) demons(d|t)rantum" google zu fressen gegeben. Treffer hatte ich bei allen vieren und bei "quod erat demonstrandum" die meisten. Ich definiere diese hiermit als die einzig wahre[TM].
H.
On Fri, Jun 01, 2001 at 09:36:59AM +0200, Hilmar Preusse wrote:
Ich hab jetzt mal alle vier Schreibweisen "quod era(d|t) demons(d|t)rantum" google zu fressen gegeben. Treffer hatte ich bei allen vieren und bei "quod erat demonstrandum" die meisten. Ich definiere diese hiermit als die einzig wahre[TM].
Seit wann wir eine Unwahrheit durch häufiges wiederholen wahr? (nicht das ich wüßte, wie es richtig geschrieben wird... oder doch: http://www.chemie.fu-berlin.de/cgi-bin/acronym?qed )
Eric
On 01.06.01 Eric Schäfer (eric@gixgax.de) wrote:
On Fri, Jun 01, 2001 at 09:36:59AM +0200, Hilmar Preusse wrote:
Moin,
Ich hab jetzt mal alle vier Schreibweisen "quod era(d|t) demons(d|t)rantum" google zu fressen gegeben. Treffer hatte ich bei allen vieren und bei "quod erat demonstrandum" die meisten. Ich definiere diese hiermit als die einzig wahre[TM].
Seit wann wir eine Unwahrheit durch häufiges wiederholen wahr?
Hab ich nicht behauptet. Nur die Wahrscheinlichkeit, daß es 98% richtig und nur 2% falsch schreiben ist relativ hoch.[1]
H.
[1] Die Zahlen stammen aus einer Google-Messung von heute vormittag.
Mein Duden (Band 5, Fremdwoerterbuch) sagt:
'quod erat demonstrandum'
HTH Stefan
PS: vielen Dank an Konrad fuer die ausfuehrliche Codekritik. Ich hoffe, das wird fuer die naechsten newbies archiviert :-)
Allen ein gesegnetes Pfingstfest.
Am Thu, 31 May 2001 schrieb Sebastian Roth:
/*KR: mehr als 100 wird er wohl net tippen, auch wenn diese Schätzung noch riskant ist...*/
S: Wieso sollte er? Jemand, der das Prog richtig nutzen will, wird schon S: nicht so einen Käse machen oder was denkst du?
char gvar[100]; float s,v,t;
printf("Grundgleichung: s=v*t\n"); printf("Bitte die gesuchte Variable eingeben:"); scanf("%s",gvar);/*KR: Strings ohne & übergeben, sie sind schon pointer*/
scanf("%99s",gvar);/*KR: Strings ohne & übergeben, sie sind schon... ^^ Sollte eigentlich das Überlaufen des Buffers verhindern.
Bye, Sebastian
Alexander
On Thursday 31 May 2001 13:13, Sebastian Roth wrote:
ich möchte gerne ein kleines Programm schreiben (um ein wenig C zu lernen). Jenes soll physikalische Gleichungen ausrechnen können. Allerdings gibt es ein Problem. Versucht mal das Weg-Zeit Gesetz bei einer gleichförmigen Bewegung. Wenn man dann die erste gesuchte Variable (s) eingeben soll, wars das, ende.
Zumindest in der korrigierten Fassung tritt das bei mir nicht auf... Zugegeben gvar, war einer der Teile, die ich korrigiert habe....
Mit einem ddd habe ich einen exit-code 16 festgestellt. Wenn ich allerdings dir Variable gvar als integer initialisiere, klappt es! Warum denn das nun?
Also exitcode 16 klingt schonmal sehr seltsam. Normalerweise kommt bei dieser Fehlerklasse ein Segmentation Fault (Signal SIGSEGV, Nummer 11) und der produziert im normalen Modus (ohne Signal-Handler) einen Exitcode 127.
16 habe ich noch nie gesehen! (Es sei denn ich habe ihn selbst definiert.)
Der Compiler meldet keinen Fehler auch beim compilieren mit gvar=char. Da bin ich doch schon etwas ratlos. Kennt sich einer aus?
Logisch. Der Compiler kann solche Fehler nicht entdecken.
Was verwendest Du für ein System? SuSE X.y? Debian? Rotkäppchen?
Was Deine Idee mit char statt int angeht: wahrscheinlich möchtest Du, dass man einfach eine Taste drückt und nicht noch Enter hinterherdrücken muss - oder? Das kann man mit ANSI-C nicht machen, dazu müsstest Du weiter in die POSIX-Programmierung eindringen. Aber ich würde Dir empfehlen Dich erstmal ein paar Wochen oder Monate mit ANSI-C zu befassen, das ist schwer genug.
Noch eine Warnung: // funktioniert nur bei gcc und C++ Compilern! ANSI-C definiert nur diesen /*Kommentar*/.
Konrad
On Thu, May 31, 2001 at 01:13:07PM +0200, Sebastian Roth wrote:
Hallo an alle (besonders C-Programmierer),
ich möchte gerne ein kleines Programm schreiben (um ein wenig C zu lernen). Jenes soll physikalische Gleichungen ausrechnen können. Allerdings gibt es ein Problem. Versucht mal das Weg-Zeit Gesetz bei einer gleichförmigen Bewegung. Wenn man dann die erste gesuchte Variable (s) eingeben soll, wars das, ende. Mit einem ddd habe ich einen exit-code 16 festgestellt. Wenn ich allerdings dir Variable gvar als integer initialisiere, klappt es! Warum denn das nun? Der Compiler meldet keinen Fehler auch beim compilieren mit gvar=char. Da bin ich doch schon etwas ratlos. Kennt sich einer aus?
Also, laut Debugger tritt der Fehler bei dem scanf-Aufruf in Zeile 174 auf, ist also nicht deine Schuld. Bei mir meldet gdb allerdings einen exit-Code von 64 bzw. 12, je nachdem ob ich gvar als char (=>Fehler) deklariere oder das Programm mehr oder weniger durchläuft (=>SIG_INT :) und code 64).
Im Übrigen stimme ich Konrad zu, dein Code sieht schrecklich aus (ich will mich jetzt aber nicht erinnern, wie meine ersten Versuche aussahen :)). Vor allem die Funktionsverschachtelung war sehr ... kreativ.
Bye, Sebastian
cu, Ulf
P.S.: Das Proggi ist mal mit als Anhang (als C-Datei) , hoffe das fördert statt nervt :-)
Fördert? Ohne den Code hätte dir hier vermutlich keiner helfen können (sorry, aber mir fehlt die passende Kugel zum Hellsehen :))
Hi Ulf,
Also, laut Debugger tritt der Fehler bei dem scanf-Aufruf in Zeile 174 auf, ist also nicht deine Schuld. Bei mir meldet gdb allerdings einen exit-Code von 64 bzw. 12, je nachdem ob ich gvar als char (=>Fehler) deklariere oder das Programm mehr oder weniger durchläuft (=>SIG_INT :) und code 64).
aha. Ein weiteres Mal mit ddd geguckt und der alte Quelltext hatte einen exit Code 12. Sehr interressant :-)
Im Übrigen stimme ich Konrad zu, dein Code sieht schrecklich aus (ich will mich jetzt aber nicht erinnern, wie meine ersten Versuche aussahen :)). Vor allem die Funktionsverschachtelung war sehr ... kreativ.
ok, ich werde mich um Verbesserung bemühen. Hoffe aber das kein Mitlesender erblindet ist :-)
cu, Ulf
Bye, Sebastian
On Fri, Jun 01, 2001 at 03:14:24PM +0200, Sebastian Roth wrote:
aha. Ein weiteres Mal mit ddd geguckt und der alte Quelltext hatte einen exit Code 12. Sehr interressant :-)
Kleiner Tip am Rande: Um den Exit Code des Programms abzulesen, musst du nicht extra ddd anwerfen. Die aufrufende Shell bekommt diese Information auch und rückst sie auf Wunsch auch raus. Mit folgender Zeile in der ~/.bach_profile wird der exit-Status von Programmen genau dann angezeigt, wenn er ungleich 0 ist:
export PROMPT_COMMAND='V=$?; [ $V = 0 ] || echo Exit $V'
(inspired by 'set printexitvalue' der tcsh :)
Reinhard
On Friday 01 June 2001 16:05, Reinhard Foerster wrote:
On Fri, Jun 01, 2001 at 03:14:24PM +0200, Sebastian Roth wrote:
aha. Ein weiteres Mal mit ddd geguckt und der alte Quelltext hatte einen exit Code 12. Sehr interressant :-)
Kleiner Tip am Rande: Um den Exit Code des Programms abzulesen, musst du nicht extra ddd anwerfen. Die aufrufende Shell bekommt diese Information auch und rückst sie auf Wunsch auch raus. Mit folgender Zeile in der ~/.bach_profile wird der exit-Status von Programmen genau dann angezeigt, wenn er ungleich 0 ist:
export PROMPT_COMMAND='V=$?; [ $V = 0 ] || echo Exit $V'
(inspired by 'set printexitvalue' der tcsh :)
Reinhard, Du hast mal wieder die Angewohnheit die komplizierteste Variante auszuwählen. ;-)
Das geht auch Kommandoweise: ./main ; echo Exitcode: $?
Wenn Du es (wie Reinhard) nur bei !=0 haben willst: ./main || echo Exitcode: $?
Noch mehr solchen Unsinn: http://lugddlect.sf.net/books/shell
Konrad
Am Fri den 01 Jun 2001 um 05:24:24PM +0200 schrieb Konrad Rosenbaum:
On Friday 01 June 2001 16:05, Reinhard Foerster wrote:
On Fri, Jun 01, 2001 at 03:14:24PM +0200, Sebastian Roth wrote:
aha. Ein weiteres Mal mit ddd geguckt und der alte Quelltext hatte einen exit Code 12. Sehr interressant :-)
Kleiner Tip am Rande: Um den Exit Code des Programms abzulesen, musst du nicht extra ddd anwerfen. Die aufrufende Shell bekommt diese Information auch und rückst sie auf Wunsch auch raus. Mit folgender Zeile in der ~/.bach_profile wird der exit-Status von Programmen genau dann angezeigt, wenn er ungleich 0 ist:
export PROMPT_COMMAND='V=$?; [ $V = 0 ] || echo Exit $V'
(inspired by 'set printexitvalue' der tcsh :)
Reinhard, Du hast mal wieder die Angewohnheit die komplizierteste Variante auszuwählen. ;-)
Das geht auch Kommandoweise: ./main ; echo Exitcode: $?
Das tut aber nicht das, was PROMT_COMMAND bei rf11 tut.
Wenn Du es (wie Reinhard) nur bei !=0 haben willst: ./main || echo Exitcode: $?
Das auch nicht.
andre
On Friday 01 June 2001 17:39, Andre Schulze wrote:
Am Fri den 01 Jun 2001 um 05:24:24PM +0200 schrieb Konrad Rosenbaum:
On Friday 01 June 2001 16:05, Reinhard Foerster wrote:
export PROMPT_COMMAND='V=$?; [ $V = 0 ] || echo Exit $V'
(inspired by 'set printexitvalue' der tcsh :)
Reinhard, Du hast mal wieder die Angewohnheit die komplizierteste Variante auszuwählen. ;-)
Das geht auch Kommandoweise: ./main ; echo Exitcode: $?
Das tut aber nicht das, was PROMT_COMMAND bei rf11 tut.
Wenn Du es (wie Reinhard) nur bei !=0 haben willst: ./main || echo Exitcode: $?
Das auch nicht.
Erst Fragen lesen, dann mosern. :-(
Das Problem war der Exitcode und nicht den Exitcode immer und überall bekommen.
Und daher sind alle 3 Varianten (die von RF und meine) Lösungen des Problems, ebenso, wie der Ansatz das Programm im ddd auszuführen. Jeder möge sich das raussuchen, was ihm am besten liegt.
Konrad
On Fri, Jun 01, 2001 at 05:24:24PM +0200, Konrad Rosenbaum wrote:
export PROMPT_COMMAND='V=$?; [ $V = 0 ] || echo Exit $V'
(inspired by 'set printexitvalue' der tcsh :)
Reinhard, Du hast mal wieder die Angewohnheit die komplizierteste Variante auszuwählen. ;-)
Das geht auch Kommandoweise: ./main ; echo Exitcode: $?
Wenn Du es (wie Reinhard) nur bei !=0 haben willst: ./main || echo Exitcode: $?
Du musst bei deiner Variante JEDESMAL das "; echo Exitcode: $?" bzw. "|| echo Exitcode: $?" eingeben während ich vor etwa 7 Jahren obige Zeile EINMAL in meine .bash_profile geschrieben habe und seitdem die Exitcodes != 0 immer angezeigt bekomme. Der Leser möge entscheiden, welches die kompliziertere Variante ist.
Noch mehr solchen Unsinn: http://lugddlect.sf.net/books/shell
Da bekomme ich ne 404 zurück. Die sind wohl noch am Reparieren nach dem Hack womit zig-tausend opensource-projekte eine Pause einlegen durften. Ich liebe solche zentralistischen Systeme zur SW-Entwicklung. *argl*
Reinhard
On Friday 01 June 2001 17:52, Reinhard Foerster wrote:
On Fri, Jun 01, 2001 at 05:24:24PM +0200, Konrad Rosenbaum wrote:
Noch mehr solchen Unsinn: http://lugddlect.sf.net/books/shell
Da bekomme ich ne 404 zurück. Die sind wohl noch am Reparieren nach dem Hack womit zig-tausend opensource-projekte eine Pause einlegen durften. Ich liebe solche zentralistischen Systeme zur SW-Entwicklung. *argl*
Hast Du ein schwein! Ich bekomme weder von sf.net, noch von apache.org irgendwelche Seiten ausgeliefert.
Naja, abwarten, Tee drinken und hoffen, dass kein großer Schaden angerichtet wurde... ;-)
Konrad
On Friday, 1. June 2001 17:52, Reinhard Foerster wrote:
Da bekomme ich ne 404 zurück. Die sind wohl noch am Reparieren nach dem Hack womit zig-tausend opensource-projekte eine Pause einlegen durften. Ich liebe solche zentralistischen Systeme zur SW-Entwicklung. *argl*
Die Mailingliste war im Eimer (inkl. Bugtracker), und SSH. Aber warum sollte das schlimm sein? Entwickler sind Entwickler weil sie dann nicht den MS-Support anrufen, sondern mit mirror.sh die ganze Sache auf berlios.de oder so zaubern. Wenigstens gibt es sowas wie SF - nicht jeder kann sich einen Server leisten der so sehr auf Entwicklungswerkzeuge spezialisiert ist und permanent am Netz hängt.
Josef Spillner (der sich freut, mit berlios.de einen Dienst gefunden zu haben, der vom Bund 1 Million DM bekommen hat und dennoch nur 50 Projekte hostet, davon 4 meine - Platzhirsch ;-))
On Fri, Jun 01, 2001 at 07:31:07PM +0200, Josef Spillner wrote:
Josef Spillner (der sich freut, mit berlios.de einen Dienst gefunden zu haben, der vom Bund 1 Million DM bekommen hat und dennoch nur 50 Projekte hostet, davon 4 meine - Platzhirsch ;-))
Ich finde es ziemlich bescheuert, daß der Bund in sowas ein Mio Steuergelder steckt wo doch das Beispiel Sourceforge zeigt, daß andere Finanzierungsmodelle möglich sind. Für die 1 Mio, die der Staat da ausgibt, sind mindesten 2 Mio Steuern eingenommen worden. Kein anderer denkbarer Finanzierer eines solchen Projektes arbeitet derart uneffizient wie der Staat. Somit ist eine Finazierung durch den Staat die schlechteste aller Alternativen.
Aber irgendwie muß der deutsche Staat ja auch auf den momentanen Opensource-Hype reagieren :-( Er ist ja sooooo modern.
Irgendwie haben die staatlichen Geldverteiler immer noch nicht verstanden, daß Geld allein an der Stelle gar nichts (oder zumindest im Vergleich zu den eingestzten Mitteln extrem wenig) bringt. Die 50k für GnuPG waren mMn auch so ein staatlicher Schnellschuß zur kurzfristigen Erlangung von Popularität.
Der Staat hätte 1000 andere Möglichkeiten, Opensource sinnvoller zu unterstützen. Ich sage nur Änderung der Rahmenbedingungen bei Ausschreibungen öffentlicher Stellen u.ä.
Reinhard
On Friday, 1. June 2001 20:43, Reinhard Foerster wrote:
Der Staat hätte 1000 andere Möglichkeiten, Opensource sinnvoller zu unterstützen. Ich sage nur Änderung der Rahmenbedingungen bei Ausschreibungen öffentlicher Stellen u.ä.
Das wird wohl nicht passieren. Die Diskussion, ob Linux im Bundestag eingesetzt werden soll auf breiter Front, ist doch auch eher zu einer Frage mutiert, welche Firma (namentlich mit grünem Wappentier) aller Wahrscheinlichkeit nach diesen Großauftrag bekommen wird.
Aber insgesamt gesehen zieht bei denen immer das Argument, nicht von großen Softwareproduzenten abhängig zu sein - auch wenn das wie schon erwähnt in Südamerika um Welten weiter fortgeschritten ist.
Josef Spillner
On Friday 01 June 2001 20:43, Reinhard Foerster wrote:
Der Staat hätte 1000 andere Möglichkeiten, Opensource sinnvoller zu unterstützen. Ich sage nur Änderung der Rahmenbedingungen bei Ausschreibungen öffentlicher Stellen u.ä.
Das Problem mit öffentlichen Ausschreibungen ist IMHO nicht, dass die glauben OSS wäre schlechter, sondern dass die jemanden brauchen, den sie anschreien können, wenn was nicht funktioniert. Und wenn's dann immernoch nicht klappt muss man ihn verklagen _und_ Erfolg haben können. Da kommen also eindeutig nur Firmen mit genug Kapazität zum Strafe-zahlen in Frage (jetzt erklär denen mal einer, dass M$ eher den Staat aufkauft als ihm was zu zahlen). Der Grund ist einfach: die Mittel werden jählich gekürzt, man bekommt auf diese Weise das Geld für die Reparatur. Und selbermachen ist nicht, weil es die guten Informatiker eher in die Wirtschaft als in die Behörden und anderen öffentlichen Einrichtungen zieht (Geld hat Gravitation).
Konrad
lug-dd@mailman.schlittermann.de