Forum: PC-Programmierung glibc: abs() hat int als Rückgabetyp. Warum?


von zitter_ned_aso (Gast)


Lesenswert?

Hallo,

warum hat die Funktion abs() "int" als Rückgabetyp?

https://github.com/bminor/glibc/blob/master/stdlib/abs.c

http://git.musl-libc.org/cgit/musl/tree/src/stdlib/abs.c

Das würde doch bei INT_MIN einen falschen Wert liefern. Weil INT_MAX ja 
um 1 kleiner als |INT_MIN| (Betrag) ist.

Ein "unsigned int"-Wert wäre doch sinnvoller oder nicht?

von ichWillNurRaus (Gast)


Lesenswert?

Nicht unbedingt: in den meisten realen Anwendungen willst du das 
Ergebnis ja weiterverarbeiten. Addieren, multiplizieren, vergleichen. 
Und da nützt dir ein unsigned dazwischen meist nichts.

von Michael Gugelhupf (Gast)


Lesenswert?

zitter_ned_aso schrieb:
> Das würde doch bei INT_MIN einen falschen Wert liefern. Weil INT_MAX ja
> um 1 kleiner als |INT_MIN| (Betrag) ist.

Zumindest beim Zweierkomplement. Daher ist auf solchen Systemen 
abs(INT_MIN) nicht definiert. Bei Systemen mit Einerkomplement ist es 
kein Problem, da dort abs(INT_MAX) == abs(INT_MIN) == INT_MAX ist.

> Ein "unsigned int"-Wert wäre doch sinnvoller oder nicht?

Das ist so eine Sache. abs() ist schon ziemlich lange in C. Meines 
Wissens war es schon in der ersten Version des ANSI-C Standards im Jahr 
1989. Schon damals war bekannt dass abs(INT_MIN) ein Problem sein kann. 
Das Verhalten nach 30 Jahren zu ändern? Wer weiß wie viel vorhandener 
Code dann auseinander fliegt.

von Bastler (Gast)


Lesenswert?

zitter_ned_aso schrieb:
> warum hat die Funktion abs() "int" als Rückgabetyp?

Weil in den meisten Programmiersprachen, C eingeschlossen, der Datentyp 
nichts mit dem Zahlenart im mathematischen Sinne zu tun hat.

von Jens (Gast)


Lesenswert?

Michael Gugelhupf schrieb:
> Daher ist auf solchen Systemen abs(INT_MIN) nicht definiert.

Damit kann man übrigens schöne vulnerabilities bauen. Haben wir im 
Studium als Übungsaufgabe bekommen (vuln finden und exploiten).

> Das Verhalten nach 30 Jahren zu ändern? Wer weiß wie viel vorhandener
> Code dann auseinander fliegt.

Korrekt. Eigentlich muss man vor jeder Verwendung einer 
Standard-C-Funktion die zugehörige manpage lesen und sich solcher und 
ähnlicher Fallstricke bewusst sein.

man 3 abs:
> NOTES
>        Trying to take the absolute value of the most negative
>        integer is not defined.

von Nop (Gast)


Lesenswert?

ichWillNurRaus schrieb:

> Und da nützt dir ein unsigned dazwischen meist nichts.

Schlimmer noch: UNsigned ist ANsteckend, so die Eselsbrücke. Da würde 
ansonsten der ganze Ausdruck mit unsigned gerechnet.

von Rolf M. (rmagnus)


Lesenswert?

Bastler schrieb:
> zitter_ned_aso schrieb:
>> warum hat die Funktion abs() "int" als Rückgabetyp?
>
> Weil in den meisten Programmiersprachen, C eingeschlossen, der Datentyp
> nichts mit dem Zahlenart im mathematischen Sinne zu tun hat.

Aber eigentlich wählt man den Datentyp geschickterweise so, dass er den 
nötigen Wertebereich auch voll unterstützt. Das ist hier nicht gegeben, 
da abs(INT_MAX) eben beim Zweierkomplement auf Grund des Rückgabetyps 
nicht darstellbar ist.

