C - Call by reference

Gast #5532432
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
Moderator Persönliche Seite #5532467
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.
Gast #5532526
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
Persönliche Seite #5532547
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.
#5532886
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.
Gast #5532900
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.
Gast #5532973
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?
Gast #5533239
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
#5533473
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++.

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren