Schrift
Wiki:Tipp zum Debugging: use Data::Dumper; local $Data::Dumper::Useqq = 1; print Dumper \@var;
[thread]10676[/thread]

u.a. Adressierungsoperator

Leser: 1


<< >> 8 Einträge, 1 Seite
RPerl
 2007-10-27 19:18
#101469 #101469
User since
2006-11-26
384 Artikel
BenutzerIn

user image
Hallo jungs,

erstmal danke fuer das klicken :)
ich hab mal wieder ein Problem mit C und eine Frage bzgl. C.


1. Frage: Warum brauch *ich* den Adressierungsoperator in dieser Situation:

Code: (dl )
sprintf(&str[0], "INSERT INTO data (v1, v2, v3, v4) VALUES ('%s', '%s', '%s', '%s')", f[0], f[1], f[2], f[3]);


str sieht so aus:

Code: (dl )
1
2
3
[...]
char *str[1024];
[...]


Vielleicht weiss das ja hier jemand :)

2. Problem: Wieso geht das:

Code: (dl )
1
2
3
4
  sprintf(&str[0], "INSERT INTO data (v1, v2, v3, v4) VALUES ('%s', '%s', '%s', '%s')\0", f[0], f[1], f[2], f[3]);
printf("%s\n", &str[0]);
sprintf(&str[1], "INSERT INTO data (v1, v2, v3, v4) VALUES ('%s', '%s', '%s', '%s')", f[4], f[5], f[6], f[7]);
printf("%s\n", &str[1]);


Output: INSERT INTO data (v1, v2, v3, v4) VALUES ('aaa', 'bbb', 'ccc', 'ddd')
INSERT INTO data (v1, v2, v3, v4) VALUES ('eee', 'fff', 'ggg', 'hhh')

=> Erwartete Ausgabe.


Aber das hier nicht:

Code: (dl )
1
2
3
4
  sprintf(&str[0], "INSERT INTO data (v1, v2, v3, v4) VALUES ('%s', '%s', '%s', '%s')\0", f[0], f[1], f[2], f[3]);
sprintf(&str[1], "INSERT INTO data (v1, v2, v3, v4) VALUES ('%s', '%s', '%s', '%s')", f[4], f[5], f[6], f[7]);
printf("%s\n", &str[0]);
printf("%s\n", &str[1]);


Output: INSEINSERT INTO data (v1, v2, v3, v4) VALUES ('eee', 'fff', 'ggg', 'hhh') INSERT INTO data (v1, v2, v3, v4) VALUES ('eee', 'fff', 'ggg', 'hhh')

Hier werden offenbar die Statements ueberschrieben. Man siehts am fett markierten und an der Tatsache, das die Werte falsch sind. *g*

Wer Lust / Interesse hat, das hier zu beantworten, gerne. Aber bitte kein geflame wie mans aus manch anderen Foren kennt. :)

Vielen Dank, schoenen Samstag

gruß

rperl

// Nachtrag: So gehts uebrigens:

Code: (dl )
1
2
3
  sprintf(&str[0], "INSERT INTO data (v1, v2, v3, v4) VALUES ('%s', '%s', '%s', '%s')\0", f[0], f[1], f[2], f[3]);
sprintf(&str[25], "INSERT INTO data (v1, v2, v3, v4) VALUES ('%s', '%s', '%s', '%s')\0", f[4], f[5], f[6], f[7]);
printf("%s", &str[25])


Seltsam *g*
murphy
 2007-10-27 21:45
#101480 #101480
User since
2004-07-19
1776 Artikel
HausmeisterIn
[Homepage]
user image
Es ist eigentlich klar, warum der Code genau das tut, was Du beschreibst: sprintf bekommt als ersten Parameter einen Puffer, nicht eine Speicherstelle wo eine neu allozierte Zeichenkette abgelegt werden soll. Und printf bekommt als Zeichenkettenargumente ebenfalls die Puffer übergeben, nicht Zeiger darauf. Du machst also zwei Fehler, die sich teilweise wieder aufheben ;-)

Mit char *str[1024]; deklarierst Du ein Feld von 1024 Zeigern auf Zeichen, dann aber sagst Du mittels sprintf(&str[0], ...); "Nimm das erste Element des Feldes str -- also den ersten Zeiger -- berechne seine Adresse, konvertiere diese Adresse implizit vom Typ Zeiger auf Zeiger auf Zeichen in den Typ Zeiger auf Zeichen und speichere dort die Zeichenkette, die sprintf berechnet". Das ist -- wegen der Typkonversion -- eine komische Anweisung, die Dein Compiler vermutlich auch angemeckert hätte, wenn Du vorsichtigerweise mit eingeschalteten Warnungen kompiliert hättest.

