Forum: Compiler & IDEs Array/String Länge ermitteln ohne NULL am Ende


von Michael S. (jackson)


Lesenswert?

Hallo,

wie kann ich in C (nicht C++) die Länge eines Arrays/Strings ermitteln 
wenn in den Nutzdaten alle Werte zw. 0-FF Verwendung finden?

Hintergund es geht um das versenden von Daten im TP2.0/KWP 
CAN-Protokoll.

Die Länge der zu Übermittelnen Daten ist variabel.

Beispiel:
Länge 2.Bytes + Befehl/SID 1.Byte + variable Daten: 31(SID)+ 00 11 22 33 
44 55 66 77 88 99 AA BB CC DD EE FF(Daten) sind insges. 17 Bytes

Übertagen wird dann:

Länge Befehl+Daten
00 11 31 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF

Wie kann ich diese variable Länge am besten errechnen wenn es kein 
"Ende" Zeichen gibt?

Gruß
Jackson

von Dr. Sommer (Gast)


Lesenswert?

Michael S. schrieb:
> Wie kann ich diese variable Länge am besten errechnen wenn es kein
> "Ende" Zeichen gibt?
Gar nicht. Du musst die Länge explizit übergeben, ala:
1
void sendData (char* data, size_t length) {
2
  // length Bytes aus data senden ...
3
}
4
5
int main () {
6
  char data [] = { 1, 2, 3, 4 };
7
  sendData (data, sizeof (data));
8
}

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Michael S. schrieb:

> Länge Befehl+Daten
> 00 11 31 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF
>
> Wie kann ich diese variable Länge am besten errechnen wenn es kein
> "Ende" Zeichen gibt?

Die Länge steht in den ersten 2 Bytes (Big Endian): 0x0011, also 17 
dezimal. Wobei die Länge selbst nicht in diese 17 Bytes eingerechnet 
ist.

: Bearbeitet durch User
von Michael S. (jackson)


Lesenswert?

Danke erstmal für die Antworten!

Ich habe es noch anders gelöst indem ich die Hex-Werte in einem 
ascii-String übergebe da gibt es ja \0 am Ende.

Sieht jetzt so aus:

SendObdCommand("31 00 11 22 33 44 55 66 77 88 99 AA BB CC DD EE FF");

Die Routine rechnet die ascii Hexzahlen in echte Hex-Werte/Bytes um und 
zählt dann auch wieviele Bytes es waren. Danach verschickt sie dann die 
Daten mit der errechneten Länge. Funzt wunderbar soweit.

Aber wie bekomme ich jetzt in das String noch variable Werte eingefügt?

Ich möchte Beispielsweise folgenden senden:
SendObdCommand("27 04" + zahl1 + zahl2 + "00");

Sollte dann folgendes übermitteln wenn zahl1=C3 und zahl2=02 ist:
SendObdCommand("27 04 C3 02 00");

Gruß
Jackson

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Michael S. schrieb:
> Ich habe es noch anders gelöst indem ich die Hex-Werte in einem
> ascii-String übergebe da gibt es ja \0 am Ende.
Das ist nicht dein Ernst?! Du willst die Binärdaten erst in einen 
Hexa-ASCII-String konvertieren, an eine Funktion übergeben, und in der 
wieder dekodieren, nur weil du es nicht schaffst die Länge zu 
übergeben?!

Michael S. schrieb:
> SendObdCommand("27 04" + zahl1 + zahl2 + "00");
^-- in diesem Fall weißt du ja scheinbar schon dass deine Binärdaten 4 
Bytes lang sind. Was spricht dagegen, diese "4" explizit mit zu 
übergeben?!
1
char data [] = { 0x24, 0x04, zahl1, zahl2 };
2
SendObdCommand (data, 4);
3
...
4
void SendObdCommand (char* data, size_t length) {
5
  // Schleife über alle Bytes
6
  for (int i = 0; i < length; i++) {
7
    sendByte (data [i]); // Ein Byte senden...
8
  }
9
}
So sparst du dir die rechen&speicher aufwändige En&De-Kodierung und es 
funktioniert auch!

von Michael S. (jackson)


Lesenswert?

