Forum: Mikrocontroller und Digitale Elektronik uart raspberry pi, warum neuer pointer zum senden?


von Alex (Gast)


Lesenswert?

Hallo,
auf 
https://www.einplatinencomputer.com/raspberry-pi-uart-senden-und-empfangen-in-c/ 
habe ich folgenden Code gefunden, um den uart bei einem RaspberryPi zu 
benutzen.

zum senden wird ein pointer auf einen buffer gelegt.
Was hat das für einen hintergrund? warum verwendet man den buffer nicht 
direkt?
Der kann ja auch eine länge von 100 oder so haben. Bei der funktion 
write wird dann ja die länge mitgegeben.

1
#include <stdio.h>
2
#include <stdlib.h>
3
#include <string.h>
4
#include <errno.h>
5
#include <unistd.h>
6
#include <fcntl.h>
7
#include <termios.h>
8
9
int main() {
10
  
11
int uart0_filestream = -1;
12
uart0_filestream = open("/dev/ttyAMA0", O_RDWR | O_NOCTTY | O_NDELAY);
13
if (uart0_filestream == -1) {
14
  printf("[ERROR] UART open()\n");
15
}
16
17
struct termios options;
18
tcgetattr(uart0_filestream, &options);
19
  options.c_cflag = B9600 | CS8 | CLOCAL | CREAD; 
20
  options.c_iflag = IGNPAR;
21
  options.c_oflag = 0;
22
  options.c_lflag = 0;
23
tcflush(uart0_filestream, TCIFLUSH);
24
tcsetattr(uart0_filestream, TCSANOW, &options);
25
26
27
// Bytes senden
28
unsigned char BUF_TX[20];
29
unsigned char *TX;
30
31
TX = &BUF_TX[0];
32
*TX++ = 'R';
33
*TX++ = 'a';
34
*TX++ = 's';
35
*TX++ = 'p';
36
*TX++ = 'b';
37
*TX++ = 'e';
38
*TX++ = 'r';
39
*TX++ = 'r';
40
*TX++ = 'y';
41
*TX++ = ' ';
42
*TX++ = 'P';
43
*TX++ = 'i';
44
  
45
if (uart0_filestream != -1)  {
46
  int out = write(uart0_filestream, &BUF_TX[0], (TX - &BUF_TX[0])); // 
47
  if (out < 0) {
48
    printf("[ERROR] UART TX\n");
49
  } else {
50
    printf("[STATUS: TX %i Bytes] %s\n", out, BUF_TX);
51
  }
52
} // if uart0
53
54
sleep(1);
55
56
// Bytes empfangen
57
if (uart0_filestream != -1) {
58
  unsigned char BUF_RX[50];
59
  int rx_length = read(uart0_filestream, (void*)BUF_RX, 50);
60
61
  if (rx_length < 0) {
62
    printf("[ERROR] UART RX\n");
63
  } else if (rx_length == 0) {
64
    printf("[ERROR] UART RX - no data\n");
65
  } else {
66
    BUF_RX[rx_length] = '\0';
67
    printf("[STATUS: RX %i Bytes] %s\n", rx_length, BUF_RX);
68
  } //rx_length check
69
} //if uart0
70
71
close(uart0_filestream);
72
  
73
return 0;
74
}

von Boris (Gast)


Lesenswert?

Klemmt deine Shift-Taste oder bist du der Meinung, dass schlampige 
Rechtschreibung nicht weiter schlimm ist?

von Stefan F. (Gast)


Lesenswert?

Alex schrieb:
> zum senden wird ein pointer auf einen buffer gelegt.

Wenn du den ganzen Buffer an die Funktion übergeben würdest, müsste er 
auf den Stack der aufgerufenen Funktion kopiert werden, denn 
Funktions-Argumente sind in C immer "by value" (eine Kopie).

von Stefan F. (Gast)


Lesenswert?

Alex schrieb:
> // Bytes senden
> unsigned char BUF_TX[20];
> unsigned char *TX;
>
> TX = &BUF_TX[0];
> *TX++ = 'R';
> *TX++ = 'a';
> *TX++ = 's';
> *TX++ = 'p';
> *TX++ = 'b';
> *TX++ = 'e';
> *TX++ = 'r';
> *TX++ = 'r';
> *TX++ = 'y';
> *TX++ = ' ';
> *TX++ = 'P';
> *TX++ = 'i';

