Ich habe ein kleines "Problem" mit dem const Qualifier und
Funktionsargumenten, bei dem mir die c-Experten sicher helfen können.
Eine Funktion soll in einem string die Position des ersten Zeichens
bestimmen, das kein Leerzeichen ist und einen Zeiger auf dieses Zeichen
für den Benutzer zur Verfügung stellen. Der Rückgabewert steht nicht für
den Zeiger zur Verfügung, da er für irgendeine andere wichtige
Information benötigt wird.
funktion1 löst diese Aufgabe. Ich würde jetzt gerne die Argumentliste
mit const Qualifiern erweitern, um anzuzeigen, dass der eigentliche
string nicht verändert wird und damit mir der compiler dabei hilft den
string in der Funktion nicht zu verändern. Ich frage mich allerdings wie
ich das mache, ohne dabei einen cast anzuwenden. Ich habe verschiedene
Versionen und die entsprechenden Warnungen des compilers angegeben.
Mir ist klar warum es diese Warnungen gibt und mir ist auch klar, dass
char const** nicht sehr sinnvoll ist.
1
#include<stdlib.h>
2
#include<stdio.h>
3
#include<string.h>
4
5
voidfunktion1(char*start,char**ende){
6
while(*start&&*start==' '){
7
start++;
8
}
9
*ende=start;
10
}
11
12
voidfunktion2(charconst*start,charconst**ende){
13
while(*start&&*start==' '){
14
start++;
15
}
16
*ende=start;
17
}
18
19
voidfunktion3(charconst*start,char**ende){
20
while(*start&&*start==' '){
21
start++;
22
}
23
*ende=start;// warning: assignment discards ‘const’ qualifier from pointer target type [-Wdiscarded-qualifiers]
24
}
25
26
voidfunktion4(charconst*start,char**ende){
27
while(*start&&*start==' '){
28
start++;
29
}
30
*ende=(char*)start;
31
}
32
33
charconst*funktion5(charconst*start){
34
while(*start&&*start==' '){
35
start++;
36
}
37
returnstart;
38
}
39
40
intmain(){
41
charconst*test=" Hallo Welt!";
42
charconst*ende;
43
44
funktion1(test,&ende);// warning: passing arguments 1 and 2 of ‘funktion1’ discards ‘const’ qualifier from pointer target types [-Wdiscarded-qualifiers]
45
funktion2(test,&ende);
46
funktion3(test,&ende);// warning: passing argument 2 of ‘funktion3’ from incompatible pointer type [-Wincompatible-pointer-types]
47
funktion4(test,&ende);// warning: passing argument 2 of ‘funktion4’ from incompatible pointer type [-Wincompatible-pointer-types]
48
ende=funktion5(test);
49
50
chartest2[20];
51
char*ende2;
52
strcpy(test2,test);
53
54
funktion1(test2,&ende2);
55
funktion2(test2,&ende2);// warning: passing argument 2 of ‘funktion2’ from incompatible pointer type [-Wincompatible-pointer-types]
56
funktion3(test2,&ende2);
57
funktion4(test2,&ende2);
58
ende2=funktion5(test2);// warning: assignment makes integer from pointer without a cast [-Wint-conversion]
59
}
Alle Varianten haben entweder beim Aufruf mit const oder nicht const
Argumenten Probleme.
Ein Beispiel aus der Standardbibliothek mit ähnlichen Problemen ist
strtod.
1
doublestrtod(charconst*str,char**str_end);
Eine Implementation die ich dazu gefunden habe benötigt intern einen
cast von const char* nach char*, um str_end zu erhalten.
Gibt es eine Möglichkeit ohne cast auszukommen, oder das Problem elegant
zu umgehen?
Vielen Dank fürs Lesen und alle konstruktiven Antworten!
mh schrieb:> Gibt es eine Möglichkeit ohne cast auszukommen, oder das Problem elegant> zu umgehen?
Wüßte auf Anhieb auch nicht wie. Entweder mit cast (funktion4) oder zwei
separate Funktionen (1 und 2).
Häufig reicht allerdings ein offset statt eines pointers. Dann bietet
sich an:
Wow, schon zwei Antworten vor mir und keine davon sagt, dass funktion2
der richtige Ansatz ist.
Diesen Link sollte man sich für die richtig komplizierten Fälle merken:
http://cdecl.org/
Gibt es auch als Programm für die Konsole.
Btw., sehr wenige Leute schreiben char const statt const char.
Man muss eben die richtigen Teile auf const setzen. Ich habe const noch
nie an die Stelle gesetzt an der es bei dir steht, worauf bezieht es
sich dort?
Ich würde folgendes Probieren: (ungetestet)
start schrieb:> Die Prüfung, ob *start ungleich 0 ist, ist überflüssig.
Da hast du natürlich recht.
Mikro 7. schrieb:> Häufig reicht allerdings ein offset statt eines pointers. Dann bietet> sich an:> void funktion(char const* start, size_t *offset) ;
Das ist vermutlich in diesem Fall die beste Lösung. Da hätte ich mir
vielleicht etwas mehr Mühe geben müssen, um ein Beispiel zu konstruieren
bei dem man zwingend einen Zeiger von der Funktion benötigt.
Daniel schrieb:> Wow, schon zwei Antworten vor mir und keine davon sagt, dass funktion2> der richtige Ansatz ist.
Inwiefern ist funktion2 der "richtige" Ansatz? Er liefert offensichtlich
Warnungen beim zweiten Aufruf.
Daniel A. schrieb:> Man muss eben die richtigen Teile auf const setzen. Ich habe const noch> nie an die Stelle gesetzt an der es bei dir steht, worauf bezieht es> sich dort?
const char und char const ist identisch.
> const char**const ende
Das zweite const wäre meiner Meinung nach für das Beispiel unwirksam. Es
verhindert, dass man ende in der Funktion verändern darf.
Wenn in der Funktion steht:
Daniel A. schrieb:> Man muss eben die richtigen Teile auf const setzen. Ich habe const noch> nie an die Stelle gesetzt an der es bei dir steht, worauf bezieht es> sich dort?
const bezieht sich immer auf das, was direkt links davon steht, mit
einer Ausnahme: Wenn es ganz links steht, bezieht es sich stattdessen
auf das, was rechts davon steht. Die meisten Leute kennen lustigerweise
nur diese Ausnahme, aber nicht die Regel.
mh schrieb:> Mir ist klar warum es diese Warnungen gibt und mir ist auch klar, dass> char const** nicht sehr sinnvoll ist.
Wie kommst du darauf das char const** nicht sinnvoll ist.
Wenn der String nicht veraenderbar sein soll muss der Rueckgabetyp
const char** sein.
Falls du char* zurueckgibst ist der String ueber diesen Pointer
veraenderbar.
Funktion2 ist korrekt und die Warnung beim 2. Aufruf ist auch voellig
korrekt, da dort eine Zuweisung von const char* auf char* auftritt.
Josef schrieb:> mh schrieb:>> Mir ist klar warum es diese Warnungen gibt und mir ist auch klar, dass>> char const** nicht sehr sinnvoll ist.>> Wie kommst du darauf das char const** nicht sinnvoll ist.
Weil eben eine Warnung kommt, wenn man die Adresse eines Zeigers auf
(non-const) char übergibt.
> Wenn der String nicht veraenderbar sein soll muss der Rueckgabetyp> const char** sein.> Falls du char* zurueckgibst ist der String ueber diesen Pointer> veraenderbar.>> Funktion2 ist korrekt und die Warnung beim 2. Aufruf ist auch voellig> korrekt, da dort eine Zuweisung von const char* auf char* auftritt.
Ich sehe dort nirgends eine solche Zuweisung.
Die Warnung kommt, weil char* und const char* unterschiedliche Typen
sind. Zeiger auf diese beiden Zeigertypen sind daher nicht ohne Cast
zuweisungskompatibel.
Rolf M. schrieb:> Josef schrieb:>> mh schrieb:>>> Mir ist klar warum es diese Warnungen gibt und mir ist auch klar, dass>>> char const** nicht sehr sinnvoll ist.>>>> Wie kommst du darauf das char const** nicht sinnvoll ist.>> Weil eben eine Warnung kommt, wenn man die Adresse eines Zeigers auf> (non-const) char übergibt.>
Er will einen char const** benutzen, damit der String nicht aenderbar
ist.
Wegen der Warnung ist das nicht sinnvoll?
>> Wenn der String nicht veraenderbar sein soll muss der Rueckgabetyp>> const char** sein.>> Falls du char* zurueckgibst ist der String ueber diesen Pointer>> veraenderbar.>>>> Funktion2 ist korrekt und die Warnung beim 2. Aufruf ist auch voellig>> korrekt, da dort eine Zuweisung von const char* auf char* auftritt.>> Ich sehe dort nirgends eine solche Zuweisung.> Die Warnung kommt, weil char* und const char* unterschiedliche Typen> sind. Zeiger auf diese beiden Zeigertypen sind daher nicht ohne Cast> zuweisungskompatibel.
Die Warnung kommt daher weil die Ausdruecke nicht zuweisungskompatibel
sind. Aber wenn da keine Zuweisung ist, dann gibt es auch keine Warnung.
Problem geloest.
Ich glaub ich bin im falschen Film.
Josef schrieb:> Er will einen char const** benutzen, damit der String nicht aenderbar> ist.> Wegen der Warnung ist das nicht sinnvoll?
Naja, sagen wir so: Es gibt keine Möglichkeit, das universell zu machen,
so dass man da keinen Cast braucht. Es kann eben auch sein, dass er
einen Zeiger auf non-const char hat und dessen Adresse übergeben will.
Das geht dann aber nicht ohne Cast.
Auf das Wesentliche runtergebrochen:
Bei einer Funktion
1
voidfoo(constchar*p)
kann man auch einen Zeiger auf non-const übergeben, ohne dass das ein
Problem wäre. Aber bei einer Funktion
1
voidfoo(constchar**p)
gibt es die Warnung, wenn man Zeiger auf einen Zeiger auf non-const
übergibt. Und das ist das Problem, und das hat nichts mit einer Zuweisug
eines const char* an einen char* zu tun.
> Die Warnung kommt daher weil die Ausdruecke nicht zuweisungskompatibel> sind. Aber wenn da keine Zuweisung ist, dann gibt es auch keine Warnung.> Problem geloest.>> Ich glaub ich bin im falschen Film.
Nur weil du's nicht verstanden hast, musst du dich nicht über andere
lustig machen. Aber damit du nicht dumm stirbst:
Zuweisungskompatibilität ist nicht nur für Zuweisungen wichtig.
Fakt ist: Bei der Übergabe gibt es beim Aufruf der funktion2 nirgends
eine Zuweisung eines const char* an einen char*. Hier mal der relevante
Teil:
1
voidfunktion2(charconst*start,charconst**ende){
2
3
//...
4
5
chartest2[20];
6
char*ende2;
7
strcpy(test2,test);
8
9
funktion2(test2,&ende2);// warning: passing argument 2 of ‘funktion2’ from incompatible pointer type [-Wincompatible-pointer-types]
Und nun sag mir bitte, wie hier die Zuweisung eines const char* an einen
char* Grund für diese Warnung sein soll.
mh schrieb:> Gibt es eine Möglichkeit ohne cast auszukommen, oder das Problem elegant> zu umgehen?
Es ist vielleicht nicht Elegant, aber man könnte mehrere Funktionen
erstellen. Mit C11 Generics kann man noch soetwas wie Überladung
erzeugen.
Daniel A. schrieb:> Es ist vielleicht nicht Elegant, aber man könnte mehrere Funktionen> erstellen. Mit C11 Generics kann man noch soetwas wie Überladung> erzeugen.
Generics sehen interessant aus und ich habe sie auf meine TODO Liste
gesetzt. Allerdings hab ich auf die Schnelle gefunden, dass die Nutzung
(noch?) compilerabhängig ist.
http://en.cppreference.com/w/c/language/generic
mh schrieb:> Allerdings hab ich auf die Schnelle gefunden, dass die Nutzung> (noch?) compilerabhängig ist
Es steht im C11 Standard, somit ist es nicht compilerabhängig. Der
Compiler muss jedoch den C11 Standard vollständig unterstützen. Bei GCC
kann man den c11 Standard mit dem Parameter --std=c11 aktivieren. Bei
clang müsste das genauso gehen. MSVC hängt glaub ich immernoch irgendwo
zwischen C90/C98 herum...