Hallo zusammen
Könnt ihr mir sagen, was passiert wenn ich beim Aufruf der Funktion
void setBit(unsigned int *var, unsigned char bitNo)
{
*var |= (1UL<<bitNo);
}
einen anderen Datentyp des Pointers in den Parametern verwende:
z.B. so:
unsigned char myChar = 0;
setBit(&myChar, 5);
oder so:
unsigned long myLong = 0;
setBit(&myLong, 5);
bei dem Parameter "bitNo" würden ja die oberen bits abgeschnitten
werden, wenn ich einen grösseren Datentyp als char verwenden würde. Aber
wie sieht es mit den Pointern aus?
Vielen Dank im voraus
mfg
Iguan
Iguan schrieb:> Könnt ihr mir sagen, was passiert
Wie oben schon gesagt: Der Compiler wird das nicht zulassen, sondern
eine Fehlermeldung ausspucken. Wenn Du nun, um den Compiler zu
überlisten,
1
unsignedcharmyChar=0;
2
setBit((unsignedint*)&myChar,5);
schreibst, dann zeigt Dein Pointer auf einen int, der etwas größer ist
als Dein myChar. Die aufgerufene Funktion schreibt dann undefiniert in
den Speicher, in der Regel hinter myChar.
Wenn Du nämlich schreibst:
1
unsignedcharmyChar=0;
2
unsignedcharcheck=0;
3
setBit((unsignedint*)&myChar,5);
wirst Du (auf vielen Plattformen) beobachten können, dass der Wert der
Variablen "check" sich plötzlich geändert hat.
Aber mal eine Gegenfrage: Gehts um Arduino? Wenn ja, handelt es sich um
C++ und nicht um C. Hier hast Du folgende Möglichkeit, die Funktion
mehrfach zu implementieren, nämlich für verschiedene Typen von
Parametern:
1
voidsetBit(unsignedchar*var,unsignedcharbitNo)
2
{
3
*var|=(1UL<<bitNo);
4
}
5
6
voidsetBit(unsignedint*var,unsignedcharbitNo)
7
{
8
*var|=(1UL<<bitNo);
9
}
10
11
voidsetBit(unsignedlong*var,unsignedcharbitNo)
12
{
13
*var|=(1UL<<bitNo);
14
}
Der C++-Compiler wählt dann automatisch die richtige Funktion aus, je
nachdem, mit welchem Parameter Du sie aufrufst.
Bei nacktem C müsstest Du die drei Funktionen unterschiedlich benennen,
also z.B. setBit_uchar(), setBit_uint() und setBit_ulong(). Du musst
dann selbst darauf achten, die richtige Funktion aufzurufen.
Ich benutze die Entwicklungsumgebung "mikroC PRO for ARM v6.0.0" und der
Prozessor STM32F051R8T6. Unten im Ausgabefenster steht die warnung
"Suspicious pointer conversion". Kompilieren und runterladen tut er aber
trotzdem!
Vielen dank für die Erklärungen... auch mit dem C++ / C unterschied!
mfg
Iguan
Iguan schrieb:> Kompilieren und runterladen tut er aber> trotzdem!
Aber nicht lange. Früher oder später wird das Programm sich auf
unerwartete Weise falsch verhalten, denn dieser Cast involviert sog.
"undefined behaviour". Daher lieber gleich richtig machen: Da du C
verwendest, mehrere Funktionen definieren, eine pro Datentyp. In C11
kann man das mit Generic Macros kapseln. In C++ ginge es sogar noch
einfacher als von Frank gezeigt:
Niklas G. schrieb:> Da du C> verwendest, mehrere Funktionen definieren, eine pro Datentyp. In C11> kann man das mit Generic Macros kapseln.
Oder man macht ein Macro:
1
#define SET_BIT(V,N) do { V |= 1llu << (N); } while(0)
Iguan schrieb:> Unten im Ausgabefenster steht die warnung "Suspicious pointer> conversion".
Der Compiler möchte dir etwas sagen, und zwar, dass du etwas tust was
vermutlich falsch ist. In 99% der Fälle ist es dann auch was falsch. Du
solltest also auf den Compiler hören. Die anderen hier haben ja schon
beschrieben, was alles schief gehen kann.
Bei Prozessoren mit alignment check kann es beim Zugriff zur Laufzeit
scheppern, weil ein "int" Zugriff auf Daten mit "char" alignment nicht
funktionieren muss.
Und wenn Du genau sehen möchtest, wss der Compiler draus gemacht, schau
Dir die erzeugte Ausgabe-Datei mal mit einem Debugger deiner Wahl an.
Dann sollte Dir anschaulich klar werden, was da schief geht.
DPA schrieb:> Niklas G. schrieb:>> Da du C>> verwendest, mehrere Funktionen definieren, eine pro Datentyp. In C11>> kann man das mit Generic Macros kapseln.>> Oder man macht ein Macro:#define SET_BIT(V,N) do { V |= 1llu << (N); }> while(0)
Warum macht man da eine do while(0) herum?
hl. Stephanus schrieb:> Warum macht man da eine do while(0) herum?
Damit man es einklammern und trotzdem ein ; dahinter schreiben kann.
Bei einfachem {} kann man das nicht immer:
if () Makro; else ...
wird dann zu
if () { ... }; else ...
und wirft einen Fehler. Bei do { ... } while(0) wird es zu
if () do { ... } while(0); else ...
und ist ok.
hl. Stephanus schrieb:> geht doch
Von wirklicher Bedeutung ist das do...while(0) erst dann, wenn darin
mehrere Statements stehen, nicht bloss eines. Dann klappt das mit ()
nicht mehr.
Alternativ kannst du dir angewöhnen, sowas wie if/else immer mit {} zu
schreiben. Dann gibts auch kein Problem.
Ähm doch noch eine Frage..
A. K. schrieb:> Von wirklicher Bedeutung ist das do...while(0) erst dann, wenn darin> mehrere Statements stehen, nicht bloss eines. Dann klappt das mit ()> nicht mehr.
ist ein define in dem Fall dann das richtige Mittel?
A. K. schrieb:> hl. Stephanus schrieb:>> ist ein define in dem Fall dann das richtige Mittel?>> Inline-Funktionen sind besser, stehen aber nicht überall zu Verfügung.
Das Makro wurde hier nicht benutzt, um keinen Funktionsaufrufs-Overhead
zu haben, sondern damit es mit mehreren verschiedenen Datentypen zurecht
kommt. Das geht in C mit Funktionen einfach nicht, außer wenn man sie
für jeden Datentyp separat implementiert und jeweils auch einen anderen
Namen vergibt.
Überladungen und Templates als Alternative wurden ja schon genannt, aber
die gibt's eben nur in C++.