Warum nicht einfach:

char* TX_BUFFER="Raspberry Pi";

Oder wenigstens strncpy() benutzen, um den String zu kopieren.

von foobar (Gast)


Lesenswert?

> Wenn du den ganzen Buffer an die Funktion übergeben würdest, müsste er
> auf den Stack der aufgerufenen Funktion kopiert werden, denn
> Funktions-Argumente sind in C immer "by value" (eine Kopie).

Die übergebene "value" ist der Pointer auf den Buffer.  Bei z.B.
1
write(fd, "Hello World\n", 12);
2
oder
3
write(fd, buffer, strlen(buffer));
 wird nichts kopiert.  Komplette Arrays kannst du nur über Umwege "by 
value" übergeben und wird aus den von dir genannten Gründen als 
schlechter Stil angesehen.

Der Kode ist von einem Programmieranfänger geschrieben und sollte nicht 
als Beispiel für saubere oder sinnvolle Programmierung angesehen werden.

von Alex (Gast)


Lesenswert?

Stefanus F. schrieb:
> Wenn du den ganzen Buffer an die Funktion übergeben würdest, müsste er
> auf den Stack der aufgerufenen Funktion kopiert werden, denn
> Funktions-Argumente sind in C immer "by value" (eine Kopie).

Das heisst TX ist nur so groß, wie es elemente hat?

foobar schrieb:
> Der Kode ist von einem Programmieranfänger geschrieben und sollte nicht
> als Beispiel für saubere oder sinnvolle Programmierung angesehen werden.

ob jetzt jedes Element einzeln geschrieben, oder ob man TX mit strncpy 
beschreiben würde.
Ist das mit dem Pointer auf dem BUF_TX denn so gängig? Bzw. eine 'gute' 
Methode?
Oder wie sollte es man besser machen?

von foobar (Gast)


Lesenswert?

> ob jetzt jedes Element einzeln geschrieben, oder ob man TX mit strncpy
> beschreiben würde.
> Ist das mit dem Pointer auf dem BUF_TX denn so gängig? Bzw. eine 'gute'
> Methode?
> Oder wie sollte es man besser machen?

Das (leicht gekürzte) Original:
1
   unsigned char BUF_TX[20];
2
   unsigned char *TX;
3
4
   TX = &BUF_TX[0];
5
   *TX++ = 'R';
6
   *TX++ = 'a';
7
   *TX++ = 's';
8
   *TX++ = 'p';
9
   *TX++ = 'b';
10
   *TX++ = 'e';
11
   *TX++ = 'r';
12
   *TX++ = 'r';
13
   *TX++ = 'y';
14
   *TX++ = ' ';
15
   *TX++ = 'P';
16
   *TX++ = 'i';
17
18
   out = write(uart0_filestream, &BUF_TX[0], (TX - &BUF_TX[0]));
Macht das gleiche wie:
1
   out = write(uart0_filestream, "Raspberry pi", 12);
oder
1
   static const char str[] = "Raspberry pi";
2
3
   out = write(uart0_filestream, str, sizeof(str));
oder, wenn man, z.B. wegen dynamischer Daten, einen Buffer nutzen 
möchte:
1
   char buffer[64];
2
3
   sprintf(buffer, "Raspberry pi, 3*4=%d\n", 3*4);
4
   out = write(uart0_filestream, buffer, strlen(buffer));

Such dir selbst aus, welche Variante "besser" ist.  Programmierer sind 
flexibel - man nutzt das, was gerade am besten passt.  Die erste 
Variante aber wohl eher selten ...


PS: Variablen nur aus Großbuchstaben sind ein "no go" - die Namen sind 
für den Präprozessor ("defines") reserviert.

von foobar (Gast)


Lesenswert?

Muss natürlich "sizeof(str)-1" heißen :-/

von Johannes (Gast)


Lesenswert?

Stefanus F. schrieb:
> Wenn du den ganzen Buffer an die Funktion übergeben würdest, müsste er
> auf den Stack der aufgerufenen Funktion kopiert werden, denn
> Funktions-Argumente sind in C immer "by value" (eine Kopie).