Jens schrieb:
> Eigentlich muss man vor jeder Verwendung einer Standard-C-Funktion die
> zugehörige manpage lesen und sich solcher und ähnlicher Fallstricke bewusst
> sein.

Nicht unbedingt vor jeder Verwendung, denn diese Fallstricke ändern sich 
ja nicht dauernd. Aber man sollte für jede Funktion wissen, welche 
potenziellen Probleme es damit geben kann und diese entsprechend 
berücksichtigen.
Wenn man eine Funktion nur alle 5 Jahre mal benutzt, ist es aber 
natürlich besser, jedes mal nachzuschauen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

zitter_ned_aso schrieb:
> warum hat die Funktion abs() "int" als Rückgabetyp?

> Ein "unsigned int"-Wert wäre doch sinnvoller oder nicht?

Aus dem gleiche Grund könntest du fragen, warum die Addition und
Multiplikation zweier int-Werte wieder ein int und nicht ein long
ergeben.

In C sind alle arithmetischen Datentypen mit einer Größe von mindestens
int bzgl. aller darauf definierter Rechenoperationen abgeschlossen (bzw.
für bestimmte Operanden undefiniert). Da wäre es komisch, wenn abs eine
Ausnahme machen würde.

: Bearbeitet durch Moderator
von Rolf M. (rmagnus)


Lesenswert?

Yalu X. schrieb:
> Aus dem gleiche Grund könntest du fragen, warum die Addition und
> Multiplikation zweier int-Werte wieder ein int und nicht ein long
> ergeben.

Gerade bei der Multiplikation wäre das durchaus sehr praktisch gewesen. 
Auf Assembler-Ebene passiert meistens genau das: Das Ergebnis ist 
doppelt so breit wie die Operanden. Aber da C das nicht auch so macht, 
muss man Multiplikation dort öfter mal mit dem doppelten der eigentlich 
benötigten Bitbreite durchführen, damit nicht die Hälfte des Ergebnisses 
verworfen wird.
1
void func(int16_t a, int16_t b)
2
{
3
    // Rechnung als 16 Bit * 16 Bit, Ergebnis wird abgeschnitten
4
    int32_t result = a * b;
5
6
     // korrektes Ergebnis, aber Multiplikation muss (meist unnötigerweise) in
7
     // 32 Bit durchgeführt werden.
8
     int32_t result = (int32_t)a * (int32_t)b;
9
}

von Nop (Gast)


Lesenswert?

Rolf M. schrieb:

>     // Rechnung als 16 Bit * 16 Bit, Ergebnis wird abgeschnitten
>     int32_t result = a * b;

Das kommt drauf an, was auf der jeweiligen Plattform denn die Breite von 
int ist - Stichwort "integer promotion". Wenn int als 32 bit ist, wird 
da nichts abgeschnitten.

von Michael Gugelhupf (Gast)


Lesenswert?

Rolf M. schrieb:
> Aber da C das nicht auch so macht,
> muss man Multiplikation dort öfter mal mit dem doppelten der eigentlich
> benötigten Bitbreite durchführen, damit nicht die Hälfte des Ergebnisses
> verworfen wird.

Dass ist dann der Moment wo du hoffst, dass der Optimizer erkennt was 
wirklich passieren soll und selbstständig eine 16x16 -> 32 Bit 
Multiplikation einfügt.

von MaWin O. (mawin_original)


Lesenswert?

Michael Gugelhupf schrieb:
> dass der Optimizer erkennt was
> wirklich passieren soll und selbstständig eine 16x16 -> 32 Bit
> Multiplikation einfügt.

Das hat überhaupt nichts mit dem Optimizer zu tun und irgendeiner 
magischen Erweiterung von 16 auf 32 Bit.
Die Prozesse, die im Compiler ablaufen, heißen Typ-Promotion und 
Typ-Balancierung. Diese sind sehr genau definiert und keineswegs 
zufällig von irgendeiner Implementierung des Optimizers abhängig.