Bei der sprintf(&str[1], ...); Anweisung schreibst Du dann Daten ab dem zweiten Element in das Feld str, also auf einer 32bit Maschine 4 Bytes vom Anfang des Feldes entfernt. Daher erklärt sich die Überlappung der Zeichenketten um 4 Zeichen bei der Ausgabe.

Bei der Ausgabe machst Du nun konsequenterweise denselben Typisierungsfehler, weshalb das ganze -- zumindest halbwegs -- funktioniert.

Das Programm verhält sich genau so, wie es sollte, allerdings verletzt Du hier die strikte Typisierung der Zeiger, was schlechter Stil ist, und Du führst keinerlei Überprüfungen von Zeichenkettenlängen durch, um Pufferüberläufe zu verhindern, was noch viel schlechterer Stil ist.

Eigentlich meintest Du wohl viel eher so etwas wie
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
#define FMT "INSERT INTO data (v1, v2, v3, v4) VALUES ('%s', '%s', '%s', '%s')"
#define NSTMTS 3
#define BUFSIZE 1024

int i;
char stmts[NSTMTS][BUFSIZE];

for (i = 0; i < NSTMTS; i++) {
/* ... get data into the array f ... */
assert(snprintf(stmts[i], BUFSIZE, FMT, f[0], f[1], f[2], f[3]) < BUFSIZE - 1);
}

oder vielleicht mit dynamischer Allokation
Code: (dl )
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#define FMT "INSERT INTO data (v1, v2, v3, v4) VALUES ('%s', '%s', '%s', '%s')"
#define NSTMTS 3

int i;
char *stmts[NSTMTS];

for (i = 0; i < NSTMTS; i++) {
size_t s;
char *p;

/* ... get data into the array f ... */
s = snprintf(NULL, 0, FMT, f[0], f[1], f[2], f[3]) + 1;
assert((p = (char *)malloc(s)) != NULL);

snprintf(p, s, FMT, f[0], f[1], f[2], f[3]);
}

/* ... do something with the statements ... */

for (i = 0; i < NSTMTS; i++)
free(stmts[i]);
When C++ is your hammer, every problem looks like your thumb.
GwenDragon
 2007-10-27 21:50
#101481 #101481
User since
2005-01-17
14761 Artikel
Admin1
[Homepage]
user image
RPerl+2007-10-27 17:18:02--
Hallo jungs,

Hallo Mädel!

Ich empfehle ein Standard-C-Lehrbuch von Kernigham&Richie zu lesen! Nicht C++, wohl gemerkt.
Dann werden die Datentypen und deren Definition unter C auch klarer!
RPerl
 2007-10-27 22:04
#101483 #101483
User since
2006-11-26
384 Artikel
BenutzerIn

user image
Quote
Mit char *str[1024]; deklarierst Du ein Feld von 1024 Zeigern auf Zeichen, dann aber sagst Du mittels sprintf(&str[0], ...); "Nimm das erste Element des Feldes str -- also den ersten Zeiger -- berechne seine Adresse, konvertiere diese Adresse implizit vom Typ Zeiger auf Zeiger auf Zeichen in den Typ Zeiger auf Zeichen und speichere dort die Zeichenkette, die sprintf berechnet". Das ist -- wegen der Typkonversion -- eine komische Anweisung, die Dein Compiler vermutlich auch angemeckert hätte, wenn Du vorsichtigerweise mit eingeschalteten Warnungen kompiliert hättest.

Ok - wenn ich nur:

char str[1024]