Das Problem ist nicht die Länge zu übergeben sondern jedesmal mühselig 
die Länge von Hand zu zählen... Teilweise längen von über 80 Bytes und 
viele Befehle hintereinander! Wenn man sich da mal verzählt kommt das 
ganze Protokoll durcheinander und man sucht sich dussselig nach dem 
Fehler...

Der MC ist schnell genug und soll das für mich erledigen.

Wie kann ich den in C zwei Zeichenkette zusammenfügen?
SendObdCommand("27 04" + "00");
oder wenn c="11 22"
SendObdCommand("27 04" + c);

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Michael S. schrieb:
> Das Problem ist nicht die Länge zu übergeben sondern jedesmal mühselig
> die Länge von Hand zu zählen...
Auch keine Problem, das kann der Compiler.
1
char myData [] = { 1, 2, 3, 4, zahl1, zahl2, 5, 6, 7, 42 };
2
SendObdCommand (data, sizeof(myData));

Michael S. schrieb:
> Wie kann ich den in C zwei Zeichenkette zusammenfügen?
Indem du die Längen der Zeichenketten zählst (ha!) und eine Zeichenkette 
der Länge dieser Summe anlegst, und die einzelnen hineinkopierst:
1
char string1 [] = "01 02 03";
2
char string2 [] = " 04 05 06";
3
char gesamt [sizeof (string1) + sizeof(string2) - 1];
4
memcpy (gesamt, string1, sizeof(string1)-1);
5
memcpy (gesamt+sizeof(string1)-1, string2, sizeof(string2));
6
SendObdCommand (gesamt);
Das ist, besonders auf Plattformen wie AVR, extrem ineffizient. Und 
sag mir nicht man könne sich nicht leicht vertun bei der 
Offset-Rechnerei mit den richtigen -1 an den richtigen Stellen.

Zusammenfassend kann man sagen dass du es unfassbar umständlich und 
ineffizient machst, es geht viel einfacher und viel effizienter.

von PittyJ (Gast)


Lesenswert?

Eingentlich wollte ich jetzt schreiben: kauf und lese ein C-Buch.
Aber das erspar ich mir.

Jemand möchte Can programmieren, und hat schon beim einfachsten C 
Probleme?

von Michael S. (jackson)


Lesenswert?