von Michael Gugelhupf (Gast)


Lesenswert?

MaWi N. schrieb:
> Das hat überhaupt nichts mit dem Optimizer zu tun und irgendeiner
> magischen Erweiterung von 16 auf 32 Bit.
> Die Prozesse, die im Compiler ablaufen, heißen Typ-Promotion und
> Typ-Balancierung. Diese sind sehr genau definiert und keineswegs
> zufällig von irgendeiner Implementierung des Optimizers abhängig.

Das ist jetzt auch wieder so eine Antwort die du nur geschrieben hast um 
irgend was zu sagen?

Im C Standard ist klar geregelt das der Compiler optimieren darf wenn 
das Ergebnis genau so aussieht wie wenn er die vorgegebenen Abläufe 
wörtlich ausführen würde.

von Rolf M. (rmagnus)


Lesenswert?

MaWi N. schrieb:
> Michael Gugelhupf schrieb:
>> dass der Optimizer erkennt was
>> wirklich passieren soll und selbstständig eine 16x16 -> 32 Bit
>> Multiplikation einfügt.
>
> Das hat überhaupt nichts mit dem Optimizer zu tun und irgendeiner
> magischen Erweiterung von 16 auf 32 Bit.

Doch, hat es. Du hast das Problem nur noch nicht verstanden.

> Die Prozesse, die im Compiler ablaufen, heißen Typ-Promotion und
> Typ-Balancierung. Diese sind sehr genau definiert und keineswegs
> zufällig von irgendeiner Implementierung des Optimizers abhängig.

Das hat auch keiner behauptet. Wenn ich aber einen 16 Bit breiten int 
habe und den mit einem zweiten int multipliziere, wird eine 
16-Bit-Multiplikation durchgeführt und damit ist das Ergebnis aber auch 
auf 16 Bit begrenzt, selbst wenn der Prozessor eine Instruktion hat, die 
daraus ein 32-Bit-Ergebnis produziert. Dann wirft der Compiler einfach 
die obere Hälfte des Ergebnisses weg.
Will ich ein 32-Bit-Ergebnis, muss ich mindestens einen der Operanden 
auf 32 Bit casten, obwohl ich nur eine 16x16-Bit-Multikplikation 
brauche. Und dann muss ich hoffen, dass der Compiler merkt, dass der 
Operand eigentlich ein 16-Bit-Wert wäre und nur auf 32 Bit gecastet 
wird, damit ich auch ein 32-bit-Ergebnis bekomme.

von Bastler (Gast)


Lesenswert?

Rolf M. schrieb:
> Aber eigentlich wählt man den Datentyp geschickterweise so, dass er den
> nötigen Wertebereich auch voll unterstützt.

Ein frommer Wunsch, der eine Sprache wie C zur praktischen 
Nicht-Nutzbarkeit degradieren würde.

Selbst bei einer einfachen Addition müsste der Ergebnis-Datentyp um 
mindestens 1-Bit größer sein, als der größte der beiden Operanden.

Der Denkfehler ist, ein C-Datentyp wie "int" wäre identisch zum 
Zahlenbereich der ganzen Zahlen mit ihren Gesetzen.

von Bastler (Gast)


Lesenswert?

Rolf M. schrieb:
> Und dann muss ich hoffen, dass der Compiler merkt, dass der
> Operand eigentlich ein 16-Bit-Wert wäre und nur auf 32 Bit gecastet
> wird, damit ich auch ein 32-bit-Ergebnis bekomme.

Die Vorstellung, die Definition einer Programmiersprache wie C hat etwas 
mit einer konkreten Implementierung für einen spezifischen Prozessor zu 
tun, ist infantil.

Und ja, wenn Du in der Sprache C ein 32-Bit Ergebnis einer 
16x16-Multiplikation haben willst, musst Du vorher casten. Genau so 
funktioniert es eben in "C". Wie das der Compiler in Maschinensprache 
umsetzt, ist in C eben nicht definiert.