mache, bekomme ich wieder eben falsche Werte.
Output:
Code: (dl )
1
2
3
---
INSERT INTO data (v1, v2,INSERT INTO data (v1, v2,INSERT INTO data (v1, v2,INSERT INTO data (v1, v2,INSERT INTO data (v1, v2,INSERT INTO data (v1, v2,INSERT INTO data (v1, v2,INSERT INTO data (v1, v2,INSERT INTO data (v1, v2,INSERT INTO data (v1, v2,INSERT INTO data (v1, v2,INSERT INTO data (v1, v2, v3, v4) VALUES ('127.0.0.1', 'hostname', 'port', 'test')
---

Ueberlappung.

Wenn ich oberes lasse, bekomme ich:

Quote
warning: passing argument 1 of ‘sprintf’ from incompatible pointer type
zwar eine Warnung, aber der code tut was er tun soll.

Also was soll ich tun? Die Warnung hinnehmen, was ich persoenlich nicht gut finde, oder diesen Fehler suchen und auch noch ausmerzen?

Quote
Das Programm verhält sich genau so, wie es sollte, allerdings verletzt Du hier die strikte Typisierung der Zeiger, was schlechter Stil ist, und Du führst keinerlei Überprüfungen von Zeichenkettenlängen durch, um Pufferüberläufe zu verhindern, was noch viel schlechterer Stil ist.

Darum kuemmer ich mich natuerlich sowieso noch. *g*

Ich haette fuer die query Generierung auch Bind parameter von mysql.h nutzen koennen, aber dafuer haet ich umstaendlicherweise eine Struktur erstellen muessen - das ist etwas "oversized".

Tausend Dank fuer deine freundlichen Tipps, Erklaerungen und Verbesserungen!
GwenDragon
 2007-10-27 22:08
#101484 #101484
User since
2005-01-17
14761 Artikel
Admin1
[Homepage]
user image
Sehr mutig, Warnungen zu ignorieren. ;)
Kommt allerdings auch darauf an wie regelkonform du C-Code schreiben willst.
Und welchen Warnlevel du einstellst.

C lässt ja viele nette Zeigertricksereien und Typkonversionen zu, da solltest du lieber die Warnings des Compilers/Präprozessors nicht ignorieren.

Schönen Abend noch.
RPerl
 2007-10-27 22:27
#101485 #101485
User since
2006-11-26
384 Artikel
BenutzerIn

user image
GwenDragon+2007-10-27 19:50:45--
RPerl+2007-10-27 17:18:02--
Hallo jungs,

Hallo Mädel!
[...]


Ooops, natuerlich sorry ;-)

GwenDragon
Ich empfehle ein Standard-C-Lehrbuch von Kernigham&Richie zu lesen! Nicht C++, wohl gemerkt.
Dann werden die Datentypen und deren Definition unter C auch klarer!

Danke fuer den Tipp.

GwenDragon
Schönen Abend noch.

Danke, gleichfalls. Bin ungewoehnlich gut gelaunt heute.. echt seltsam...
murphy
 2007-10-27 22:32
#101486 #101486
User since
2004-07-19
1776 Artikel
HausmeisterIn
[Homepage]
user image
RPerl+2007-10-27 20:04:05--
[...]
Ok - wenn ich nur:

char str[1024]

mache, bekomme ich wieder eben falsche Werte.


Na klar funktioniert das nicht! Schau Dir doch bitte nochmal die Codeschnipsel an, die ich in meinen letzten Beitrag geschrieben habe -- die sollten nämlich tun, was Du willst. Fall sie es nicht tun, weil ich zu schnell getippt habe, oder falls Dir daran etwas unklar ist, kannst Du ja nachfragen.

Außerdem solltest Du Dir auch überlegen, ob Du wirklich erst alle SQL-Anweisungen in ein Feld von Zeichenketten verpacken möchtest, oder ob es nicht ausreicht, immer die aktuelle Anweisung zu generieren, auszuführen und dann wieder den Speicher für die nächste Anweisung weiterzuverwenden.

Quote
[...]
Ich haette fuer die query Generierung auch Bind parameter von mysql.h nutzen koennen, aber dafuer haet ich umstaendlicherweise eine Struktur erstellen muessen - das ist etwas "oversized".


Bind-Parameter zu benutzen wäre definitiv eine viel bessere Lösung, als ineffizient Strings im Speicher zusammenzustückeln und sich mit Quotingregeln herumzuschlagen, obwohl das gar nicht nötig wäre. Außerdem wird das ganze Programm wahrscheinlich um Größenordnungen schneller laufen, wenn Du Bind-Parameter benutzt, weil MySQL dann den SQL-Befehl nur genau einmal kompilieren muss und Du danach praktisch nur noch die Daten einfütterst.

Und wenn es Dir zu viel Aufwand ist, eine simple Struktur wie MYSQL_BIND auf dem Stack anzulegen, dann befürchte ich, dass Du mit C insgesamt nicht viel Freude haben wirst...
When C++ is your hammer, every problem looks like your thumb.
RPerl
 2007-10-27 22:54
#101487 #101487
User since
2006-11-26
384 Artikel
BenutzerIn

user image
Guten Abend,

Quote
Na klar funktioniert das nicht! Schau Dir doch bitte nochmal die Codeschnipsel an, die ich in meinen letzten Beitrag geschrieben habe -- die sollten nämlich tun, was Du willst. Fall sie es nicht tun, weil ich zu schnell getippt habe, oder falls Dir daran etwas unklar ist, kannst Du ja nachfragen.

ich schick dir gleich mal 'ne PN, bei mir klappt dein code bzw. deine Loesung gar nicht.

Quote
Und wenn es Dir zu viel Aufwand ist, eine simple Struktur wie MYSQL_BIND auf dem Stack anzulegen, dann befürchte ich, dass Du mit C insgesamt nicht viel Freude haben wirst...

Aufwand ist das falsche Wort - ich weiß nur nicht, wie ich das machen soll. Es gibt keine ordentlichen Dokus. Und in meinen Buechern werden die strings in Speicher angepasst und so uebergeben.
<< >> 8 Einträge, 1 Seite



View all threads created 2007-10-27 19:18.