Warum funktioniert das? [gelöst]

Vom einfachen Programm zum fertigen Debian-Paket, Fragen rund um Programmiersprachen, Scripting und Lizenzierung.
Antworten
dakuan
Beiträge: 107
Registriert: 28.04.2011 22:09:39

Warum funktioniert das? [gelöst]

Beitrag von dakuan » 15.06.2021 19:49:22

oder habe ich etwas übersehen?

Ich benötige gerade eine C-Funktion, die aus einem String etwas herausschneidet. Bei der ersten Version wurde nicht immer das abschließende Nullbyte mit kopiert. strcpy() kann ich dafür nicht nehmen, da die Strings sich nicht überschneiden dürfen.

Ich habe dann mal im Quellcode von Small-C (uralt) nachgesehen wie strcpy() dort realisiert wurde. Herausgekommen ist dann so etwas:

Code: Alles auswählen

void char_shift( char * d, int offset ) {
    char * s = d + offset;
    while( (*d++ = *s++) );
}
Das funktioniert auch (erstmal), aber ich verstehe nicht ganz warum. Gegenüber der Originalversion musste ich nur die internen Klammern hinzufügen, sonst kommt folgende Warnung:

Code: Alles auswählen

mimax_com.c: In function ‘char_shift’:
mimax_com.c:104:12: warning: suggest parentheses around assignment used as truth value [-Wparentheses]
     while( *d++ = *s++ );
            ^
BTW, die Funktion wird nur aufgerufen wenn offset > 0 ist.
Zuletzt geändert von dakuan am 16.06.2021 18:55:03, insgesamt 1-mal geändert.

Benutzeravatar
Meillo
Moderator
Beiträge: 9241
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Warum funktioniert das?

Beitrag von Meillo » 15.06.2021 19:57:27

Ueberschneiden sich deine Strings oder nicht? Beschreibe bitte genau wie der Speicher deines Quell- und Zielstrings aussieht. Am besten du postest ein Minimal-Code-Beispiel. Dann wird viel schneller klar wie sich die Situation verhaelt und die Erklaerungen koennen exakt und auch nachpruefbar sein.
Use ed once in a while!

dakuan
Beiträge: 107
Registriert: 28.04.2011 22:09:39

Re: Warum funktioniert das?

Beitrag von dakuan » 15.06.2021 20:13:35

Ja, die Strings überschneiden sich. Da wird "in place" ein Teil herausgeschnitten, z.B. so etwas:
",n:##"
Das ist eine Zählerangabe mit 2 oder 3 Ziffern und später sollen noch weitere Angaben dazu kommen.

Minimalcode schaffe ich heute nicht mehr, ich dachte leichtsinnigerweise es geht auch ohne. Ein kleines Beispiel:

Code: Alles auswählen

alt: irgend ein Text,n:20,sz:900-1280
neu: irgend ein Text,sz:900-1280

Benutzeravatar
Meillo
Moderator
Beiträge: 9241
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Warum funktioniert das?

Beitrag von Meillo » 15.06.2021 20:40:07

Wenn man byteweise von hinten nach vorne kopiert, dann funktioniert das mit so trivialen strcpy(3)-Implementierungen wie die von der du den Code gepostet hast. Implementierungen in optimierten C-Libraries kopieren vermutlich nicht byteweise sondern versuchen das ``besser'' zu machen, was in so Faellen dann wohl nicht mehr geht.

Deine Implementierung funktioniert in anderen Faellen auch nicht, z.B. strcpy(s+8, s) ... einen genuegend grossen Buffer vorausgesetzt:

Code: Alles auswählen

alt: 1280-xxxxxxxx
neu: 1280-xxx1280-xxxxxxxx
Das Nach-vorne-Ruecken ist ein Sonderfall, den eine Implementierung dieser (naheliegenden trivialen) Art auch bei Ueberlappung beherrscht. Andere Implementierungen oder andere ueberlappende Faelle funktionieren nicht. Darum sagt meine Manpage:
Manpage zu strcpy(3) hat geschrieben: For all of strcpy(), strncpy(), stpcpy(), and stpncpy(), the result is
undefined if src and dst overlap.
Was rauskommt ist undefiniert: Es kann klappen oder auch nicht. Das haengt von der konkreten Situation und von der strcpy-Implementierung ab.