von Nop (Gast)


Lesenswert?

Rolf M. schrieb:

> Das hat auch keiner behauptet. Wenn ich aber einen 16 Bit breiten int
> habe und den mit einem zweiten int multipliziere, wird eine
> 16-Bit-Multiplikation durchgeführt und damit ist das Ergebnis aber auch
> auf 16 Bit begrenzt

Hast Du Beitrag "Re: glibc: abs() hat int als Rückgabetyp. Warum?" nicht 
gelesen oder nicht verstanden?

Beitrag #6312428 wurde vom Autor gelöscht.
von Rolf M. (rmagnus)


Lesenswert?

Bastler schrieb:
> Rolf M. schrieb:
>> Und dann muss ich hoffen, dass der Compiler merkt, dass der
>> Operand eigentlich ein 16-Bit-Wert wäre und nur auf 32 Bit gecastet
>> wird, damit ich auch ein 32-bit-Ergebnis bekomme.
>
> Die Vorstellung, die Definition einer Programmiersprache wie C hat etwas
> mit einer konkreten Implementierung für einen spezifischen Prozessor zu
> tun, ist infantil.

Allerdings ist es genauso "infantil" zu glauben, dass 
Programmiersprachen, zumindest solche eher hardwarenahen wie C, völlig 
losgelöst von jeglichen Hardware-Eigenschaften der üblichen 
Architekturen entwickelt werden. Denn am Ende soll die Sache ja auch 
performant sein.

> Und ja, wenn Du in der Sprache C ein 32-Bit Ergebnis einer
> 16x16-Multiplikation haben willst, musst Du vorher casten. Genau so
> funktioniert es eben in "C".

Ist das deiner Erfahrung nach auch die Art, wie es die überwiegende Zahl 
der CPU-Architekturen umsetzt? Die, die ich kenne, machen es alle nicht 
so.

> Wie das der Compiler in Maschinensprache umsetzt, ist in C eben nicht
> definiert.

von MaWin O. (mawin_original)


Lesenswert?

Rolf M. schrieb:
> Das hat auch keiner behauptet. Wenn ich aber einen 16 Bit breiten int
> habe und den mit einem zweiten int multipliziere, wird eine
> 16-Bit-Multiplikation durchgeführt

Nein. Das ist so pauschal gesagt nicht richtig.
Typ-Promotion.

von MaWin O. (mawin_original)


Lesenswert?

Rolf M. schrieb:
> Wenn ich aber einen 16 Bit breiten int
> habe und den mit einem zweiten int multipliziere, wird eine
> 16-Bit-Multiplikation durchgeführt und damit ist das Ergebnis aber auch
> auf 16 Bit begrenzt, selbst wenn der Prozessor eine Instruktion hat, die
> daraus ein 32-Bit-Ergebnis produziert. Dann wirft der Compiler einfach
> die obere Hälfte des Ergebnisses weg.

Falsch.
1
$ cat t.c 
2
#include <stdio.h>
3
#include <stdint.h>
4
int main(void)
5
{
6
        int16_t a = 30000;
7
        int16_t b = 30000;
8
        int32_t c = a * b;
9
        printf("%d\n", c);
10
}
11
$ gcc -o t t.c
12
$ ./t
13
900000000

von Yalu X. (yalu) (Moderator)


Lesenswert?

@MaWi: Ihr redet aneinander vorbei.

Rolf schrieb von einem 16 Bit breiten int, du hingegen verwendest auf
einem System mit 32 Bit breitem int ein int16_t. Auf diesem System ist
int16_t eben etwas anderes als int.

von Rolf M. (rmagnus)


Lesenswert?

Yalu X. schrieb:
> @MaWi: Ihr redet aneinander vorbei.
>
> Rolf schrieb von einem 16 Bit breiten int, du hingegen verwendest auf
> einem System mit 32 Bit breitem int ein int16_t. Auf diesem System ist
> int16_t eben etwas anderes als int.

