Variablenwert während Laufzeit verändert


  • Mod

    Ich kann das Programm nicht einmal übersetzen (zu wenige Argumente für pcre_exec) und bekomme zudem noch ungefähr 25 Warnungen, von denen mindestens die Hälfte kritisch ist. Wundern dich da solche Fehler? Beheb zuerst einmal alle Probleme, die der Compiler melden kann. Danach kann man über eventuelle weitere Probleme reden, sofern diese noch vorhanden sind. Es könnte auch hilfreich sein, wenn du aufhörst, Drei-Sterne-Code zu schreiben.

    Da du offenbar nicht selber in der Lage warst, Compilerwarnungen zu aktivieren (spätestens jetzt solltest du es tun!) und zur Erleichterung für andere Leser, hier die Warnungen:

    test.c: In function \u2018preg_match_all\u2019:
    test.c:36:24: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
         for (i = 0; offset < strlen(subject) && i < matches_count && (rc = pcre_exec(re, NULL, subject + offset, (int) strlen(subject + offset), 0, ovector, oveccount)) > 0; i++)
                            ^
    test.c:36:5: warning: passing argument 6 of \u2018pcre_exec\u2019 makes integer from pointer without a cast [enabled by default]
         for (i = 0; offset < strlen(subject) && i < matches_count && (rc = pcre_exec(re, NULL, subject + offset, (int) strlen(subject + offset), 0, ovector, oveccount)) > 0; i++)
         ^
    In file included from test.c:3:0:
    /usr/include/pcre.h:443:20: note: expected \u2018int\u2019 but argument is of type \u2018int *\u2019
     PCRE_EXP_DECL int  pcre_exec(const pcre *, const pcre_extra *, PCRE_SPTR,
                        ^
    test.c:36:5: warning: passing argument 7 of \u2018pcre_exec\u2019 makes pointer from integer without a cast [enabled by default]
         for (i = 0; offset < strlen(subject) && i < matches_count && (rc = pcre_exec(re, NULL, subject + offset, (int) strlen(subject + offset), 0, ovector, oveccount)) > 0; i++)
         ^
    In file included from test.c:3:0:
    /usr/include/pcre.h:443:20: note: expected \u2018int *\u2019 but argument is of type \u2018int\u2019
     PCRE_EXP_DECL int  pcre_exec(const pcre *, const pcre_extra *, PCRE_SPTR,
                        ^
    test.c:36:5: error: too few arguments to function \u2018pcre_exec\u2019
         for (i = 0; offset < strlen(subject) && i < matches_count && (rc = pcre_exec(re, NULL, subject + offset, (int) strlen(subject + offset), 0, ovector, oveccount)) > 0; i++)
         ^
    In file included from test.c:3:0:
    /usr/include/pcre.h:443:20: note: declared here
     PCRE_EXP_DECL int  pcre_exec(const pcre *, const pcre_extra *, PCRE_SPTR,
                        ^
    test.c:46:13: warning: passing argument 1 of \u2018sprintf\u2019 from incompatible pointer type [enabled by default]
                 sprintf((match + i)[j], "%.*s", substring_length, substring_start);
                 ^
    In file included from test.c:1:0:
    /usr/include/stdio.h:364:12: note: expected \u2018char * __restrict__\u2019 but argument is of type \u2018char **\u2019
     extern int sprintf (char *__restrict __s,
                ^
    test.c:48:13: warning: format \u2018%s\u2019 expects argument of type \u2018char *\u2019, but argument 2 has type \u2018char **\u2019 [-Wformat=]
                 printf("%s\n", (match + i)[j]);
                 ^
    test.c:59:9: warning: format \u2018%x\u2019 expects argument of type \u2018unsigned int\u2019, but argument 4 has type \u2018int *\u2019 [-Wformat=]
             printf("matches[%d]: %d (%x)\n", i + 1, matches[i + 1], &matches[i + 1]);
             ^
    test.c:63:5: warning: format \u2018%x\u2019 expects argument of type \u2018unsigned int\u2019, but argument 3 has type \u2018int *\u2019 [-Wformat=]
         printf("\nmatches[1]: %d (%x)\nmatches[2]: %d (%x)\n\n", matches[1], &matches[1], matches[2], &matches[2]);
         ^
    test.c:63:5: warning: format \u2018%x\u2019 expects argument of type \u2018unsigned int\u2019, but argument 5 has type \u2018int *\u2019 [-Wformat=]
    test.c:75:17: warning: function returns address of local variable [-Wreturn-local-addr]
                     return matches;
                     ^
    test.c:82:13: warning: function returns address of local variable [-Wreturn-local-addr]
                 return matches;
                 ^
    test.c:90:9: warning: function returns address of local variable [-Wreturn-local-addr]
             return matches;
             ^
    test.c:95:5: warning: function returns address of local variable [-Wreturn-local-addr]
         return matches;
         ^
    test.c: At top level:
    test.c:98:6: warning: return type of \u2018main\u2019 is not \u2018int\u2019 [-Wmain]
     void main (void)
          ^
    test.c: In function \u2018main\u2019:
    test.c:106:5: warning: passing argument 3 of \u2018preg_match_all\u2019 from incompatible pointer type [enabled by default]
         result = preg_match_all(pattern, subject, matches, 10, 10, 0);
         ^
    test.c:5:6: note: expected \u2018char ***\u2019 but argument is of type \u2018char * (*)[10]\u2019
     int* preg_match_all (char *pattern, char *subject, char ***match, const int matches_count, const int subpatterns_count, int flags)
          ^
    test.c:104:12: warning: unused variable \u2018j\u2019 [-Wunused-variable]
         int i, j;
                ^
    test.c:104:9: warning: unused variable \u2018i\u2019 [-Wunused-variable]
         int i, j;
             ^
    test.c:100:10: warning: variable \u2018result\u2019 set but not used [-Wunused-but-set-variable]
         int *result;
              ^
    

    Am kritischten (das heißt nicht, dass die anderen nicht auch berechtigt wären) sind wohl die Warnungen zu den Zeilen 36, 46, 48, 59, 63, 75, 82, 90, 95 und 106. Also eigentlich fast alle.

    PS: Die Warnungen, die ich als kritisch einschätzen würde sind auch genau die, die vom MSVC (im Beitrag vor meinem) erzeugt werden. Ich habe meinen Compiler auf extra-pingelig gestellt, so dass er auch vor so etwas wie unbenutzten Variablen warnt. Das sind keine kritischen Fehler, aber sie sind ein deutliches Zeichen, dass schlampig programmiert wurde. Und wo schlampig programmiert wird, treten Fehler auf, auch wenn die unbenutzte Variable selber nicht direkt ein Fehler ist. Die anderen Warnungen sind aber echte Fehler. Sie sind bloß technisch gesehen gerade noch korrektes C, weil in C davon ausgegangen wird, dass der Programmierer sich auskennt und ein komisch aussehendes Konstrukt mit Absicht benutzt hat. Das ist hier aber nicht der Fall, denn der Programmierer hat das komische Konstrukt benutzt, weil er es nicht besser (lies: richtig) konnte.

    PPS: Ich sehe gerade, dass die Sonderzeichen in meinen Warnmeldungen falsch dargestellt werden. Das korrigiere ich jetzt mal nicht. Es sind bloß lauter Anführungsstriche um Bezeichner, die hier falsch übertragen wurden.



  • Ich habe mir eure Mahnungen einmal zu Herzen genommen und mich ein wenig intensiver mit Pointern und Arrays beschäftigt. Für jemanden, der seit jeher PHP programmiert hat, ist der Umstieg auf eine maschinennahe Sprache nicht intuitiv. Dort konnte man derlei Warnungen noch beflissentlich ignorieren, ohne dass einem direkt das gesamte Skript um die Ohren fliegt. 😉 Wenngleich man dies aber natürlich auch dort nicht tun sollte.

    Nun funktioniert doch tatsächlich alles wie gewünscht. Die Fehlermeldung bezüglich des fehlenden Parameters erklärt sich, indem ich meinerseits feststellen musste, dass die pcre_exec, die mein Borland-Free-Compiler (inzwischen von Embarcadero vertrieben) mitliefert, von der Handbuchfunktion abweicht. Es fehlt, wenn ich mich recht erinnere, der offset-Parameter.

    #include <stdio.h>
    #include <string.h>
    #include <pcre.h>
    
    int* preg_match_all (char *pattern, char *subject, char **match, const int matches_count, const int subpatterns_count, const int flags)
    {
    	pcre *re;
    	const char *error;
    	const int oveccount = subpatterns_count * 3 + 3;
    	int erroffset;
    	int *ovector;
    	int rc, i, j, k;
    	int *matches = calloc(2, sizeof(int));
    	unsigned int offset = 0;
    
    	// Allocate memory for output vector
    	ovector = calloc(oveccount, sizeof(int));
    
    	for (i = 0; i < oveccount; i++)
    	{
    		ovector[i] = -1;
    	}
    
    	// Compile regex pattern
    	re = pcre_compile(pattern, flags, &error, &erroffset, NULL);
    
    	if (re == NULL)
    	{
    		fprintf(stderr, "PCRE compilation failed at offset %d: %s\n", erroffset, error);
    		return NULL;
    	}
    
    	// Apply pattern to subject and afterwards set subject pointer to next position behind last full match
    	// Continue if subject end is  reached or if there is not enough space provided by user as determined in matches_count
    	// Also continue if there is no further match
    	for (i = 0; offset < strlen(subject)
    		&& i < matches_count
    		&& (rc = pcre_exec(re, NULL, subject + offset, (int) strlen(subject + offset), 0, ovector, oveccount)) > 0; i++)
    	{
    
    		for (j = 0; j < rc; j++)
    		{
    			int index = (i * subpatterns_count) + j;
    			char *substring_start = subject + offset + ovector[2 * j];
    			int substring_length = ovector[2 * j + 1] - ovector[2 * j];
    			printf("%d %d %d\n", i, j, rc);
    
    			match[index] = calloc(substring_length + 1, sizeof(char));
    			sprintf(match[index], "%.*s", substring_length, substring_start);
    			match[index][substring_length] = '\0';
    			printf("%s\n", match[index]);
    		}
    
    		matches[i + 1] = j;
    		offset += ovector[1];
    
    		for (k = 0; k < (subpatterns_count * 3 + 3); k++)
    		{
    			ovector[k] = -1;
    		}
    
    		printf("%d: matches[%d]: %d (%x)\n", i, i + 1, matches[i + 1], &matches[i + 1]);
    		printf("Match succeeded.\n\n");
    	}
    
    	printf("\nmatches[1]: %d (%x)\nmatches[2]: %d (%x)\n\n", matches[1], &matches[1], matches[2], &matches[2]);
    
    	// Handle prce_exec errors
    	if (rc < 0)
    	{
    		if (rc == PCRE_ERROR_NOMATCH)
    		{
    			// We always get PCRE_ERROR_NOMATCH, but only relevant if there has been no single successful pcre_exec
    			if (i == 0)
    			{
    				fprintf(stderr, "No match.\n");
    				matches[0] = 0; // Set full-match counter to zero
    				return matches;
    			}
    		}
    		else
    		{
    			fprintf(stderr, "Matching error %d.\n", rc);
    			matches[0] = 0;
    			return matches;
    		}
    	}
    
    	if (rc == 0)
    	{
    		fprintf(stderr, "ovector only has room for %d captured subtrings.\n", oveccount / 3 - 1);
    		matches[0] = 0;
    		return matches;
    	}
    
    	matches[0] = i; // Set full-match counter
    
    	return matches;
    }
    
    void main (void)
    {
    	int *result;
    	char *pattern = "foo(bar)?";
    	char *subject = "foobarfoobarfoo";
    	char *matches[10][10];
    	int i, j;
    
    	result = preg_match_all(pattern, subject, matches, 10, 10, 0);
    }
    

    Ich musste nicht viel ändern, der Hinweis auf den Drei-Sterne-Operator hat schon genügt. Ich hätte schon viel früher misstrauisch werden müssen, als ich bei Google nichts zu "pointer to pointer to pointer" gefunden habe. 😉



  • thorr schrieb:

    Ich habe mir eure Mahnungen einmal zu Herzen genommen und mich ein wenig intensiver mit Pointern und Arrays beschäftigt. Für jemanden, der seit jeher PHP programmiert hat, ist der Umstieg auf eine maschinennahe Sprache nicht intuitiv.

    Jo.
    Kannste Dich noch ein weiter an Pointer und Arrays gewöhnen und Deine ganze Funktion weggintensivieren?



  • thorr schrieb:

    ... und mich ein wenig intensiver mit Pointern und Arrays beschäftigt.

    lol
    Ist immer sinnvoll, die Grundlagen einer Sprache zu beherrschen, bevor man sie einsetzt. Und Pointer und Arrays sind die essenziellen Grundlagen bei C.

    thorr schrieb:

    ...Dort konnte man derlei Warnungen noch beflissentlich ignorieren, ohne dass einem direkt das gesamte Skript um die Ohren fliegt....

    lol
    C ist was für Profis und nichts für Anfänger.
    Wenn ein C Compiler ein Programm übersetzt, heißt das noch lange nicht, dass es 'funktioniert'.
    Es ist dem Compilerhersteller überlassen, bei welchem Programmiererunsinn er weitermacht und nur warnt oder wann er mit einem Fehler abbricht.



  • thorr schrieb:

    Ich musste nicht viel ändern,

    , char **match,
    passt nicht zu
    char *matches[10][10];
    

    - calloc ohne free

    sprintf(match[index], "%.*s", substring_length, substring_start);
                match[index][substring_length] = '\0';
    

    die 2. Zeile ist überflüssig, du hast sprintf nicht verstanden.
    u.v.a.m.
    Dein Code lässt sich problemlos auf 1/3 kürzen, dafür dann aber fehlerfrei.



  • C ist was für Profis und nichts für Anfänger.

    Und wie zum Profi werden ? Finde C "einfacher" für den Anfang zu lernen wie C++

    Aber wie gesagt, Wie zum Profi für C werden ??


  • Mod

    beg_c schrieb:

    Aber wie gesagt, Wie zum Profi für C werden ??

    Die Sachen, die hier bemängelt wurden, sind schon ziemliche Grundlagen. Was bedeutet, dass du vermutlich einfach drauf los programmiert hast, ohne mal einen Blick in ein gutes Grundlagenbuch zu werfen. So lang sind die ja nicht für C. Oder dein Grundlagenbuch war irgendwelcher Schrott (Galileo-Verlag?). Dann wäre es zwar nicht deine Schuld, aber die Grundlagen beherrscht du trotzdem nicht.

    Finde C "einfacher" für den Anfang zu lernen wie C++

    Wovon redest du? Was hat C++ hiermit zu tun? C ist genau so schwer wie es ist und das ist doch, worum es hier geht. C ist auch einfacher als Malbolge* und es ist schwieriger als Logo.
    edit: Ich ahne schlimmes: Du willst C++ über den Umweg C lernen? Falls ja: Du bist auf dem Holzweg gelandet :p . Lern direkt die Sprache, die du lernen möchtest. C und C++ sind völlig verschiedene Sprachen. Wissen in der einen, hindert dich sogar eher beim Erlernen der anderen. So wie man hier im Thread sehr schön sehen kann, was passiert, wenn man mit einer PHP-Einstellung an C heran geht.

    *: Steilvorlage für alle Trolle, das Gegenteil zu behaupten.



  • Ich ahne schlimmes: Du willst C++ über den Umweg C lernen? Falls ja: Du bist auf dem Holzweg gelandet

    Nicht ganz, aber so ähnlich. Ich will generell demnächst anfangen, C++ zu lernen. Ich gehe aber davon aus, das ein paar C-Kenntinsse jetzt nicht das schlechteste für C++ oder für´s =>Programmieren Allgemein<= sind.

    Ursprünglich wollte ich mit meinem Beitrag darauf raus, das jeder mal mit irgendeiner Sprache anfangen muss zum lernen, bei vielen ist oder war das C, und da ist eben noch kein Meister vom Himmel gefallen.

    Die Sachen, die hier bemängelt wurden, sind schon ziemliche Grundlagen

    Hab den Code nur überflogen, aber wenn der Thread-Ersteller wirklich keine Ahnung von C hat, bzw. selten was macht, ist es ja gar nicht sooo schlimm, da hab ich hier schon andere Sachen gesehn 🙂



  • Wutz schrieb:

    lol

    Schön, dass ich dich belustigen konnte.

    Wutz schrieb:

    Ist immer sinnvoll, die Grundlagen einer Sprache zu beherrschen, bevor man sie einsetzt. Und Pointer und Arrays sind die essenziellen Grundlagen bei C.

    Ich finde es nicht abwegig, als C-Anfänger bei einem zweidimensionalen Array von einem Pointer auf einen Pointer, der beispielsweise auf einen int zeigt, auszugehen.

    Wutz schrieb:

    C ist was für Profis und nichts für Anfänger.

    Wo kein Anfänger, da auch kein Profi. Learning by doing. Ich wüsste nicht, was daran verwerflich sein sollte.

    beg_c schrieb:

    Ursprünglich wollte ich mit meinem Beitrag darauf raus, das jeder mal mit irgendeiner Sprache anfangen muss zum lernen, bei vielen ist oder war das C, und da ist eben noch kein Meister vom Himmel gefallen.

    Das will ich damit sagen. 😉

    Wutz schrieb:

    Wenn ein C Compiler ein Programm übersetzt, heißt das noch lange nicht, dass es 'funktioniert'.
    Es ist dem Compilerhersteller überlassen, bei welchem Programmiererunsinn er weitermacht und nur warnt oder wann er mit einem Fehler abbricht.

    Du kannst nicht bestreiten, dass ein PHP-Parser zugunsten der Funktionalität mit E_NOTICE ein geringeres Level zur Ausgabe von Warnungen besitzt. Um diese Feinheiten habe ich mich in der PHP-Programmierung meist erst nach Fertigstellung grundlegender Funktionalitäten gekümmert und bin damit eigentlich immer gut gefahren. Dass das in C mitunter fatal sein kann, habe ich jetzt gelernt.

    Wutz schrieb:

    , char **match,
    passt nicht zu
    char *matches[10][10];
    

    Da matches die Speicheradresse des Elements [0][0] und somit dieselbe wie matches[0] besitzt, müsste es sich ja eigentlich ebenso um einen Pointer auf einen Pointer (Char-Array) handeln, der ansich korrekt an der Stelle eingesetzt ist, oder?

    SeppJ schrieb:

    Oder dein Grundlagenbuch war irgendwelcher Schrott (Galileo-Verlag?). Dann wäre es zwar nicht deine Schuld, aber die Grundlagen beherrscht du trotzdem nicht.

    Ich habe als Grundlagenlektüre ein Buch des bhv-Verlags durchgearbeitet, welches für einen Umsteiger im Nachhinein tatsächlich nicht ideal gewesen ist. Viele grundlegende Themen, die mir bereits aus anderen Programmiersprachen geläufig waren, werden ausführlich behandelt, stattdessen fällt dann eben ein tieferer Blick in das Thema Pointer und Speicherbereiche hinten über. Dazu kommen handfeste syntaktische oder logische Fehler in den Beispielquelltexten, die für einen Einsteiger nicht gerade förderlich sind - und das sogar noch in der fünften Auflage.



  • thorr schrieb:

    Da matches die Speicheradresse des Elements [0][0] und somit dieselbe wie matches[0] besitzt, müsste es sich ja eigentlich ebenso ...

    Das ist der Aberglaube aller Anfänger, Fachbuchautoren und Hochschullehrer.
    Neben Bitbreite, Bitreihenfolge und Alignment, die nicht für alle Datenzeiger gleich und kompatibel sein muss, spielt bei Zeigern die Dereferenzierung die entscheidende Rolle. Das hast du noch nicht begriffen, die meisten begreifen es nie.

    char c = 1;
    char *cp = &c;
    void *p = cp;
    char **sp = p;
    char x = **sp;
    

    http://ideone.com/GqA8v0

    Dreimal darfst du raten, warum dieser C-technisch fehler- und warnungsfreie Code trotzdem UB ist (viele Laufzeitumgebungen sind hier so gnädig und produzieren einen Abbruch).
    Weil der Programmierer (in diesem Fall ausnahmsweise mal ich) mit typinkompatiblen Zeigern rumhantiert und wie du siehst, kommt das dicke Ende nicht bei den Zuweisungen der Adressen zustande, sondern beim einzig wahren Anwendungsfall eines Zeigers, nämlich seiner Dereferenzierung, weil hier nämlich die Typinformation aufgelöst wird, und dadurch "auch wenn die Adresse selbst gleich sein mag" trotzdem Unsinn herauskommt.


Anmelden zum Antworten