Discussion:
Signedness von Pointern
(zu alt für eine Antwort)
Оlе Ѕtrеісhеr
2017-05-16 04:03:03 UTC
Permalink
Hallo,

ich habe hier (30 Jahre alten) Code, der folgendes macht [1]:

#define ADDR_TO_LOC(addr) ((int)((short *)(addr)))>>(sizeof(short)-1)

Es wird also praktisch die Adresse um ein bit nach rechts verschoben und
dann nach "int" konvertiert. Im Weiteren wird angenommen, dass das
Ergebnis positiv ist.

Gibt es irgendeine Aussage darüber, ob ei Pointer signed oder unsigned
ist und ob das Vorzeichen ins MSB kopiert wird?

Schöne Grüße

Ole

[1] https://github.com/iraf/iraf-v216/blob/master/unix/hlib/libc/kernel.h
Peter J. Holzer
2017-05-16 07:42:17 UTC
Permalink
Post by Оlе Ѕtrеісhеr
#define ADDR_TO_LOC(addr) ((int)((short *)(addr)))>>(sizeof(short)-1)
Es wird also praktisch die Adresse um ein bit nach rechts verschoben und
dann nach "int" konvertiert.
Nein, es wird zuerst nach int konvertiert und dann verschoben. Der Cast
bindet stärker als der Shift-Operator. (Außerdem ist >> nur auf
Integer-Typen definiert, der Versuch, einen Pointer zu shiften wäre also
eine Constraint-Violation).
Post by Оlе Ѕtrеісhеr
Im Weiteren wird angenommen, dass das
Ergebnis positiv ist.
Gibt es irgendeine Aussage darüber, ob ei Pointer signed oder unsigned
ist und ob das Vorzeichen ins MSB kopiert wird?
Pointer sind weder signed noch unsigned. Die Konversion in Integer-Typen
ist implementation-defined. Alles was wir wissen, ist dass es mindestens
einen Integer-Typ gibt, für den die Konversion (void *) -> T -> (void *)
verlustfrei ist.

In der Praxis ist bei 32-Bit-Architekturen meistens die Konversion von
einem beliebigen Pointer nach int und zurück verlustfrei möglich.
Außerdem ist der Adressraum linear und die "Konversion" ist einfach nur
eine Kopie. In diesem Fall funktioniert dieser Code, wenn jeder Pointer
in die ersten 2 GB des virtuellen Adressraums zeigt. Das war
traditionell bei vielen Unixen der Fall, aber nicht bei allen: In
Linux/i386 stehen dem Prozess üblicherweise 3 GB virtueller Adressraum
zur Verfügung. Dort würde der Code also auf die Nase fallen, weil das
Ergebnis des Casts negativ sein kann und das negative Vorzeichen beim
Shift erhalten bleibt (bei den üblichen C-Compilern).

Auf 16-Bit-Architekturen dürfte das vor 30 Jahren schon nicht
funktioniert haben.
Post by Оlе Ѕtrеісhеr
[1] https://github.com/iraf/iraf-v216/blob/master/unix/hlib/libc/kernel.h
"Machine dependent definitions for the 4.1BSD UNIX IRAF Kernel."

Auf BSD 4.1 dürften die getroffenen Annahmen (32-Bit-Architektur, <= 2
GB linearer Adressraum, ...) für alle Systeme zugetroffen haben.

hp
--
_ | Peter J. Holzer | Fluch der elektronischen Textverarbeitung:
|_|_) | | Man feilt solange an seinen Text um, bis
| | | ***@hjp.at | die Satzbestandteile des Satzes nicht mehr
__/ | http://www.hjp.at/ | zusammenpaßt. -- Ralph Babel
Claus Reibenstein
2017-05-16 20:35:37 UTC
Permalink
Post by Peter J. Holzer
Post by Оlе Ѕtrеісhеr
#define ADDR_TO_LOC(addr) ((int)((short *)(addr)))>>(sizeof(short)-1)
Es wird also praktisch die Adresse um ein bit nach rechts verschoben und
dann nach "int" konvertiert.
Nein, es wird zuerst nach int konvertiert und dann verschoben. Der Cast
bindet stärker als der Shift-Operator.
Die Bindung wird durch die Klammern bestimmt, nicht durch die
Prioritäten der beteiligten Operatoren.

