Helmut Schellong <
r...@schellong.biz>:
Ein Hinweis, was »result« umfasst, könnten folgende Sätze aus dem
oben genannten Abschnitt »DESCRIPTION« sein:
Before any action described below is taken, and if nbyte is
zero, the read() function may detect and return errors as
described below. In the absence of errors, or if error
detection is not performed, the read() function shall return
zero and have no other results.
»shall return zero and have no other results« legt nahe, dass
»shall return zero« ein Teil dessen ist, was »results« umfasst,
denn anderenfalls wäre das Wort »other« fehl am Platz.
»no other results« legt nahe, dass »results« nicht nur den
Funktionswert sondern die ganze Wirkung, die der Funktionsaufruf
hat, umfasst.
Das im Hinterkopf behaltend, nochmal zitiert:
>> If the value of nbyte is greater than {SSIZE_MAX}, the
>> result is implementation-defined.
=> Wenn man read mit einem Größenparameter > SSIZE_MAX aufruft,
ist alles – Funktionswert und Wirkung – implementation‐defined.
<
http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap01.html#tag_01_05_02>
erklärt »implementation‐defined«:
implementation-defined
Describes a value or behavior that is not defined by
POSIX.1-2017 but is selected by an implementor. The value or
behavior may vary among implementations that conform to
POSIX.1-2017. An application should not rely on the existence
of the value or behavior. An application that relies on such a
value or behavior cannot be assured to be portable across
conforming implementations.
The implementor shall document such a value or behavior so that
it can be used correctly by an application.
=> Wer will, dass sein Programm unter allen POSIX‐konformen
Umgebungen das gleiche Verhalten hat, muss den Größenparameter auf
Werte zwischen 0 und SSIZE_MAX beschränken.
Wer größere Werte nutzen will, dem garantiert der POSIX‐Standard,
dass er in der Dokumentation der Implementierung, die er nutzt,
nachlesen kann, was »read()« dann tut.
Die Implementierung ist völlig frei darin, festzulegen, was sie
dann tut. Angefangen von Stefans Beispiel einer Implementierung
auf einer 16‐Bit‐Maschine, bei der SSIZE_MAX = 32768 und
SIZE_MAX = 65536 ist und jeder negative Funktionswert < -1 nicht
als Fehleranzeiger sondern als Funktionswert + 65536 zu verstehen
ist, über eine Implementierung, die keine Daten liest und -1 als
Funktionswert liefert (also einen Fehler anzeigt), bis zu einer
Implementierung, die den aufrufenden Prozess mit einem KILL‐Signal
erschlägt, ist alles möglich. Es muss nur dokumentiert sein.
(In der »read«‐Handbuchseite meines Rechners heißt es:
SYNOPSIS
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
[…]
If count is greater than SSIZE_MAX, the result is
unspecified.
Auf gut Deutsch: »Wir wollen oder können nicht sagen, was dann
geschieht«.)
>
>In der Rationale steht noch folgendes:
>
>|This volume of POSIX.1-2017 also limits the range further
>|by requiring that the byte count be limited so
>|that a signed return value remains meaningful.
>|Since the return type is also a (signed) abstract type, the
>|byte count can be defined by the implementation
>|to be larger than an int can hold.
Da kommt mir noch eine Idee:
<
http://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html#tag_13_65_03>:
DESCRIPTION
The <sys/types.h> header shall define at least the following
types:
[…]
size_t
Used for sizes of objects.
ssize_t
Used for a count of bytes or an error indication.
Dass »an error indication« tatsächlich der Wert -1 sein muss,
steht da nicht. Könnte es nicht auch ein Wert sein, der beim
Vergleich mit -1 den Wahrheitswert »wahr« liefert, wie im
folgenden?
Wenn ssize_t ein unsigned Integer‐Typ wäre, würde der
C‐Compiler im Code‐Schnipsel
ssize_t rc=read(…);
if (rc == (ssize_t) -1)
// Fehlerfall hier behandeln
den Fehlertest »rc == (ssize_t) -1« in
»rc == SSIZE_MAX«
umsetzen.
Wenn ssize_t dabei mindestens die Größe des Integer‐Typs unsigned
int hätte, könnte man den Typecast »(ssize_t)« sparen:
ssize_t rc=read(…);
if (rc == -1)
// Fehlerfall hier behandeln
Das ist quellcode‐kompatibel zu dem Fall, dass ssize_t ein signed
Typ ist.
Es funktioniert nur, wenn die Maschine negative Zahlen im
Zweierkomplement darstellt: Bei Einerkomplementdarstellung gibt
es keine negative Zahl, die beim Umwandeln per Type‐Cast die
positive Zahl SSIZE_MAX + 1 ergibt.
Scheitern würde allerdings
»if (rc < 0)«, weil »rc« keine Werte < 0 annehmen könnte.
Ist irgendwo festgelegt, dass »ssize_t« ein signed Integer‐Typ
sein muss? Falls nicht, spräche das sehr dafür, den Fehlertest am
Funktionswert der Funktion »read()« nicht auf < 0 sondern auf
== -1 zu machen.
>
>> <
http://pubs.opengroup.org/onlinepubs/9699919799/functions/read.html#tag_16_474_04>:
>>
>> RETURN VALUE
>>
>> Upon successful completion, these functions shall return a
>> non-negative integer indicating the number of bytes actually
>> read. Otherwise, the functions shall return -1 and set errno
>> to indicate the error.
>>
>> => Über Funktionswerte <-1 wird im POSIX‐Standard keine Aussage
>> gemacht, außer der, dass für den Fall, dass mehr als SSIZE_MAX
>> Bytes gelesen werden sollen, die Implementierung festlegen muss,
>> was dann geschehen soll. Der C‐Standard macht überhaupt keine
>> Aussage, weil die Funktion »read()« nicht darin enthalten ist.
>
>POSIX legt nicht fest, daß nbyte>SSIZE_MAX eine Wirkung
>auf den return-value haben soll.
POSIX legt fest, dass nbyte > SSIZE_MAX die Wirkung hat, die von
der Implementierung definiert ist. Diese Wirkung kann (s. o.)
auch den Funktionswert umfassen.
=> Die Implementierung ist völlig frei darin, was sie im Fall
nbyte > SSIZE_MAX macht – den Funktionswert eingeschlossen. Sie
muss es allerdings festlegen.