Compiler warning: operation on ‘x’ may be undefined

OP #2756635
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.
Gast #2756666
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.
OP #2756693
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. :-)
#2756706
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.
OP #2756722
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.
#2756758
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.
#2756768
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?
#2756984
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.
#2756988
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.
Gast #2757293
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
#2757313
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
#2757318
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.
OP #2757434
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"
OP #2757473
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
}

Antwort schreiben

Bitte melde dich an, um einen Beitrag zu schreiben.

oder

Mit Google-Account einloggen

Die Registrierung ist kostenlos und dauert nur eine Minute.

Jetzt registrieren