Gruß
Claus
Peter J. Holzer
2017-05-18 16:27:06 UTC
Permalink
Post by Claus Reibenstein
Post by Peter J. Holzer
Post by Оlе Ѕtrеісhеr
#define ADDR_TO_LOC(addr) ((int)((short *)(addr)))>>(sizeof(short)-1)
Es wird also praktisch die Adresse um ein bit nach rechts verschoben und
dann nach "int" konvertiert.
Nein, es wird zuerst nach int konvertiert und dann verschoben. Der Cast
bindet stärker als der Shift-Operator.
Die Bindung wird durch die Klammern bestimmt, nicht durch die
Prioritäten der beteiligten Operatoren.
Stimmt. Da habe ich offensichtlich gesehen, was sinnvoll gewesen wäre,
nicht was da stand.

Ich hoffe, es hat nie jemand dieses Makro in einem Ausdruck verwendet.
x = ADDR_TO_LOC(p) + 2
tut nämlich nicht, was man erwarten würde.

hp
--
_ | Peter J. Holzer | Fluch der elektronischen Textverarbeitung:
|_|_) | | Man feilt solange an seinen Text um, bis
| | | ***@hjp.at | die Satzbestandteile des Satzes nicht mehr
__/ | http://www.hjp.at/ | zusammenpaßt. -- Ralph Babel
Оlе Ѕtrеісhеr
2017-05-21 20:03:06 UTC
Permalink
Post by Peter J. Holzer
Post by Оlе Ѕtrеісhеr
#define ADDR_TO_LOC(addr) ((int)((short *)(addr)))>>(sizeof(short)-1)
Ich hoffe, es hat nie jemand dieses Makro in einem Ausdruck verwendet.
x = ADDR_TO_LOC(p) + 2
tut nämlich nicht, was man erwarten würde.
Zum Glück nicht. Die einzige Anwending ist

*buf = ADDR_TO_LOC(bufptr);