Edit:

Dein char_shift() ist doch eine tolle Hilfsfunktion. Ich wuerde nur noch etwas die Benennung verbessern: `str' statt `char', weil sie auf einem String arbeitet und nicht mit einem einzelnen Char ... also vielleicht `strdelpart'. Und statt `offset' besser `nchars' -- so viele Zeichen willst du ja aus dem String loeschen. Den ersten Parameter wuerde ich vielleicht `from' nennen.

Benennungen sollte man aus Aufrufersicht machen. Deine Funktion entfernt aus dem String, den du im ersten Argument uebergibst, die ersten n Zeichen, was im zweiten Argument angegeben ist.
Use ed once in a while!

dakuan
Beiträge: 107
Registriert: 28.04.2011 22:09:39

Re: Warum funktioniert das?

Beitrag von dakuan » 15.06.2021 20:48:57

Ok, noch 12 Minuten bis zum Fußballspiel.

In meinem Fall ist garantiert, dass der String kleiner wird. Aber was ich nicht verstehe ist die Abbruchbedingung der Schleife. Es ist ja kein Test sondern eigentlich nur eine Zuweisung.

Benutzeravatar
Meillo
Moderator
Beiträge: 9241
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Warum funktioniert das?

Beitrag von Meillo » 15.06.2021 20:56:54

dakuan hat geschrieben: ↑ zum Beitrag ↑
15.06.2021 20:48:57
In meinem Fall ist garantiert, dass der String kleiner wird. Aber was ich nicht verstehe ist die Abbruchbedingung der Schleife. Es ist ja kein Test sondern eigentlich nur eine Zuweisung.
In C (und den C-verwandten Sprachen) ist eine Zuweisung zugleich eine Expression. Sie evaluiert also zu dem Wert, der zugewiesen worden ist.

Wenn das abschliessende Null-Byte vom rechten Pointer in den linken kopiert worden ist, ist das zugleich der Wert des Ausdrucks und da '\0' gleich 0 und damit boolean false ist, bricht die Schleife danach ab. ;-)

