something unclear about switch-case - c

Something about switch-case statement is very unclear for me. First of all I could understand the case part as an if statement, but what dose switch do itself? I mean when we pass argument to switch, what happens there?
c=getchar();
switch(c)
2: Why there is no need to put ; after switch? What kind of the function is it?
3: How compilers implement switch-case statement at the first time? (just C or use assembly)
4: We can't use switch-case like this:
switch(string)
{
case "aaaa":
...
case "bbbb":
...
we use if-strcmp instead. Is it possible to create switch-case like statement for string condition? I see some library like getopt use struct to handle command line string arguments:
{"help",...,'h'},
{"version",...,'v'}
...
but I want to use string directly.

first of all I could understand "case" part as a "if" statement, but what dose switch do itself? I mean when we pass argument to switch, what happens there?
In case, there's always a constant. You need to "compare" something with this constant. And switch(a) means - "compare a with:" and it "compares" it to each case statement.
2: why there is no need to put ";" after switch? what kind of the function is it?
It's not a function. You're not calling switch function, you're just starting to write a switch statement.
3: how compilers implement switch-case statement at the first time? (just C or use assembly)
That's platform specific. Sometimes, it's implemented using jumps (asm) and offsets.
4: we can't use switch-case like this:
Because switch works only with integral types (that is int, enum, char, etc. and not double, strings, etc.)

The switch statement begins the scope of the cases. Scopes in C/C++ are always bound by curly braces ( {} ), specifying the scope of local variables (on the stack) which is used to hold the switch parameter. It hence doesn't need a ; since it is grammatically much like a while loop.
Compilers implement switchs as direct jump in assembly. This is what makes them really more efficient than ifs (which are mostly compiled as different comparisons in assembly). This is also the reason why you can't use a switch on text. The value of a char* is only a pointer, hence cannot be used to correctly identify the target section to execute.

To use strings in switch case() in c what you can do is, to store the possible strings of switch in a string as
char * switchstr[]={"abc", "def", "ghi"};
then search for the input string in the switchstr[] and use the index of matching string as switch case parameter.
And to know why strings are not supported in switch case read this answer. IT has discussion about why string cant be used directly in switch statement in C/C++.
And also switch case checks for equality and we know in C it is not possible to check the equality of string directly by using = sign, instead we need to use strcmp() function.

first of all I could understand "case" part as a "if" statement, but what dose switch do itself?
The switch part sets the expression that is used to compare with the values in the associated case labels.
2: why there is no need to put ";" after switch? what kind of the function is it?
It is not a function; switch is a language keyword that is part of the switch statement (i.e. the whole switch/case construct). It is not a function just because it uses parentheses any more than while(...) or if(...) are functions.
3: how compilers implement switch-case statement at the first time? (just C or use assembly)
That is up to the compiler. How things are implemented is generally not within the purview of a language standard, only (as precisely as possible) what should happen.
In practice, a switch statements tend to be implemented with branches if there are only a few cases, and with a jump table if there are many.
4: we can't use switch-case [with strings]
Because C defines the switch statement as working with integral types. A string is not an integral type. To provide the kind of functionality you envisage would require adding (at least) “content equality” testing for strings to the core C language. To do this consistently in a way that does not break existing code is probably impossible and contrary to the “minimalist” philosophy of C.

Related

Don't allow nested switches in GCC

I'm using coroutines in C using several macros to simplify writing code. As it is known, these macros encapsulate the code inside a big switch statement, and use case __LINE__ to know where to return. The problem is that using a switch inside a coroutine will make it fail, because the case __LINE__ will belong to the inner switch, and not the outer one.
Is it possible, by passing a parameter to GCC, to "disable" nested switch support, thus ensuring that putting a switch inside another switch will return an error or, at least, a warning?
Is it possible, by passing a parameter to GCC, to "disable" nested switch support, thus ensuring that putting a switch inside another switch will return an error or, at least, a warning?
No, gcc does not have support for this.

Why did the creators of C decide to support one statement while loops without braces?

I am reading The C Programming Language (K&R) and noticed C allows the use of while loops preceding a single statement to function without any braces; why did the creators of C decide to support this? I presume this introduces some extra complexity for the compiler, is the desire for single statement while loops so common (for readability, perhaps?) it was worth whatever trade-off was required to allow them?
It doesn't add any special complexity to the compiler, and it's not just while loops. All of the control structures (if, for, while, etc.) govern a "statement", where a block is just a special case of a statement (called a "compound-statement") containing 0 or more declarations or statements. There isn't any specific use case or rationale for applying this rule to while, but none is really needed, other than maybe simplicity or consistency.

C macro or GCC directive to consume next code statement

Is there a C macro, a GCC directive or pragma, to consume the next code statement following the macro, without explicitly passing it as an argument to the macro?
Something like this:
#define CONSUME_NEXT_IF(a) if (a) { (<next>) } else;
And I would use it as:
CONSUME_NEXT_IF(a) stmt1;
And expect it to expand to:
if (a) stmt1;
else;
I am using an if statement here just as an example. The conditional statement isn't the point, rather the ability to consume stmt1 by the macro without actually passing it as an argument.
#define CONSUME_NEXT_IF(a) if (!(a)) {} else
will achieve the effect of only executing the "next statement" (between use of the macro and next ;) if a is true (or non-zero). If you have suitable constraints on what type of expression a is, you might be able to remove the () on (a).
Personally, although you've explained in comments that you want a similar effect to annotations, I consider this will introduce more maintenance concerns - including code obfuscation - than it alleviates. Particularly if it interacts with other macros being used in a or stmt1.
And, of course, it would be necessary to modify your "large code base" to use the macro.
This also leaves dead code in your executable - it doesn't stop code for stmt1 being emitted to the executable (unless a has a fixed compile-time value, and your compiler has capability to detect and optimise code in such circumstances). Therefore such a construct will mean you cannot satisfy requirements of several assurance standards that require prevention of dead code.

In C99, can I use a return value without first assigning it to a variable?

I am replacing macros in a large C99 code base with inline functions to see if the compiler can do a better job optimizing. There are a lot of macros which expand to a data structure. The code uses them not as functions, but as a replacement for the data structure itself.
For example...
#define AvARRAY(av) ((av)->sv_u.svu_array)
AvARRAY(av)[--key] = &PL_sv_undef;
Not only is there a lot of code which does this, published code outside of my control does this, so I would rather leave this idiom in place if possible.
Is there a way to define an inline function version of AvARRAY which is compatible with the sort of code above?
Yes, you can use the return value without assigning it to a variable.
However, in general case it is not possible to replace this macro with a function. The whole point of using macros in for this purpose is that macros can "evaluate" to lvalues. Functions in C cannot produce lvalues, unfortunately. In other words, no, in general case you can't directly replace such macros with a functions.
But in specific case it could be different. Do they really use those macros as lvalues? In your specific example it is not used as an lvalue, so in your case you can just do
inline whatever_type_it_has *AvARRAY(struct_type *av)
{
return av->sv_u.svu_array;
}
and use it later exactly as it is used in your example
AvARRAY(av)[--key] = &PL_sv_undef;
But if somewhere else in the code you have something like
AvARRAY(av) = malloc(some_size);
or
whatever_type_it_has **pptr = &AvARRAY(av);
then you'll be out of luck. The function version will not work, while the original macro will.
The only way to [almost] fully "emulate" the functionality of macro with a function in this case is to lift it to a higher level of indirection, i.e. assume that the function always returns a pointer to the target data field
inline whatever_type_it_has **AvARRAY_P(struct_type *av)
{
return &av->sv_u.svu_array;
}
In that case you will have to remember to dereference that pointer every time
(*(AvARRAY_P(av))[--key] = &PL_sv_undef;
but this will work
*AvARRAY_P(av) = malloc(some_size);
whatever_type_it_has **pptr = &*AvARRAY_P(av);
But this will not work with bit-fields, while the macro version will.

C Switch-case curly braces after every case

In a C switch-case flow control, it's required to put curly braces { } after a case if variables are being defined in that block.
Is it bad practice to put curly braces after every case, regardless of variable declaration?
For example:
switch(i) {
case 1: {
int j = 4;
...code...
} break;
case 2: { //No variable being declared! Brace OK?
...code...
} break;
}
It's certainly not invalid to use braces in every case block, and it's not necessarily bad style either. If you have some case blocks with braces due to variable declarations, adding braces to the others can make the coding style more consistent.
That being said, it's probably not a good idea to declare variables inside case blocks in straight C. While that might be allowed by your compiler, there's probably a cleaner solution. Mutually-exclusive case blocks may be able to share several common temporary variables, or you may find that your case blocks would work better as helper functions.
Braces may be used in every case statement without any speed penalty, due to the way compilers optimize code. So it's just the style and the preference of the coder.
The most preferred usage is not using braces, though the usage of them in every case during an active development may be found easier to make some additions on the code every now and then.
It's just the easthetics; because a 'case' statement doesn't need only a single command, but will walk through the code as it works as a label. So blocks are not needed, and are not invalid.
In 'case's with variables; braces are used just-in-case, to create contexts for variables, and it makes big sense to use them. Some compilers on different platforms show different behaviours if they are not included.
I consider it bad style to use braces in each case. Cases are labels in C, akin to goto labels. And in the current C language, you're free to declare variables in each case (or anywhere you like) without introducing new blocks, though some people (myself included) also consider that bad style.
Generally it is bad practice jump over the initialization of a variable, be it with goto or switch. This is what happens when you don't have the the blocks per case.
There is even a case in C99 where jumping over the initialization is illegal, namely variable length arrays. They must be "constructed" similarly as non-PODs in C++, their initialization is necessary for the access of the variable later. So in this case you must use the block statement.
Just to add a minor point many editors & IDEs allow blocks to be collapsed and/or auto indented and several allow you to jump to the matching brace - I personally don't know of any that allow you to jump from a break to the matching case statement.
When debugging, or re-factoring, other peoples, (or even your own after a few months), code that contains complex case statements the ability to both collapse sections of the code and to jump to matching cases is invaluable, especially if the code contains indentation variations.
That said it is almost always good advice to avoid complex case statements like the plague.

Resources