Ja genau. Hiermit:

Rolf M. schrieb:
> Wenn ich aber einen 16 Bit breiten int habe und den mit einem zweiten int
> multipliziere, wird eine 16-Bit-Multiplikation durchgeführt

meinte ich nicht den Fall irgendeines 16 Bit breiten Integer-Typen, 
sondern wirklich speziell den Fall, dass ich einen Compiler habe, bei 
dem der Datentyp "int" 16 Bit breit ist. War vielleicht etwas 
missverständlich formuliert.

von MaWin O. (mawin_original)


Lesenswert?

Alles klar. Das war ein Missverständnis.
Vorschlag für die Zukunft:

"wenn ich einen 16 bit breiten int habe"
->
"Wenn int 16 bit breit ist"

Außerdem steht im Titel des Threads "glibc".
Soweit ich weiß, unterstützt glibc keine Microcontroller mit 16 bit int.

von Nop (Gast)


Lesenswert?

Rolf M. schrieb:

> meinte ich nicht den Fall irgendeines 16 Bit breiten Integer-Typen,
> sondern wirklich speziell den Fall, dass ich einen Compiler habe, bei
> dem der Datentyp "int" 16 Bit breit ist.

Das ist in der Realität nur auf zwei Sorten von CPUs der Fall:

1) 8-Bitter, weil int mindestens 16 bit breit ist, aber da hast Du 
sowieso keine 16-bit-Multiplikation in Hardware und folglich auch kein 
32-bit-Ergebnis in Registern.

2) 16-Bitter, weil das der Registerbreite entspricht, und der letzte 
wirklich verbreitete war der 286er. Da war das tatsächlich so, daß das 
Ergebnis der Multiplikation von 16-bit-Operanden im Doppelregister DX:AX 
vorlag. Allerdings konnte man damit sowieso nicht direkt weiterrechnen, 
weil z.B. Addition nicht mit Doppelregistern ging.

Aber auch wenn man das auf einem 16-bitter mit dem manuellen 
Operanden-Cast nach int32_t macht, heißt das keineswegs, daß die 
Multiplikation selber mit 32-bit-Operanden tatsächlich so durchgeführt 
wird.

C hat nämlich die "as-if-Regel". Der Compiler darf machen, was er will, 
solange nur das Ergebnis identisch ist. Das bedeutet, daß er auf einem 
286er das hier:
1
int a = b = 300;
2
int32_t c = ((int32_t a) * ((int32_t b));

Sehr wohl optimieren darf zu genau dem, was ein 286er in Hardware kann. 
Also die Multiplikation eben nicht mit 32-bit-Operanden ausführen, 
sondern mit 16 bit, und nur das 32-bittige Ergebnis aus DX:AX in die 
Variable c schieben.

Wenn der Compiler das nicht tut, ist es ein schlechter Compiler.

von Egon D. (Gast)


Lesenswert?

Nop schrieb:

> 2) 16-Bitter, weil das der Registerbreite entspricht,
> und der letzte wirklich verbreitete war der 286er.

Das könnte TI eventuell etwas anders sehen. Der MSP430
wird in zahlreichen Varianten seit 27 Jahren verkauft.

von zitter_ned_aso (Gast)


Lesenswert?

hmmm, mit unsigned int kommt man also auch nicht weiter. Dann muss es so 
bleiben wie es ist ;-)

Es ist aber schon komisch, dass eine Funktion mit einem Rückgabetyp 
arbeitet, der gar nicht in der Lage ist alles darzustellen, was aus 
Funktionsparametern berechnet werden kann.

Klar, auch Rechenoperationen können zu diesem Problem führen, aber das 
sind ja keine Funktionen. Das ist für mich schon was anderes. Es gibt 
unendlich viele Zahlenn - es ist egal mit welchen Datentypen man 
rechnet. Irgendwann wird man sowieso an die Grenzen stoßen.