Der Compiler warnt vor Zuweisungen als Schleifenbedingungen, weil vielleicht ja `==' gemeint war. Wenn man tatsaechlich den Wert der Zuweisung evaluieren will (was bei solchen Pointer-Interations-Schleifen in C ueblich ist), dann ist die Konvention, ein weiteres (semantisch bedeutungsloses) Paar runder Klammern hinzuzufuegen. Der syntaktische Unterschied faellt dem Code-Leser dann auf und er weiss, dass die Zuweisung als Schleifenbedingung Absicht ist. Moderne Compiler denken da freundlicherweise fuer den Programmierer mit.


(Pass nur auf, nicht dass dieses C-Zeug am Ende noch spannender ist als das Fussballspiel. :-D ... Viel Vergnuegen!)
Use ed once in a while!

Benutzeravatar
MSfree
Beiträge: 11608
Registriert: 25.09.2007 19:59:30

Re: Warum funktioniert das?

Beitrag von MSfree » 15.06.2021 20:57:32

dakuan hat geschrieben: ↑ zum Beitrag ↑
15.06.2021 20:48:57
Aber was ich nicht verstehe ist die Abbruchbedingung der Schleife.

Code: Alles auswählen

while( (*d++ = *s++) );
ist nichts anderes als

Code: Alles auswählen

while( ( *d = *s ) ) {
  d++;
  s++;
}

Benutzeravatar
Meillo
Moderator
Beiträge: 9241
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Warum funktioniert das?

Beitrag von Meillo » 15.06.2021 21:01:41

MSfree hat geschrieben: ↑ zum Beitrag ↑
15.06.2021 20:57:32
dakuan hat geschrieben: ↑ zum Beitrag ↑
15.06.2021 20:48:57
Aber was ich nicht verstehe ist die Abbruchbedingung der Schleife.

Code: Alles auswählen

while( (*d++ = *s++) );
ist nichts anderes als

Code: Alles auswählen

while( ( *d = *s ) ) {
  d++;
  s++;
}
Ja. ... bloss kuerzer und cooler (und abschreckender fuer Nicht-C-Programmierer :-P ) ... und einfach besser. ;-)


Allerdings finde ich so Strichpunkte am Zeilenende statt einem Schleifenrumpf schlecht lesbar. Ich schreibe das so:

Code: Alles auswählen

while ((*d++ = *s++)) {
}
Use ed once in a while!

dakuan
Beiträge: 107
Registriert: 28.04.2011 22:09:39

Re: Warum funktioniert das?

Beitrag von dakuan » 15.06.2021 22:18:00

In C (und den C-verwandten Sprachen) ist eine Zuweisung zugleich eine Expression. Sie evaluiert also zu dem Wert, der zugewiesen worden ist.
Das war mir bisher so nicht klar. Und das erklärt auch, warum das Nullbyte noch kopiert wird.

Mit passenden Name hatte ich schon immer Probleme, aber die Vorschläge werde ich nach dem Fußballspiel abarbeiten.
Wenn man tatsaechlich den Wert der Zuweisung evaluieren will (was bei solchen Pointer-Interations-Schleifen in C ueblich ist), dann ist die Konvention, ein weiteres (semantisch bedeutungsloses) Paar runder Klammern hinzuzufuegen.
So einen Fall hatte ich bisher noch nicht bewusst erlebt. deshalb die Fragezeichen in meinen Augen.
Allerdings finde ich so Strichpunkte am Zeilenende statt einem Schleifenrumpf schlecht lesbar.
Das stammt aus der Small-C Vorlage. Könnte man auch in der nächsten Zeile eingerückt machen.
(Pass nur auf, nicht dass dieses C-Zeug am Ende noch spannender ist als das Fussballspiel. :-D ... Viel Vergnuegen!)
Ich wollte eigentlich schreiben dass dem so ist, aber die letzten Minuten veranlassen mich jetzt doch wieder zum großen TV ins Wohnzimmer zu wechseln. Bis später.

Benutzeravatar
Meillo
Moderator
Beiträge: 9241
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Warum funktioniert das?

Beitrag von Meillo » 15.06.2021 22:53:07

:THX:
Use ed once in a while!

dakuan
Beiträge: 107
Registriert: 28.04.2011 22:09:39

Re: Warum funktioniert das?

Beitrag von dakuan » 16.06.2021 18:54:15

Eigentlich hätte ich das erkennen müssen. Schließlich benutze ich oft etwas ähnliches:

Code: Alles auswählen

    if( (p = malloc( size )) == NULL )
Aber ohne den Vergleich sieht das eben ungewohnt aus.

Benutzeravatar
Meillo
Moderator
Beiträge: 9241
Registriert: 21.06.2005 14:55:06
Wohnort: Balmora
Kontaktdaten:

Re: Warum funktioniert das? [gelöst]

Beitrag von Meillo » 16.06.2021 21:07:02

Man haette es auch so schreiben koennen:

Code: Alles auswählen

while ((*d++ = *s++) != '\0') {
}
... macht halt bloss kein( C-Programmier)er. ;-)
Use ed once in a while!

dakuan
Beiträge: 107
Registriert: 28.04.2011 22:09:39

Re: Warum funktioniert das? [gelöst]

Beitrag von dakuan » 16.06.2021 21:30:44

... macht halt bloss kein( C-Programmier)er.
Ja, die sind manchmal etwas verschrien. Da hatte mal jemand über die Programmiersprache C geschrieben:
Sieht aus als hätte man ein zusammengerolltes Gürteltier auf der Tastatur hin und her gerollt.
Das fand ich allerdings schon damals etwas übertrieben. Trifft wohl eher auf Perl oder Brainfuck zu.

Antworten