heinz hat geschrieben: 01.03.2019 04:01:21
Vermutlich kompilierst du mit g++, der wirft keine Fehler.
Scheint so... Damit habe ich mich nie beschaeftigt...
Code: Alles auswählen
~$ which c++
/usr/bin/c++
~$ file /usr/bin/c++
/usr/bin/c++: symbolic link to `/etc/alternatives/c++'
~$ file /etc/alternatives/c++
/etc/alternatives/c++: symbolic link to `/usr/bin/g++'
Ja, C++, nicht C (wo der Compiler `cc' oder `gcc' heissen wuerde). Die Sache ist die, dass dein ganzer Code ganz C-artig ist und keine C++-Features verwendet ausser `bool'. Das ist so vollkommen erlaubt, es wundert mich nur, dass du eigentlich C programmierst, das aber in einer C++-Umgebung. Ist aber nicht so wichtig.
- `pipeE' und `pipeK' sind wenig hilfreiche Bezeichner,
Das ist eine der Sachen die ich nicht so richtig verstehe, weshalb ich den Pipes estmal irgendeine Bezeichnung gab um sie, fuer mich, ueberhaupt unterscheiden zu koennen...
Okay. Dazu in einem separaten Post etwas.
- Code hinter einem exec(3) wird nicht ausgefuehrt ... ausser wenn exec() fehlschlaegt.
Aber auch wenn, wie in diesem falle, der bc durch "quit" beendet wird. Oder auch dann nicht?
Zumindest nach den close-Befehlen sollte aber direkt ein exit kommen da hast Du recht.
Da musst du zuerst fork/exec verstehen. Da geht es um Grundlagenwissen von Unix. Fork dupliziert den Prozess. Es ist eine exakte Kopie, ausser dass sich die Prozess-ID unterscheidet. Beide Prozesse arbeiten im gleichen Code an der gleichen Stelle weiter. Darum macht man normalerweise direkt hinter dem fork() eine Unterscheidung nach PID, damit die zwei Prozesse dann unterschiedlichen Dinge machen.
Exec() ersetzt das Prozessimage. D.h. der Programmcode des Prozesses wird ersetzt/ueberschrieben. Der Prozess fuehrt dann nicht mehr dein Programm aus sondern in deinem Fall dann bc(1). Von deinem Programm ist in dem Prozess dann nichts mehr uebrig. Es gibt dann keinen Code von dir mehr, der in dem Prozess noch ausgefuehrt werden koennte, der ist komplett ersetzt. Exec() ist wie exit(), bloss dass sich der Prozess nicht beendet sondern ein anderes Programm startet.
Die einzige Moeglichkeit wie Code hinter exec() ausgefuehrt wird, ist wenn exec() fehlschlaegt. (Identisch zu exit(), wo auch kein Code dahinter mehr ausgefuehrt wird, ausser wenn exit() fehlschlagen wuerde ... was aber kaum passieren wird, waehrend es bei exec() z.B. dann vorkommt, wenn die auszufuehrende Datei nicht gefunden wird.)
- Warum initialisierst du mit {""} und nicht mit ""? Ist das C++-Style?
Gewohnheit. Um den genauen Grund habe ich mich, ehrlich gesagt, nie gekuemmert...
Irgendwann, nach einem "Umzug" auf ein neues System, musste ich viele meiner Programme mit diesen >{}< "nachruesten" (Fehlermeldung: missing braces)
um sie wieder lauffaehig zu bekommen.
Seitdem mache ich es immer so...
Das klingt fuer Dich jetzt sicher fuerchterlich und stuemperhaft aber die wirklichen Unterschiede zwischen C und C++ kenne ich eigentlich kaum...
Ich verwende was funktioniert.
Wenn man nicht weiss warum es funktioniert, dann funktioniert es vielleicht nur zufaellig richtig, in anderen Szenarien aber nicht mehr.
Du legst an der Stelle ein Char-Array an. Dieses initialisiert man korrekterweise als Array von Chars:
Da dies sehr aufwaendig zu schreiben ist, gibt es in C den syntactic Sugar, dass folgender Code identisch damit ist:
Wenn man darum geschweifte Klammern macht, entspricht das ja:
Was das bringen soll, verstehe ich nicht.
Vielleicht haengt das mit diesem Phaenomen zusammen, ueber das ich mal gestoplert bin:
http://marmaro.de/lue/txt/2015-11-06.txt
Wenn beim Compilieren mit -Wall nichts "auftaucht", der Code ueber laengere Zeit tut was er soll und keine Fehler oder Abstuerze produziert ist er fuer mich OK.
Ich hoffe Du hilfst mir trotzdem noch ein wenig...
Kein Problem. Ich kann dein Verhalten natuerlich schon verstehen. Das ist dann halt der Unterschied zwischen Programmierern die verstehen was sie tun und welchen, die irgendwie etwas produzieren das meistens vermutlich das tut was es soll. Das ist ein Qualitaetsunterschied. Ich will damit aber keinesfall sagen, dass du nicht so programmieren solltest wie du es tust, weil auch du nutzt Computer zu deinem Vorteil. Bloss Atomkraftwerksteuerungen und Fahrerassistenzsysteme und OpenSSL und alles wo personenbezogene Daten verarbeitet werden sollte man nicht auf diese Weise programmieren.
- Du darfst keine 32 Bytes lesen, sondern nur eines weniger, damit du den String null-terminieren kannst. Ausserdem solltest du keine solche Magic-Number verwenden sondern lieber ``sizeof(buf
Laenge minus 1. Werde ich beherzigen.
Allerdings habe ich etwas schlechte Erfahrung damit gemacht ein sizeof auf ein, an eine Funktion uebergebenes, Array anzuwenden...
(Irgendwie habe ich die moeglicherweise dumme Angewohnheit, solche "Dinge" dann immer zu vermeiden...)
Verstehen ist das Entscheidende! Du musst sicherstellen, dass Strings terminiert sind. Dazu brauchst du Platz fuer ein Null-Byte und dieses muss eingefuegt werden ... auch welche Weise auch immer. Das zu pruefen und sicherzustellen ist in C deine Aufgabe als Programmierer. In C muss man bei jeder String-Operation an die Terminierung denken und sie pruefen.
Mit dem an eine Funktion uebergenes Array hast du Recht, da muss man aufpassen. Der Unterschied zwischen einem Array und einem Pointer ist in C naemlich die Information zur Laenge. An Funktionen kann man keine Arrays uebergeben, nur Pointer. In diesen Faellen muss man darum die Laenge als separaten Parameter durchreichen.
Nichts desto trotz sollte man Magic Numbers (also direkt hingeschriebene Zahlen im Code) vermeiden. Verwende `sizeof' und reiche den Wert falls noetig an die Funktionen weiter.
- read(2) und write(2) koennen fehlschlagen. Da fehlt die Fehlerbehandlung. (Das meinte ich mit dem vielen Overhead, wenn man so low-level programmiert.)
Da sprichst Du etwas interessantes an.
Wenn die Formel naemlich einen syntaktischen Fehler enthaelt, gibt bc nichts zurueck und der read wartet endlos auf die rueckgabe des Ergebnisses.
Wie ich gelesen habe (man read), scheint >timerfd_create< da der richtige "Angriffspunkt" fuer zu sein. Sieht nach der naechsten "Baustelle" aus...
Gibt es vlt. noch eine andere Moeglichkeit einen solchen Fehler abzufangen?
Eine weitere Pipe fuer stderr vielleicht?
Oder du duplizierst stdout auf stderr bevor du den exec() auf `bc' machst. Dann bekommst du stderr-Ausgaben auch auf stdout zum lesen. Alles was in der Shell geht kannst du natuerlich auch in C machen. (Grundsaetzlch kannst du alles was du ueberhaupt mit Unix machen kannst, auf Systemcall-Ebene machen, weil das das einzige Interface zum Kernel ist.)
- Statt read(2) und write(2) kannst du aber auch printf(3) und fgets(3) verwenden.
Danke fuer den Hinweis. Einen Vorteil oder eine Vereinfachung kann ich darin allerdings nicht erkennen...
Siehe hierzu diesen Thread:
viewtopic.php?p=1182218#p1182218
- Das memset(3) brauchst du nicht, wenn du dafuer sorgst, dass dein String terminiert ist.
Ich dachte halt, durch einen memset mit 0, koennte ich "egal was" in den String "kippen" und er waere immer null-terminiert...
Statt mit dem Holzhammer solltest du besser genau das richtige machen, naemlich nur genau das Zeichen hinter dem letzten gelesenen auf '\0' setzen. Dabei wird dir dann vielleicht auch bewusst, dass du noch ein Zeichen Platz in deinem Puffer brauchst und darum ein Zeichen weniger lesen darfst als der Puffer gross ist.
Wenn du deinen Puffer schoen ausnullst und dann soviele Zeichen reinschreibst wie der Puffer gross ist, dann ist der dort enthaltene String ja doch nicht null-terminiert.
- dup2(2) schliesst den Ziel-FD automatisch. Das close(2) davor ist also unnoetig.
In >man dup< habe ich folgendes gelesen:
dup2() makes newfd be the copy of oldfd, closing newfd first if necessary...
aber vlt. falsch verstanden. (Mein Englisch ist nicht so toll...)
Du hast aber recht, ohne diese Zeilen laeuft der Code genauso...
Du machst:
Die Manpage schreibst, dass dup2() dieses close(newfd) automatisch macht.