Dr. Sommer schrieb:
> Michael S. schrieb:
>> Das Problem ist nicht die Länge zu übergeben sondern jedesmal mühselig
>> die Länge von Hand zu zählen...
> Auch keine Problem, das kann der Compiler.
>
1
char myData [] = { 1, 2, 3, 4, zahl1, zahl2, 5, 6, 7, 42 };
2
> SendObdCommand (mydata, sizeof(myData));
>
OK, und wie mache ich das wenn myData immer wieder andere Daten/Längen 
hat
1
char myData [] = { 1, 2, 3, 4, zahl1, zahl2, 5, 6, 7, 42 };
2
SendObdCommand (mydata, sizeof(myData));
3
neue Daten mit verschiedenen laengen in myData
4
SendObdCommand (mydata, sizeof(myData));[/
5
neue Daten mit verschiedenen laengen in myData
6
SendObdCommand (mydata, sizeof(myData));[/
7
neue Daten mit verschiedenen laengen in myData
8
SendObdCommand (mydata, sizeof(myData));

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Michael S. schrieb:
> OK, und wie mache ich das wenn myData immer wieder andere Daten/Längen
> hat
1
if (fall1) {
2
  char myData [] = { 1, 2, 3, 4, zahl1, zahl2, 5, 6, 7, 42 };
3
  SendObdCommand (mydata, sizeof(myData));
4
} else if (fall2) {
5
  char myData [] = { 1, zahl1, 42 };
6
  SendObdCommand (mydata, sizeof(myData));
7
} else if (fall3) {
8
  char myData [] = { 1 };
9
  SendObdCommand (mydata, sizeof(myData));
10
}
Aber btw, CAN akzeptiert doch onehin nur 0-8-Byte-Blöcke, wie willst du 
denn da 80 Bytes übertragen...

von Rolf Magnus (Gast)


Lesenswert?

Michael S. schrieb:
> Dr. Sommer schrieb:
>> Michael S. schrieb:
>>> Das Problem ist nicht die Länge zu übergeben sondern jedesmal mühselig
>>> die Länge von Hand zu zählen...
>> Auch keine Problem, das kann der Compiler.
>>char myData [] = { 1, 2, 3, 4, zahl1, zahl2, 5, 6, 7, 42 };
>> SendObdCommand (mydata, sizeof(myData));>
> OK, und wie mache ich das wenn myData immer wieder andere Daten/Längen
> hat
> char myData [] = { 1, 2, 3, 4, zahl1, zahl2, 5, 6, 7, 42 };
> SendObdCommand (mydata, sizeof(myData));
> neue Daten mit verschiedenen längen in myData

Wie kommen die neuen Daten denn nach myData? Hol die Länge dieser Daten 
von deren Quelle. Die muß sie ja wissen.

von slip (Gast)


Lesenswert?

Wenn du partout die Länge nicht übergeben möchtest, könntest du auch so 
was ähnliches wie SLIP encoding machen. Führe ein Escape-Byte ein (zB 
0xFF), dann kannst du zB End-Of-Data als (0xFF 0x00) kodieren, und ein 
eigentliches 0xFF in den Datenbytes als (0xFF 0xFF). Es würden dann 
sogar noch 254 andere Kontroll-Bytes übrig bleiben...

Aber das ist trotzdem nur eine pfuschige Behelfslösung an der Stelle, 
übergib die Länge separat (wie von allen empfohlen)

von Michael S. (jackson)


Lesenswert?

Wir reden aneinander vorbei, bislang sah meine main() so aus (Zufall das 
hier grade die Länge immer 2 ist, kann wie gesagt auch anders sein):
1
//Init Obd Session
2
SendObdCommand("10 89");
3
ReceiveObdCommand();
4
SendObdCommand("1A 9B");
5
ReceiveObdCommand();
6
SendObdCommand("27 03");
7
ReceiveObdCommand();

Die Komandos müssen alle einzelnt gesendet werden und es wird vom 
Emfänger auf jedes einzelne Komando eine Antwort zurück geschickt die 
mit ReceiveObdCommand gelesen wird und ggf. danach auch ausgewertet 
werden muss.

Und richtig, CAN kann pro Message max. 8 Byte übertragen.
Dafür ist ja SendObdCommand gedacht, die zerhackt und verschickt die 
Daten dann im TP2.0 Protokoll als Single oder Multimessages je nach 
Datenlänge.

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Michael S. schrieb:
> Wir reden aneinander vorbei, bislang sah meine main() so aus
Ja dann ists doch supereinfach das entsprechend umzubauen:
1
//Init Obd Session
2
{ char data [] = { 0x10, 0x89 };
3
SendObdCommand(data, sizeof (data)); }
4
{ char data [] = { 0x1A, 0x9B };
5
SendObdCommand(data, sizeof (data)); }
6
{ char data [] = { 0x27, 0x03 };
7
SendObdCommand(data, sizeof (data)); }
Wo ist das Problem? In C++ könnte man das ganze sogar noch etwas kürzer 
machen.

von Michael S. (jackson)


Lesenswert?

Die Lösung ist Top danke!

Perfekt wäre jetzt nur noch wenn die Länge immer als short (2Byte Wert) 
vor die eigendlichen Daten gestellt würde, dann bräuchte ich die Länge 
nicht noch einzel an die Send...Routine übergeben. Ist das auch so 
"einfach" möglich? Beispiel:
1
{ char data [] = { 0x10, 0x89 }; // + irgendwie sizeof Länge davor?
2
SendObdCommand(data); }
data sollte dann so aussehen (Hex): 00 02 10 89

: Bearbeitet durch User
von Ralf G. (ralg)


Lesenswert?

@Dr. Sommer
Lass' dir erstmal 'ne Kiste Bier schicken!

von Dr. Sommer (Gast)


Lesenswert?

Michael S. schrieb:
> Perfekt wäre jetzt nur noch wenn die Länge immer als short (2Byte Wert)
> vor die eigendlichen Daten gestellt würde, dann bräuchte ich die Länge
> nicht noch einzel an die Send...Routine übergeben.
Vielleicht geht das irgendwie lustig mit variadischen Makros, aber sonst 
wüsste ich jetzt spontan keine Möglichkeit in C. In C++ ists einfach:

Möglichkeit #1: Komplettes Array übergeben und decayen lassen:
1
#include <iostream>
2
3
// Eigentliche Sendefunktion
4
void SendObdCommandInt (const char* data, size_t size) {
5
        for (int i = 0; i < size; i++) {
6
                std::cout << (int) data [i];
7
        }
8
}
9
10
// Proxy-Funktion um automatisch die Größe zu bestimmen
11
template <typename T>
12
inline void SendObdCommand (const T& data) {
13
        SendObdCommandInt (data, sizeof (data));
14
}
15
16
int main () {
17
        SendObdCommand ((char []) {1, 2, 3, 4, 5});
18
        SendObdCommand ((char []) {3, 42});
19
}

Möglichkeit #2: Array-Initializer als Argumente übergeben und Array 
zusammenbauen:
1
#include <iostream>
2
3
// Eigentliche Sendefunktion
4
void SendObdCommandInt (const char* data, size_t size) {
5
        for (int i = 0; i < size; i++) {
6
                std::cout << (int) data [i];
7
        }
8
}
9
10
// Proxy-Funktion um automatisch Array zu erstellen und mit Größe zu übergeben
11
template <typename... T>
12
inline void SendObdCommand (T... args) {
13
        const char data [sizeof...(args)] { static_cast<char>(args)... };
14
        SendObdCommandInt (data, sizeof...(args));
15
}
16
17
int main () {
18
        int etwas = 42;
19
        SendObdCommand (1, 2, 3, etwas, 4, 5);
20
}

von Fabian O. (xfr)


Lesenswert?

Wenns nur darum geht, den Code kurz zu halten, kannst Du Dir auch so ein 
Makro definieren:
1
// Einmal definieren
2
#define SEND_OBD_COMMAND(x) do {unsigned char data[] = x; SendObdCommand(data, sizeof(data));} while(0)
3
4
// Verwendung
5
SEND_OBD_COMMAND({0x10, 0x89});
6
SEND_OBD_COMMAND({0x1A, 0x9A});
7
SEND_OBD_COMMAND({0x27, 0x03});

Sauberer wäre allerdings, den Kommandos Namen zu geben und keine Magic 
Numbers in den Code zu schreiben.

von Michael S. (jackson)


Lesenswert?

@xfr: Wäre jedesmal weniger zu tippen und sähe natürlich sauberer aus, 
aber leider schmeisst er nen Syntaxfehler:

*** ..\src\main.c(397) E4062C: syntax error near `;'
*** ..\src\main.c(397) E4062C: syntax error near `)'
*** ..\src\main.c(397) E4189C: automatic symbol has zero or negative 
size

bezogen auf Zeile:
SEND_OBD_COMMAND({0x10, 0x89});

von Michael S. (jackson)


Lesenswert?

@Dr. Sommer: Leider gibt es noch ein Problem wenn ich ein char/Byte 
übergeben will:
1
{ char SendData[] = { 0x27, 0x04, z };
2
SendObdCommand(SendData, sizeof(SendData)); }

Schmeisst Fehlermeldung:
*** ..\src\main.c(410) E4032C: initialization: constant expression is 
expected for variable: `SendData'
make: *** [src/main.o] Error 2

: Bearbeitet durch User
von Fabian O. (xfr)


Lesenswert?

Michael S. schrieb:
> @xfr: Wäre jedesmal weniger zu tippen und sähe natürlich sauberer aus,
> aber leider schmeisst er nen Syntaxfehler:

Hm, habs mir schon fast gedacht. Hatte den Code nicht getestet ...

Na gut, dann eben doch per variadischem Makro, der Compiler muss dann 
aber C99 unterstützen:
1
define SEND_OBD_COMMAND(...) do {unsigned char data[] = {__VA_ARGS__}; SendObdCommand(data, sizeof(data));} while(0)

Alternativ:
1
#define SEND_OBD_COMMAND(...) SendObdCommand((unsigned char[]) {__VA_ARGS__}, sizeof((unsigned char[]) {__VA_ARGS__}))

Verwendung:
1
SEND_OBD_COMMAND(0x10, 0x89);
2
SEND_OBD_COMMAND(0x1A, 0x9A);
3
SEND_OBD_COMMAND(0x27, 0x03);

Zu Deinem zweiten Problem:
Wenn der Wert der Variable z erst zur Laufzeit bekannt ist, wirst Du 
nicht drumrum kommen, die Daten von Hand zusammenzubauen bzw. als 
eigenen Parameter an die Funktion zu übergeben. Ein Array kann in C nur 
mit Daten initialisiert werden, die schon zur Compilezeit bekannt sind.

: Bearbeitet durch User
von Michael S. (jackson)


Lesenswert?

@xfr: geht leider auch nicht, er mag _VA_ARGS_ nicht...
Wie geschrieben leider nur c und kein c++ compiler...

von Joachim D. (Firma: JDCC) (scheppertreiber)


Lesenswert?

Michael S. schrieb:
> @xfr: geht leider auch nicht, er mag _VA_ARGS_ nicht...
> Wie geschrieben leider nur c und kein c++ compiler...

C kann das. Definiert ist das bei mir (Watcom C) in stdarg.h.

von Läubi .. (laeubi) Benutzerseite


Lesenswert?

Also ich würde hier zwei Möglichkeiten vorschlagen:

a) Definiere dir eine Struktur, welche das Kommando + läge enthält.
Dann kannst du dir für jedes Kommando eine benannte Konstante anlegen, 
mußt maximal einmal zählen (falls man das nicht irgendwie trickreich im 
Init erledigen lassen kann). und kannst in der Folge dan sehr bequem 
damit arbeiten. Auch Erweiterungen sind so sehr einfach möglich.
Außerdem wäre es der Architekturtechnisch sauberste Weg.

c) VARG Funktion, welche aber alle Daten mit dem nächstgrößerem Datentyp 
(z.B. int) entgegennimmt und einen Wert > 0xFF als "Ende-Zeichen" 
definiert.
Nachteil: Doppelter Stackverbrauch, gechaste, magic numbers...

: Bearbeitet durch User
von Dr. Sommer (Gast)


Lesenswert?

Aus Interesse, was fuer eine Plattform ist das denn, dass es keinen C++ 
Compiler gibt und selbst C derart eingeschraenkt ist? Da muesste man 
dann ja einen Bogen drum machen...

von Michael S. (jackson)


Lesenswert?

Der Compiler heisst fcc907s und ist für die Fujistu FX16 Serie

http://www.fujitsu.com/downloads/MICRO/fma/pdfmcu/CM42-00328-3E.pdf

Wenn ich die stdarg.h mit einbinde nimmt er die VA_ARGS aber bei
SEND_OBD_COMMAND(0x10, 0x89);
SEND_OBD_COMMAND(0x1A, 0x9A);
SEND_OBD_COMMAND(0x27, 0x03);

hat er dann immer noch Syntaxfehler und ich weis nicht wieso...

von Dr. Sommer (Gast)


Lesenswert?

Michael S. schrieb:
> hat er dann immer noch Syntaxfehler und ich weis nicht wieso...
Lass dir doch mal das Ergebnis des C Preprocessings ausgeben (beim GCC 
ginge das mit der Compileroption -E, bei deinem keine Ahnung) dann sieht 
man leichter was falsch sein könnte...

von Peter D. (peda)


Lesenswert?

Bei konstanten Arrays kann man die Länge einfach mit sizeof ermitteln.
Hier mal ein Beispiel für ein GLCD:
1
typedef prog_uint8_t    pu8;
2
#define TFT_FKT(x)         tft_wr( sizeof(x) - 1, x, 1 )
3
uint8_t tft_wr( uint8_t len, uint8_t *dat, uint8_t flash );
4
          // write to GLCD from SRAM or Flash
5
6
pu8 KBD_FONT[] = "\x1bZF\2\x1bZZ\2\2";
7
8
  TFT_FKT( KBD_FONT );

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.