PDA

Archiv verlassen und diese Seite im Standarddesign anzeigen : [C] Seltsames Verhalten in Schleife



falke2203
07-08-2006, 16:04
Tach zusammen...

ich bin noch recht unerfahren was C-Programmierung angeht und bin gerade am verzweifeln. Ziel ist eine Methode, die einen String in einen anderen String konvertiert (Konkret: beliebigen Text in ein Java-byte[] - fragt nicht warum ich sowas mache :)).
Es geht um folgenden Code:


#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char* convertDecToHex(char* str);

char* convert(char *str) {
const char *ba = "byte[] ba = new byte[] {";
int baLen = strlen(ba);
int parLen = strlen(str);
char *pt = malloc((baLen + parLen*12) * sizeof(char));
pt = strcpy(pt, ba);
int i;
char *tmp = malloc(2 * sizeof(char));
for (i = 0; str[i]; i++) {
tmp = strncpy(tmp, convertDecToHex(str[i]), 2); // diese Funktion liefert zu einem char die dazugehörige Repräsentation in Hex
printf("loop: %s\n", tmp);
pt = strcat(pt, "(byte)0x");
pt = strncat(pt, tmp, 2);
pt = strcat(pt, ", ");
}
int nLen = strlen(pt);
char *nep = malloc((nLen + 1) * sizeof(char));
nep = strncpy(nep, pt, nLen-2); // letztes Komma abschneiden
nep = strcat(nep, "};");
free(pt);
return nep;
}


Dabei tritt folgendes Problem auf: Beim ersten Durchlauf der Schleife wird tmp scheinbar kein Wert zugewiesen (Ausgabe ist "loop: " und pt endet nach dem ersten Durchlauf der Schleife mit "(byte)0x,"). Zur Kontrolle lasse ich mir in der convertDecToHex-Funktion vor dem return den Wert ausgeben, da ist alles richtig. Auch in allen folgenden Iterationen arbeitet das Programm korrekt. Jemand ne Idee was da los ist?

Danke, Der Falke

PS: Ich weiß, dass das ganze in Java (und meinetwegen auch in C++) wesentlich einfacher zu realisieren wäre, muss aber -leider- in C sein...
PPS: google-Recherchen und Suche hier im Forum war erfolglos

quinte17
07-08-2006, 16:10
#include <stdio.h>
#include <string.h>
#include <stdlib.h>

char* convertDecToHex(char* str);

char* convert(char *str) {
const char *ba = "byte[] ba = new byte[] {";
int baLen = strlen(ba);
int parLen = strlen(str);
char *pt = malloc((baLen + parLen*12) * sizeof(char));
pt = strcpy(pt, ba);
int i;
char *tmp = malloc(2 * sizeof(char));
for (i = 0; str[i]; i++) {
tmp = strncpy(tmp, convertDecToHex(str[i]), 2); // diese Funktion liefert zu einem char die dazugehörige Repräsentation in Hex
//BUG: und zugleich wurde tmp eine zeile drüber mit dem ergebnis der strncpy funktion überschrieben...
printf("loop: %s\n", tmp);
pt = strcat(pt, "(byte)0x");
pt = strncat(pt, tmp, 2);
pt = strcat(pt, ", ");
}
int nLen = strlen(pt);
char *nep = malloc((nLen + 1) * sizeof(char));
nep = strncpy(nep, pt, nLen-2); // letztes Komma abschneiden
nep = strcat(nep, "};");
free(pt);
return nep;
}


greetz

ps: nicht getestet, aber das BUG oben sollte auf den fehler hinweisen
pps: eigentlich hätte man dich noch ein wenig zappeln lassen sollen :p

falke2203
07-08-2006, 16:30
Danke für die schnelle Antwort, allerdings kann ich dir nicht so ganz folgen bzw. sehe den Fehler nicht.
Folgendes habe ich mir bei dem Teil des Codes gedacht:

Ich allokiere für tmp den für 2 char notwendigen Speicher (die Rückgabe der convertDecToHex-Funktion ist immer 2 char lang)
die strncpy-Funktion benutze ich, da ich ja nicht den Pointer umhängen sondern die Rückgabe von convertDecToHex in den Speicherbereich packen will, auf den tmp zeigt


Wo ist dabei mein Denkfehler? Und, was das ganze für mich noch verwirrender macht, warum funktioniert es ab der zweiten Iteration einwandfrei und nur bei der ersten nicht?

quinte17
07-08-2006, 16:46
tmp = strncpy(tmp, convertDecToHex(str[i]), 2);
diese zeile ist dein bug.
strncpy schreibt das ergebnis von convertDecToHex nach tmp. DANACH gibt strncpy ein ergebnis zurück, ob dies nun geklappt hat oder nicht. und dies schreibst du in den pointer "tmp" indem du ja

