Datentyp für Zeigerarithmetik
-
Hallo!
Wenn ich folgendes Beispielprogramm als x64 erstelle
#include <stdlib.h> int main() { float *data; data = malloc(10 * 12 * sizeof(*data)); if(data == NULL) { return 1; } for(int i = 0; i < 10; i++) { for(int j = 0; j < 12; j++) { *(data + i * 12 + j) = 1.0f; } } return 0; }
erhalte ich folgende Fehlermeldung:
Warnung C26451 Arithmetischer Überlauf: Wenn Sie den Operator "*" für einen 4-Bytewert verwenden und das Ergebnis in einen 8-Bytewert umwandeln, wandeln Sie den Wert in den breiteren Typ um, bevor Sie den Operator "*" verwenden, um einen Überlauf zu vermeiden (io.2). ConsoleApplication1 C:\Users\Ralf\source\repos\ConsoleApplication1\ConsoleApplication1\ConsoleApplication1.c 17
Welcher Datentyp wäre da denn als "breiterer Typ" geeignet?
-
@Peter-Viehweger
Das Einzige, was ich mir vorstellen könnte, ist, dassdata
einuint64
ist und der Compiler bei der Bestimmung der Zieladresse etwas beunruhigt ist, weil in eineruint64
Multiplikationint
beteiligt ist. Probier für die Schleifenvariablen mal einen 64bit unsigned Datentyp aus.Wird bei dieser Alternative auch gewarnt?
int main() { float *data = malloc(10 * 12 * sizeof(*data)); if( data == NULL ) { return 1; } float* tmp = data; for(int i = 0; i < 10; i++) { for(int j = 0; j < 12; j++) { *tmp = 1.0f; ++tmp; } } // geht das nicht auch einfacher so:? // Edit: Ne, geht nicht float proto = 1.0f; memset( data, &proto, 10 * 12 ); return 0; }
-
Bist du sicher, dass du das richtige Programm gepostet hast? Ich sehe nicht, wo hier gewarnt werden soll. (abgesehen davon, dass du ein malloc, aber kein free im Programm hast).
-
https://www.bilder-upload.eu/bild-9b6a86-1618236385.png.html
Ja das free() habe ich in der Aufregung vergessen......
-
Ok, aber Werte wie 12 * 10 passen locker in einen int. Also egal.
-
@Peter-Viehweger sagte in Datentyp für Zeigerarithmetik:
*(data + i * 12 + j) = 1.0f;
=>
data[i * 12 + j] = 1.0f;
-
Es geht um die Indexberechnung bei
*(data + i * 12 + j)
, weili
undj
alsint
deklariert sind (auch als x64 kompiliert bleiben es - beim MSVC - 4 Byte). Verwendesize_t
als Schleifenvariablen (auch wenn in deinem Code bei den kleinen Werten so niemals die 4 Byte-Grenze überschritten wird).
-
Also bei
*(data + (size_t) i * 12 + j) = 1.0f;
ist die Warnung dann weg und Umwandlungen zu größeren Typen sind ja eigentlich kein Problem, oder?
@manni66 sagte in Datentyp für Zeigerarithmetik:
data[i * 12 + j] = 1.0f;
Funktioniert da irgendetwas anders, oder haben die bei Microsoft einfach nur vergessen, hier auch eine Warnung einzublenden?
-
@Peter-Viehweger sagte in Datentyp für Zeigerarithmetik:
Funktioniert da irgendetwas anders, oder haben die bei Microsoft einfach nur vergessen, hier auch eine Warnung einzublenden?
Das Ergebnis sollte identisch sein. Die Warnung an dieser Stelle finde ich fragwürdig.
-
Zu 1)
Dann kannst statt int auch sofort size_t als Schleifenvariable nehmenZu 2)
Ja, bei der Indexberechnung sind nur ints beteiligt und kein uint64 mehr
-
@Peter-Viehweger sagte in Datentyp für Zeigerarithmetik:
Also bei
*(data + (size_t) i * 12 + j) = 1.0f;
ist die Warnung dann weg und Umwandlungen zu größeren Typen sind ja eigentlich kein Problem, oder?
Prinzipiell richtig. Aber wenn man einen
int
deklariert und bei der einzigen Nutzung diesen insize_t
castet, dann hat man wahrscheinlich den falschen Typen gewählt. Warum hörst du nicht auf Th69?@manni66 sagte in Datentyp für Zeigerarithmetik:
data[i * 12 + j] = 1.0f;
Funktioniert da irgendetwas anders, oder haben die bei Microsoft einfach nur vergessen, hier auch eine Warnung einzublenden?
Es bestehen die gleichen Gefahren, vor denen hier so übertrieben gewarnt wird. Bei Warnungen ist es Abwägungssache, was der Programmierer wohl gemeint hat und ob das wirklich zu dem Code passt. Der Code ist ja nicht direkt falsch. Warnungen gibt es deshalb bei üblichen Fehlermustern, wo prinzipiell korrekter Code nicht das macht, was die meisten Leute denken. Überlaufende Arrayindizes können ja in irgendwelchen verschwurbelten Szenarien noch Sinn machen. Aber das Szenario
(großer Typ) + (potentiell überlaufende Rechnung mit kleinen Typen)
kommt auch ganz losgelöst von Pointertypen eher vor und ist wohl nie das was wirklich gemeint ist.
-
@manni66 sagte in Datentyp für Zeigerarithmetik:
Das Ergebnis sollte identisch sein. Die Warnung an dieser Stelle finde ich fragwürdig.
Ja ich dachte auch immer, dass diese Zugriffsweisen beliebig getauscht werden können; deshalb frage ich ja.
Das sind die ganz normalen Standardeinstellungen von Visual Studio. Wenn ich den Warnlevel verringere, bleibt die Warnung natürlich weg, aber das ist ja auch nicht gut, oder? Theoretisch könnte es ja sein, dass die Grenze bzw. der Wert von i irgendwann einmal größer ist.
@SeppJ sagte in Datentyp für Zeigerarithmetik:
Prinzipiell richtig. Aber wenn man einen
int
deklariert und bei der einzigen Nutzung diesen insize_t
castet, dann hat man wahrscheinlich den falschen Typen gewählt. Warum hörst du nicht auf Th69?Weil die OpenGL-Funktion "glDrawElements" eben gerne int bzw. unsigned int haben möchte und dann wieder woanders eine Warnung auftaucht.
Es bestehen die gleichen Gefahren, vor denen hier so übertrieben gewarnt wird. Bei Warnungen ist es Abwägungssache, was der Programmierer wohl gemeint hat und ob das wirklich zu dem Code passt. Der Code ist ja nicht direkt falsch. Warnungen gibt es deshalb bei üblichen Fehlermustern, wo prinzipiell korrekter Code nicht das macht, was die meisten Leute denken. Überlaufende Arrayindizes können ja in irgendwelchen verschwurbelten Szenarien noch Sinn machen. Aber das Szenario
(großer Typ) + (potentiell überlaufende Rechnung mit kleinen Typen)
kommt auch ganz losgelöst von Pointertypen eher vor und ist wohl nie das was wirklich gemeint ist.Eigentlich habe ich mich ja nur an das gehalten, was der Compiler verlangt hat und vor der Multiplikation in einen breiteren Datentypen umgewandelt.
-
@Peter-Viehweger sagte in Datentyp für Zeigerarithmetik:
Weil die OpenGL-Funktion "glDrawElements" eben gerne int bzw. unsigned int haben möchte und dann wieder woanders eine Warnung auftaucht.
Erm, bitte was? Du sollst für deine lokalen Schleifenvariablen
size_t
benutzen, die tauchen nur noch im Schleifenrumpf auf. Würde mich sehr wundern, wenn es dadurch Warnungen geben sollte. Pauschal nur int32/uint32 zu benutzen, weil es irgendwo mit OpenGL Probleme geben könnte halte ich für fragwürdig. Benutze die richtigen Datentypen im richtigen Kontext! Dafür zahle ich auch gern 1€ Euro in´s Phrasenschwein.
-
Also das hier ist jetzt mal mehr oder weniger der "Originalcode" zur Berechnung der Unterseite eines Würfels (d.h. eigentlich ist das Ganze 6 Mal so lang, mir ist da noch nichts Besseres eingefallen):
#include <stdlib.h> float *vertices; unsigned int numvertices; unsigned int *indices; unsigned int numindices; int main() { size_t i; size_t j; float *verticesptr; unsigned int *indicesptr; unsigned int indicesoffset; unsigned int xelements = 12; unsigned int yelements = 12; unsigned int zelements = 12; numvertices = 2 * (xelements + 1) * (yelements + 1) * 6 + 2 * (xelements + 1) * (zelements + 1) * 6 + 2 * (yelements + 1) * (zelements + 1); vertices = malloc(numvertices * sizeof(*vertices)); if(vertices == NULL) { return 1; } numindices = 2 * xelements * (yelements + 1) * 3 + 2 * (xelements + 1) * yelements * 3 + 2 * xelements * (zelements + 1) * 3 + 2 * (yelements + 1) * zelements * 3 + 2 * yelements * (zelements + 1) * 3; indices = malloc(numindices * sizeof(*indices)); if(indices == NULL) { return 2; } //vertices for bottom side verticesptr = vertices; for(i = 0; i < (yelements + 1); i++) { for(j = 0; j < (xelements + 1); j++) { *(verticesptr + i * (xelements + 1) * 6 + j * 6 + 0) = (float) j / (float) xelements; *(verticesptr + i * (xelements + 1) * 6 + j * 6 + 1) = (float) i / (float) yelements; *(verticesptr + i * (xelements + 1) * 6 + j * 6 + 2) = 0.0f; } } //indices for horizontal lines on bottom side indicesptr = indices; indicesoffset = 0; for(i = 0; i < (yelements + 1); i++) { for(j = 0; j < xelements; j++) { *(indicesptr + i * xelements * 3 + j * 3 + 0) = indicesoffset + i * (xelements + 1) + j; *(indicesptr + i * xelements * 3 + j * 3 + 1) = indicesoffset + i * (xelements + 1) + j + 1; *(indicesptr + i * xelements * 3 + j * 3 + 2) = indicesoffset + i * (xelements + 1) + j + 1; } } //indices for vertical lines on bottom side indicesptr = indicesptr + yelements * (xelements + 1) * 3; indicesoffset = indicesoffset + (yelements + 1) * xelements; for(i = 0; i < yelements; i++) { for(j = 0; j < (xelements + 1); j++) { *(indicesptr + i * (xelements + 1) * 3 + j * 3 + 0) = indicesoffset + i * (xelements + 1) + j; *(indicesptr + i * (xelements + 1) * 3 + j * 3 + 1) = indicesoffset + (i + 1) * (xelements + 1) + j; *(indicesptr + i * (xelements + 1) * 3 + j * 3 + 2) = indicesoffset + (i + 1) * (xelements + 1) + j; } } return 0; }
Die globalen Variablen ganz oben sind übrigens nur im Codebeispiel vorhanden und werden eigentlich als Parameter an die Funktion übergeben.
1>warning C4267: "=": Konvertierung von "size_t" nach "unsigned int", Datenverlust möglich 1>warning C4267: "=": Konvertierung von "size_t" nach "unsigned int", Datenverlust möglich 1>warning C4267: "=": Konvertierung von "size_t" nach "unsigned int", Datenverlust möglich 1>warning C4267: "=": Konvertierung von "size_t" nach "unsigned int", Datenverlust möglich 1>warning C4267: "=": Konvertierung von "size_t" nach "unsigned int", Datenverlust möglich 1>warning C4267: "=": Konvertierung von "size_t" nach "unsigned int", Datenverlust möglich
Also auch nicht besser......
-
Ne, wie auch? Du benutzt ja immer noch
uint64
undunsigned int
bei der Multiplikation (Zeilen 43-45, 57-59 und 71-73). Sorg´ dafür, dass alle Operatoren vom Typuint64
odersize_t
sind, dann verschwinden auch die Warnungen.
-
#include <stdlib.h> float *vertices; // size_t numvertices; //Eigentlich gibt es eine Funktion, die diesen Code ausführt und unsigned int *indices; //Zeiger auf diese Variablen als Parameter übernimmt size_t numindices; // int main() { size_t i; size_t j; float *verticesptr; unsigned int *indicesptr; size_t indicesoffset; size_t xelements = 12; size_t yelements = 12; size_t zelements = 12; numvertices = 2 * (xelements + 1) * (yelements + 1) * 6 + 2 * (xelements + 1) * (zelements + 1) * 6 + 2 * (yelements + 1) * (zelements + 1); vertices = malloc(numvertices * sizeof(*vertices)); if(vertices == NULL) { return 1; } numindices = 2 * xelements * (yelements + 1) * 3 + 2 * (xelements + 1) * yelements * 3 + 2 * xelements * (zelements + 1) * 3 + 2 * (yelements + 1) * zelements * 3 + 2 * yelements * (zelements + 1) * 3; indices = malloc(numindices * sizeof(*indices)); if(indices == NULL) { return 2; } //vertices for bottom side verticesptr = vertices; for(i = 0; i < (yelements + 1); i++) { for(j = 0; j < (xelements + 1); j++) { *(verticesptr + i * (xelements + 1) * 6 + j * 6 + 0) = (float) j / (float) xelements; *(verticesptr + i * (xelements + 1) * 6 + j * 6 + 1) = (float) i / (float) yelements; *(verticesptr + i * (xelements + 1) * 6 + j * 6 + 2) = 0.0f; } } //indices for horizontal lines on bottom side indicesptr = indices; indicesoffset = 0; for(i = 0; i < (yelements + 1); i++) { for(j = 0; j < xelements; j++) { *(indicesptr + i * xelements * 3 + j * 3 + 0) = indicesoffset + i * (xelements + 1) + j; *(indicesptr + i * xelements * 3 + j * 3 + 1) = indicesoffset + i * (xelements + 1) + j + 1; *(indicesptr + i * xelements * 3 + j * 3 + 2) = indicesoffset + i * (xelements + 1) + j + 1; } } //indices for vertical lines on bottom side indicesptr = indicesptr + yelements * (xelements + 1) * 3; indicesoffset = indicesoffset + (yelements + 1) * xelements; for(i = 0; i < yelements; i++) { for(j = 0; j < (xelements + 1); j++) { *(indicesptr + i * (xelements + 1) * 3 + j * 3 + 0) = indicesoffset + i * (xelements + 1) + j; *(indicesptr + i * (xelements + 1) * 3 + j * 3 + 1) = indicesoffset + (i + 1) * (xelements + 1) + j; *(indicesptr + i * (xelements + 1) * 3 + j * 3 + 2) = indicesoffset + (i + 1) * (xelements + 1) + j; } } return 0; }
1>warning C4267: "=": Konvertierung von "size_t" nach "unsigned int", Datenverlust möglich 1>warning C4267: "=": Konvertierung von "size_t" nach "unsigned int", Datenverlust möglich 1>warning C4267: "=": Konvertierung von "size_t" nach "unsigned int", Datenverlust möglich 1>warning C4267: "=": Konvertierung von "size_t" nach "unsigned int", Datenverlust möglich 1>warning C4267: "=": Konvertierung von "size_t" nach "unsigned int", Datenverlust möglich 1>warning C4267: "=": Konvertierung von "size_t" nach "unsigned int", Datenverlust möglich
Für "indices" ist "unsigned int" als Datentyp fest vorgegeben: https://www.khronos.org/registry/OpenGL-Refpages/gl4/html/glDrawElements.xhtml
type
Specifies the type of the values in indices. Must be one of GL_UNSIGNED_BYTE, GL_UNSIGNED_SHORT, or GL_UNSIGNED_INT.
-
Wenn du keine der beiden Warnings haben willst (gut), und auch keine der beiden Warnings deaktivieren/unterdrücken (vermutlich auch gut), dann bleibt dir nur übrig irgendwo zu casten.
Also entweder mit 32 Bit Schleifenzähler: vor der Multiplikation 32 -> 64 Bit casten. Oder mit 64 Bit Schleifenzähler: vor der Zuweisung 64 -> 32 Bit casten.
-
@Peter-Viehweger
Eine kleine Frage. Kompilierst du das Programm unter 64Bit?Wenn ich nämlich dein Programm unter 64Bit kompiliere, komme ich auf die gleichen Warnungen. Unter 32 Bit nicht.
Im folgenden Beispiel ist i vom Typ size_t und die 12 vom Typ int.
*(data + i * 12 + j) = 1.0f;
Also habe ich Testweise den Typ von size_t auf unsigned int umgestellt und das Integer Literal u benutzt. Dadurch nutzen alle Elemente der Rechnung den gleichen Datentyp.
Probiere doch mal ob der folgende Code nun läuft.
#include <stdlib.h> float* vertices; // unsigned int numvertices; //Eigentlich gibt es eine Funktion, die diesen Code ausführt und unsigned int* indices; //Zeiger auf diese Variablen als Parameter übernimmt unsigned numindices; // int main() { unsigned int i; unsigned int j; float* verticesptr; unsigned int* indicesptr; unsigned int indicesoffset; unsigned int xelements = 12; unsigned int yelements = 12; unsigned int zelements = 12; numvertices = 2u * (xelements + 1u) * (yelements + 1u) * 6u + 2u * (xelements + 1u) * (zelements + 1u) * 6u + 2u * (yelements + 1u) * (zelements + 1u); vertices = malloc(numvertices * sizeof(*vertices)); if (vertices == NULL) { return 1; } numindices = 2u * xelements * (yelements + 1u) * 3u + 2u * (xelements + 1u) * yelements * 3u + 2u * xelements * (zelements + 1u) * 3u + 2u * (yelements + 1u) * zelements * 3u + 2u * yelements * (zelements + 1u) * 3u; indices = malloc(numindices * sizeof(*indices)); if (indices == NULL) { return 2; } //vertices for bottom side verticesptr = vertices; for (i = 0; i < (yelements + 1u); i++) { for (j = 0; j < (xelements + 1u); j++) { *(verticesptr + i * (xelements + 1u) * 6u + j * 6u + 0u) = (float)j / (float)xelements; *(verticesptr + i * (xelements + 1u) * 6u + j * 6u + 1u) = (float)i / (float)yelements; *(verticesptr + i * (xelements + 1u) * 6u + j * 6u + 2u) = 0.0f; } } //indices for horizontal lines on bottom side indicesptr = indices; indicesoffset = 0; for (i = 0; i < (yelements + 1u); i++) { for (j = 0; j < xelements; j++) { *(indicesptr + i * xelements * 3u + j * 3u + 0u) = indicesoffset + i * (xelements + 1u) + j; *(indicesptr + i * xelements * 3u + j * 3u + 1u) = indicesoffset + i * (xelements + 1u) + j + 1u; *(indicesptr + i * xelements * 3u + j * 3u + 2u) = indicesoffset + i * (xelements + 1u) + j + 1u; } } //indices for vertical lines on bottom side indicesptr = indicesptr + yelements * (xelements + 1u) * 3u; indicesoffset = indicesoffset + (yelements + 1u) * xelements; for (i = 0; i < yelements; i++) { for (j = 0; j < (xelements + 1u); j++) { *(indicesptr + i * (xelements + 1u) * 3u + j * 3u + 0u) = indicesoffset + i * (xelements + 1u) + j; *(indicesptr + i * (xelements + 1u) * 3u + j * 3u + 1u) = indicesoffset + (i + 1u) * (xelements + 1u) + j; *(indicesptr + i * (xelements + 1u) * 3u + j * 3u + 2u) = indicesoffset + (i + 1u) * (xelements + 1u) + j; } } return 0; }
-
@Quiche-Lorraine: Deine "Lösung" geht am Problem vorbei
-
@Quiche-Lorraine sagte in Datentyp für Zeigerarithmetik:
Dadurch nutzen alle Elemente der Rechnung den gleichen Datentyp.
Eben nicht. z.B.
(indicesptr + i * (xelements + 1u) * 3u + j * 3u + 1u)
Wenn rechts allesunsigned int
ist, dann bleibt da immer nochptr + unsigned int
. Und diesen Operator gibt es nicht - es gibt blossptr + ptrdiff_t
. Also Konvertierungunsigned_int -> ptrdiff_t
. Also 32 -> 64 Bit auf einem 64 Bit System.
Und daher dann die Warning dass man erst mit 32 Bit rumrechnet und danach erst auf 64 Bit erweitert. Was zur Folge hat dass man Überläufe bekommen kann, die man hätte vermeiden können wenn man erst auf 64 Bit castet bevor man rechnet.
-
@hustbaer
Danke für die Info, das habe ich übersehen