READV Systemaufruf aus C-Programm

Hallo an alle.

Im Rahmen einer Übung versuche ich einen readv() Unix/Linux-Systemcall in einem C-Programm abzusetzen. Folgender Code:

**#include „stdio.h“
#include „sys/types.h“
#include „sys/stat.h“
#include „sys/uio.h“
#include „fcntl.h“
#include „string.h“

int main(){
struct iovec iov[2];
int nr, fd, i;

char *buff[2];
fd=open(„ler.txt“,O_RDONLY);

for(i=0;i**

Der readv() Aufruf meldet „-1“ zurück. Scheitert also. Ich verstehe den Grund nicht.
Wenn mein Verständniss für diesen Systemcall richtig ist, dann muss ein Vector spezifiziert werden. Das tue ich in der FOR-Schleife.

Der funktionierende writev() Aufruf sieht so aus:

**int main(){
struct iovec iov[5];
int nr, fd, i;

char* buf[]={
„There was a young lady named Bright\n“,
„Her speed was faster than light\n“,
„She went out one day\n“,
„In a relative way\n“,
„And returned the previous night\n“};

/*fd=open(„rel.txt“,O_WRONLY|O_CREAT|O_TRUNC,0644);

for(i=0;i**

Bin für jede Hilfe dankbar!

Hallo Vitali,
ich denke, daß ich den Grund für dein Problem gefunden habe. In C müssen Strings 0-terminiert sein. readv liefert aber einen „rohen“ String (wie read auch), d.h. es werden die Bytes in den Buffer eingelesen, so wie sie in der Datei stehen (ohne abschliessendes ‚0‘).
Für die ordentliche Ausgabe mit printf muss also dieses terminierende 0 noch gesetzt werden. Ich habe dein Programm in diesem Sinne abgeändert:

#include 
#include 
#include 
#include 
#include 
#include 

#define MAXCHARS (sizeof(buff[0]) - 1)

int main(){
 struct iovec iov[2];
 int nr, fd, i;
 char buff[2][5]; // 1 Byte länger für die 0-Terminierung

 fd=open("ler.txt", O\_RDONLY);
 if (!fd) {
 printf("FEHLER: %s\n",strerror(errno));
 return 1;
 }

 for(i=0;i%s
damit ergibt sich dann auch eine korrekte Ausgabe:


    Meldung: Success
    8 Bytes gelesen
    0: -\>hhh
    vvv
    
    oder für deine mit writev erzeugte Limerick-Datei diese Ausgabe:
    
    
        Meldung: Success
        8 Bytes gelesen
        0: -\>There wa
        Viele Grüße
        Marvin

Hallo Marvin.

Ja, einer Gründe liegt wohl im Abschluß der Zeichenkette.
Der grundsätzliche Fehler liegt, denke ich, darin, dass ich char *buff[2] verwende. Fälschlicherweise ging ich davon aus, dass ich in diesem Array 2 Zeichenketten speichern könnte. Es ist aber so, dass ich hier lediglich eine Zeichenkette von 2 Zeichen speichern kann. Richtig?

Mit dem Zeichenabschluß habe ich noch Schwierigkeiten.
Um es etwas einfacher zu halten habe ich ein Array char *buff[8] definiert in den ich nacheinander alles schreiben werde.
Danach initialisiere ich das iov-Array:

iov[0].iov_base=buff[0];
iov[0].iov_len=3;
iov[1].iov_base=buff[4];
iov[1].iov_len=3;

Und daraufhin versuche ich auf die jeweils letzte Stelle, buff[4] und buff[8] ein „\0“ zu schreiben:

buff[3]="\0";
buff[7]="\0";

Auslesen des Buffers mit:

printf(„Inhalt buff[0]: %s\n“,buff[0]);
printf(„Inhalt buff[3]: %s\n“,buff[4]);

readv() gibt mir zurück, dass 6 Byte gelesen wurden. Das ist korrrekt.
(Habe die rel.txt auch vereinfacht und alles in eine Zeile geschrieben: hhhvvv)
Aber das erste printf() gibt nicht nur die Zeichen bis zum „\0“ aus, sondern alles was scheinbar im buff[] steckt:

ubuntu@ubuntu:~/Desktop$ ./llio4.o
File descriptor Nr.: 3
Mit readv() gelesen: 6 Bytes
Inhalt buff[0]: hhh�I��l�
Segmentation fault (core dumped)

Da beim ersten printf() das gesamte Array ausgegeben wurde, greift er beim zweiten printf() wie es scheint in fremden Specherbereich und scheitert.

Hier bin ich noch am probieren.
Evtl. ist es irgendeine Eigenheit des printf() Befehls. Oder der readv() Aufruf schreibt nicht auf die richtigen Stellen. Oder das „\0“ wird nicht als binäre Null geschrieben.

Hier der ganze Code:

#include „stdio.h“
#include „sys/types.h“
#include „sys/stat.h“
#include „sys/uio.h“
#include „fcntl.h“
#include „string.h“

int main(){
struct iovec iov[2];
int nr, fd, i;

char *buff[8];
fd=open(„ler.txt“,O_RDONLY);
printf(„File descriptor Nr.: %i\n“, fd);

iov[0].iov_base=buff[0];
iov[0].iov_len=3;
iov[1].iov_base=buff[4];
iov[1].iov_len=3;
buff[3]="\0";
buff[7]="\0";
nr=readv(fd,iov,2);
close(fd);

printf(„Mit readv() gelesen: %d Bytes\n“,nr);
printf(„Inhalt buff[0]: %s\n“,buff[0]);
printf(„Inhalt buff[4]: %s\n“,buff[4]);
}

Hallo Vitali !

Der grundsätzliche Fehler liegt, denke ich, darin, dass ich
char *buff[2] verwende. Fälschlicherweise ging ich davon aus,
dass ich in diesem Array 2 Zeichenketten speichern könnte. Es
ist aber so, dass ich hier lediglich eine Zeichenkette von 2
Zeichen speichern kann. Richtig?

Nein.
char *buff[2] heisst du hast ein Array mit 2 Elementen die jeweils einen Pointer auf einen String (eigentlich auf einen Character der das erste Zeichen des Strings ist) enthalten.

Diese Pointer musst du erst initialisieren indem du sie auf einen Speicherbereich zeigen lässt der den String enthalten soll.
z.B.

buff[0] = "Das ist ein String";

Der Pointer in buff[0] zeigt jetzt auf den angegebenen String den der Compiler im Speicher angelegt hat.

buff[1] = (char\*)malloc(sizeof(char) \* (MAXCHARS + 1));

Der Pointer in buff[1] zeigt jetzt auf einen Speicherbereich der mittels malloc() angelegt wurde und Platz für MAXCHARS Characters plus ein ‚\0‘ hat.
(sizeof(char) ist eigentlich überflüssig weil char praktisch immer die Grösse 1 hat, aber so ist man auch auf Fälle wo das nicht der Fall ist vorbereitet)

Um es etwas einfacher zu halten habe ich ein Array char
*buff[8] definiert in den ich nacheinander alles schreiben
werde.

Das ist jetzt ein Array mit 8 Elementen die jeweils einen Pointer auf einen String enthalten. Diese Pointer sind nicht initialisiert und zeigen irgendwohin in die Wildnis :wink: !

Danach initialisiere ich das iov-Array:

iov[0].iov_base=buff[0];

iov[0].iov_len=3;

iov[1].iov_base=buff[4];

iov[1].iov_len=3;

iov[0].iov_base zeigt jetzt irgendwohin weil buff[0] nicht initialisiert war !

Und daraufhin versuche ich auf die jeweils letzte Stelle,
buff[4] und buff[8] ein „\0“ zu schreiben:

buff[3]="\0";

buff[7]="\0";

Du schreibts jetzt in buff[3] und buff[7] einen Pointer auf jeweils einen String der nur aus dem Zeichen ‚\0‘ besteht.
Beachte den Unterschied:

buff[3] = "\0";

ist zwar korrekt, weil „\0“ ein String ist den der Compiler im Speicher angelegt hat und dessen Adresse er dann in buff[3] schreibt.
Du wolltest aber wohl

buff[3] = '\0';

schreiben, also an die Stelle buff[3] das Zeichen ‚\0‘, was aber zu einem Compilerfehler geführt hätte weil buff ja kein Array von Charactern ist sonder ein Array von Pointern auf Character !

Auslesen des Buffers mit:

printf(„Inhalt buff[0]: %s\n“,buff[0]);

printf(„Inhalt buff[3]: %s\n“,buff[4]);

readv() gibt mir zurück, dass 6 Byte gelesen wurden. Das ist
korrrekt.
(Habe die rel.txt auch vereinfacht und alles in eine Zeile
geschrieben: hhhvvv)
Aber das erste printf() gibt nicht nur die Zeichen bis zum
„\0“ aus, sondern alles was scheinbar im buff[] steckt:

ubuntu@ubuntu:~/Desktop$ ./llio4.o
File descriptor Nr.: 3
Mit readv() gelesen: 6 Bytes
Inhalt buff[0]: hhh�I��l�
Segmentation fault (core dumped)

Nein, das ist ein Irrtum.
Weil buff[0] nie initialisiert wurde zeigt der Pointer in buff[0] irgendwohin, ebenso wie alle anderen ausser buff[3] unf buff[7].
readv() hat jetzt Daten in Speicherbereiche gelesen die zufällig irgendwo stehen, ein Zufall dass nicht bereits da der Fehler auftrat.

Da beim ersten printf() das gesamte Array ausgegeben wurde,
greift er beim zweiten printf() wie es scheint in fremden
Specherbereich und scheitert.

Nein, beim printf von buff[0] wird alles ab der Stelle ausgegeben auf die buff[0] zeigt, dass ist das was readv gelesen hat und alles danach bis zum ersten ‚\0‘ das es zufällig findet (das von dir geschriebene buff[3] = „\0“ hat ja überhaupt nichts mit buff[0] zu tun), dabei kam es dann wohl auch schon zum Speicherüberlauf (Segmentation fault)

Du musst bei der Definition von Arrays darauf achten was du da definierst:

char buff[8];

erzeugt ein Array von Characters der Länge 8

char buff[2][4];

(in der Art wie in der Antwort von Marvin weiter oben) erzeugt ein zweidimensionales Array von 2 * 4 Characters. Man kann dies gleich wie ein lineares Array von 8 Characters behandeln, das kann bei manchen Compilern aber auch schiefgehen.

char \*buff[2];

erzeugt dagegen ein Array der Länge 2 aus Pointern auf Character !

Mir scheint, du wolltest statt dem Array auf Strings einen String direkt verwenden, das hätte dann so aussehen müssen:

char buff[8];

dann weiter unten

iov[0].iov\_base = &(buff[0]);
iov[0].iov\_base = &(buff[4]);

also die Adressen von buff[0] und buff[4];
desweiteren

buff[3] = '\0';
buff[7] = '\0';




printf("Inhalt buff[0]: %s\n", &(buff[0]));

(oder einfach

printf("Inhalt buff[0]: %s\n", buff));

)

printf("Inhalt buff[4]: %s\n", &(buff[4]));

Die alternative Lösung wäre natürlich wirklich mehrere Buffer zu verwenden:

#include "stdio.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/uio.h"
#include "fcntl.h"
#include "string.h"

#define MAXCHARS 3

int main(){
 struct iovec iov[2];
 int nr, fd, i;
 char \*buff[2];

 buff[0] = (char\*)malloc(sizeof(char) \* MAXCHARS + 1); // MAXCHARS + ein '\0'
 buff[1] = (char\*)malloc(sizeof(char) \* MAXCHARS + 1); // MAXCHARS + ein '\0'
 memset(buff[0], 0, sizeof(char) \* MAXCHARS + 1); // mit 0-en initialisieren, nach dem letzen eingelesenen Zeichen kommt dann sicher ein '\0' (sofern max. MAXCHARS Zeichen gelesen wurden)
 memset(buff[1], 0, sizeof(char) \* MAXCHARS + 1); // mit 0-en initialisieren, nach dem letzen eingelesenen Zeichen kommt dann sicher ein '\0' (sofern max. MAXCHARS Zeichen gelesen wurden)

 fd=open("ler.txt", O\_RDONLY);
 printf("File descriptor Nr.: %i\n", fd);
 iov[0].iov\_base = buff[0];
 iov[0].iov\_len = MAXCHARS;
 iov[1].iov\_base = buff[1];
 iov[1].iov\_len=MAXCHARS;
 nr=readv(fd, iov, 2);
 close(fd);

 printf("Mit readv() gelesen: %d Bytes\n",nr);
 printf("Inhalt buff[0]: %s\n",buff[0]);
 printf("Inhalt buff[4]: %s\n",buff[4]);
}

mfg
Christof

1 Like

Moin Christof!

Danke für Deine ausführliche Antwort.
Die Zeiger und Arrays sind nicht einfach zu verstehen (für mich). Ich werde mir Deine Antwort gleich gründlich durchlesen, zurechtlegen und durchprobieren.

Folgendes ist mir bereits aufgefallen was ich nicht verstehe:

ubuntu@ubuntu:~/Desktop$ ./llio4.o
File descriptor Nr.: 3
Mit readv() gelesen: 6 Bytes
Inhalt buff[0]: hhh�I��l�
Segmentation fault (core dumped)

Nein, das ist ein Irrtum.
Weil buff[0] nie initialisiert wurde zeigt der Pointer in
buff[0] irgendwohin, ebenso wie alle anderen ausser buff[3]
unf buff[7].
readv() hat jetzt Daten in Speicherbereiche gelesen die
zufällig irgendwo stehen, ein Zufall dass nicht bereits da der
Fehler auftrat.

Warum gibt das erste printf() immer die richtigen ersten paar Bytes aus? Egal wie oft ich das Programm durchlaufen lasse, die Ausgabe beginnt immer mit hhh, wo der Zeiger buff[0] doch irgendwohin zeigen sollte?

Einen Schlüßelsatz habe ich in der Zwischenzeit in einem Buch entdeckt („Programmieren in C“, Hanser, 1990):

_char *pmessage = „now is the time“; /*ein Zeiger */

… Andererseits ist pmessage ein Zeiger, der so initialisiert ist, dass er auf eine konstante Zeichenkette zeigt.
Versucht man den Inhalt der konstanten Zeichenkette zu ändern, ist das Resultat undefiniert._

Also ist die Zuweisung

buff[3] = "\0"

oder

buff[3] = '\0'

in jedem Fall nicht möglich oder unwirksam.

Daraufhin habe ich den Code (nur ein wenig) geändert und das Ergebnis war so wie es sein sollte. Das was ich geändert habe, ist auch genau das was Du vorgeschlagen hast, Christof: Nicht mit Zeigern, sondern mit Verktoren arbeiten.

Aus

char \*buff[8]

wurde

char buff[8]

Aus

iov[0].iov\_base=buff[0]

wurde

iov[0].iov\_base=&buff[0]

Und aus

printf("Inhalt buff[0]: %s\n",buff[0])

wurde

printf("Inhalt ab buff[0]: %s\n",&buff[0])

Hier noch das ganze Programm:

#include "stdio.h"
#include "sys/types.h"
#include "sys/stat.h"
#include "sys/uio.h"
#include "fcntl.h"
#include "string.h"

int main(){
 struct iovec iov[2];
 int nr, fd, i;

 char buff[8];
 fd=open("ler.txt",O\_RDONLY);
 printf("File descriptor Nr.: %i\n", fd);

 printf("Speicheradresse buff[0]: %p\n",&buff[0]);
 iov[0].iov\_base=&buff[0];
 iov[0].iov\_len=3;
 printf("Speicheradresse buff[4]: %p\n",&buff[4]);
 iov[1].iov\_base=&buff[4];
 iov[1].iov\_len=3;
 /\*}\*/
 buff[3]='\0';
 buff[7]='\0';
 nr=readv(fd,iov,2);
 close(fd);

 printf("Mit readv() gelesen: %d Bytes\n",nr);
 printf("Inhalt buff: %s\n",buff);
 printf("Inhalt ab buff[0]: %s\n",&buff[0]);
 printf("Inhalt ab buff[4]: %s\n",&buff[4]);
}

Und die Ausgabe:

ubuntu@ubuntu:~/Desktop$ ./llio4.o 
File descriptor Nr.: 3
Speicheradresse buff[0]: 0xbfdb0864
Speicheradresse buff[4]: 0xbfdb0868
Mit readv() gelesen: 6 Bytes
Inhalt buff: hhh
Inhalt ab buff[0]: hhh
Inhalt ab buff[4]: vvv

Ich werde es jetzt versuchen mit Zeigern zu realisieren. Mein Prof. legt Wert auf „Zeigerrumballerei“.

Gruß
Vitali

Hallo Vitali,
Dank der ausgezeichneten Erklärung von Christof und mittels einiger angestaubter Papiere aus dem Keller habe ich nun noch eine Variante mit Pointern programmiert:

#include 
#include 
#include 
#include 
#include 
#include 
#include 

#define ANZ\_BUFFER 2
#define LEN\_BUFFER 3

int main(){
 struct iovec iov[ANZ\_BUFFER];
 int i, nr, fd;

 char \*buff[ANZ\_BUFFER];
 for(i=0; i
Bei mir funktioniert das prima.



> Ich werde es jetzt versuchen mit Zeigern zu realisieren. Mein  
> Prof. legt Wert auf "Zeigerrumballerei".

Ich denke, zu Recht. C hat man wahrscheinlich erst dann richtig verstanden, wenn man diesen Zeigermechanismus vollkommen kapiert hat. Insofern weiß ich, warum ich schon lange kein C mehr programmiert habe :wink:

Viele Grüße
Marvin

Ich werde es jetzt versuchen mit Zeigern zu realisieren. Mein
Prof. legt Wert auf „Zeigerrumballerei“.

Ich denke, zu Recht. C hat man wahrscheinlich erst dann
richtig verstanden, wenn man diesen Zeigermechanismus
vollkommen kapiert hat.

Diese Aussage ich nur unterstreichen. Und „vollkommen kapiert“ ist leider nicht identisch damit, dass der Compiler fehlerfrei durchläuft.
Allerdings helfen scharfgeschaltete Warnung des Compilers sehr, solche Anfängerfehler zu entdecken.

Insofern weiß ich, warum ich schon
lange kein C mehr programmiert habe :wink:

Hier bin ich nicht mehr d’accord: Denn die Zeigerei ist sehr eng mit dem Verständnis verbunden, wie die Verarbeitung und die Speicherverwaltung funktionieren. Und das trifft nicht nur auf die C-Programmierung zu, sondern auf viele andere Sprachen und vor allem auf die Nutzung von Bibliotheken.

Wenn’s nur für die Schule ist, gibt es weniger scharfkantige Sprachen wie C. Für’s professionelle Entwickeln führt aber m.E. kein Weg daran vorbei, sich die ganze Zeiger, Stack- und Heap-Geschichte anzutun.
Auch wenn dann C nicht mehr die Sprache der Wahl sein solle.

Ciao, Allesquatsch

Hallo Vitali !

Warum gibt das erste printf() immer die richtigen ersten paar
Bytes aus? Egal wie oft ich das Programm durchlaufen lasse,
die Ausgabe beginnt immer mit hhh, wo der Zeiger buff[0] doch
irgendwohin zeigen sollte?

Naja, mit ‚der Zeiger zeigt irgendwohin‘ ist gemeint, dass der Ort, auf den er zeigt, irgendwo zufällig im Speicherbereich liegt, dieser Ort ist aber natürlich konstant: wenn buff(0) dort hinzeigt wird readv() dort hinschreiben, und wenn es dabei keinen Segmentation fault (also unerlaubten Zugriff), gab, dann steht das danach natürlich auch dort, so dass printf das dann liest. Das Problem ist nur: du hast damit irgendwo im Speicher etwas hingeschrieben, wenn dieser Bereich aber von einem anderen Programm (oder dem eigenen an einer anderen Stelle) verwendet wird steht dort nicht mehr dass was da stehen sollte, das andere Programm wird dann wahrscheinlich abstürzen, vielleicht einfach auch nur falsche Resultate liefern, oder es wirkt sich auch gar nicht aus weil diese Stelle irrelevant war. Das heisst: die Folgen so einer Aktion (Schreiben in einen zufällig ausgewählten Speicherbereich) sind nicht vorhersehbar (selten jedoch positiver Natur :wink: ).

Einen Schlüßelsatz habe ich in der Zwischenzeit in einem Buch
entdeckt („Programmieren in C“, Hanser, 1990):

_char *pmessage = „now is the time“; /*ein Zeiger */

… Andererseits ist pmessage ein Zeiger, der so initialisiert
ist, dass er auf eine konstante Zeichenkette zeigt.
Versucht man den Inhalt der konstanten Zeichenkette zu ändern,
ist das Resultat undefiniert._

Also ist die Zuweisung

buff[3] = „\0“

oder

buff[3] = ‚\0‘

in jedem Fall nicht möglich oder unwirksam.

Das hängt allerdings vom verwendeten Compiler ab wie diese Zuweisung implementiert wird, ist aber auf alle Fälle nicht zu empfehlen.
Ich habe diese Version ja auch nur zum Verdeutlichen gebracht, was da genau passiert, im Codebeispiel weiter unten mit echten mehrfachen Buffern stand dann ja die Version mit malloc() :smile:
(P.S.: Tippfehler: die letzte Zeile dort sollte natürlich printf(„Inhalt buff[1]: %s\n“,buff[1]); heissen)

Daraufhin habe ich den Code (nur ein wenig) geändert und das
Ergebnis war so wie es sein sollte. Das was ich geändert habe,
ist auch genau das was Du vorgeschlagen hast, Christof: Nicht
mit Zeigern, sondern mit Verktoren arbeiten.

Naja, der ‚Vorschlag‘ war eher als Frage gedacht, ob du das vielleicht so im Sinn hattest.
Allerdings stellt sich dann natürlich die Frage warum du da die Version readv() verwendest, die ja eigentlich speziell für den Zweck gedacht ist, dass man mehrere Buffer hat, zum Lesen in einen einzelnen Buffer gibts doch die dafür vorgesehen wesentlich einfacheren read()-Befehle.

mfg
Christof

Moin Marvin.

Also ich sehe, dass Dein Programm vom Prinzip genauso arbeitet wie das von Christof. Du hast es aber mit Schleifen aufgewertet :smile:

Um mein Verständnis auf die Probe zu stellen, habe ich einige Passagen kommentiert:

#include
#include
#include
#include
#include
#include
#include

#define ANZ_BUFFER 2
#define LEN_BUFFER 3

Du legst die Anzahl der Puffer und deren Größe fest.

int main(){
struct iovec iov[ANZ_BUFFER];

Ist klar: das struct iovec wird von der gleichen Anzahl angelegt wie die Puffer

int i, nr, fd;

char *buff[ANZ_BUFFER];

Du deklarierst ein Array von Zeigern auf char Elemente bestehend aus 2 Elementen. Ein Element kann ein einzelner Buchstabe/eine Zahl, aber auch ein Satz sein.
Das Array bleibt leer. Nein, besser: der Arrayinhalt bleibt udefiniert.
Es werden hier keine Adressen hineingeschrieben.

for(i=0; i

Für jeden Puffer allokierst Du Speicher und schreibst die Adresse des zugewiesenen Speichers in die einzelnen „Array-Register“.
Generell: wird ein Array von Zeigern auf char deklariert, so muss nicht zwingend eine Speicherallokation mittels malloc() statt finden. Richtig?
Man könnte eine bereits vorhandene Speicheradresse hineinschreiben: buff[0]=xy (wenn es vorher char xy=„D“ oder char xy[]=„Das ist ein String“ hieße)?
Hier müssen wir aber Speicher allokieren, da wir die Adresse an eine andere Funktion weitergeben müssen ohne vorher dieses char definiert zu haben.

Der allokierte Speicherbereich wird nicht (mit memset()) genullt.

{
perror(„malloc“);
exit(1);
}
else {
iov[i].iov_base=buff[i];

Dem iov_base wird der Inhalt von buff[i] übergeben, also eine Adresse wohin die readv()-Finktion das aus der Datei Gelesene schreiben (puffern) wird.

iov[i].iov_len=LEN_BUFFER;
}
}
fd=open(„ler.txt“,O_RDONLY);
printf(„File descriptor Nr.: %i\n“, fd);
nr=readv(fd, iov, ANZ_BUFFER);

Die readv()-Funktion liest aus der ler.txt genau soviel Byte wie sie mit iov_len definiert sind (ist das wirklich so?) und legt das Gelesene auf die Adresse die aus dem iov_base hervorgeht. Es wird so of gemacht wie es ANZ_BUFFER vorgibt.

close(fd);
printf(„Mit readv() gelesen: %d Bytes\n“, nr);

for(i=0; i

Mit printf() wird der Inhalt des Arrays, die einzelnen Register, ausgegeben. Im Array stehen zwar nur Adressen, aber dadurch, dass die Ausgabe mit %s formatiert ist, wird nicht die Adresse ausgegeben, sondern der String welcher an der Adresse beginnt und bis zur binären Null geht.
(Wird die binäre Null durch die readv()-Funktion hinzugefügt?)

}
return 0;
}

Es kann doch nicht sein, dass ich alles richtig kapiert habe, oder? :smile:

Gruß & Danke

Vitali

Hallo Vitali,

Du hast es aber mit Schleifen
aufgewertet :smile:

Ich war schon im Kindergarten für meine Schleifchen berühmt :wink:

Um mein Verständnis auf die Probe zu stellen, habe ich einige
Passagen kommentiert:

Oh je, ich habe mein C gerade erst wieder entstaubt und bin froh, daß mein Progrämmchen überhaupt läuft. Eigentlich bin ich hier völlig fehl am Platz. Christof kann deine Kommentare garantiert besser kommentieren. Ich äußere mich also jetzt nur zu dem, was mir nicht so ganz richtig vorkommt. Aber ob meine Bemerkungen nun „richtiger“ sind?

Du deklarierst ein Array von Zeigern auf char Elemente
bestehend aus 2 Elementen. Ein Element kann ein einzelner
Buchstabe/eine Zahl, aber auch ein Satz sein.

Ein Array von zwei Zeigern auf char-Variable. Auf Zahlen sollten sie aber nicht zeigen, sonst hätte ich ja int *pcount deklariert.

der Arrayinhalt bleibt
udefiniert.

Nicht ganz. Das Array besteht aus zwei Zeigern, die zufällig irgendwohin zeigen, wie Christof so schön sagte. Aber gut, das kann man sicher als „undefiniert“ bezeichnen.

Generell: wird ein Array von Zeigern auf char deklariert, so
muss nicht zwingend eine Speicherallokation mittels malloc()
statt finden. Richtig?

Hm, ich glaube nicht ganz. Klar, Du kannst auch schreiben

char \*farben[2]= {"rot", "gelb"};

aber da findet die Speicher-Allozierung sozusagen im Hintergrund durch die Zuweisung statt.

Man könnte eine bereits vorhandene Speicheradresse
hineinschreiben: buff[0]=xy (wenn es vorher char xy=„D“ oder
char xy[]=„Das ist ein String“ hieße)?

Ja, das funktioniert. Aber ob das guter Programmierstil ist, weiss ich nicht so recht. Ich habe jedenfalls kein gutes Gefühl dabei, kann es aber nicht begründen.

Hier müssen wir aber Speicher allokieren, da wir die Adresse
an eine andere Funktion weitergeben müssen ohne vorher dieses
char definiert zu haben.

Naja, wir müssen Speicher anfordern, weil wir eine vernünftige Adresse brauchen. Vielleicht meinst Du ja das gleiche.

Die readv()-Funktion liest aus der ler.txt genau soviel Byte
wie sie mit iov_len definiert sind (ist das wirklich so?)

Ja. readv füllt zuerst buff[0], dann buff[1], oder allgemeiner die übergebenen Buffer werden der Reihe nach gefüllt. Wenn die Datei nicht groß genug ist, gehen natürlich der/die letzten Buffer leer aus.
Siehe

man readv

„Buffers are processed in array order. This means that readv() completely fills iov[0] before proceeding to iov[1], and so on. (If there is insufficient data, then not all buffers pointed to by iov may be filled.)“

Mit printf() wird der Inhalt des Arrays, die einzelnen
Register, ausgegeben. Im Array stehen zwar nur Adressen, aber
dadurch, dass die Ausgabe mit %s formatiert ist, wird nicht
die Adresse ausgegeben, sondern der String welcher an der
Adresse beginnt und bis zur binären Null geht.

Ja, sieht man gut an diesem kleinen Beispiel

#include 
#define ANZAHL 2

int main(){
 int i;
 char \*farben[ANZAHL]={"schwarz", "gelb"};
 for(i=0; i
Da steht ja zweimal farben[i], aber das erste Mal wird die Adresse ausgegeben und das zweite Mal der String der an dieser Adresse beginnt:


    Adresse: 0x40061c Wert: schwarz
    Adresse: 0x400624 Wert: gelb




> (Wird die binäre Null durch die readv()-Funktion hinzugefügt?)

Gute Frage, nächste Frage :wink:
Das wüsste ich jetzt auch gern. Bei meinem ersten Beispiel hat es ja nur funktioniert, weil ich die '\0' von Hand hinzugefügt habe. Jetzt ist das plötzlich nicht mehr notwendig, aber warum, habe ich auch nicht verstanden. Liegt vielleicht eher an der Art, wie ich den Buffer deklariere als an readv. Alles was ich gelesen habe sagt, daß readv die Daten "roh" liest, also ohne Umwandlung und ohne was hinzuzufügen. Von dort kommt die 0 also eher nicht. Aber das sollen mal die wahren Experten erklären...


> Es kann doch nicht sein, dass ich alles richtig kapiert habe,  
> oder? :smile:

Doch, doch. Die nächste Klausur schreibst Du mit Bravour.

Viele Grüße
Marvin

Hallo Christof und alle anderen Zuschauer.

Bin mir nicht sicher wie weit ich diesen Baum auffächern darf und wie weit ich mich von meiner ursprünglichen Frage entfernen darf.

Meine Frage bezieht sich auf die vorletzte Codezeile aus deinem Code mit den Zeigern:

printf(„Inhalt buff[0]: %s\n“,buff[0]);

Ist das richtig, dass mein der GCC sich weigert, wenn ich die Zeile wie folgt ändere:

printf("Inhalt buff[0]: %s\n",buff);

Die Meldung des GCC: warning: format ‘%s’ expects type ‘char *’, but argument 2 has type ‘char **’

buff und buff[0] verweisen doch beide auf buff[0].
Irgendwie scheint es jetzt verwirrend zu werden: ist buff ein Zeiger der auf ein Array mit Zeigern zeigt?

Gruß
Vitali

Danke für’s Mutmachen und die qualifizierten Antworten, Marvin. Deine und Christofs Antworten haben mir zum besseren Verständnis veholfen.

readv() ist nicht der einzige Systemaufruf den ich benötigen werde. fork(), execl(), system() und so weiter. Da werde ich diese Tage sicherlich noch Fragen haben. Aber diese dann wieder im Linux Thread.

Grüße
Vitali

Hallo Vitali !

ist buff ein Zeiger der auf ein Array mit Zeigern zeigt?

Ja.
buff ist ja als

char \*buf[2]

definiert, also als 1-dimensionales Array mit 2 Elementen das Pointer auf char enthält. buf ist also vom Typ char **.
Mit

char buf[2]

hättest du ein 1-dimensionales Array mit 2 Elementen das char enthält. buf ist also vom typ char *.
Mit

int \*buf[2]

hättest du ein 1-dimensionales Array mit 2 Elementen das Pointer auf int enthält. buf ist also vom typ int **.
Mit

int buf[2]

hättest du ein 1-dimensionales Array mit 2 Elementen das int enthält. buf ist also vom Typ int *.

mfg
Christof