Forum: PC-Programmierung Compiler warning: operation on ‘x’ may be undefined


von Jürgen W. (lovos)


Lesenswert?

Hallo,
ich habe folgenden code:
1
  char line[0x100];
2
  char *s1=NULL;
3
  int noMan;
4
  ...
5
  if (strncmp(line,s1="Number=",strlen(s1))==0) {
6
    noMan = atoi(line+strlen(s1));
7
  }
8
  else if (strncmp(line,s1="Value=",strlen(s1))==0) {...}
9
  else if (strncmp(line,s1="Pos=",strlen(s1))==0) {...}
Der zwar funktioniert, aber der Compiler (gcc) erzeugt die Warnung:
1
warning: operation on ‘s1’ may be undefined

Warum eigentlich?
Vor dem Aufruf einer Funktion werden die Argumente von links nach rechts 
berechnet. Beim Zweiten Argument ist 's1' bekannt und wird beim 3. 
Argument verwendet.
Die Schreibweise
1
  if (strncmp(line,"Number=",7)==0) {}
halte ich für zu riskant.
Was anderes fällt mir nicht ein, um einen String in einer else-if-Chain 
auf verschiedene Tokens zu überprüfen.

von Peter II (Gast)


Lesenswert?

Jürgen G. schrieb:
> Vor dem Aufruf einer Funktion werden die Argumente von links nach rechts
> berechnet.
wer sagt das?

Man könnte mit defines arbeiten:

#define _STR_NUMBER "Number="

if (strncmp(line,_STR_NUMBER ,strlen(_STR_NUMBER))==0) {
  noMan = atoi(line+strlen(_STR_NUMBER));
}

dürfte auch noch schneller sein, weil der compiler schon strlen 
ausrechnen kann.

von Jürgen W. (lovos)


Lesenswert?

>> Vor dem Aufruf einer Funktion werden die Argumente von links nach rechts
>> berechnet.
>wer sagt das?

In C ist (fast) alles definiert bzgl. Auswertung von Ausdrücken.

Aber das scheint bei "arguments to functions" nicht der Fall zu sein.

http://en.wikipedia.org/wiki/C_%28programming_language%29
Expressions can use a variety of built-in operators (see below) and may 
contain function calls. The order in which arguments to functions and 
operands to most operators are evaluated is unspecified. The evaluations 
may even be interleaved.

PS: defines sind nicht schön. :-)

von Karl H. (kbuchegg)


Lesenswert?

Jürgen G. schrieb:
>>> Vor dem Aufruf einer Funktion werden die Argumente von links nach rechts
>>> berechnet.
>>wer sagt das?
>
> In C ist (fast) alles definiert bzgl. Auswertung von Ausdrücken.

das genaue Gegenteil ist der Fall.
In C ist es fast immer so, dass sich der Compiler die 
Auswertereihenfolge aussuchen kann.
Verwechsle nicht 'Operator precedence' mit Auswertereihenfolge. Das sind 
2 verschiedene Dinge.

In
    a + b * c

ist zwar definiert, dass b mit c multipliziert werden muss und zu diesem 
Zwischenergebnis a dazuaddiert wird. Das sagt aber nichts darüber aus, 
in welcher Reihenfolge die Werte von a, b und c berechnet werden.

    c = foo() + bar()

dem Compiler steht es frei, die Funktionen foo bzw. bar in der 
Reihenfolge aufzurufen, die ihm am besten in den Kram passt.

von Jürgen W. (lovos)


Lesenswert?

> Verwechsle nicht 'Operator precedence' mit Auswertereihenfolge. Das sind
> 2 verschiedene Dinge.

Ich habe mich immer darauf verlassen, dass Ausdrücke wie
1
*p++ != 0 && *q++ != 0
richtig von links nach rechts berechnet wurden.

Bei bestimmten Ausdrücken (wie oben) ist das auch garantiert, siehe

http://en.wikipedia.org/wiki/Sequence_point
 Between evaluation of the left and right operands of the && (logical 
AND), || (logical OR), and comma operators. For example, in the 
expression *p++ != 0 && *q++ != 0, all side effects of the 
sub-expression *p++ != 0 are completed before any attempt to access q.

von Peter II (Gast)


Lesenswert?

hier ein schönes beispiel:
1
#include <stdio.h>
2
3
void test( int a, int b, int c )
4
{
5
   printf("%d, %d, %d \n", a, b, c );
6
}
7
8
int main()
9
{
10
   int i = 0;
11
   test( i++, i++, i++ );
12
};

von Karl H. (kbuchegg)


Lesenswert?

