Ich möchte mit snprintf maximal 8 Zeichen + Null in ein Array schreiben
lassen. Das funktioniert auch soweit. Im Array steht danach dann z.b.
"Temp20.4\0" drin.
Hier ein Auszug aus dem Code, der angemeckert wird:
(Es sind noch mehrere andere Stellen, aber immer nach dem selben Muster.
%2d.)
Wie bekomme ich die Warnungen weg? In temperature wird nie etwas
grösseres also 450-500 stehen (45.0°-50.0°). Muss ich erstmal zwei
Variablen anlegen und sicherstellen, dass die nicht grösser als 2 bzw 1
Stelle werden?
Danke,
OhWeia
Oh, ich habe Compiler vergessen:
gcc-arm-none-eabi-8-2018-q4-major
gcc version 8.2.1 20181213 (release) [gcc-8-branch revision 267074] (GNU
Tools for Arm Embedded Processors 8-2018-q4-major)
Die libc ist die newlib.
Andere Frage: Wenn Du eh printf benutzt - was kosten Dich 5 Bytes mehr
im globalen Puffer?
Nebenbei: Wenn snprintf die volle Länge 9 ausnutzt, ist der String nicht
mehr Null-terminiert.
Walter T. schrieb:> Nebenbei: Wenn snprintf die volle Länge 9 ausnutzt, ist der String nicht> mehr Null-terminiert.
Das steht in der manpage:
>The functions snprintf() and vsnprintf() write at most size bytes (including the
terminating null byte ('\0')) to str.
Ich interpretiere das so, dass es die 0 immer schreibt. Wenn ich als
länge also 9 angebe, passen maximal 8 Zeichen rein.
Walter T. schrieb:> Andere Frage: Wenn Du eh printf benutzt - was kosten Dich 5 Bytes mehr> im globalen Puffer?
Nichts, aber ich würde gerne wissen, wie ich mit den Formatbezeichnern
die Kommazahl so anzeigen kann. %2d.%1d scheint aber wohl zu einfach
gedacht zu sein...
OhWeia schrieb:> Nichts, aber ich würde gerne wissen, wie ich mit den Formatbezeichnern> die Kommazahl so anzeigen kann. %2d.%1d scheint aber wohl zu einfach> gedacht zu sein...
Das sollte funktionieren. Der Fehler liegt an einer anderen Stelle.
Außer dass man üblicherweise ein Leerzeichen zwischen Text und Zahl
läßt.
OhWeia schrieb:> Walter T. schrieb:>> Nebenbei: Wenn snprintf die volle Länge 9 ausnutzt, ist der String nicht>> mehr Null-terminiert.>> Das steht in der manpage:>>The functions snprintf() and vsnprintf() write at most size bytes (including the> terminating null byte ('\0')) to str.> Ich interpretiere das so, dass es die 0 immer schreibt. Wenn ich als> länge also 9 angebe, passen maximal 8 Zeichen rein.
Das ist - ziemlich sicher - anders gemeint: Bei der Ermittlung der Größe
wird die terminierende \0 mitgezählt. Wenn du 9 angibst, schreibt er
(maximal) 9 Bytes rein, nicht 9 Bytes + \0.
OhWeia schrieb:> Walter T. schrieb:>> Andere Frage: Wenn Du eh printf benutzt - was kosten Dich 5 Bytes mehr>> im globalen Puffer?>> Nichts, aber ich würde gerne wissen, wie ich mit den Formatbezeichnern> die Kommazahl so anzeigen kann. %2d.%1d scheint aber wohl zu einfach> gedacht zu sein...
Nein, %2d ist nur die Mindestlänge des Strings:
http://www.cplusplus.com/reference/cstdio/printf/
"Minimum number of characters to be printed. If the value to be printed
is shorter than this number, the result is padded with blank spaces. The
value is not truncated even if the result is larger."
MfG, Arno
Arno schrieb:> Das ist - ziemlich sicher - anders gemeint: Bei der Ermittlung der Größe> wird die terminierende \0 mitgezählt. Wenn du 9 angibst, schreibt er> (maximal) 9 Bytes rein, nicht 9 Bytes + \0.
Ja, das schrieb ich doch so. 8 Zeichen + 0.
Walter T. schrieb:> Außer dass man üblicherweise ein Leerzeichen zwischen Text und Zahl> läßt.
"%2d" meinst du? Das meckert er auch an und funktioniert auch nicht. Da
steht dann einfach nichts an den Stellen im Array.
Ein Leerzeichen ist kein Bezeichner... Das hab ich auch noch nie
gesehen.
Arno schrieb:> "Minimum number of characters to be printed. If the value to be printed> is shorter than this number, the result is padded with blank spaces. The> value is not truncated even if the result is larger."
Dann bleibt mir wohl nichts anderes, als das Array grösser zu machen.
Danke euch! :)
Arno schrieb:> Das ist - ziemlich sicher - anders gemeint: Bei der Ermittlung der Größe> wird die terminierende \0 mitgezählt. Wenn du 9 angibst, schreibt er> (maximal) 9 Bytes rein, nicht 9 Bytes + \0.
Sagen wir es so: In der Realität ist es leider Compilerabhängig, ob ein
mit snprintf erzeugter String mit zu kleiner Bufferlänge Nullterminiert
ist. Mit _snprintf des MSVC ist es das nämlich z.B. nicht.
Du hast dem Compiler (direkt oder indirekt) die Option
-Wformat-truncation=2
mitgegeben. Damit prüft der Compiler auf pessimistische Weise, ob der
resultierende String einschließlich der Nullterminierung länger als 9
werden kann und deswegen abgeschnitten wird.
https://gcc.gnu.org/onlinedocs/gcc/Warning-Options.html
Das kann hier tatsächlich passieren, nämlich dann, wenn
temperature/10 >= 100
oder
temperature/10 <= -10
ist.
Du kannst den Pessimismus des Compilers reduzieren mit
-Wformat-truncation=1
oder die Warnung mit
-Wformat-truncation=0
oder
-Wno-format-truncation
ganz abschalten.
Du kannst die Warnung auch eingeschaltet lassen und versuchen, dem
Compiler klarzumachen, dass temperature/10 niemals negativ oder mehr als
zweistellig wird. Von welchem Datentyp ist temperature?
temperature ist uint32_t.
Warnungen abschalten finde ich nicht so schön, an anderer Stelle entgeht
mir dann vielleicht noch etwas...
Ich habe jetzt den buffer grösser gemacht und die Ausgabe-Routine gibt
nicht mehr von Stelle 0 bis \0 aus, sondern von Stelle 0 bis Stelle 7.
(Das Display hat 8 Stellen).
So bin ich die Warnung los und im Fehlerfall hab ich halt Müll auf dem
Display und keinen Pointer-Amoklauf.
OhWeia schrieb:> temperature ist uint32_t.
Dieser Code (nur auf dem PC getestet mit GCC 8.2.1) liefert auch mit
-Wformat-truncation=2
keine Warnung:
1
#include<stdio.h>
2
#include<assert.h>
3
#include<stdint.h>
4
5
uint32_ttemp;
6
7
intmain(void){
8
staticcharbuf[9];
9
assert(temp<100);
10
snprintf(buf,9,"Temp%2u.%1u",temp/10,temp%10);
11
}
Durch die Verwendung von unsigned weiß der Compiler, dass temp/10 und
temp%10 nicht negativ werden können, und durch das assert ist für
temp/10 auch eine obere Schranke definiert. Des Weiteren erkennt der
Compiler offensichtlich auch, dass temp%10 immer <10 ist.
Das assert benötigt allerdings etwas zusätzliche Rechenzeit. Deaktiviert
man es mit #define NDEBUG, erscheint auch die Warnung wieder.
Die internen Gedankengänge des Compilers sind auch ziemlich schwer
nachvollziehbar. So bleibt auch mit assert(temp<101) die Warnung aus,
obwohl temp==100 schon ein Problem darstellt. Erst mit assert(temp<1001)
erscheint sie wieder.
PS: Für uint32_t sollte man eigentlich das Format "%"PRIu32 statt "%u"
verwenden. Bei 32-Bit-Ints tritt der Unterschied aber nicht in
Erscheinung.
Yalu X. schrieb:> Das assert benötigt allerdings etwas zusätzliche Rechenzeit.Yalu X. schrieb:> Aber auch diese Varianten sind aber bzgl. Programmgröße und Rechenzeit> nicht kostenlos.
Tja, bei MS würde man einfach "__analysis_assume(temp<100);" verwenden.
GCC scheint sowas allerdings nicht zu kennen.
Yalu X. schrieb:> Dieser Code (nur auf dem PC getestet mit GCC 8.2.1) liefert auch mit>> -Wformat-truncation=2>> keine Warnung:
Der liefert hier mit gcc 8.3.0 weder mit noch ohne das assert eine
Warnung. Das scheint also sehr implementationsabhängig zu sein.
Oliver
guest schrieb:> Tja, bei MS würde man einfach "__analysis_assume(temp<100);" verwenden.> GCC scheint sowas allerdings nicht zu kennen.
Bei GCC geht das mit __builtin_unreachable(), du Experte
Jemand schrieb:> guest schrieb:>> Tja, bei MS würde man einfach "__analysis_assume(temp<100);" verwenden.>> GCC scheint sowas allerdings nicht zu kennen.>> Bei GCC geht das mit __builtin_unreachable(), du Experte
Unfug.
Das macht etwas völlig anderes.
Lisa-Marie schrieb:> Jemand schrieb:>> Bei GCC geht das mit __builtin_unreachable(), du Experte>> Unfug.> Das macht etwas völlig anderes.
So völlig anders nun auch wieder nicht:
__builtin_unreachable() erzeugt keinen Code, ein Statement wie
1
if(temp>=100)
2
__builtin_unreachable();
erzeugt also auch keinen Code. Und den Analyse-Passes kann man so
mitteilen, dass temp nicht > 99 werden kann so dass Warnungen
zielgenauer werden. In manchen Fällen kann damit auch die Codeerzeugung
verbessert werden, aber man will kaum den Code mit solchen
compilerabhängigen Statements verunstalten...