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
Martin M. schrieb: > I2SMode = I2S_MODE_MASTER | I2S_MODE_TX; // Das geht nicht !!!!!!! Warum soll das nicht gehen?
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.
:
Bearbeitet durch User
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*/ |
9 | I2S_MODE_PDM = 64, |
10 | } i2s_mode_t; |
11 | |
12 | |
13 | i2s_mode_t I2SMode = I2S_MODE_MASTER | I2S_MODE_TX; // <- desch gehd |
14 | |
15 | |
16 | void f() |
17 | { |
18 | I2SMode = I2S_MODE_MASTER | I2S_MODE_TX; // <- desch gehd ahh |
19 | } |
20 | |
21 | $ 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.
Welcher C-Compiler gibt denn als Fehlermeldung "Das geht nicht!" aus? Solange Compiler-Aufruf und Ausgabe geheim bleiben, glaub ich gar nichts.
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 | typedef enum { |
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_t Tag; |
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 | typedef enum { |
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_t I2SMode; |
16 | |
17 | int main () { |
18 | I2SMode = I2S_MODE_MASTER | I2S_MODE_TX; |
19 | return 0; |
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:
1 | I2SMode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX ); |
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:
1 | I2SMode = static_cast<i2s_mode_t> (I2S_MODE_MASTER | I2S_MODE_TX); |
Da es sowieso C++ ist geht auch sowas:
1 | #include <cstdint> |
2 | |
3 | using i2s_mode_t = uint8_t; |
4 | static constexpr uint8_t I2S_MODE_MASTER = 1; |
5 | static constexpr uint8_t I2S_MODE_SLAVE = 2; |
6 | static constexpr uint8_t I2S_MODE_TX = 4; |
7 | static constexpr uint8_t I2S_MODE_RX = 8; |
8 | static constexpr uint8_t I2S_MODE_DAC_BUILT_IN = 16; |
9 | static constexpr uint8_t I2S_MODE_ADC_BUILT_IN = 32; |
10 | static constexpr uint8_t I2S_MODE_PDM = 64; |
11 | |
12 | i2s_mode_t I2SMode; |
13 | |
14 | int main () { |
15 | I2SMode= I2S_MODE_MASTER | I2S_MODE_TX; |
16 | return 0; |
17 | }
|
Oder noch besser:
1 | #include <type_traits> |
2 | #include <cstdint> |
3 | |
4 | enum class i2s_mode_t : uint8_t { |
5 | NONE = 0, |
6 | MASTER = 1, |
7 | SLAVE = 2, |
8 | TX = 4, |
9 | RX = 8, |
10 | DAC_BUILT_IN = 16, /*!< Output I2S data to built-in |
11 | DAC, no matter the data format is 16bit or 32 bit, the DAC module will
|
12 | only take the 8bits from MSB*/
|
13 | ADC_BUILT_IN = 32, /*!< Input I2S data from built-in |
14 | ADC, each data can be 12-bit width at most*/
|
15 | PDM = 64, |
16 | };
|
17 | |
18 | inline i2s_mode_t operator | (i2s_mode_t lhs, i2s_mode_t rhs) { |
19 | return static_cast<i2s_mode_t> (static_cast<std::underlying_type_t<i2s_mode_t>> (lhs) | static_cast<std::underlying_type_t<i2s_mode_t>> (rhs)); |
20 | }
|
21 | |
22 | inline i2s_mode_t operator & (i2s_mode_t lhs, i2s_mode_t rhs) { |
23 | return static_cast<i2s_mode_t> (static_cast<std::underlying_type_t<i2s_mode_t>> (lhs) & static_cast<std::underlying_type_t<i2s_mode_t>> (rhs)); |
24 | }
|
25 | |
26 | int main () { |
27 | i2s_mode_t I2SMode= i2s_mode_t::MASTER | i2s_mode_t::TX; |
28 | |
29 | // ...
|
30 | |
31 | if ((I2SMode & i2s_mode_t::TX) != i2s_mode_t::NONE) { |
32 | // Transmitter einschalten ...
|
33 | }
|
34 | }
|
Warum überhaupt die "typedef enum"-Konstruktion? Dies ist nur in C nötig, nicht in C++.
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.
:
Bearbeitet durch User
Habe eine Lösung gefunden So geht es I2S_Mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX); Danke für eure Hilfe!!!
Martin M. schrieb: > I2S_Mode = (i2s_mode_t)(I2S_MODE_MASTER | I2S_MODE_TX); Lieber einen named cast nutzen:
1 | I2S_Mode = static_cast<i2s_mode_t>(I2S_MODE_MASTER | I2S_MODE_TX); |
Dann ist klarer was passiert, und man kann weniger falsch machen. https://stackoverflow.com/a/1609185
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.
:
Bearbeitet durch User
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?
:
Bearbeitet durch User
Martin M. schrieb: > die Orginal Syntax sieht folgendermassen aus > > I2S_Mode = { > .mode = I2S_Mode, Das geht natürlich nicht...
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 eine einzige 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.
:
Bearbeitet durch User
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. :-(
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
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.