Eine Funktion schränkt aber durch die Parameterliste den Zahlenberech 
ein. Hier wird ja nur das Vorzeichen geändert (wenn überhaupt). Das ist 
nicht sowas wie:

pow(INT_MAX, pow(INT_MAX, INT_MAX)) usw..

von MaWin O. (mawin_original)


Lesenswert?

zitter_ned_aso schrieb:
> Es ist aber schon komisch, dass eine Funktion mit einem Rückgabetyp
> arbeitet, der gar nicht in der Lage ist alles darzustellen, was aus
> Funktionsparametern berechnet werden kann.

Wie bereits gesagt, das ist historisch bedingt.
Die nicht-signed-int-Typen sind erst nach und nach zum Sprachumfang 
hinzugekommen.

von Yalu X. (yalu) (Moderator)


Lesenswert?

zitter_ned_aso schrieb:
> Klar, auch Rechenoperationen können zu diesem Problem führen, aber das
> sind ja keine Funktionen.

Natürlich sind das Funktionen. Sie unterscheiden sich von "gewöhnlichen" 
Funktionen nur syntaktisch:

- Statt aus Buchstaben und Ziffern besteht ihr Name aus Sonderzeichen.

- Ihr Aufruf erfolgt in Infix- statt in Präfixnotation.

von foobar (Gast)


Lesenswert?

> Es ist aber schon komisch, dass eine Funktion mit einem Rückgabetyp
> arbeitet, der gar nicht in der Lage ist alles darzustellen,

Ein Fall geht schief, der Rest läuft besser.

Nop hat es oben schon gesagt: unsigned ist ansteckend und versteckte 
Typenwechsel mitten in einem Ausdruck sind häßlich: "abs(3) > -1" - true 
or false?  Und "-3 * abs(3) < 0"?

von Rolf M. (rmagnus)


Lesenswert?

Nop schrieb:
> Rolf M. schrieb:
>
>> meinte ich nicht den Fall irgendeines 16 Bit breiten Integer-Typen,
>> sondern wirklich speziell den Fall, dass ich einen Compiler habe, bei
>> dem der Datentyp "int" 16 Bit breit ist.
>
> Das ist in der Realität nur auf zwei Sorten von CPUs der Fall:

Dann nimm halt eine Multiplikation zweier 32-bit-Integer zu einem mit 64 
Bit. Gleiches Problem, aber auch auf 32-Bit-Plattformen relevant.

