Forum: Compiler & IDEs [C] enum Warnungen


von Bauform B. (bauformb)


Lesenswert?

Servus!

Eigentlich wollte ich fragen, ob der gcc vielleicht noch mehr 
enum-Warnungen liefern könnte. Das wäre nett, aber beim minimalen 
Testfall fehlt jetzt eine, die ich wirklich vermisse:
1
#include <stdio.h>
2
#include <time.h>
3
4
typedef enum foo_enum {
5
   FOO_NONE,
6
   FOO_A,
7
   FOO_B
8
} foo_enum;
9
10
int enum_test (foo_enum p1)
11
{
12
   int i32;
13
14
   printf ("%ld %ld\n", sizeof i32, sizeof (time_t));
15
   i32 = time (NULL);
16
   if (p1 == FOO_A) {
17
      p1 = time (NULL);
18
   }
19
   switch (p1) {
20
   case FOO_A:
21
      printf ("A\n");
22
      break;
23
   case 42:
24
      printf ("B\n");
25
      break;
26
   case FOO_NONE:
27
      printf ("Sorry.\n");
28
      break;
29
   }
30
   return 0;
31
}
1
enum_test.c: In function 'enum_test':
2
enum_test.c:16:10: warning: conversion from 'time_t' {aka 'long int'} to 'int' may change value [-Wconversion]
3
   16 |    i32 = time (NULL);
4
      |          ^~~~
5
enum_test.c:20:4: warning: enumeration value 'FOO_B' not handled in switch [-Wswitch]
6
   20 |    switch (p1) {
7
      |    ^~~~~~
8
enum_test.c:24:4: warning: case value '42' not in enumerated type 'foo_enum' [-Wswitch]
9
   24 |    case 42:
10
      |    ^~~~
Zeile 16: reine Routine, Zeile 20 und 24 finde sehr nützlich. Aber Zeile 
18 "p1 = time (NULL);" ist angeblich total ok?

Mein gcc kennt immerhin schon -std=c2x, der ist doch nicht völlig 
veraltet?
1
gcc (Debian 12.2.0-14+deb12u1) 12.2.0
2
Copyright (C) 2022 Free Software Foundation, Inc.
Vor 20 Jahren bekam jemand eine Warnung vom IAR, wie ich sie auch gerne 
hätte:
Beitrag "Enum Warnung"

Edit: na klar, time() von heute = 1772093657, passt noch in int32_t ;)

: Bearbeitet durch User
von Harald K. (kirnbichler)


Lesenswert?

Bauform B. schrieb:
> Aber Zeile
> 18 "p1 = time (NULL);" ist angeblich total ok?

Wie sieht der Prototyp der Funktion time aus? Hast Du mal in time.h 
nachgesehen?

Da dürfte was stehen wie
1
time_t time(time_t *);

Der Funktion einen Nullpointer zu übergeben ist legitim, den 
Rückgabewert einem int zuzuweisen, kann auch vollkommen legitim sein (je 
nachdem, wie in Deinem System time_t definiert ist).

Was erwartest Du?

von Daniel A. (daniel-a)


Lesenswert?

Naja, die Sache ist halt, enum konstanten sind nicht vom sie 
definierenden enum typ, sondern (meistens) ein int. Und wenn man den Typ 
explizit angibt, wird der enum gleich ganz als solchen behandelt...
1
#include <stdio.h>
2
3
int main(){
4
    {
5
      enum a { A };
6
      printf("%s %s %s\n",
7
        _Generic(A, enum a: "enum", int: "int"),
8
        _Generic((enum a)A, enum a: "enum", int: "int"),
9
        _Generic(((enum a)A)+1, enum a: "enum", int: "int")
10
      );
11
    }
12
13
    /*
14
    { // error: '_Generic' specifies two compatible types
15
      enum a : int { A };
16
      printf("%s %s %s\n",
17
        _Generic(A, enum a: "enum", int: "int"),
18
        _Generic((enum a)A, enum a: "enum", int: "int"),
19
        _Generic(((enum a)A)+1, enum a: "enum", int: "int")
20
      );
21
    }
22
    */
23
}
Ausgabe: int enum enum

Also in anderen Worten, bei `enum a x = A;`, is A nicht vom Typ `enum 
a`, darum kann der Compiler da auch den Typ nicht checken.

: Bearbeitet durch User
von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Code mit "g++" statt "gcc" kompilieren und "enum class" statt "enum" 
hinschreiben

von Bauform B. (bauformb)


Lesenswert?

Harald K. schrieb:
> Der Funktion einen Nullpointer zu übergeben ist legitim, den
> Rückgabewert einem int zuzuweisen, kann auch vollkommen legitim sein (je
> nachdem, wie in Deinem System time_t definiert ist).
>
> Was erwartest Du?

Die gleiche Warnung wie für Zeile 16, da funktioniert es doch: 
"conversion from 'time_t' {aka 'long int'} to 'int'..." Da steht 'long 
int' und mein enum sollte wirklich nicht long sein.

OK, die Warnung würde praktisch nicht viel nützen und auf einem System 
mit 64-bit int würde es garnicht auffallen. Aber hier müsste die Warnung 
doch zwangsläufig kommen und das irritiert mich.


Niklas G. schrieb:
> Code mit "g++" statt "gcc" kompilieren und "enum class" statt "enum"
> hinschreiben