foobar schrieb:
> oder, wenn man, z.B. wegen dynamischer Daten, einen Buffer nutzen
> möchte:   char buffer[64];
>
>    sprintf(buffer, "Raspberry pi, 3*4=%d\n", 3*4);
>    out = write(uart0_filestream, buffer, strlen(buffer));


Dazu habe ich nochmal eine Nachfrage. Den weg von foobar habe ich sonst 
auch immer gemacht, wenn ich arrays an einer Funktion übergeben habe.
Stefanus F. aussage habe ich jetzt so verstanden, dass auch wenn ich 
eine länge kleiner der länge des Arrays angegeben wird, der komplette 
Array (also 64 Elemente) in die Funktion kopiert.
Wenn ich einen Pointer auf den buffer mache und diesen dann mit der 
entsprechenden länge übergebe, ist der pointer auch nur so lang wie 
beschrieben (im obigen Beispiel also 12).

Habe ich das so richtig verstanden?

von Stefan F. (Gast)


Lesenswert?

Alex schrieb:
> Das heisst TX ist nur so groß, wie es elemente hat?

TX ist ein Zeicher auf ein Array von Zeichen. Also 32bit oder 4 Bytes, 
unter der Annahme, dass bei diesem System all Zeiger 32bit groß sind.

> Ist das mit dem Pointer auf dem BUF_TX denn so gängig? Bzw. eine 'gute'
Methode?

Ja und Ja, sonst wäre das in der Standard-C Bibliothek nicht so 
festgelegt. Die Bibliothek ist zwar alt, aber ihr Erfinder war kein 
Dummkopf und hatte bereits viele Jahre Erfahrung mit anderen 
Programmiersprachen.

von Sebastian (Gast)


Lesenswert?

Johannes schrieb:
> Stefanus F. aussage habe ich jetzt so verstanden, dass auch wenn ich
> eine länge kleiner der länge des Arrays angegeben wird, der komplette
> Array (also 64 Elemente) in die Funktion kopiert


Ist falsch, wird nicht kopiert. Arrays degenerieren zu Zeigern.

von Stefan F. (Gast)


Lesenswert?

Sebastian schrieb:
> Arrays degenerieren zu Zeigern.

Ja, das hatte mich anfangs auch irritiert. Schau Dir das an:
1
char* du = "Johannes";

Hier wird irgendwo ein char[] angelegt, mit dem Wort "Johannes" befüllt 
und mit einem \0 abgeschlossen. Das Array ist also 9 Bytes groß. "du" 
ist ein Zeiger auf dieses Array.
1
char ich[] = "Stefan";

Das hier ist technisch fast das Gleiche. Ehrlich gesagt weiß ich ich 
nicht, wo genau der Unterschied ist, denn man kann char[] und char* 
gleicher benutzen und mixen:
1
char* du = "Johannes";
2
char ich[] = "Stefan";
3
4
char ersterBuchstabe = du[0];
5
char ersterBuchstabe = ich[0];
6
7
char ersterBuchstabe = *du;
8
char ersterBuchstabe = *ich;
9
10
printf("Der Name ist: %s", du);
11
printf("Der Name ist: %s", ich);
12
13
void sende_string(char* name) {
14
    while (*name != '\0') {
15
        sende_zeichen(*name)
16
    }
17
}
18
19
void sende_string(char name[]) {
20
    while (*name != '\0') {
21
        sende_zeichen(*name)
22
    }
23
}
24
25
void sende_string(char* name) {
26
   for (int i=0; name[i]!='\0'; i++) {
27
        sende_zeichen(name[i])
28
    }
29
}
30
31
void sende_string(char name[]) {
32
   for (int i=0; name[i]!='\0'; i++) {
33
        sende_zeichen(name[i])
34
    }
35
}

Alle vier Funktionen kannst du wahlweise mit "ich" oder "du" aufrufen. C 
übergibt Arrays an Funktionen immer als Zeiger.

von GEKU (Gast)


Lesenswert?

Stefanus F. schrieb:
> Funktions-Argumente sind in C immer "by value" (eine Kopie).

Trifft bei Pascal zu, aber nicht bei C!
Funktions-Argumente von Arrays, Strukturen und Strings sind in C immer 
"called by reference" .

von Stefan F. (Gast)


Lesenswert?

> Stefanus F. schrieb:
>> Funktions-Argumente sind in C immer "by value" (eine Kopie).

