R2woD2wo
Anmeldungsdatum: 5. Mai 2016
Beiträge: 104
|
Hallo, habe folgendes Phänomen: C-Programme, welche CSV-Dateien anlegen und auf 32-Bit System (ebenfalls Ubuntu) erstellt und seit Jahren gute Dienste erweisen, zeigen auf 64 Bit System seltsames Verhalten bzgl. Dateigröße. Obgleich nur wenige hundert Bytes geschrieben werden, endet die Aktion gelegentlich (nicht immer, jedoch reproduzierbar!) in Dateien, die 4294967342 Bytes (4G) groß sind. Bin mit meinem Latein am Ende. Schätze, es liegt am C-Compiler? Hat jemand eine Idee was los ist? Merci!
|
Prof._Frink
Anmeldungsdatum: 29. Mai 2010
Beiträge: 1096
|
Hey, ja ich würde erstmal einfach die Dateien vergleichen, in den großen Dateien müssen dann ja irgendwelche zusätzlichen, fehlerhaften Daten liegen. Viele Grüße, Frink EDIT: Und du hast das Programm auf dem 64-bit System auch neu kompiliert und nicht einfach die Binary übertragen?
|
NORACSA
Anmeldungsdatum: 31. Januar 2010
Beiträge: 180
|
Zeig doch mal deinen Code her! Ohne den wird das wahrscheinlich höchstens in eine Raterei ausarten.
|
rleofield
Anmeldungsdatum: 14. September 2008
Beiträge: 779
Wohnort: Görlitz
|
R2woD2wo schrieb: Hallo, habe folgendes Phänomen: C-Programme, welche CSV-Dateien anlegen und auf 32-Bit System (ebenfalls Ubuntu) erstellt und seit Jahren gute Dienste erweisen, zeigen auf 64 Bit System seltsames Verhalten bzgl. Dateigröße. Obgleich nur wenige hundert Bytes geschrieben werden, endet die Aktion gelegentlich (nicht immer, jedoch reproduzierbar!) in Dateien, die 4294967342 Bytes (4G) groß sind.
Vermutlich ist int mit 32-Bit und 64-Bit gleich lang. Macros im GCC: | //// 64 bit
/*
#define __SIZEOF_INT__ 4
#define __SIZEOF_LONG__ 8
// 32 bit
#define __SIZEOF_INT__ 4
#define __SIZEOF_LONG__ 4
|
D.h., wenn das C Programm int statt long verwendet, kann das passieren. Oder: Und uint statt int hilft auch mal. Unsigned versus signed Hier eine Liste der verschiedenen Speichermodelle:
1
2
3
4
5
6
7
8
9
10
11
12 |
Datatype LP64 ILP64 LLP64 ILP32 LP32
char 8 8 8 8 8
short 16 16 16 16 16
int 32 64 32 32 16
long 64 64 32 32 32
pointer 64 64 64 32 32
size_t 64 64 64 32 32
ptrdiff_t 64 64 64 32 32
ILP32 Windows 32 bit, Linux 32bit bis auf long long
LP64 alle 64 bit Intels/AMD, ausser Itanium
|
siehe auch: http://www.cplusplus.com/reference/cstdint/ rleofield
|
R2woD2wo
(Themenstarter)
Anmeldungsdatum: 5. Mai 2016
Beiträge: 104
|
Danke für zügige Rückmeldungen. Codeschnippsel: Die Datei, welche von einem Ringpuffer Byte für Byte in die Datei schiebt: unsigned char F_Buf2Byte(struct AeFileObject_t *F,struct RBuffer_t *Buf){
unsigned char flag=0;
if (Buf->Bx!=Buf->Be){
putc(Buf->data[Buf->Bx],F->File); Buf->Bx=Nextix(Buf->mx,Buf->Bx);
} else {
flag=1; // Buffer geleert/kein weiteres Byte vorhanden.
}
return flag;
} Erkenntnis/Annahme: Hier wird pro Funktionsaufruf sicher nur 1 Byte geschrieben. Die Codestelle, welche den Ringpuffer eben obiger Low-Level-Funktion Byte für Byte zuführt und zu Debugzwecken die Bytes zählt:
unsigned int n=0;
while (F_Buf2Byte(FO,&FrameBuf)==0){
zwerg=Nextix(FrameBuf.mx,zwerg);
if (n<60000){n++;}
}
printf("\n %d geschriebene Bytes.\n",n);
} Ringpuffer und auch der Anfang der resultierenden Datei scheinen ok - wenn das Phänomen auftritt, scheint restliche Datei mit Nullen befüllt zu sein. Das IRRE: es werden egal ob das Phänomen auftritt (Dateigröße 4G) oder die Datei korrekt klein (paar zig Bytes) abgelegt wird, die Bytes richtig gezählt. Also gehe ich davon aus, die Low-Level Funktion welche für den eigentlichen Dateischrieb verantwortlich ist, wird auch nur z.B. 38 mal aufgerufen, ehe die Datei geschlossen wird. Ich bekomme Angst. Und ja - natürlich habe ich das Programm auf dem Zielrechner neu compiliert. make clean, make... Versuche nun die Antwort bzgl. int/long etc. nachzuvollziehen - obgleich ich nach den Beobachtungen nicht glaube, dass es daran liegt - oder doch? Danke für jede Hilfe/Erklärung.
|
R2woD2wo
(Themenstarter)
Anmeldungsdatum: 5. Mai 2016
Beiträge: 104
|
Wie rleofield schrieb, untescheiden sich die long und int Größen. printf("\nint: %d long: %d",sizeof(int), sizeof(long));
liefert auf dem alten System:
int: 4 long: 4
und auf dem neuen (64Bit) System:
int: 4 long: 8
Ok. Aber wie es zu dem mich ärgernden Phänomen kommt ist mir weiterhin unklar?! ☹
|
Dakuan
Anmeldungsdatum: 2. November 2004
Beiträge: 6339
Wohnort: Hamburg
|
Ich habe mir den Code jetzt nicht im Detail angesehen aber ich kenne das Problem. Um das Problem zu vermeiden gibt es den Datentyp size_t. Der ist auf jedem System so definiert, dass er zu den Libs passt (immer unsigned). Das setzt allerdings voraus, das auch alle beteiligten Funktionen diesen Typ verwenden. Bei Weiterverarbeitung und/oder Konvertierung muss man aber trotzdem aufpassen.
|
Neral
Anmeldungsdatum: 3. Oktober 2007
Beiträge: 229
|
@R2woD2wo: Ohne entweder den kompletten Quellcode oder zumindest alle relevanten Stellen (am besten in Form eines MWE) zu kennen, kann man nicht viel mehr als im Dunkeln stochern. Es könnte aber auch schon reichen, das Programm mit UBSan et. al. (-fsanitize=undefined , -fsanitize=address etc., einfach mal in der GCC-Doku nachschauen, was es da gibt) zu kompilieren. Vielleicht hilft das, wenn das Problem kein Logikfehler ist, sondern UB oder ein Speicherfehler oder so. Und natürlich alle Warnungen aufdrehen (mindestens -Wall -Wextra -Wconversion -pedantic ). Wenn das nichts bringt und das Programm zu lang zum manuellen Reduzieren ist, könnte vielleicht ein Tool wie C-Reduce helfen, ein reduziertes Beispiel zu erzeugen. -m32 existiert auch noch, um ein Programm auf einem 64-Bit-System im 32-Bit-Modus zu kompilieren.
|
R2woD2wo
(Themenstarter)
Anmeldungsdatum: 5. Mai 2016
Beiträge: 104
|
Das Flag -m32 wie folgt im Makefile eingebaut:
CC = gcc
CFLAGS = -Wall -g -m32
LDFLAGS = -lm führt zu folgendem Fehler: gcc -MM main.c > .depend
gcc -Wall -g -m32 -c -o main.o main.c
In file included from /usr/include/stdio.h:27:0,
from main.c:18:
/usr/include/features.h:367:25: fatal error: sys/cdefs.h: Datei oder Verzeichnis nicht gefunden
compilation terminated. Teste sogleich ohne m32 aber mit den weiteren Warnings.
|
R2woD2wo
(Themenstarter)
Anmeldungsdatum: 5. Mai 2016
Beiträge: 104
|
Dakuan schrieb: Ich habe mir den Code jetzt nicht im Detail angesehen aber ich kenne das Problem. Um das Problem zu vermeiden gibt es den Datentyp size_t. Der ist auf jedem System so definiert, dass er zu den Libs passt (immer unsigned). Das setzt allerdings voraus, das auch alle beteiligten Funktionen diesen Typ verwenden. Bei Weiterverarbeitung und/oder Konvertierung muss man aber trotzdem aufpassen.
Kennen == verstehen? Info: Das Programm ist ein kleines Demo-Programm welches ich mir damals geschrieben habe. Es berechnet im ersten Teil einfach eine Parabel, speichert die Punkte in einer CSV-Datei, und liest anschließend die Werte wieder aus, ändert Zellinhalte. WICHTIG!!!!!! : Es passiert scheinbar nicht beim initialen Schreiben, sondern beim erneuten Lesen der Datei mit "r+". Ich debugge mal weiter - bin sehr gespannt woran es liegt. Schätze ihr liegt schon recht nahe mit dem Tip size_t - denn es laufen jede Menge Warnings mit eingeschalteten Compiler-Flags runter a la: warning: conversion to ‘u_int8_t {aka unsigned char}’ from ‘char’ may change the sign of the result [-Wsign-conversion]
AddByte2Buffer( Buf, _CsvTrennZeichen_ );
|
R2woD2wo
(Themenstarter)
Anmeldungsdatum: 5. Mai 2016
Beiträge: 104
|
Leute! Asche auf mein Haupt! Fehler gefunden. Der Fehler passierte durch relative Positionierung und anschließendes Schreiben in einer mit Attribut "r+" geöffneten Datei. Die Ursache war eine als unsigned int definierte Variable, welche zum zurückspulen negiert einer Funktion mit Argumentdatentyp long übergeben wurde. unsigned int nField=0;
// Ermitteln der Feldgröße, d.h. nField=N mit N>0;
...
// Zurückspulen auf Startposition
F_MoveRelPos(Datei,-nField); Das ist natürlich Mist, und führt (wieso im Detail versteh ich noch nicht - ausser dass eben Bits böse umkippen und ein riesiger Sprung passiert...schon klar) zu den riesigen Dateigrößen, je nach Bitmuster. "Funktionierend" wäre nun: int nField=0;
// Ermitteln der Feldgröße, d.h. nField=N mit N>0;
...
// Zurückspulen auf Startposition
F_MoveRelPos(Datei,-nField); Ok. Damit wär mir klar, warum das gute alte Testprojekt auf der neuen Maschine versagt und das seltsame Verhalten je nachdem wie viel man reinschreibt hat. Aber wie macht man es sauber - ausser gleich alles als long zu definieren(was ich wegen portierbarkeit auf Embedded-System möglichst sparsam mache - ok an der Stelle sinnlos)? Exzessives Type-Casting, bis alle Warnings weg sind? Oder Verwendung des size_t Typs (Danke für Hinweis, obgleich mir die Konsequenzen noch unklar sind). Würde mich freuen, wenn hier kurz knackig in Codebeispielen Typecasting bzgl. Zuweisung und Übergabe auch für call by reference durchexerziert wird. Ich recherchiere sogleich selber zu dem Thema. Danke für Eure Unterstützung!
|
Dakuan
Anmeldungsdatum: 2. November 2004
Beiträge: 6339
Wohnort: Hamburg
|
Ich fang mal hinten an. Also den Typ size_t kannst du hier erstmal ausklammern. Den hatte ich erwähnt, weil ich ein Problem mit dem Datei I/O vermutete. Das wird immer dann interessant, wenn das so in der Spezifikation einer Funktion steht, also beispielsweise bei
size_t fread( void*buf, size_t size, size_t count, FILE*stream ); Wie deine Positionierung arbeitet kann ich im Moment nicht sehen. Aber du musst dir über den zu erwartenden Wertebereich Gedanken machen, also ob ein long überhaupt erforderlich ist. Und warum musst du den Parameter negieren? Du kannst doch auch einen positiven Wert subtrahieren.
... ausser dass eben Bits böse umkippen ...
Da kippt nichts böse um. Das liegt am Zweierkomplement.
Exzessives Type-Casting, bis alle Warnings weg sind?
Lieber nicht, das macht den Code schwer lesbar und wahrscheinlich auch weniger portabel.
|
R2woD2wo
(Themenstarter)
Anmeldungsdatum: 5. Mai 2016
Beiträge: 104
|
Die Funktion F_MoveRelPos ist nichts anderes als ein maskiertes fseek (s.u.) - daher auch die Sache mit dem long: void F_MoveRelPos(struct AeFileObject_t *FO,long delta){
fseek( FO->File,delta,SEEK_CUR);
return;
} Ok. Danke für die Erläuterung bzgl. size_t. Jedenfalls: Nun ist die Welt wieder gut statt böse ☺
|
R2woD2wo
(Themenstarter)
Anmeldungsdatum: 5. Mai 2016
Beiträge: 104
|
Frage nur : Was genau ist passiert ? Der Zahlenwert des Zurückspulens war 7. mit unsigned int negieren - bedeutet??? Wieso funktioniert es wenn es nun als int deklariert ist? Wäre das so nun überhaupt sauber? Oder eben doch casten bzw. nField als long deklarieren?
|
R2woD2wo
(Themenstarter)
Anmeldungsdatum: 5. Mai 2016
Beiträge: 104
|
Und weil wir gerade beim Thema wären:
warning: conversion to ‘char’ from ‘int’ may alter its value [-Wconversion]
c=getc(Datei->File); Wobei c vom Typ char ist - logisch, will ja ein Byte-Zeichen lesen. Weis jemand, wieso int? Bzw.: wie reagiert putc wenn man einen int wert (>255) übergibt? Sorry falls das gerade off-Topic ist. Sagt gern, wenn ich einen extra Thread zum Thema "Low-Level-Dateiverwaltung" aufmachen darf. Ist doch etwas wackelig was ich hier mache - a la funktioniert, Daumen hoch, kaum wechsle ich den Rechner .... Schock...
|