Jürgen G. schrieb:
>> Verwechsle nicht 'Operator precedence' mit Auswertereihenfolge. Das sind
>> 2 verschiedene Dinge.
>
> Ich habe mich immer darauf verlassen, dass Ausdrücke wie
>
1
> *p++ != 0 && *q++ != 0
2
>
> richtig von links nach rechts berechnet wurden.
>
> Bei bestimmten Ausdrücken (wie oben) ist das auch garantiert, siehe
>
> http://en.wikipedia.org/wiki/Sequence_point
>  Between evaluation of the left and right operands of the && (logical
> AND), || (logical OR), and comma operators. For example, in the
> expression *p++ != 0 && *q++ != 0, all side effects of the
> sub-expression *p++ != 0 are completed before any attempt to access q.

Richtig.

Aber in

  ... strncmp( line, s1="Number=", strlen(s1) ) ....

gibt es nur 1 Sequence Point. Und der sitzt unmittelbar vor Aufruf der 
Funktion, nachdem die Funktionsargumente ausgewertet sind. Wie und in 
welcher Reihenfolge die Argumente ausgewertet werden, ist nun mal nicht 
definiert. Der Sequence Point legt nur einen Zeitpunkt fest, zu dem alle 
Aktionen abgeschlossen sein müssen, er sagt aber nichts über 
Reihenfolgen der Aktionen zwischen den Sequence Punkten aus.

Oh. Zusätzlich noch.
Dieses Komma hier, welches Argumente voneinander trennt, ist NICHT der 
Komma-Operator. D.h. der Passus hier ...
> Between evaluation of the left and right operands of the && (logical
> AND), || (logical OR), and comma operators.
                             ****************
... ist nicht anwendbar.

von Klaus W. (mfgkw)


Lesenswert?

Jürgen G. schrieb:
> strncmp(line,s1="Number=",strlen(s1))==0)

du bist dir im Klaren darüber, daß da höchstens ein Zeichen verglichen 
wird (vom Fehler mit der undfinierten Reihenfolge abgesehen)?

Und dann auch noch gerade 1, wenn s1 eh leer ist, es also nichts zu 
vergleichen gibt. Wenn s1 mehr als Zeichen Länge hat, wird nichts 
verglichen.
Wer programmiert sowas eigentlich?

von Stefan E. (sternst)


Lesenswert?

Klaus Wachtler schrieb:
> Und dann auch noch gerade 1, wenn s1 eh leer ist, es also nichts zu
> vergleichen gibt. Wenn s1 mehr als Zeichen Länge hat, wird nichts
> verglichen.

Da hat wohl jemand die Klammer hinter strlen(s1) übersehen. ;-)

von Peter II (Gast)


Lesenswert?

Klaus Wachtler schrieb:
> du bist dir im Klaren darüber, daß da höchstens ein Zeichen verglichen
> wird (vom Fehler mit der undfinierten Reihenfolge abgesehen)?

kannst du mal bitte sagen warum? Wenn in s1 zufällig ein string mit der 
richtige länge drin steht sollte es doch passen oder nicht?

von Klaus W. (mfgkw)


Lesenswert?

Stefan Ernst schrieb:
> Da hat wohl jemand die Klammer hinter strlen(s1) übersehen. ;-)

ok, eingesehen :-(

(wobei ich dann aber noch anmerken möchte, daß es nicht besonders lesbar 
ist, alles ohne Leerzeichen aneinander zu kleben...)

von Mark B. (markbrandis)


Lesenswert?

Jürgen G. schrieb:
1
>   char line[0x100];
2
>   char *s1=NULL;
3
>   int noMan;
4
>   ...
5
>   if (strncmp(line,s1="Number=",strlen(s1))==0) {
6
>     noMan = atoi(line+strlen(s1));
7
>   }
8
>   else if (strncmp(line,s1="Value=",strlen(s1))==0) {...}
9
>   else if (strncmp(line,s1="Pos=",strlen(s1))==0) {...}

Wer so programmiert, gehört doch verprügelt. Warum diese Krankheit, 
möglichst viel in eine Zeile reinquetschen zu wollen? Es ist ein 
Märchen, dass der Code dadurch effizienter wäre.

Und wer sowas schreibt, der benutzt auch sprintf() statt snprintf() und 
macht mit Strings alle möglichen sonstigen Fehler, die potenziell zu 
Buffer Overflows führen.

von Karl H. (kbuchegg)


Lesenswert?

Jürgen G. schrieb:

Das wollte ich in der Früh schon schreiben:

> PS: defines sind nicht schön. :-)