> C hat nämlich die "as-if-Regel". Der Compiler darf machen, was er will,
> solange nur das Ergebnis identisch ist. Das bedeutet, daß er auf einem
> 286er das hier:
> int a = b = 300;
> int32_t c = ((int32_t a) * ((int32_t b));
> Sehr wohl optimieren darf zu genau dem, was ein 286er in Hardware kann.

Ja, natürlich. Hab ich ja auch schon geschrieben, und "Michael 
Gugelhupf" vor mir. Das hat keiner bestritten.

Yalu X. schrieb:
> zitter_ned_aso schrieb:
>> Klar, auch Rechenoperationen können zu diesem Problem führen, aber das
>> sind ja keine Funktionen.
>
> Natürlich sind das Funktionen. Sie unterscheiden sich von "gewöhnlichen"
> Funktionen nur syntaktisch:

Das stimmt nur in C++ für benutzerdefinierte Operatoren.

von Bastler (Gast)


Lesenswert?

Rolf M. schrieb:
> Das stimmt nur in C++ für benutzerdefinierte Operatoren.

Nein, Du siehst Unterschiede wo keine sind. Ja, in C kann man Operatoren 
nicht neu definieren. Das ist aber nicht das Thema. Ein Operator ist nur 
eine Funktion in einer anderen Schreibweise.

Ob Du nun "add(a,b)" oder "a + b" schreibst, ist per se egal. Beides 
sind Funktionsaufrufe, die (mutmaßlich) zwei Parameter addieren.

Die Sprache C stellt einen festen Vorrat an Operatoren zur Verfügung, da 
kannst Du als Programmierer nix ändern. In C++ kannst Du immerhin 
existierenden Operatoren überladen. In anderen Sprachen kannst Du auch 
neue Operatoren definieren. In wiederum anderen Sprachen kannst Du jede 
Binäre Funktion in Operatorschreibweise benutzen.

Unterm Strich bleibt: Ein Operator ist nichts anderes als eine Funktion 
in anderer Schreibweise, also nur ein Unterschied im Syntax.

von zitter_ned_aso (Gast)


Lesenswert?

foobar schrieb:
> Ein Fall geht schief, der Rest läuft besser.

aber eigentlich könnte man ja sagen, dass das kein Problem von C ist. 
Die  Zweierkomplement-Darstellung hat im negativen Bereich ein Zahl 
mehr. Hätten wir hier eine Symmetrie, so gebe es dieses Problem nicht.

Oder wenn wir eine Zahl mehr im positiven Bereich hätten.

von Rolf M. (rmagnus)


Lesenswert?

Bastler schrieb:
> Ein Operator ist nur eine Funktion in einer anderen Schreibweise.

Nein. Für einen Operator muss ich keinen Header mit einer Deklaration 
einbinden, um ihn zu nutzen. Ich kann den selben Operator auch mit 
mehreren  verschiedenen Typen verwenden, obwohl C keine 
Funktionsüberladung kennt. Und es gibt auch nicht die gleichen 
Einschränkungen wie für den Aufruf von Funktionen, wie z.B. dass man sie 
nicht außerhalb von Funktionen aufrufen kann.
1
#include <math.h>
2
3
int x = 3 * 5;  // Erlaubt, weil das Ergebnis eine Compilezeit-Konstante ist
4
double y = sin(M_PI); // Fehler: Funktionaufrufe sind an der Stelle nicht erlaubt
5
6
int main()
7
{
8
}

Der überwiegende Teil der Operatoren stellt auch keinen Sequenzpunkt dar 
im Gegensatz zu Funktionen. Es gibt in C ziemlich viele Unterschiede 
zwischen Operatoren und Funktionen.

> In C++ kannst Du immerhin existierenden Operatoren überladen.

Ja, und das sind dann wie gesagt auch tatsächlich Funktionen, für die 
die gleichen Regeln gelten wie für andere Funktionen auch.

> In anderen Sprachen kannst Du auch neue Operatoren definieren. In wiederum
> anderen Sprachen kannst Du jede Binäre Funktion in Operatorschreibweise
> benutzen.

Mag sein, dass es Sprachen gibt, in denen es keinerlei Unterschiede 
zwischen Operatoren und Funktionen gibt. C gehört aber nicht dazu.

von Yalu X. (yalu) (Moderator)


Lesenswert?

Entschuldigung, ich wollte mit der Verwendung des Begriffs "Funktion"
keine Verwirrung stiften :)

Als ich oben "Funktion" schrieb, meinte ich damit keine C-Funktion,
sondern eine Funktion im mathematischen Sinn, also etwas, was einem oder
mehreren Argumenten ein Ergebnis zuordnet. Ich dachte, das würde aus dem
Kontext klar hervorgehen, hätte mich aber wohl dennoch etwas klarer
ausdrücken sollen.

In meinem ersten beitrag wollte ich dem TE klar machen, dass die von ihm
kritisierte Einengung des Wertebereichs von abs nichts Besonderes ist,
weil es diese Einengung in ähnlicher Weise auch bei Additionen und
Multiplikation gibt.

Daraufhin entgegnete er:

zitter_ned_aso schrieb:
> Klar, auch Rechenoperationen können zu diesem Problem führen, aber das
> sind ja keine Funktionen.

Er akzeptiert das Problem also bei der Addition und Multiplikation,
nicht aber bei der Absolutwertberechnung, und begründet dies damit dass
die beiden ersteren keine Funktionen seien.

Mit meiner Antwort

Yalu X. schrieb:
> Natürlich sind das Funktionen.
> ...

