Hallo liebe C Gemeinde
Ich habe da ein enum i2s_mode_t;
typedef enum {
I2S_MODE_MASTER = 1,
I2S_MODE_SLAVE = 2,
I2S_MODE_TX = 4,
I2S_MODE_RX = 8,
I2S_MODE_DAC_BUILT_IN = 16, /*!< Output I2S data to built-in
DAC, no matter the data format is 16bit or 32 bit, the DAC module will
only take the 8bits from MSB*/
I2S_MODE_ADC_BUILT_IN = 32, /*!< Input I2S data from built-in
ADC, each data can be 12-bit width at most*/
I2S_MODE_PDM = 64,
} i2s_mode_t;
i2s_mode_t I2SMode;
I2SMode = I2S_MODE_MASTER | I2S_MODE_TX; // Das geht nicht !!!!!!!
die Orginal Syntax sieht folgendermassen aus
I2S_Mode = {
.mode = I2S_Mode, // I2S_MODE_MASTER | I2S_MODE_TX,
// Only TX
.sample_rate = SAMPLE_RATE,
.bits_per_sample = I2S_BITS_PER_SAMPLE_16BIT,
//16-bit per channel
.channel_format = I2S_CHANNEL_FMT_RIGHT_LEFT,
//2-channels
.communication_format = I2S_COMM_FORMAT_I2S |
I2S_COMM_FORMAT_I2S_MSB,
.dma_buf_count = 6,
.dma_buf_len = 60,
//
.intr_alloc_flags = ESP_INTR_FLAG_LEVEL1
//Interrupt level 1
};
In Pascal macht man Mehrzuweisungen mit einem Set of
http://wiki.freepascal.org/Set
wie macht man das in C
Das geht deshalb nicht, weil durch die Typdefinition dem Compiler
explizit mitgeteilt wurde, daß I2SMode nur einen der Werte {1, 2, 4, 16,
32, 64} annehmen kann.
I2S_MODE_MASTER | I2S_MODE_TX ist 5 und demnach außerhalb des gültigen
Wertebereichs für diesen Typ.
Mit Pascal kenne ich mich zwar nicht aus, aber dort müsste der Compiler
ebenfalls meckern, wenn man der Variablen einen Wert außerhalb des
spezifizierten "Set of" zuweisen will.
Klar geht das wenn mans richtig hinschreibt oder die Fehlermeldung
liest:
1
$ cat test.c
2
typedef enum {
3
I2S_MODE_MASTER = 1,
4
I2S_MODE_SLAVE = 2,
5
I2S_MODE_TX = 4,
6
I2S_MODE_RX = 8,
7
I2S_MODE_DAC_BUILT_IN = 16, /*!< Output I2S data to built-in DAC, no matter the data format is 16bit or 32 bit, the DAC module will only take the 8bits from MSB*/
8
I2S_MODE_ADC_BUILT_IN = 32, /*!< Input I2S data from built-in ADC, each data can be 12-bit width at most*/
$ gcc -Wall -Wextra -pedantic -std=c99 -c -o test test.c
22
$ file test
23
test: ELF 64-bit LSB relocatable, x86-64, version 1 (SYSV), not stripped
Dass der Wert "zu groß" ist zwar ist formell richtig (aber hier nicht
das Problem), allerdings gibt es meines Wissens keinen C-Cimpiler auf
dieser Welt, der das nicht trotzdem richtig machen würde. Wenn man
standardkonform sein will muss man noch einen Enumerator definieren, in
den alles rein passt (hier also mindestens den Wert 127 hat).
(Die 8 habe ich natürlich vergessen! -> {1, 2, 4, 8, 16,
32, 64})
Eine saubere Lösung wäre ggf., im enum alle in Frage kommenden
Kombinationen zu definieren, also z.B. I2S_MODE_MASTER_TX = 5.
Sonst halt die Variable als standard-Typ deklarieren (z.B. uint8_t) und
enum nur zur Definition der Bitmasken-Werte benutzen.
Hallo und Danke für die Antworten.
Thomas E.
Du hast natürlich recht, deswegen habe ich ja auch den Link von Set of
dazugefügt. Dann haben wir eine Menge und können mehrere Type
Definitionen
auswählen.
Ich kann und will den Source nicht ändern das ist im ESP32 Arduino Core
so definiert. Der Compiler
meckert mit SP32_I2S.ino: 56:10: error: invalid conversion from 'int' to
'i2s_mode_t'
Ich frag mich nun geht das in Standard C ja oder nein.
g457 schrieb:> Dass der Wert "zu groß" ist zwar ist formell richtig
Nach meiner Auffassung ist der Wert nicht "zu groß", sondern schlicht
nicht erlaubt!
Wenn ich schreibe:
1
typedefenum{
2
Montag=1,
3
Dienstag=2,
4
Mittwoch=4,
5
Donnerstag=8,
6
Freitag=16,
7
Samstag=32,
8
Sonntag=64
9
}Wochentag_t;
10
11
Wochentag_tTag;
Und wenn ich dann Tag den Wert 5 zuweise, ist der Compiler in meinen
Augen fehlerhaft, wenn er das erlaubt!
Thomas E. schrieb:> Und wenn ich dann Tag den Wert 5 zuweise, ist der Compiler in meinen> Augen fehlerhaft, wenn er das erlaubt!
Nein, das ist in C explizit erlaubt. Ein "enum i2s_mode_t" ist nur ein
alias für "int", und "I2S_MODE_MASTER" ist nur eine int-Konstante (wobei
C keine echten Konstanten hat, C++ schon).
Das hier:
1
typedefenum{
2
I2S_MODE_MASTER=1,
3
I2S_MODE_SLAVE=2,
4
I2S_MODE_TX=4,
5
I2S_MODE_RX=8,
6
I2S_MODE_DAC_BUILT_IN=16,/*!< Output I2S data to built-in
7
DAC, no matter the data format is 16bit or 32 bit, the DAC module will
8
only take the 8bits from MSB*/
9
I2S_MODE_ADC_BUILT_IN=32,/*!< Input I2S data from built-in
10
ADC, each data can be 12-bit width at most*/
11
I2S_MODE_PDM=64,
12
}i2s_mode_t;
13
14
15
i2s_mode_tI2SMode;
16
17
intmain(){
18
I2SMode=I2S_MODE_MASTER|I2S_MODE_TX;
19
return0;
20
}
Kompiliert im GCC und im Clang auf maximaler Pingeligkeit problemlos. Es
wird lediglich das überflüssige Komma am Ende des enums bemängelt.
Was für einen Compiler nutzt du denn? Kannst du Standard-Konformität
einschalten? Ist das vielleicht eine MISRA-Warnung?
In C++11 kann man mit "enum class" solche Konvertierungen explizit
verhindern; aber ich denke mal darum geht's hier nicht...
Martin M. schrieb:> Hallo und Danke für die Antworten.> Thomas E.> Du hast natürlich recht, deswegen habe ich ja auch den Link von Set of> dazugefügt. Dann haben wir eine Menge und können mehrere Type> Definitionen> auswählen.>> Ich kann und will den Source nicht ändern das ist im ESP32 Arduino Core> so definiert. Der Compiler> meckert mit SP32_I2S.ino: 56:10: error: invalid conversion from 'int' to> 'i2s_mode_t'> Ich frag mich nun geht das in Standard C ja oder nein.
Das Problem ist: es handelt sich nicht um C-Code!
Arduino ist C++ und da werden Typen etwas weniger relaxed behandelt.
probier's mal so:
Thomas E. schrieb:> Und wenn ich dann Tag den Wert 5 zuweise, ist der Compiler in meinen> Augen fehlerhaft, wenn er das erlaubt!
In C sind Enums nicht wirklich typsicher, und ganz allgemein ist
Typsicherheit nicht unbedingt das was C als Sprache auszeichnet. Böse
Zungen würden also sagen (insbesondere wenn sie einen
Pascal-Hintergrund haben) nicht der Compiler ist fehlerhaft, die ganze
Sprache ist ein einziger Fehler.
PS: Ich sehe gerade:
Martin M. schrieb:> Ich kann und will den Source nicht ändern das ist im ESP32 Arduino Core> so definiert. Der Compiler
Arduino nutzt C++, also geht es gar nicht um C. C++ ist hier etwas
strenger. Schreibe einfach:
Bernd K. schrieb:> In C sind Enums nicht wirklich typsicher, und ganz allgemein ist
Ok, aber zumindest meckert der Compiler bei "Wochentag = 5"
Aber auch bei "Wochentag = 4" oder "Wochentag = Montag | Dienstag".
Nur durch Casten kann man die Fehlermeldung verhindern:
"Tag = (Wochentag_t)5;" oder "Tag = (Wochentag_t)(Montag | Dienstag);"
compiliert ohne Fehlermeldung.
Insofern habt ihr Recht, daß der Compiler nicht wirklich auf den
erlaubten Wertebereich prüft, sondern nur auf formale Typendefinitionen.
Martin M. schrieb:> In Pascal macht man Mehrzuweisungen mit einem Set of> http://wiki.freepascal.org/Set
Das ist doch nur Eye-Candy. Letztlich machen diese ganzen
Mengenoperatoren in Pascal nix anderes als die binären logischen
Verknüpfungen in C. Der einzige Vorteil der Mengen ist, dass sie bis zu
256Bit Breite implementiert sind. Wenn die Quelle der Raubkopie aber
C-Code ist, spielt dieser Vorteil keine Rolle...
Und, fast noch wichtiger: man kann das dann auch in Pascal genauso
ausdrücken wie in C, den Eye-Candy-Quatsch der Mengenoperatoren also
einfach komplett außen vor lassen, denn Pascal kennt genau dieselben
binär-logischen Operatoren wie C. Sie heißen halt nur anders...
Dr. Sommer schrieb:> Martin M. schrieb:>> I2S_Mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX);>> Lieber einen named cast nutzen:I2S_Mode => static_cast<i2s_mode_t>(I2S_MODE_MASTER | I2S_MODE_TX);> Dann ist klarer was passiert, und man kann weniger falsch machen.
In einem is2_mode_t diesen Wert zu speichern ist schon etwas, das falsch
gemacht wurde. Wenn man da irgendwelche verODERungen der Enumeratoren
speichern will, muss IS2Mode einfach vom Typ int statt i2s_mode_t sein.
Dann braucht man auch keinen hässlichen Cast.
Rolf M. schrieb:> In einem is2_mode_t diesen Wert zu speichern ist schon etwas, das falsch> gemacht wurde. Wenn man da irgendwelche verODERungen der Enumeratoren> speichern will, muss IS2Mode einfach vom Typ int statt i2s_mode_t sein.
Ansichtssache. Bitfelder in enums zu speichern ist erlaubt und absolut
üblich. Ein Parameter "i2s_mode_t mode" ist deutlich sprechender als ein
"int mode". Einen vorzeichenbehafteten Integer sollte man auch nicht
dafür nehmen. Besser ist natürlich ein "enum class" mit entsprechend
überladenen Operatoren...
Dr. Sommer schrieb:> Ansichtssache. Bitfelder in enums zu speichern ist erlaubt und absolut> üblich.
Erlaubt ist es eben nicht (in C++). Deshalb muss man ja mit einem Cast
den Compiler dazu zwingen, es trotzdem zu akzeptieren.
"I2S_MODE_MASTER | I2S_MODE_TX" ist vom Typ int, daher würde ich auch
das Ergebnis in einem int speichern.
> Ein Parameter "i2s_mode_t mode" ist deutlich sprechender als ein> "int mode".
Dafür könnte man sich ja zur Not noch einen Typedef basteln.
> Einen vorzeichenbehafteten Integer sollte man auch nicht dafür nehmen.
Ok, für Bitgefummel nimmt man besser unsigned. Da gebe ich dir recht.
> Besser ist natürlich ein "enum class" mit entsprechend überladenen> Operatoren...
Ja.
Rolf M. schrieb:> Erlaubt ist es eben nicht (in C++).
Sicher? Wo im Standard steht das?
Rolf M. schrieb:> Warum nicht? Enumeratoren dürfen auch negativ sein.
Darf schon. Das Vorzeichenbit hat aber Sonderfunktionen, die nicht
portabel sind. Wenn man z.B. anfängt damit zu shiften könnte es Probleme
geben. Ob ein 16-bit-int den Wert -32768 darstellen kann ist auch nicht
garantiert; ein 16-bit-"unsigned int" kann aber garantiert alle 16
Bitmasken 1,2,4, ... 32768 ablegen.
Dr. Sommer schrieb:> Rolf M. schrieb:>> Erlaubt ist es eben nicht (in C++).>> Sicher? Wo im Standard steht das?
Naja, grundsätzlich erlaubt ist es per expliziter Konvertierung, solange
der Bereich der enum-Werte nicht verlassen wird. Schön finde ich es aber
nicht. Und dass man dafür einen Cast braucht, zeigt auch für mich schon,
dass es auch nicht so gedacht ist.
c-hater schrieb:> Und, fast noch wichtiger: man kann das dann auch in Pascal genauso> ausdrücken wie in C, den Eye-Candy-Quatsch der Mengenoperatoren also> einfach komplett außen vor lassen
Das ist kein Eye-Candy-Quatsch, das ist Typsicherheit und schön sauber
hinzuschreiben und zu lesen. Wenn ein Pascal-Programm endlich kompiliert
weil alle Typen stimmen und der Compiler nicht mehr meckert kannst Du
davon ausgehen daß es auch funktioniert und das tut was Du willst. Zwar
nicht immer aber ungefähr 4 bis 9 mal wahrscheinlicher als bei C.
> man kann das dann auch in Pascal genauso> ausdrücken wie in C
Lol, niemand würde das ernsthaft wollen. Wozu auch?
Bernd K. schrieb:> Das ist kein Eye-Candy-Quatsch, das ist Typsicherheit und schön sauber> hinzuschreiben und zu lesen. Wenn ein Pascal-Programm endlich kompiliert> weil alle Typen stimmen und der Compiler nicht mehr meckert kannst Du> davon ausgehen daß es auch funktioniert und das tut was Du willst. Zwar> nicht immer aber ungefähr 4 bis 9 mal wahrscheinlicher als bei C.
Bitte WAS? So einen Spruch habe ich das letzte Mal 1989 gehört, und zwar
in einem Anfängerkurs den ich gegeben habe "Aber es compiliert, dann
muss es doch funktionieren".
Wenn du das ernsthaft glaubst, dann ...
Jack schrieb:> Bitte WAS? So einen Spruch habe ich das letzte Mal 1989 gehört, und zwar> in einem Anfängerkurs den ich gegeben habe "Aber es compiliert, dann> muss es doch funktionieren".
Dann besuch mal Kurse zu anderen höheren Programmiersprachen, wie Scala
oder C++. Auch dort wird großer Wert auf Typsicherheit gelegt, da dies
tatsächlich eine Menge Fehler vermeidet. Wenn nicht alles ein "void*"
ist und der Compiler prüfen kann ob man kompatible Typen hat, muss man
das nicht selbst tun. Natürlich bedeutet "kompiliert" hier nicht
"funktioniert", aber die Wahrscheinlichkeit ist einfach größer.
Jack schrieb:> Bitte WAS?
Ja.
Es ist namlich nicht hauptsächlich der Fakt daß zufällig beim
Kompilieren die Typen geprüft werden. Es ist vielmehr die Art wie Du in
so einer Sprache programmierst, wie die Sprache Dir angewöhnt oder Dich
geradezu zwingt Dir von Anfang an eingehende Gedanken und weitreichende
Planung der Datentypen (Datenstrukturen) zu machen so daß Du oft schon
bergeweise ausgeklügelte Typdeklarationen hast die genau zusammenpassen
und den Gegenstand des Problems abbilden bevor Du auch nur eineeinzige Zeile ausführbaren Code geschrieben hast. Und das bisschen
Code das Du dann noch schreibst kann nicht mehr viel anders machen als
die Puzzlestücke in der einzig erlaubten Weise in der sie zusammenpassen
zu verwenden. Oft ist nach dem Ersinnen der Typen der Drops schon
gelutscht und der Rest fügt sich ganz von selbst.
In C kann man einfach drauf los hacken und erst später nachdenken und
das macht den Unterschied.
Martin M. schrieb:> In Pascal macht man Mehrzuweisungen mit einem Set of> http://wiki.freepascal.org/Set>> wie macht man das in C
Gar nicht. C kennt keine Mengentypen. C ist auch schlicht in Bezug auf
enum. In C ist enum nur eine konstante. Es wird implizit wild hin und
her gecastet.
An den Antworten kannst du schön ablesen, wie die Cler ticken. :-(