[C] enum Warnungen

OP #8015723
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
#8015732
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?

#8015740
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
OP #8015775
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*
#8015807
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
#8015835
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.

Persönliche Seite #8015847
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.

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