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 wenn ich beim Aufruf der Funktion Der Compiler wird rummeckern.
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 | unsigned char myChar = 0; |
2 | setBit((unsigned int *) &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 | unsigned char myChar = 0; |
2 | unsigned char check = 0; |
3 | setBit((unsigned int *) &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 | void setBit(unsigned char *var, unsigned char bitNo) |
2 | {
|
3 | *var |= (1UL<<bitNo); |
4 | }
|
5 | |
6 | void setBit(unsigned int *var, unsigned char bitNo) |
7 | {
|
8 | *var |= (1UL<<bitNo); |
9 | }
|
10 | |
11 | void setBit(unsigned long *var, unsigned char bitNo) |
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:
1 | template <typename T> |
2 | void setBit (T& var, uint_least8_t bit) { |
3 | var |= (1 << bit); |
4 | }
|
funktioniert dann mit jedem Integer-Typ korrekt.
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.
>Und warum muss da ein ; zum define dazugehören?
Mach es mal ohne, dann weisst du es.
#include<stdio.h>
#define SET_BIT(var, bit) (var |= (1 << bit))
int main()
{
int a = 0;
SET_BIT(a, 0);
printf("B0 gesetzt: %d \n", a);
if(a != 0)
{
SET_BIT(a, 1);
}
printf("B1 gesetzt: %d \n", a);
}
Ausgabe:
B0 gesetzt: 1
B1 gesetzt: 3
geht doch
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?
hl. Stephanus schrieb: > ist ein define in dem Fall dann das richtige Mittel? Inline-Funktionen sind besser, stehen aber nicht überall zu Verfügung.
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++.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.