Home > OS >  How to use a char buffer array as the case in switch-case C ?
How to use a char buffer array as the case in switch-case C ?

Time:11-25

I have a snip of the following code which should read the first 4 objects in a .wav file in order to eventually parse the header of the file. I know I'm doing something wrong here because the buffer always passes "RIFF" without printing out Riff is found

How should I use the Switch-case in order to find the correct array characters?

#include <iostream>
#include <string>
using namespace std;

int main()
{
    
    cout << "Nom: ";
    string filename;
    cout << "First Input filename:" << endl;
    cin >> filename;
#pragma warning (disable : 4996)
    FILE* InFile = fopen(filename.c_str(), "rb");        // Open wave file in read mode

      char Buffer[4];
    while (InFile) {
        fread(Buffer, sizeof Buffer[0], 4, InFile);
        switch (Buffer[4]) {
        case 'R'  'I' 'F' 'F':
            cout << "Riff is found " << endl;

        case 'c'  'r'  'i'  'f' :
            cout << "CRiff is found " << endl;
        }
    }
}

CodePudding user response:

In contrast to languages such as C#, you cannot use strings in a switch expression. You will have to use if...else statements in conjunction with std::memcmp instead:

if      ( std::memcmp( Buffer, "RIFF", 4 ) == 0 )
    std::cout << "Riff is found.\n";
else if ( std::memcmp( Buffer, "crif", 4 ) == 0 )
    std::cout << "CRiff is found.\n";

Note that std::memcmp should only be used if you are sure that all character sequences are of the same length. Otherwise, it is safer to use std::strncmp.

In your posted code, what is actually happening is the following:

The expression 'R' 'I' 'F' 'F' will evaluate to the sum of the individual character codes of these letters. This is not what you want.

CodePudding user response:

you can't compare multiple characters in a single switch.

But given that RIFF uses four character codes you can make it work with a switch statement by reading it as an int value and comparing against a multi-char literal (this is technically implementation-defined, but it'll work in all major compilers (gcc, clang, msvc))

uint32_t fourcc;
while (InFile) {
    fread(&fourcc, 4, 1, InFile);
    switch (fourcc) {
        case 'RIFF':
            std::cout << "Riff is found " << std::endl;
            break;

        case 'crif':
            std::cout << "CRiff is found " << std::endl;
            break;
    }
}

this however is not very portable and will cause problems if you need it to work on architectures with different endianness.

If you want to use a switch but not rely on implementation-defined behaviour you could e.g. use a custom string literal for this:

using fourcc = uint32_t;

struct fourcc_str {
    const char data[4];

    template<std::size_t N>
    consteval fourcc_str(const char (&str)[N]) noexcept : data{str[0], str[1], str[2], str[3]} {
        static_assert(N == 5, "A four character literal needs four characters!");
    }
};

template<fourcc_str str>
consteval fourcc operator "" _fourcc() {
    return std::bit_cast<fourcc>(str.data);
}


// then you can use it like this:
fourcc mark;
while (InFile) {
    fread(&mark, sizeof(fourcc), 1, InFile);
    switch (mark) {
        case "RIFF"_fourcc:
            std::cout << "Riff is found " << std::endl;
            break;

        case "crif"_fourcc:
            std::cout << "CRiff is found " << std::endl;
            break;
    }
}

godbolt example

CodePudding user response:

While fairly ugly you could cast your buffer to an integral type and then switch on that:

switch(*(std::uint32_t*)buffer)
{
case 'RIFF':
/* ... */
}

Note that this will very likely need careful management of endianness (not demonstrated here), either swapping the order of what is read or swapping the order of your case label values.

  •  Tags:  
  • c
  • Related