Das war ja klar :)  make[1]: g++: No such file or directory;
Später gibt es tatsächlich nützliche neue Meldungen:
1
version.c:3: warning: "_GNU_SOURCE" redefined
2
    3 | #define _GNU_SOURCE
3
      | 
4
<command-line>: note: this is the location of the previous definition
5
6
info_up.c:73:7: error: initializer-string for 'const unsigned char [128]' is too long [-fpermissive]
7
   73 |       "________________________________"
8
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
9
   74 |       "___#_%&_()_+,-._0123456789___=__"
10
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
11
   75 |       "@ABCDEFGHIJKLMNOPQRSTUVWXYZ_____"
12
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
13
   76 |       "_abcdefghijklmnopqrstuvwxyz_____";
14
      |       ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Au weia. Was macht der gcc daraus? Weiß der, dass das nie als String 
benutzt wird optimiert die '\0' am Ende weg?

Aber solche Sachen finde ich doch übertrieben, das macht doch nur 
Arbeit:
1
shm-view.c:200:15: error: invalid conversion from 'void*' to 'req_st*' [-fpermissive]
2
  200 |    req = mmap (0, sizeof (req_st), PROT_READ | PROT_WRITE, MAP_SHARED, fifo, 0);
3
      |          ~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
4
      |               |
5
      |               void*

von Norbert U. (norbert_u)


Lesenswert?

Versuche es mal mit der Option -Wenum-int-mismatch

von Bauform B. (bauformb)


Lesenswert?

Norbert U. schrieb:
> -Wenum-int-mismatch

Das hört sich sehr gut an, danke! Leider ist mein gcc wohl doch zu alt, 
aber das ist vorgemerkt.
1
unrecognized command-line option '-Wenum-int-mismatch';
2
did you mean '-Wargument-mismatch'?

: Bearbeitet durch User
von Markus F. (mfro)


Lesenswert?

in C sind enums typenlos (was leider dazu führt, daß sie auch ziemlich 
wertlos sind).
1
enum obst {APFEL, BIRNE, KIRSCHE, QUITTE};
2
enum tier {HUND, KATZE, MAUS};
3
4
...
5
6
enum obst o = KATZE;  /* führt weder zu einem Fehler noch zu einer Warnung */

also ziemlich witzlos, das ganze.

Schlimmer noch:
1
/* das führt zu Fehlermeldungen: */
2
enum haustier {HUND, KATZE};
3
enum wildtier {MAUS, ELEFANT};

führt (leider) zu Fehlern.

So daß der geneigte C-Enthusiast sich regelmäßig fragt, wozu enums gut 
sein sollen.

: Bearbeitet durch User
von Rolf M. (rmagnus)


Lesenswert?

Bauform B. schrieb:
> Da steht 'long int' und mein enum sollte wirklich nicht long sein.

Warum gibst du die Größe hier nicht einfach mit aus?
> printf ("%ld %ld\n", sizeof i32, sizeof (time_t));
(übrigens: der korrekte Format-Specifier für size_t ist %zu)

aber ja, der enum sollte die gleiche Größe wie int haben.

Bauform B. schrieb:
> Au weia. Was macht der gcc daraus? Weiß der, dass das nie als String
> benutzt wird optimiert die '\0' am Ende weg?

Er "optimiert" nicht, sondern es ist in C so, dass in so einem Fall kein 
\0 angehängt wird, also:
1
char x[6] = "Hallo"; // hängt \0 an
2
char y[5] = "Hallo"; // erlaubt, hängt aber kein(!) \0 an
3
char z[4] = "Hallo"; // Fehler, zu wenig Platz
In C++ ist das anders geregelt. Da wird das \0 immer angehängt, d.h. der 
zweite Fall ist dort auch ein Fehler, weil zu wenig Platz.

Bauform B. schrieb:
> Aber solche Sachen finde ich doch übertrieben, das macht doch nur
> Arbeit:

Naja, C++ ist da strikter und lässt zwar Konvertierungen von Zeigern 
nach void* ohne Cast zu, aber von void* in was anderes muss man explizit 
casten. Allerdings braucht man void* in C++ auch nicht so oft, wenn man 
es richtig macht.

von Niklas G. (erlkoenig) Benutzerseite


Lesenswert?

Bauform B. schrieb:
> make[1]: g++: No such file or directory;

Kurioses Setup, lässt sich aber trivial beheben!

Bauform B. schrieb:
> Au weia. Was macht der gcc daraus?

Schalt mal -Wextra ein:
1
warning: initializer-string for array of 'unsigned char' truncates NUL terminator but destination lacks 'nonstring' attribute (129 chars into 128 available) [-Wunterminated-string-initialization]

0-Byte wird also weggelassen weil das Array zu klein ist.

Bauform B. schrieb:
> Aber solche Sachen finde ich doch übertrieben, das macht doch nur
> Arbeit:

Das ist Typsicherheit... void* sollte man halt so wenig wie möglich 
verwenden. In C ist es nicht vermeidbar, in C++ größtenteils schon. 
Einen Cast hinzuzufügen, auch als Warnung an den Leser, dass hier etwas 
potenziell gefährliches passiert, schadet nicht.

Markus F. schrieb:
> in C sind enums typenlos

Sie sind nicht typenlos, sie sind Integer.

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.