wollte ich deutlich machen, dass die Berechnung einer Summe oder eines
Produkts und die Berechnung des Absolutwerts nicht so unterschiedlich
sind, dass daraus die Forderung abgeleitet werden könnte, dass die
abs-Funktion für gesamten Argumentwertebereich definiert sein muss.

Addition und Multiplikation sind natürlich keine Functions im Sinne der
Sprachspezifikation von C (das können sie schon allein deswegen nicht
sein, weil ihr Name kein gültiger C-Identifier ist). Es sind aber sehr
wohl Funktionen in dem Sinne, dass sie aus einem oder mehreren (im
konkreten Fall zwei) Zahlenwerten einen Ergebniswert berechnen. Dass sie
sich von C-Funktionen in einigen Punkten (wie bspw. Sequenzpunkten)
unterscheiden, ist bei der Betrachtung der eigentlichen Berechnung und
der Wertebereiche (um die es in diesem Thread ja ausschließlich geht)
IMHO völlig irrelevant.

von zitter_ned_aso (Gast)


Lesenswert?

abs() wäre so im mathematichen Sinne keine Funktion. Da es ja im 
Definitionsbereich eine Zahl INT_MIN gibt, zu der es keinen 
Funktionswert abs(INT_MIN) gibt.

von Yalu X. (yalu) (Moderator)


Lesenswert?

zitter_ned_aso schrieb:
> abs() wäre so im mathematichen Sinne keine Funktion. Da es ja im
> Definitionsbereich eine Zahl INT_MIN gibt, zu der es keinen
> Funktionswert abs(INT_MIN) gibt.

Das ist jetzt aber ein schräger Schluss :)

Andersherum wird ein Schuh daraus:

Da der Funktionswert von INT_MIN nicht definiert ist, gehört INT_MIN
nicht zum Definitionsbereich.

von Bastler (Gast)


Lesenswert?

zitter_ned_aso schrieb:
> abs() wäre so im mathematichen Sinne keine Funktion. Da es ja im
> Definitionsbereich eine Zahl INT_MIN gibt, zu der es keinen
> Funktionswert abs(INT_MIN) gibt.

So was nennt man glaub "partielle Funktion". Also eine Funktion die 
nicht für alle Eingangswerte ein Ergebnis liefert bzw. definiert ist. 
Eine "totale Funktion" wäre dann für alle denkbaren Eingangswerte 
definiert.

von Bastler (Gast)


Lesenswert?

Rolf M. schrieb:
> Es gibt in C ziemlich viele Unterschiede
> zwischen Operatoren und Funktionen.

Naja, die Unterschiede sind doch aber nicht von fundamentaler Natur.

Bei einem guten Glas Wein betrachtet sind Operatoren nichts anderes als 
syntaktischer Zucker für Programmierer. Ohne Operatoren hätten wir 
vielleicht:
1
#include <stdadd.h>
2
3
int beispiel(int a, int b, int c)
4
{
5
  return addint(addint(a, b), c);
6
}

Mit Operatoren ist's halt bequemer:
1
int beispiel(int a, int b, int c)
2
{
3
  return a + b + c;
4
}

Da ist kein großer Unterschied dazwischen.

von (prx) A. K. (prx)


Lesenswert?

Rolf M. schrieb:
> Auf Assembler-Ebene passiert meistens genau das: Das Ergebnis ist
> doppelt so breit wie die Operanden.

Vor Äonen war das so.

Bei einem gepipelinenten Multiplizierer  ist es jedoch sinnvoller, 
zweimal zu multiplizieren, einmal für die untere und einmal für die 
obere Hälfte, als beides zusammen zu rechnen. Grund ist das 
Ressourcenmanagement von Bussen und Registern.

Das war schon bei PowerPC so, anders als in IBMs POWER. Und die 
meistgebrauchten Multiplikationsbefehle bei x86 liefern nur die untere 
Hälfte. 32bit ARM hatte anfangs überhaupt nur diese Variante.

: Bearbeitet durch User
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.