Forum: Compiler & IDEs ((long*)mem)++; -> lvalue required as increment operand


von Paul Roßmann (Gast)


Lesenswert?

Guten Tag,
ich habe eine Funktion der eine Zeiger (mem) übergeben wird. In der 
Funktion wird der Zeiger inkrementiert.
1
static int mem_to_hexbuf (const void* mem, char* hbuf, int len){
2
...
3
  ((const long*)mem)++;
4
...
5
}
Die Zeile mit der Incrementierung liefert folgenden Compilerfehler:

   error: lvalue required as increment operand

Das verstehe ich nicht.
Ich hätte erwartet das ((const long*)mem) vom Typ const long* ist, den 
ich mit ++ incrementieren können sollte, oder?

Wenn ich die eine Zeile durch folgenden Codeausschnitt ersetze, gibt es 
keine Compilerfehler mehr:
1
  const long *a;
2
  a = ((const long*)mem);
3
  a++;
4
  mem = (void*)a;

Was mache ich falsch? Macht der 3-Zeiler das was ich möchte (auf den 
nächsten const long zeigen)?
Schon mal vielen Dank für hilfreiche Hinweise,
Paul Roßmann
ps.: als Compiler verwende ich den arm-none-eabi-gcc 4.7.3 20121207 
(release)

von Karl H. (kbuchegg)


Lesenswert?

Paul Roßmann schrieb:

> Das verstehe ich nicht.
> Ich hätte erwartet das ((const long*)mem) vom Typ const long* ist,

Ist es auch.
Aber:
Das ist ein Ausdruck. Da entsteht also ein temporäres Zwischenergebnis. 
Nämlich ein Pointer der vom Typ const long* ist.
Dein Code versucht jetzt genau dieses temporäre Zwischenergebnis zu 
inkrementieren.
Das geht zwar im Prinzip, ist aber sicher nicht das was du willst. Denn 
ein temporäres Zwischenergebnis zu inkrementieren, welches nach 
Beendigung der kompletten Anweisung (beim nächsten Sequence Point) 
wieder ins Nirvana verschwindet, macht in den meisten Fällen keinen 
Sinn.

> Wenn ich die eine Zeile durch folgenden Codeausschnitt ersetze, gibt es
> keine Compilerfehler mehr:
>
1
>   const long *a;
2
>   a = ((const long*)mem);
3
>   a++;
4
>   mem = (void*)a;
5
>

logisch.
Jetzt wird ja auch kein Zwischenergebnis mehr inkrementiert, sondern 
eine tatsächlich real existierende Variable.


Dein ursprünglicher Code ist eine Variante von
1
   int i = 5, j = 8;
2
3
   ( i - j ) ++;

i - j ist ein arithmetischer Ausdruck, der ein Ergebnis hat. Dieses 
Ergebnis kann man nicht inkrementieren. Es existiert ja nicht real im 
Speicher.
In diesem Sinne ist auch der Cast nichts anderes als diese Subtraktion 
hier: Ein Operator, der ein Argument nimmt und daraus ein Ergebnis 
erzeugt. Bei einem Cast bleibt (in diesem Fall) dann zwar der Zahlenwert 
konstant, aber der Datentyp ändert sich. Aber es ist trotzdem ein 
Operator, der aus einem Argument ein Ergebnis macht. Und dieses Ergebnis 
ist temporär.

von Rolf Magnus (Gast)


Lesenswert?

Paul Roßmann schrieb:
>    error: lvalue required as increment operand
>
> Das verstehe ich nicht.
> Ich hätte erwartet das ((const long*)mem) vom Typ const long* ist, den
> ich mit ++ incrementieren können sollte, oder?

Ersteres ist richtig. Aber das Ergebnis eines Casts ist eben kein 
lvalue. Ändern kann man aber nur lvalues. Wie sollte es auch anders 
gehen? Hier wird der Wert aus mem gelesen und dieser dann in einen const 
long* konvertiert. Es wäre jetzt wenig sinnvoll, diesen temporären Wert 
zu erhöhen, weil er nach der Zeile sowieso aufhört zu existieren. Auf 
mem hätte es so oder so keinen Einfluss.