GEKU schrieb:
> Trifft bei Pascal zu, aber nicht bei C!
> Funktions-Argumente von Arrays, Strukturen und Strings sind in C immer
> "called by reference" .

Ja, ich habe das missverständlich ausgedrückt. Die Übergabewerte werden 
immer auf den Stack der aufgerufenen Funktion kopiert, oder in CPU 
Registern übergeben.

Arrays und Strukturen zerfallen dabei zu Zeigern, weil es in der Regel 
keinen Sinn macht, größere Datenmengen zu kopieren. Es werden Zeiger auf 
die Daten übergeben.

Gibt es den Begriff "Referenz" überhaupt in C? Ich kenne nur Zeiger.

von GEKU (Gast)


Lesenswert?

Stefanus F. schrieb:
> char* TX_BUFFER="Raspberry Pi";

Bei C++ mit dem Typ string anstelle von char* funktioniert es auch.
1
string tx_buffer = "Raspberry Pi";

https://www.w3schools.com/cpp/cpp_strings.asp

von GEKU (Gast)


Lesenswert?

Stefanus F. schrieb:
> Gibt es den Begriff "Referenz" überhaupt in C? Ich kenne nur Zeiger

Ich denke schon.

http://www.lerneprogrammieren.com/blog/praxis/call-value-und-call-reference-bei-der-parameteruebergabe

Der Vorteil in Pascal von "called by value" ist, dass die ursprüngliche 
Zeichenkette erhalten bleibt auch wenn in die in die Funktion übergebene 
Zeichenkette (Kopie) verändert wird.

In C kann das nur mit dem Attribut const zur Compilerzeit verhindert 
werden.
(es wird eine Kopie im Unterprogramm erzwungen)

Allerdings kostet "called by value" viel Platz am Stack bzw. viel 
Rechnerleistung,  die im embedded Bereich sowieso zu kostbar sind.

von foobar (Gast)


Lesenswert?

> Das hier ist technisch fast das Gleiche. Ehrlich gesagt weiß ich ich
> nicht, wo genau der Unterschied ist
>
1
>  char* du = "Johannes";
2
>  char ich[] = "Stefan";
3
>

Du merkst den Unterschied, wenn du den Variablen Werte zuweisen willst 
oder per sizeof die Größe abfragst ;-)

"du" ist der Name eines Pointers, der auf einen (anonymen, 
initialisierten) Datenblock zeigt.  "ich" ist der Name eines 
(initialisierten) Datenblocks.

In Pseudoassembler ungefähr so:
1
_an314: .byte "Johannes",0
2
du:     .word _an314
3
ich:    .byte "Stefan",0

von foobar (Gast)


Lesenswert?

> Funktions-Argumente von Arrays, Strukturen und Strings sind in C immer
> "called by reference"

Nicht ganz.  Arrays, Strings (Arrays von Zeichen) und Funktionen 
degenerieren zu Pointer und werden als Pointer übergeben.  Strukturen 
werden aber neuerdings (ab C89 oder so ;-) ) kopiert!  Wird 
glücklicherweise selten benutzt - meist unbewusst von Anfängern.

von Stefan F. (Gast)


Lesenswert?

foobar schrieb:
> Du merkst den Unterschied, wenn du den Variablen Werte zuweisen willst
> oder per sizeof die Größe abfragst ;-)

Ach ja, logisch. Also kenne ich den Unterschied ja doch, war mir nur 
nicht bewusst.

von Stefan F. (Gast)


Lesenswert?

foobar schrieb:
> Strukturen werden aber neuerdings (ab C89 oder so ;-) ) kopiert!

Oh, das ist gemein. Bei STM32 habe ich mal gemerkt, dass bis zu 5 
Parameter in Registern übergeben werden. Bei mehr wird Stack verwendet.

Bei Strukturen muss man dazu wohl die einzelnen Elemente zählen, oder?

von foobar (Gast)


Lesenswert?

> Bei Strukturen muss man dazu wohl die einzelnen Elemente zählen, oder?

Keine Ahnung, wie das bei ARM/STM32 ist - wird in der ABI (Application 
Binary Interface) festgelegt und kann teilweise noch per Compilerswitch 
modifiziert werden (bei ARM z.B. gibt es mehrere verschiedene ABIs).

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.