Forum: Mikrocontroller und Digitale Elektronik C - Call by reference


von Iguan (Gast)


Lesenswert?

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

von STK500-Besitzer (Gast)


Lesenswert?

Iguan schrieb:
> Könnt ihr mir sagen, was passiert wenn ich beim Aufruf der Funktion

Der Compiler wird rummeckern.

von Volker S. (vloki)


Lesenswert?

Iguan schrieb:
> Könnt ihr mir sagen, was passiert

Kannst du das nicht einfach selber ausprobieren?

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

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.

von Iguan (Gast)


Lesenswert?

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

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

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.

von DPA (Gast)


Lesenswert?

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)

von M.K. B. (mkbit)


Lesenswert?

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.

von (prx) A. K. (prx)


Lesenswert?

Bei Prozessoren mit alignment check kann es beim Zugriff zur Laufzeit 
scheppern, weil ein "int" Zugriff auf Daten mit "char" alignment nicht 
funktionieren muss.

von Hoschti (Gast)


Lesenswert?

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.

von hl. Stephanus (Gast)


Lesenswert?

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?

von (prx) A. K. (prx)


Lesenswert?

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.

von hl. Stephanus (Gast)


Lesenswert?

Und warum muss da ein ; zum define dazugehören?

von holger (Gast)


Lesenswert?

>Und warum muss da ein ; zum define dazugehören?

Mach es mal ohne, dann weisst du es.

von hl. Stephanus (Gast)


Lesenswert?

#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

von (prx) A. K. (prx)


Lesenswert?

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.

von hl. Stephanus (Gast)


Lesenswert?

ahja, alles klar

von hl. Stephanus (Gast)


Lesenswert?

Ä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?

von (prx) A. K. (prx)


Lesenswert?

hl. Stephanus schrieb:
> ist ein define in dem Fall dann das richtige Mittel?

Inline-Funktionen sind besser, stehen aber nicht überall zu Verfügung.

von Rolf M. (rmagnus)


Lesenswert?

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
Noch kein Account? Hier anmelden.