> Wenn ich die eine Zeile durch folgenden Codeausschnitt ersetze, gibt es
> keine Compilerfehler mehr:  const long *a;
>   a = ((const long*)mem);
>   a++;
>   mem = (void*)a;
>
> Was mache ich falsch?

Hier versuchst du nicht, das Ergebnis des Casts zu inkrementieren, 
sondern stattdessen, es in eine Variable zu schreiben und dann diese zu 
inkrementieren. Das geht.

von Paul Roßmann (Gast)


Lesenswert?

Vielen Dank! Wieder was gelernt.
Um mir den 3-Zeiler und die explizit deklarierte Hilfsvariable zu sparen 
werden ich dann folgendes verwenden:
1
mem = (const void*)(((const long*)mem)+1);

Viele Grüße
Paul Roßmann

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Paul Roßmann schrieb:
> Um mir den 3-Zeiler und die explizit deklarierte Hilfsvariable zu sparen

Was sich die Leute nicht alles für schräges Zeug ausdenken.

Leut's, Hilfsvariablen kosten nichts.  Wenn der Compiler sie
wegoptimieren kann, macht er das ohnehin.  Dafür kann man danach
wenigstens auch lesen, was du da eigentlich anstellen wolltest.

von Mark B. (markbrandis)


Lesenswert?

Jörg Wunsch schrieb:
> Was sich die Leute nicht alles für schräges Zeug ausdenken.
>
> Leut's, Hilfsvariablen kosten nichts.  Wenn der Compiler sie
> wegoptimieren kann, macht er das ohnehin.  Dafür kann man danach
> wenigstens auch lesen, was du da eigentlich anstellen wolltest.

Volle Zustimmung.

Wieso glauben so viele Leute fälschlicherweise, dass Code besser würde, 
wenn man so viel wie möglich davon in eine Zeile packt? :-(

von Klaus F. (kfalser)


Lesenswert?

Mark Brandis schrieb:
> Wieso glauben so viele Leute fälschlicherweise, dass Code besser würde,
> wenn man so viel wie möglich davon in eine Zeile packt? :-(

Weil der C Compiler 1 Zeile pro Microsekunde ausführt ;-)

von Paul Roßmann (Gast)


Lesenswert?

Tatsächlich ist mir bewusst das
a) der Compiler optimiert was er kann
b) Lesbarkeit wichtig ist
c) man hier (im Forum) schnell und gut geholfen wird
d) man schreiben würde was geschrieben wurde

Ich hatte überlegt (nachdem der Einzeiler dann da stand) wieder die 
längere Variante zu nehmen, mich aber dagegen entscheiden, weil ich 
weder die eine noch die andere Variante besser lesbar finde.

Was mich noch interessieren würde ist ob auch
1
mem += sizeof(long)
 funktionieren würde. Das fände ich tatsächlich gut lesbar, einfach, 
kurz. Nur habe ich den Verdacht das das nicht immer funktioniert.

Viele Grüße
Paul Roßmann

von Masl (Gast)


Lesenswert?

Ohne Testen würde ich sagen: klappt nicht.
Angenommen, sizeof(long) liefert 4.
Dann würde der Zeiger um 4 * sizeof(Zeigertyp) erhöht werden. mem zeigt 
aber auf void.
Was ist sizeof(void)?

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Paul Roßmann schrieb:

> Was mich noch interessieren würde ist ob auch
> mem += sizeof(long)
> funktionieren würde. Das fände ich
> tatsächlich gut lesbar, einfach, kurz.

Nur als GCC-Erweiterung:

http://gcc.gnu.org/onlinedocs/gcc/Pointer-Arith.html#Pointer-Arith

von Karl H. (kbuchegg)


Lesenswert?

Der rudimentäre Code
1
static int mem_to_hexbuf (const void* mem, char* hbuf, int len){
2
...
3
  ((const long*)mem)++;
4
...
5
}
lässt mich annehmen, dass der Pointer innerhalb der Funktion nicht nur 
dazu dient um erhöht zu werden.
D.h. es wird sowieso sinnvoll sein, den void Pointer sofort auf einen 
lokalen uint8_t Pointer umzukopieren um die Funktion von Casts zu 
befreien.

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.