?
while( 1==sscanf(s+=n,"%[^\"]%n",tmp1,&n) )
%[^\"] matcht einen String aller Zeichen außer ", liest also alle Zeichen bis zum nächsten " ein. %n danach weist die Länge des gematchten Strings der zugeordneten Variable (in diesem Fall n) zu.
if( s[n]=='\"' && (n++, x++&1) )
Die Bedingung, die geprüft wird, ist "wenn s[n] ein Anführungszeichen und x ungerade ist". Als Nebeneffekt werden n und x erhöht, wenn s[n] ein Anführungszeichen ist. s[n] ist das Zeichen nach dem gerade eingelesenen Token im Originalstring (s wird jeweils vor dem Einlesen auf den Anfang des nächsten Tokens adjustiert), und x (bzw. sein letztes Bit) ist als boolscher Wert zu verstehen, der angibt, ob man sich gerade innerhalb eines String-Literals befindet oder nicht. Ggf. wird der gerade ausgelesene Token weiter auseinandergenommen. s[n] ist immer ein Anführungszeichen, außer am Ende des letzten Tokens, falls dieser nicht in Anführungszeichen steht - es wird hier ja nach Anführungszeichen gesplittet.
while( 1==sscanf(tmp2+=m,"%s%n",tmp3,&m) )
Vergleichbar zum ersten Codestück, nur wird nicht nach ", sondern nach Whitespaces gesplittet (%s halt).
Dazu ist anzumerken, dass Code der Form
p = realloc(p, new_size);
keine gute Idee ist - wenn die Allokation mal daneben gehen sollte, verlierst du auf die Art eh schon kargen Speicher. Das wäre hier im Forum kein großes Problem, wenn der Code dazu geeignet wäre, ein Konzept zu verdeutlichen - man schriebe es in eine Anmerkung und überließe es dem Benutzer, die Fehlerbehandlung gemäß seinen Wünschen zu implementieren, aber bei Wutzs Code handelt es sich um das, was die Jargon-File write-only code nennt. Ich kann dir nur dringendst ans Herz legen, dir einen übersichtlichen Programmierstil anzugewöhnen - Code, in dem jede zweite Zeile sich auf Nebeneffekte verlässt, die man beim Lesen leicht übersieht, haut dir jeder Arbeitgeber, der etwas von Technik versteht und mal auf den Code seiner Angestellten kuckt, um die Ohren. Sogar derjenige, der ihn ursprünglich geschrieben hat, wird zwei Jahre später davor sitzen und sich fragen, was er sich dabei nur gedacht hat.
In der Kürze liegt Würze, aber es geht dabei um strukturelle Kürze, nicht darum, Zeilenumbrüche zu sparen. Es ist wichtig zu verstehen, dass es bei der Programmierung in erster Linie nicht darum geht, eine Maschine dazu zu überreden, etwas zu tun, sondern um Organisation - dein Job ist es, ein komplexes Problem in weniger komplexe Probleme zu zerlegen, um diese einzeln zu lösen. Es ist in der Regel weder hilfreich, mehrere einfache Probleme in ein komplexeres Problem zusammenzufassen, um sie zusammen zu lösen, noch die Verständlichkeit der Lösung dadurch zu senken, dass man sie mit bedeutungslosen Bezeichnern betreibt. Wenn ich
struct foo {
struct foo *p, *q;
int x;
};
struct foo *bar(struct foo *s, int x) {
struct foo *q = malloc(sizeof(struct foo));
return q->x = x, q->q = 0, q->p = s, s->q = q;
}
schreibe, musst du erstmal nachdenken, worauf ich damit hinaus will. Schreibe ich dagegen
struct list {
struct list *next;
struct list *prev;
int value;
};
struct list *list_push_front(struct list *head, int value) {
struct list *new_head = malloc(sizeof(struct list));
new_head->value = value;
new_head->prev = 0;
new_head->next = head;
head ->prev = new_head;
return new_head;
}
ist es sofort verständlich. Variablennamen wie tmp1, tmp2 und tmp3 sind nicht hilfreich.
Außerdem betreibt man die Allokation Arrays variabler Länge üblicherweise blockweise, um nicht andauernd an den relativ teuren Heap gehen zu müssen, zumal das diesen auf Dauer fragmentiert. Besonders in nebenläufigen Applikationen kann es ein echtes Performanceproblem werden, wenn sich mehrere Threads dauernd um den Heap streiten müssen.