tmp =
verwendest. somit wird der pointer irgendwo in den ram gesetzt, und der ursprüngliche reservierte speicherbereich geht im ramnirvana mit den richtigen bytefolgen verloren.
eine lösung für dein problem wäre:


char *ergebnis;
ergebnis = strncpy(tmp, convertDecToHex(str[i]), 2);

greetz

edit: oder stehe ich auch auf dem schlauch?

falke2203
07-08-2006, 17:54
Aus der strcpy manpage:


char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);

DESCRIPTION
The strcpy() function copies the string pointed to by src (including the terminating '\0' character) to the array pointed to by dest. [...]
The strncpy() function is similar, except that not more than n bytes of src are copied[...]

RETURN VALUE
The strcpy() and strncpy() functions return a pointer to the destination string dest.


So wie ich das verstehe, macht diese Funktion also nichts anderes, als den Inhalt von src (in meinem Fall das Ergebnis des convertDecToHex-Aufrufs) in den Speicherbereich beginnend an der Stelle auf die dest (in meinem Fall tmp) zeigt zu kopieren.

Bis VOR der ersten Iteration der Schleife ist der Inhalt des entsprechenden Speicherbereichs (auf den tmp zeigt) nicht definiert (oder wird bei malloc irgendwas da reingeschrieben?), nach der ersten Zuweisung IN der Schleife (tmp = strncpy(...) müsste der Inhalt doch eigentlich dem Rückgabewert der convertDecToHex Funktion entsprechen (da ja von innen nach außen ausgewertet wird, also erst convertDecToHex, danach erst strncpy). tmp zeigt aber nach dieser Anweisung gem. der manpage auf den gleichen Speicherbereich wie vorher auch.

Wie gesagt, prinzipiell gibt convertDecToHex IMMER einen korrekten Wert zurück (den ich mir ja zur Kontrolle auch ausgeben lasse), nur bei der ersten Iteration geht in der Schleife irgendwo was schief. Eine Ausgabe sieht bspw so aus (fkt ist die ausgabe in der convertDecToHex):


fkt: 54
loop:
fkt: 65
loop: 65
fkt: 73
loop: 73
...

jeebee
07-08-2006, 21:04
char* convert(char *str) {
const char *ba = "byte[] ba = new byte[] {";
int baLen = strlen(ba);
int parLen = strlen(str);
char *pt = malloc((baLen + parLen*12) * sizeof(char));
pt = strcpy(pt, ba);
int i;
char tmp[3]; // Länge 3, da snprintf ein '\0' an den String anfügt.
for (i = 0; str[i]; i++) {
/* simples C + Ausgabeformatierung: %x gibt Datentyp int als
vorzeichenlose Hexadezimalzahl aus (auch auf char anwendbar). */
snprintf(tmp,3,"%x", str[i]);
printf("loop: %s\n", tmp);
pt = strcat(pt, "(byte)0x");
pt = strncat(pt, tmp, 2);
pt = strcat(pt, ", ");
}
int nLen = strlen(pt);
char *nep = malloc((nLen + 1) * sizeof(char));
nep = strncpy(nep, pt, nLen-2); // letztes Komma abschneiden
nep = strcat(nep, "};");
free(pt);
return nep;
}

Bei mir hats mit dem Beispieltext "Hello, world!" funktioniert (ich weiss allerdings nicht ob deine Funktion convertDecToHex dasselbe wie mein snprintf tut, da ich den Funktionscode nicht habe.
Als allgemeiner Tipp: poste solche Funktionsschnipsel immer so, dass jemand anderes das Programm ohne grosse Arbeit testen kann.

MfG jeebee

rgubatz
07-08-2006, 23:11
Hallo,
1. Wenn Ihr ein malloc(); macht, prüft bitte den Rückgabewert auf NULL.
2. Wenn Ihr euren Code nur mit dem GCC übersetzt, könnt ihr auch solche Sachen machen:


FILE *
concat_fopen (char *s1, char *s2, char *mode)
{
char str[strlen (s1) + strlen (s2) + 1];
strcpy (str, s1);
strcat (str, s2);
return fopen (str, mode);
}

Quelle (http://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Variable-Length.html#Variable-Length)

Das spart ein malloc();. Generell solltet Ihr auf eine schöne API achten.
Was ist besser: char* convert(char *) oder int convert_dec_to_hex(const char*,char*,int); ?

Rene

falke2203
08-08-2006, 07:43
char* convert(char *str) {
const char *ba = "byte[] ba = new byte[] {";
int baLen = strlen(ba);
int parLen = strlen(str);
char *pt = malloc((baLen + parLen*12) * sizeof(char));
pt = strcpy(pt, ba);
int i;
char tmp[3]; // Länge 3, da snprintf ein '\0' an den String anfügt.
for (i = 0; str[i]; i++) {
/* simples C + Ausgabeformatierung: %x gibt Datentyp int als
vorzeichenlose Hexadezimalzahl aus (auch auf char anwendbar). */
snprintf(tmp,3,"%x", str[i]);
printf("loop: %s\n", tmp);
pt = strcat(pt, "(byte)0x");
pt = strncat(pt, tmp, 2);
pt = strcat(pt, ", ");
}
int nLen = strlen(pt);
char *nep = malloc((nLen + 1) * sizeof(char));
nep = strncpy(nep, pt, nLen-2); // letztes Komma abschneiden
nep = strcat(nep, "};");
free(pt);
return nep;
}

Bei mir hats mit dem Beispieltext "Hello, world!" funktioniert (ich weiss allerdings nicht ob deine Funktion convertDecToHex dasselbe wie mein snprintf tut, da ich den Funktionscode nicht habe.
Als allgemeiner Tipp: poste solche Funktionsschnipsel immer so, dass jemand anderes das Programm ohne grosse Arbeit testen kann.

MfG jeebee

You made my day :D

Mit dieser snprintf kommt exakt das raus was ich mit der convertDecToHex-Funktion erreichen wollte...

Recht herzlichen Dank an alle die sich diesem Thema angenommen haben :)

jeebee
08-08-2006, 20:35
Hallo,
1. Wenn Ihr ein malloc(); macht, prüft bitte den Rückgabewert auf NULL.
mach ich in selbst geschriebenem Code natürlich auch...

2. Wenn Ihr euren Code nur mit dem GCC übersetzt, könnt ihr auch solche Sachen machen:


FILE *
concat_fopen (char *s1, char *s2, char *mode)
{
char str[strlen (s1) + strlen (s2) + 1];
strcpy (str, s1);
strcat (str, s2);
return fopen (str, mode);
}

Quelle (http://gcc.gnu.org/onlinedocs/gcc-3.4.6/gcc/Variable-Length.html#Variable-Length)

Das spart ein malloc();.
Cool, wusste ich nicht.

Generell solltet Ihr auf eine schöne API achten.
Was ist besser: char* convert(char *) oder int convert_dec_to_hex(const char*,char*,int); ?
das zweite, wobei ich nicht genau sehe, wofür du das const char* verwenden willst (ausser du übergibst den Dezimalwert als char*, dann const weil am Übergabewert nichts geändert werden soll; dann aber: warum noch ein int in den Funktionsparametern?). Rückgabewert: 0 falls erfolgreich, sonst etwas anderes.

MfG jeebee

rgubatz
08-08-2006, 21:15
Hallo,

int convert_dec_to_hex(const char *inbuf,char *outbuf,int outbuflen);
, weil innerhalb der Funktion die Größe von outbuf unbekannt ist.

Dies war ja auch nur mal ein Beispiel um euch das API-Problem zu verdeutlichen.

Rene

jeebee
09-08-2006, 16:36
ok, stimmt, gibt aber auch die funktion strlen() in <string.h> um die Länge eines char-arrays zu bestimmen.

rgubatz
09-08-2006, 18:57
Hallo,
das habe ich mir schon gedacht, daß das kommt. ;)

Folgendes Beispiel:


int main()
{
char buf[1024];
convert_dec_to_hex("Hallo Welt!",buf,sizeof(buf));
}

Weil buf nicht initialisiert wurde, kannst du innerhalb von convert_dec_to_hex() die Länge nicht bestimmen, weil ja irgendwo ein Nullbyte stehen könnte.

Oder aber du hast buf vorher schon benutzt und zwar so hier:

strncpy(buf,1024,"Hallo Welt!");
Nun dann hättte innerhalb deiner Funktion buf die Länge von 12 Bytes.

Oder der schlimmste anzunehmende Fall, du initialisierst buf vorher mit memset(buf,0,1024);, dann hätte buf innerhalb deiner Funktion die Länge 0.

Funktionen sollte nicht nur aussagekräftige Parameter haben, sondern in jeder erdenklichen Situation funktionieren. Es darf nicht sein, daß äußere Parameter das Verhalten meiner Funktion negativ beeinflussen können. Wo soll da die Fehlersuche beginnen?

Rene

jeebee
09-08-2006, 20:34
na gut, hast mich überzeugt. ;-)