sie sind aber an dieser Stelle das Mittel der Wahl. Compile-Time 
Konstanten direkt im Code zu haben, ist noch schlimmer.
Ok. Man könnte höchstens noch über ein

 const char * const NumberTag = "Number=";

nachdenken, damit man dann auch noch was fürs Auge bzw. für den Debugger 
bietet.

von Sebastian L. (Gast)


Lesenswert?

Hi,

schließe mich auch an das defines nicht schön sind aber dazu gibt es 
doch auch alternativen

Wie wäre es dann mit Konstanten
1
   char *const num="Number=";
2
   char *const val="Value=";
3
   char *const pos="Pos=";
4
5
   char line[0x100];
6
   int noMan;
7
   ...
8
   if (strncmp(line,num,strlen(num))==0) {
9
     noMan = atoi(line+strlen(num));
10
   }
11
   else if (strncmp(line,val,strlen(val))==0) {...}
12
   else if (strncmp(line,pos,strlen(pos))==0

von Vlad T. (vlad_tepesch)


Lesenswert?

Sebastian L. schrieb:
Ich würde lieber folgendes schreiben:
1
   char const num[]="Number=";
2
   char const val[]="Value=";
3
   char const pos[]="Pos=";
weiß aber nicht ob das selbe rauskommt, kann sein, dass der Compiler das 
sowieso optimiert, glaub ich aber eher nicht.

optimiert der compiler strlen() für Konstanten?
so sollte es auf jeden Fall effizient sein:
1
/// string muss ein array (kein Pointer!) sein
2
#define STRLEN(string) (sizeof(string)-1)
3
4
...
5
6
   char line[0x100];
7
   int noMan;
8
   ...
9
   if (strncmp(line,num,STRLEN(num))==0) {
10
     noMan = atoi(line+STRLEN(num));
11
   }
12
   else if (strncmp(line,val,STRLEN(val))==0) {...}
13
   else if (strncmp(line,pos,STRLEN(pos))==0

von Vlad T. (vlad_tepesch)


Lesenswert?

Btw:

Ich glaube nicht, dass der Code das macht, was der Autor im Sinn hat:

Jürgen G. schrieb:
> if (strncmp(line,s1="Number=",strlen(s1))==0) {
>     noMan = atoi(line+strlen(s1));
>   }

wahrscheinlich meiner er eher:
1
  char const num[]="Number=";
2
3
  if (strstr(line,num) == line) {
4
    noMan = atoi(line+STRLEN(num));
5
  }

gibts auch eine "sichere" Variante von strstr?


Edit:
Erklärung:
strncmp gibt nur 0 zurück, wenn beide Strings komplett gleich sind, also 
nicht, wenn nur der Anfang überein stimmt.


Edit Edit:

Ok - vergesst es, es werden ja nur die ersten Zeichen verglichen - 
sollte also doch passen.

von Jürgen W. (lovos)


Lesenswert?

1
char *const num="Number=";
bedeutet: "num" ist ein konstanter Zeiger auf ein Char-Array.
d.h. der Pointer darf nicht verändert werden, aber der Speicher, wo er 
hinzeigt schon.
1
char const * num="Number=";
bedeutet: "num" ist ein Zeiger auf ein konstantes Char-Array.
d.h. der Pointer-Wert darf verändert werden, aber der Speicher, wo er 
hinzeigt, darf nicht verändert werden.

Man könnte auch noch schreiben:
1
const char * const num="Number=";
das wäre dann wirklich "konstant"

von Jürgen W. (lovos)


Lesenswert?

Hier ein kleines Test-Programm zum obigen Beitrag (Unterschied zwischen 
"const *" und "* const").

1
#include <stdio.h>
2
#include <stdlib.h>
3
4
int main() {
5
const char *a = malloc(10);
6
char * const b = malloc(10);
7
const char * const c = malloc(10);
8
9
a=NULL;
10
b=NULL; //error: assignment of read-only variable ‘b’
11
c=NULL; //error: assignment of read-only variable ‘c’
12
13
a[0]=0; //error: assignment of read-only location ‘*a’
14
b[0]=0;
15
c[0]=0; //error: assignment of read-only location ‘*c’
16
}

von Vlad T. (vlad_tepesch)


Lesenswert?

Jürgen G. schrieb:
> Man könnte auch noch schreiben:const char * const num="Number=";
> das wäre dann wirklich "konstant"

man könnte sich auch den Speicher für den Zeiger sparen und es so 
machen, wie ich oben geschrieben habe.
Beitrag "Re: Compiler warning: operation on ‘x’ may be undefined"

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.