Ich will aber nicht sagen, dass ich besonders glücklich über diesen
Codehaufen bin :-( Aber was macht man mit >>30 Jahre altem Code, der
fachlich heute noch benötigt wird?

Es gibt da noch mehr schöne "Überraschungen", z.B. eine
Arraydeklaration als

struct {
/* das ist eigentlich ein mem[60000], das weiß man hier nur nicht genau */
int mem[1];
} memptr;

Aus der Deklaration folgern moderne Compiler offenbar, dass es sich nur
um eine einzige Zahl handeln kann, und optimieren bei
aufeinanderfolgenden Zuweisungen schon mal die erste weg:

...
memptr.mem[i1] = a; // das verschwindet bei Optimierung
memptr.mem[i2] = b;
...

Gruselcode das alles... sowas findet leider auch kein Codechecker
(oder?)

Danke jedenfalls an alle für die Antworten.

Schöne Grüße

Ole
Оlе Ѕtrеісhеr
2017-05-21 20:23:04 UTC
Permalink
Post by Оlе Ѕtrеісhеr
Post by Оlе Ѕtrеісhеr
#define ADDR_TO_LOC(addr) ((int)((short *)(addr)))>>(sizeof(short)-1)
Danke jedenfalls an alle für die Antworten.
Als Zusatzfrage hätte ich aber noch: wie kann man denn portabel ein
rechtsshifting (mit beliebiger bitzahl) garantiert eine Null
nachgeschoben wird?

Schöne Grüße

Ole
Rainer Weikusat
2017-05-21 21:56:28 UTC
Permalink
Post by Оlе Ѕtrеісhеr
Post by Оlе Ѕtrеісhеr
Post by Оlе Ѕtrеісhеr
#define ADDR_TO_LOC(addr) ((int)((short *)(addr)))>>(sizeof(short)-1)
Danke jedenfalls an alle für die Antworten.
Als Zusatzfrage hätte ich aber noch: wie kann man denn portabel ein
rechtsshifting (mit beliebiger bitzahl) garantiert eine Null
nachgeschoben wird?
Vorzeichenlose Typen benutzen.
Оlе Ѕtrеісhеr
2017-05-22 06:17:10 UTC
Permalink
Post by Rainer Weikusat
Post by Оlе Ѕtrеісhеr
Post by Оlе Ѕtrеісhеr
Post by Оlе Ѕtrеісhеr
#define ADDR_TO_LOC(addr) ((int)((short *)(addr)))>>(sizeof(short)-1)
Danke jedenfalls an alle für die Antworten.
Als Zusatzfrage hätte ich aber noch: wie kann man denn portabel ein
rechtsshifting (mit beliebiger bitzahl) garantiert eine Null
nachgeschoben wird?
Vorzeichenlose Typen benutzen.
Soweit ich weiß, ist es auch bei vorzeichenlosen Typen
implementationsabhängig. Der gcc macht das so, clang offenbar auch. Aber
wie wäre eine Lösung in ISO-C?

Ole
Claus Reibenstein
2017-05-22 06:47:30 UTC
Permalink
Post by Оlе Ѕtrеісhеr
Post by Rainer Weikusat
Post by Оlе Ѕtrеісhеr
Als Zusatzfrage hätte ich aber noch: wie kann man denn portabel
ein rechtsshifting (mit beliebiger bitzahl) garantiert eine Null
nachgeschoben wird?
Vorzeichenlose Typen benutzen.
Soweit ich weiß, ist es auch bei vorzeichenlosen Typen
implementationsabhängig.
Implementationsabhängig ist es nur bei vorzeichenbehafteten Typen, und
dort auch nur dann, wenn der aktuelle Wert negativ ist. Das war schon in
C99 so:

| The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1
| has an unsigned type or if E1 has a signed type and a nonnegative
| value, the value of the result is the integral part of the quotient
| of E1 / 2E2. If E1 has a signed type and a negative value, the
| resulting value is implementation-defined.

(Quelle: ISO/IEC 9899/1999, §6.5.8, Punkt 5)
Post by Оlе Ѕtrеісhеr
Der gcc macht das so, clang offenbar auch. Aber wie wäre eine Lösung
in ISO-C?
Das _ist_ die Lösung in ISO-C.

Gruß
Claus
Оlе Ѕtrеісhеr
2017-05-22 07:14:15 UTC
Permalink
Post by Claus Reibenstein
Post by Оlе Ѕtrеісhеr
Post by Rainer Weikusat
Post by Оlе Ѕtrеісhеr
Als Zusatzfrage hätte ich aber noch: wie kann man denn portabel
ein rechtsshifting (mit beliebiger bitzahl) garantiert eine Null
nachgeschoben wird?
Vorzeichenlose Typen benutzen.
Soweit ich weiß, ist es auch bei vorzeichenlosen Typen
implementationsabhängig.
Implementationsabhängig ist es nur bei vorzeichenbehafteten Typen, und
dort auch nur dann, wenn der aktuelle Wert negativ ist. Das war schon in
| The result of E1 >> E2 is E1 right-shifted E2 bit positions. If E1
| has an unsigned type or if E1 has a signed type and a nonnegative
| value, the value of the result is the integral part of the quotient
| of E1 / 2E2. If E1 has a signed type and a negative value, the
| resulting value is implementation-defined.
(Quelle: ISO/IEC 9899/1999, §6.5.8, Punkt 5)
Post by Оlе Ѕtrеісhеr
Der gcc macht das so, clang offenbar auch. Aber wie wäre eine Lösung
in ISO-C?
Das _ist_ die Lösung in ISO-C.
OK, danke. Ich hatte das anders in Erinnerung.

Schöne Grüße

Ole
Rainer Weikusat
2017-05-21 21:59:18 UTC
Permalink
ole-usenet-***@gmx.net (Оlе Ѕtrеісhеr) writes:

[...]
Post by Оlе Ѕtrеісhеr
struct {
/* das ist eigentlich ein mem[60000], das weiß man hier nur nicht genau */
int mem[1];
} memptr;
Aus der Deklaration folgern moderne Compiler offenbar, dass es sich nur
um eine einzige Zahl handeln kann, und optimieren bei
...
memptr.mem[i1] = a; // das verschwindet bei Optimierung
memptr.mem[i2] = b;
Hmm ... das bezweifle ich jetzt mal: Traditionall konnte man in C keine
Felder mit Groesse 0 deklarieren, vgl 'info gcc' 5.14. Insofern war/ ist
das ein ueblicher Workaround fuer 'Feld variabler Laenge'.
Оlе Ѕtrеісhеr
2017-05-22 06:20:34 UTC
Permalink
Post by Rainer Weikusat
[...]
Post by Оlе Ѕtrеісhеr
struct {
/* das ist eigentlich ein mem[60000], das weiß man hier nur nicht genau */
int mem[1];
} memptr;
Aus der Deklaration folgern moderne Compiler offenbar, dass es sich nur
um eine einzige Zahl handeln kann, und optimieren bei
...
memptr.mem[i1] = a; // das verschwindet bei Optimierung
memptr.mem[i2] = b;
Hmm ... das bezweifle ich jetzt mal: Traditionall konnte man in C keine
Felder mit Groesse 0 deklarieren, vgl 'info gcc' 5.14. Insofern war/ ist
das ein ueblicher Workaround fuer 'Feld variabler Laenge'.
Einer, der nicht standardkonform ist und "neuerdings" (so seit gcc-4.8)
auch nicht mehr funktioniert.

Das Beispiel zeigt sehr gut, wie ein "passt schon irgendwie, habe es
kompiliert und es ging alles" einem irgendwann auf die Füße fällt und
sehr schwer zu findende Fehler verursacht. Die übliche Reaktion ist
dann, laut "Compilerbug!!!" zu rufen, dabei ist man selber schuld.

Ole
Оlе Ѕtrеісhеr
2017-05-22 10:24:00 UTC
Permalink
Post by Rainer Weikusat
Post by Оlе Ѕtrеісhеr
memptr.mem[i1] = a; // das verschwindet bei Optimierung
memptr.mem[i2] = b;
Hmm ... das bezweifle ich jetzt mal: Traditionall konnte man in C keine
Felder mit Groesse 0 deklarieren, vgl 'info gcc' 5.14. Insofern war/ ist
das ein ueblicher Workaround fuer 'Feld variabler Laenge'.
Ich habe mal das in konkreten selbständigen Code gegossen: 2
Quelldateien;

1. mem.c
-------------------->8------- mem.c --------------------------
#include <stdio.h>

struct {
int m[2000];
} mem;

void printmem(int i) {
printf("%i %i\n", i, mem.m[i]);
}
-------------------->8------- mem.c --------------------------

2. main.c
-------------------->8------- main.c -------------------------
#include <stdlib.h>
void printmem(int i);

extern struct {
int m[1];
} mem;


int main(int argc, const char ** argv) {
int i, a1, a2;
a1 = atoi(argv[1]);
a2 = atoi(argv[2]);
mem.m[a1] = a2;
mem.m[a2] = a1;

printmem(a1);
printmem(a2);
return 0;
}
-------------------->8------- main.c -------------------------

Beide mit Optimierung kompilieren, zusammenlinken und dann starten:

$ gcc -O2 -o main mem.c main.c
$ ./main 12 34
12 0
34 12

Ohne Optimierung, oder mit der korrekten Deklaration in main.c ist alles
OK.

Schöne Grüße

Ole
Bastian Blank
2017-05-22 11:44:35 UTC
Permalink
Post by Оlе Ѕtrеісhеr
Post by Rainer Weikusat
Post by Оlе Ѕtrеісhеr
memptr.mem[i1] = a; // das verschwindet bei Optimierung
memptr.mem[i2] = b;
Hmm ... das bezweifle ich jetzt mal: Traditionall konnte man in C keine
Felder mit Groesse 0 deklarieren, vgl 'info gcc' 5.14. Insofern war/ ist
das ein ueblicher Workaround fuer 'Feld variabler Laenge'.
Ich habe mal das in konkreten selbständigen Code gegossen: 2
Quelldateien;
Bitte lies noch mal was Rainer geschrieben hat.
Post by Оlе Ѕtrеісhеr
extern struct {
int m[1];
} mem;
int main(int argc, const char ** argv) {
int i, a1, a2;
a1 = atoi(argv[1]);
a2 = atoi(argv[2]);
mem.m[a1] = a2;
mem.m[a2] = a1;
mem.m is genau ein Element lang. Du kannst also nur "mem.m[0]"
beschreiben, alles andere ist UB.
Post by Оlе Ѕtrеісhеr
$ gcc -O2 -o main mem.c main.c
$ ./main 12 34
12 0
34 12
Vollkommen korrektes Verhalten.

Bastian
Оlе Ѕtrеісhеr
2017-05-22 11:50:56 UTC
Permalink
Post by Bastian Blank
Post by Оlе Ѕtrеісhеr
Post by Rainer Weikusat
Post by Оlе Ѕtrеісhеr
memptr.mem[i1] = a; // das verschwindet bei Optimierung
memptr.mem[i2] = b;
Hmm ... das bezweifle ich jetzt mal: Traditionall konnte man in C keine
Felder mit Groesse 0 deklarieren, vgl 'info gcc' 5.14. Insofern war/ ist
das ein ueblicher Workaround fuer 'Feld variabler Laenge'.
Ich habe mal das in konkreten selbständigen Code gegossen: 2
Quelldateien;
Bitte lies noch mal was Rainer geschrieben hat.
Ja, und? Habe ich etwas überlesen?
Post by Bastian Blank
Post by Оlе Ѕtrеісhеr
$ gcc -O2 -o main mem.c main.c
$ ./main 12 34
12 0
34 12
Vollkommen korrektes Verhalten.
Richtig. Und dieses Verhalten hat Rainer bezweifelt.

Ole
Thomas Jahns
2017-05-22 12:44:31 UTC
Permalink
Post by Bastian Blank
Vollkommen korrektes Verhalten.
eher nicht: wegen UB kann man für dieses Nicht-Programm ja gar kein korrektes
Verhalten formulieren.

Insoferen keine Neuigkeiten: nicht korrekte Eingaben (wobei mit Eingabe das
Nicht-Programm gemeint ist) liefern auch keine korrekten Ausgaben.

Thomas
Peter J. Holzer
2017-05-22 17:23:47 UTC
Permalink
Post by Thomas Jahns
Post by Bastian Blank
Vollkommen korrektes Verhalten.
eher nicht: wegen UB kann man für dieses Nicht-Programm ja gar kein korrektes
Verhalten formulieren.
Bei UB ist jedes Verhalten korrekt. Das Programm könnte auch "Kikeriki"
ausgeben und Nasendämonen beschwören. Wäre auch vollkommen korrekt.

hp
--
_ | Peter J. Holzer | Fluch der elektronischen Textverarbeitung:
|_|_) | | Man feilt solange an seinen Text um, bis
| | | ***@hjp.at | die Satzbestandteile des Satzes nicht mehr
__/ | http://www.hjp.at/ | zusammenpaßt. -- Ralph Babel
Rainer Weikusat
2017-05-22 13:56:21 UTC
Permalink
Post by Оlе Ѕtrеісhеr
Post by Rainer Weikusat
Post by Оlе Ѕtrеісhеr
memptr.mem[i1] = a; // das verschwindet bei Optimierung
memptr.mem[i2] = b;
Hmm ... das bezweifle ich jetzt mal: Traditionall konnte man in C keine
Felder mit Groesse 0 deklarieren, vgl 'info gcc' 5.14. Insofern war/ ist
das ein ueblicher Workaround fuer 'Feld variabler Laenge'.
Ich habe mal das in konkreten selbständigen Code gegossen: 2
Quelldateien;
1. mem.c
-------------------->8------- mem.c --------------------------
#include <stdio.h>
struct {
int m[2000];
} mem;
Das hier ist keine Struktur-Deklaration sondern eine Struktur-Definition.
Post by Оlе Ѕtrеісhеr
void printmem(int i) {
printf("%i %i\n", i, mem.m[i]);
}
-------------------->8------- mem.c --------------------------
2. main.c
-------------------->8------- main.c -------------------------
#include <stdlib.h>
void printmem(int i);
extern struct {
int m[1];
} mem;
Und das undefiniertes Verhalten, da diese Deklaration nicht kompatibel
zur Definition von mem ist. Was ich gemeint hatte war sowas:

--------
#include <stdio.h>
#include <stdlib.h>

struct ia {
int a[1];
};

int main(int argc, const char ** argv) {
struct ia *ia;

ia = malloc(sizeof(int) * 50);

int i, a1, a2;
a1 = atoi(argv[1]);
a2 = atoi(argv[2]);
ia->a[a1] = a2;
ia->a[a2] = a1;

printf("%d, %d\n", ia->a[a1], ia->a[a1]);

return 0;
}
Peter J. Holzer
2017-05-22 17:39:54 UTC
Permalink
Post by Rainer Weikusat
Und das undefiniertes Verhalten, da diese Deklaration nicht kompatibel
--------
#include <stdio.h>
#include <stdlib.h>
struct ia {
int a[1];
};
int main(int argc, const char ** argv) {
struct ia *ia;
ia = malloc(sizeof(int) * 50);
int i, a1, a2;
[...]
Post by Rainer Weikusat
ia->a[a1] = a2;
Ist genauso undefined behaviour, wenn auch aus anderen Gründen.

Aus diesem Grund wurde in C99 die von Thomas erwähnte Möglichkeit
eingeführt, als letztes struct-Element ein Array ohne Längenangabe zu
verwenden.

hp
--
_ | Peter J. Holzer | Fluch der elektronischen Textverarbeitung:
|_|_) | | Man feilt solange an seinen Text um, bis
| | | ***@hjp.at | die Satzbestandteile des Satzes nicht mehr
__/ | http://www.hjp.at/ | zusammenpaßt. -- Ralph Babel
Rainer Weikusat
2017-05-22 17:41:46 UTC
Permalink
Post by Peter J. Holzer
Post by Rainer Weikusat
Und das undefiniertes Verhalten, da diese Deklaration nicht kompatibel
--------
#include <stdio.h>
#include <stdlib.h>
struct ia {
int a[1];
};
int main(int argc, const char ** argv) {
struct ia *ia;
ia = malloc(sizeof(int) * 50);
int i, a1, a2;
[...]
Post by Rainer Weikusat
ia->a[a1] = a2;
Ist genauso undefined behaviour, wenn auch aus anderen Gründen.
Und zwar welchen?
Peter J. Holzer
2017-05-22 19:28:38 UTC
Permalink
Post by Rainer Weikusat
Post by Peter J. Holzer
Post by Rainer Weikusat
Und das undefiniertes Verhalten, da diese Deklaration nicht kompatibel
--------
#include <stdio.h>
#include <stdlib.h>
struct ia {
int a[1];
};
int main(int argc, const char ** argv) {
struct ia *ia;
ia = malloc(sizeof(int) * 50);
int i, a1, a2;
[...]
Post by Rainer Weikusat
ia->a[a1] = a2;
Ist genauso undefined behaviour, wenn auch aus anderen Gründen.
Und zwar welchen?
§ 6.5.6:

| If both the pointer operand and the result point to elements of the
| same array object, or one past the last element of the array object,
| the evaluation shall not produce an overflow; otherwise, the behavior
| is undefined.

Das array object ist hier a und das besteht aus genau einem int. Dass
genug Platz für 50 struct ia reserviert hast, ist irrelevant.

hp
--
_ | Peter J. Holzer | Fluch der elektronischen Textverarbeitung:
|_|_) | | Man feilt solange an seinen Text um, bis
| | | ***@hjp.at | die Satzbestandteile des Satzes nicht mehr
__/ | http://www.hjp.at/ | zusammenpaßt. -- Ralph Babel
Rainer Weikusat
2017-05-22 20:40:22 UTC
Permalink
Post by Peter J. Holzer
Post by Rainer Weikusat
Post by Peter J. Holzer
Post by Rainer Weikusat
Und das undefiniertes Verhalten, da diese Deklaration nicht kompatibel
--------
#include <stdio.h>
#include <stdlib.h>
struct ia {
int a[1];
};
int main(int argc, const char ** argv) {
struct ia *ia;
ia = malloc(sizeof(int) * 50);
int i, a1, a2;
[...]
Post by Rainer Weikusat
ia->a[a1] = a2;
Ist genauso undefined behaviour, wenn auch aus anderen Gründen.
Und zwar welchen?
| If both the pointer operand and the result point to elements of the
| same array object, or one past the last element of the array object,
| the evaluation shall not produce an overflow; otherwise, the behavior
| is undefined.
Das array object ist hier a und das besteht aus genau einem int. Dass
genug Platz für 50 struct ia reserviert hast, ist irrelevant.
Eine Deklaration fuehrt nicht dazu, dass Objekte definiert werden, und
beide Zeiger zeigen in ein array (falls entsprechende Argumente
uebergeben wurden).

Hier koennte man eher die Allokation bemaengeln: int * vs struct ia
*. Oder auch dass ueber das Layout einer ia struct nichts bekannt ist
ausser "sie enthaelt wenigstens einen int object". Es koennte davor oder
dahinter padding geben.

Helmut Schellong
2017-05-22 18:20:18 UTC
Permalink
Post by Peter J. Holzer
Post by Rainer Weikusat
ia->a[a1] = a2;
Ist genauso undefined behaviour, wenn auch aus anderen Gründen.
Aus diesem Grund wurde in C99 die von Thomas erwähnte Möglichkeit
eingeführt, als letztes struct-Element ein Array ohne Längenangabe zu
verwenden.
Die ist auch nicht durch C11 als optional eingestuft worden:

_ _STDC_ANALYZABLE_ _ The integer constant 1, intended to indicate conformance to
the specifications in annex L (Analyzability).
_ _STDC_IEC_559_ _ The integer constant 1, intended to indicate conformance
to the
specifications in annex F (IEC 60559 floating-point arithmetic).
_ _STDC_IEC_559_COMPLEX_ _ The integer constant 1, intended to indicate
adherence to the specifications in annex G (IEC 60559 compatible complex
arithmetic).
_ _STDC_LIB_EXT1_ _ The integer constant 201ymmL, intended to indicate support
for the extensions defined in annex K (Bounds-checking interfaces).179)
_ _STDC_NO_ATOMICS_ _ The integer constant 1, intended to indicate that the
implementation does not support atomic types (including the _Atomic
type qualifier) and the <stdatomic.h> header.
_ _STDC_NO_COMPLEX_ _ The integer constant 1, intended to indicate that the
implementation does not support complex types or the <complex.h>
header.
_ _STDC_NO_THREADS_ _ The integer constant 1, intended to indicate that the
implementation does not support the <threads.h> header.
_ _STDC_NO_VLA_ _ The integer constant 1, intended to indicate that the
implementation does not support variable length arrays or variably
modified types.
An implementation that defines _ _STDC_NO_COMPLEX_ _ shall not define
_ _STDC_IEC_559_COMPLEX_ _.
--
Mit freundlichen Grüßen
Helmut Schellong ***@schellong.biz
www.schellong.de www.schellong.com www.schellong.biz
http://www.schellong.de/c.htm
Thomas Jahns
2017-05-22 12:44:27 UTC
Permalink
Post by Оlе Ѕtrеісhеr
Gruselcode das alles... sowas findet leider auch kein Codechecker
(oder?)
Es gibt ein Plug-In[1] für clang bzw. llvm 3.4 mit dem man das findet. Da Du
aber auch die Transformationen von pointer nach intptr_t in Deinem Code hast,
könnte es sein, dass besagten Plugin schon beim Kompilieren nicht mitspielt.
Vortrag dazu gab es auf dem 30C3[2,3].

Thomas

[1] <https://www.cs.rutgers.edu/~santosh.nagarakatte/softbound/>
[2] <https://events.ccc.de/congress/2013/Fahrplan/events/5412.html>
[3]
<https://media.ccc.de/v/30C3_-_5412_-_en_-_saal_1_-_201312271830_-_bug_class_genocide_-_andreas_bogk>
Thomas Koenig
2017-05-22 16:54:38 UTC
Permalink
Post by Оlе Ѕtrеісhеr
Es gibt da noch mehr schöne "Überraschungen", z.B. eine
Arraydeklaration als
struct {
/* das ist eigentlich ein mem[60000], das weiß man hier nur nicht genau */
int mem[1];
} memptr;
Aus der Deklaration folgern moderne Compiler offenbar, dass es sich nur
um eine einzige Zahl handeln kann, und optimieren bei
Es gibt für dieses Idiom (was AFAIK nie legal war) den Ausweg
der "flexible array member":

struct {
int mem[];
} memptr;

Oder gehört das auch zu den Features, die in einer Nachfolgenorm
als optional gekennzeichnet wurden, wie VLAs?
Helmut Schellong
2017-05-16 09:58:43 UTC
Permalink
Post by Оlе Ѕtrеісhеr
Hallo,
#define ADDR_TO_LOC(addr) ((int)((short *)(addr)))>>(sizeof(short)-1)
Es wird also praktisch die Adresse um ein bit nach rechts verschoben und
dann nach "int" konvertiert. Im Weiteren wird angenommen, dass das
Ergebnis positiv ist.
Gibt es irgendeine Aussage darüber, ob ei Pointer signed oder unsigned
ist und ob das Vorzeichen ins MSB kopiert wird?
Ich finde den Code zu spezifisch, nicht generisch genug.

((int)...) >> 1

Daß da erst geschoben wird und danach nach 'int' gecastet wird, kann
nicht sein, schon allein wegen der ().

Ich würde grundsätzlich den Typ 'uintptr_t' verwenden (hier statt 'int').

Vorzeichenbehaftete Adressen hatte es wohl mal gegeben oder gibt es
immer noch (intptr_t) - aber diese wären auf jeden Fall exotisch.
--
Mit freundlichen Grüßen
Helmut Schellong ***@schellong.biz
www.schellong.de www.schellong.com www.schellong.biz
http://www.schellong.de/c.htm
Patrick.Schluter
2017-05-22 18:09:26 UTC
Permalink
Post by Helmut Schellong
Post by Оlе Ѕtrеісhеr
Hallo,
#define ADDR_TO_LOC(addr) ((int)((short *)(addr)))>>(sizeof(short)-1)
Es wird also praktisch die Adresse um ein bit nach rechts verschoben und
dann nach "int" konvertiert. Im Weiteren wird angenommen, dass das
Ergebnis positiv ist.
Gibt es irgendeine Aussage darüber, ob ei Pointer signed oder unsigned
ist und ob das Vorzeichen ins MSB kopiert wird?
Ich finde den Code zu spezifisch, nicht generisch genug.
((int)...) >> 1
Daß da erst geschoben wird und danach nach 'int' gecastet wird, kann
nicht sein, schon allein wegen der ().
Ich würde grundsätzlich den Typ 'uintptr_t' verwenden (hier statt 'int').
Vorzeichenbehaftete Adressen hatte es wohl mal gegeben oder gibt es
immer noch (intptr_t) - aber diese wären auf jeden Fall exotisch.
Naja, Solaris auf SPARC würde ich nicht als exotisch bezeichnen. Davon
abgesehen funktioniert auch alles mit uintptr_t auch wenn der
Adressbereich mit negativen Adressen arbeitet und das manchmal ganz
praktisch war (Pointer nach intptr_t konvertiern,
wenn >0 && < 0x1_0000_0000, Daten, code oder bss
wenn >0x1_0000_0000 vom Heap alloziert (man kann free aufrufen z.B)
wenn <0 kommt von einem memory mapped file (oder shared object).

Zum Debuggen ziemlich Cool. Selbstverständlich nicht Portabel.
Loading...