Better solution than if-else if? - c

I have to do some things based on the value of a big number like 83025 (which is >65535). For this I can't use switch-case since it uses only integral arguments of a max value of 255. (Or at least this is how I know. I still tried however and it compiled but the switch-case did not work well.)
So I thought I would make an if-else if ladder like this below but it seems not too elegant.
if ((refnum == 32120) ||
(refnum == 32075)) {
else if (refnum == 51036) {
else if ((refnum == 61024) ||
(refnum == 61060)) {
else if ((refnum == 71030) ||
(refnum == 71048)) {
else if ((refnum == 72012) ||
(refnum == 72024) ||
(refnum == 72048)) {
else if ((refnum == 81025) ||
(refnum == 81050) ||
(refnum == 81100)) {
else if ((refnum == 82012) ||
(refnum == 82024) ||
(refnum == 82048) ||
(refnum == 82096)) {
else if ((refnum == 83050) ||
(refnum == 83100)) {
Can you confirm that this is the proper way to do this? Or do you have a better idea?
Additional info:
refnum is a 32bit unsigned integer
the big number comes from the middle of a string and strtol converts it to a DWORD
the things i have to do in each case is to perform a strcpy and then return a certain value.
the code is embedded and runs on a 16 bit microcontroller

have to do some things based on the value of a big number like 83025
Then make sure that all variables involved use uint32_t.
For this I can't use switch-case since it uses only integral arguments of a max value of 255
This is a misunderstanding, not sure where you got this idea from. The switch statement works on all integer constant expressions. There's no numerical limits in the switch statement itself.
(In fact the controlling expression of a switch statement is implicitly promoted to type int, if it happens to be a smaller integer type.)
So I thought I would make an if-else if ladder like this below but it seems not too elegant. Can you confirm that this is the proper way to do this? Or do you have a better idea?
The if-else will likley yield the very same machine code as a corresponding switch. The switch might increase readability a bit and is therefore perhaps a better choice:
switch (refnum)
{
case 32120: do_this(); break;
case 61024: do_that(); break;
...
default: do_something();
}
Alternative:
I notice that these are integer values in sorted order. If there are lots of values or if fast look-up is required, you could also replace the whole thing with a binary search. This will possibly give faster code but also increase complexity. Preferably, use C standard bsearch().
However, if what you wish to achieve in the end is to return a pointer to a string, this solution might be ideal. You could then store the numbers and strings as key-value pairs:
typedef struct
{
uint32_t key;
const char* str;
} thingie_t;
static const thingie_t things [] =
{
{ 32120, "text string" },
{ 32075, "another text string" },
...
};
the big number comes from the middle of a string and strtol converts it to a DWORD
Why are you using signed numbers? The data does not seem to be signed. What is DWORD? That's some smelly type from Windows programming, which should definitely be avoided in embedded systems. Use the types from stdint.h, not some ugly, home-made types.

I have to do some things based on the value of a big number like 83025 (which is >65535). For this I can't use switch-case since it uses only integral arguments of a max value of 255. (Or at least this is how I know).
Your understanding is incorrect: here is the wording of the C Standard:
6.8.4.2 The switch statement
The controlling expression of a switch statement shall have integer type.
[...] The expression of each case label shall be an integer constant expression and no two of the case constant expressions in the same switch statement shall have the same value after conversion. [...]
The integer promotions are performed on the controlling expression. The constant expression in each case label is converted to the promoted type of the controlling expression. If a converted value matches that of the promoted controlling expression, control jumps to the statement following the matched case label. [...]
Thus there is no limit at 65535 for the case values in general, the maximum value is at least 18446744073709551615 if the switch expression has type unsigned long long. If your switch expression is an unsigned int and your target platform had 16-bit ints, then the maximum value for a case expression would be 65535 but given the values you want to test, refnum's type must be a larger than that.
I still tried however and it compiled but the switch-case did not work well.
You did not post the offending code... Unless your compiler is ancient or seriously broken, the problem is not where you suspect, more likely a bug in your code.
EDIT from the extra information provided, the target platform indeed has 16-bit int, but refnum must be larger than int or unsigned int to accommodate for values larger than 65535, either a long or an unsigned long. The compiler should then accept case values larger than 65535. Ancient compilers may not be compliant with this... in this case, you will probably have many more problems to deal with.

Switch statement can do the job for you in this case.
switch (refnum) {
case 0:
case 1:
//Do stuff when refnum is 0 or 1
break;
case 2:
//Do stuff when refnum is 2
break;
case 36371:
case 36372:
case 36373:
case 36374:
// if (refnum == 36371 || refnum == 36372 || refnum == 36373 || refnum == 36374)
break;
default: break;
}
Beauty is that you can apply multiple case statements (like case 0 and case 1 in my case) which acts like or in your if statement.
GCC extension also allows you to write your switch this way:
switch (refnum) {
case 36371 ... 36374:
//Do the job when refnum is >= 36371 && refnum <= 36374
break;
}
But remember! This is not available in all compilers.

You appear to have a very restrictive non-standard compiler.
You could create a function for each action then generate a lookup-table of refnum to function-pointer and scan that. For example:
int action1( unsigned long refnum ) ;
int action2( unsigned long refnum ) ;
int action3( unsigned long refnum ) ;
int action4( unsigned long refnum ) ;
int action5( unsigned long refnum ) ;
...
int action8( (unsigned long ) ;
int doRefnumAction( unsigned long refnum )
{
typedef void(*refnumFn)(unsigned long ) ;
static const struct
{
unsigned long refnum,
refnumFn refnum_action
} refnum_lookup[] = { {32120, action1}, {32075, action1},
{51036, action2},
...
{82012, action7}, {82024, action7}, {82048, action7}, {82096, , action7},
{83050, action8}, {83100, action8} } ;
// Find refnum in lookup and call action...
for( int i = 0 ;
i < sizeof(refnum_lookup) / sizeof(*refnum_lookup) ;
i++ )
{
if( refnum == refnum_lookup[i].refnum )
{
return refnum_lookup[i].refnum_action( refnum ) ;
}
}
}
Whether that is better in any way that the if-else if solution is perhaps a matter of opinion, probably larger in code-space terms, and slower in execution but arguably more easily maintained.

Once we know details of your compiler and your real code, we can find out why you feel you cannot use a switch statement. Until then, everyone is just guessing.
I have used a large number of C compilers over the years, and never heard of one that restricts switches in this way. It sounds like the kind of pseudo-C compilers that existed for 8-bit microcontrollers in the 1990's - not something you would see today.
If you are using a decent compiler, and have optimisation enabled, then a switch will be the fastest way. Someone mentioned binary searches as a more complex but faster solution - a good compiler will generate a jump table or a binary search automatically from a switch statement to give you the most efficient code.

You could create a global array with all your values to check. And simply loop trought this array. If you need a specific behavior for each else if you could create an array of struct and use function pointer to set a different behavior.
Here's an example :
// For int variables
#include <stdint.h>
// For printf
#include <stdio.h>
#define VALUE_TO_GUESS 3
#define NB_VALUES 4
#define MY_VALUE_1 1
#define MY_VALUE_2 2
#define MY_VALUE_3 3
#define MY_VALUE_4 4
void behavior_1(void) {
printf("Behavior 1 !\n");
}
void behavior_2(void) {
printf("Behavior 2 !\n");
}
void behavior_3(void) {
printf("Behavior 3 !\n");
}
void behavior_4(void) {
printf("Behavior 4 !\n");
}
// Definition of the struct using a function pointer
typedef struct s_compare {
uint32_t value_to_compare;
void (*ptr)(void);
}t_compare;
// Setting up my struct
t_compare g_compare[] = {
{MY_VALUE_1, behavior_1},
{MY_VALUE_2, behavior_2},
{MY_VALUE_3, behavior_3},
{MY_VALUE_4, behavior_4}
};
int main(void) {
for (int i = 0; i < NB_VALUES; i++) {
/* If your current value match the value set in the current struct
then call the function pointer associated, with these 2 lines i can
compare an infinite quantity of numbers */
if (g_compare[i].value_to_compare == VALUE_TO_GUESS) {
g_compare[i].ptr();
}
}
}
Feel free to tell me if you need some explanations about this one.
This need a bit of setup but its a powerful solution to your problem and a more elegant solution than a tree of if / else / else if.

Related

Running a conditional into a true statement of a conditional

I'm currently going through the CS50 course and already hit the first obstacle. Basically I am trying to check different conditions to be true in order for an output, however even if what I am introducing is correct, it just seems to not work.
#include <stdio.h>
#include <cs50.h>
int main(void)
{
int test = 0;
double nr = get_double("Your card number please:\n ");
while(test < 1)
{
if(nr / 100000000000000 < 10)
{
if(nr / 10000000000000 == 37)
{
printf("AMEX");
test++;
}
else if(nr / 10000000000000 == 34)
{
printf("AMEX");
test++;
}
}
else {
printf("false");
}
}
}
Here is an example that should work:378282246310005. Even if I introduce a wrong number, nothing is being displayed.
Also, I reckon the while loop is not breaking because the conditions aren't run, or is there another mistake I am missing?
Thanks in advance peeps
You are using a double (a representation of a floating point number that need not be an integer), meaning that when you divide it by your divisors, the result is not truncated. For your example (input is 378282246310005), the value of nr / 10000000000000 is not 37, but is actually a double with value approximately 37.8282246...
The input will not fit into an int, so the get_int function available in cs50.h will be inappropriate. However, the value will fit into a long, on the environment that CS50 uses1
Declare nr with type long, and use get_long() to obtain the input from the user. Then the value of nr / 10000000000000 will be truncated toward zero to become 37, exactly as you need here.
Note regarding types:
1 The authors of libcs50 document get_long() with a 64-bit signed type for long in mind, having the expected toolchain and environment in mind. This is not always the case, especially if your environment (OS, compiler, etc) doesn't match what the authors of CS50 expect you to use.
You will want to make sure that your assumptions hold if you want to use long on other environments; it is unwise to generalize that long will be 64 bits in the future, without first carefully verifying that this is the case. For the purposes of this assignment, the given type will be sufficient, but you may want to consider learning about the stdint.h header, which provides types that have a guaranteed, specified size.

Should you use '>=' instead of '==' to be extra safe when coding with other types than float?

When working with floating-points one should use
a <= 0.0
instead of
a == 0.0
to make sure that you get the desired behaviour, as there is the problem with round-off errors when using floating-point variables.
But when using other variable types like int could it be useful to do the same? Like you have a for loop iterating over an int variable (like an index) and when it gets to a number it should do something. Should you then set the comparison to be >= instead of == when it should result in the same output? Like could there ever be a case where the == is not evaluated in the following case:
for (int i = 0; i < 10; i++)
{
if (i == 5)
{
break;
}
}
And that it would be "safer" to do the following instead:
for (int i = 0; i < 10; i++)
{
if (i >= 5)
{
break;
}
}
If there is no difference between the two when it comes to coding "safe" is there any performance or readability difference or other thing that can make one choose between the ways to code?
Tried to google this but couldn't find anything stating either way. But that might have to do with the problem with searching for operators.
Am I too paranoid for asking this?
The premise of the question is wrong; blindly using a <= 0.0 instead of a == 0.0 is not a valid practice with floating point. See How dangerous is it to compare floating point values? for a treatment of the topic.
With that said, there are some cases where use of inequality relational operators are "more hardened" than use of the equality operator, and vice versa. In general, I would recommend against it since it's likely to give you a false sense of safety; for example, in the example in your question with i==5 vs i>=5, the compiler is likely to be able to prove they're the same, and optimize either to the other if needed. This means it will not necessarily do anything to protect you against stack overflows or neutrinos or any other cause by which the value of i might become something other than 0..5 outside of the defined semantics of the language.
There are also some cases where use of equality is defined by inequality is not, particularly involving pointers. If pos and end are both pointers, pos==end is well-defined as long as they are both valid. But if they are both null, while pos==end is well-defined and true, pos>=end is undefined behavior. Likewise if they both point into different arrays (in particular if end points to a sentinel not part of your array), pos==end will always be false, but pos>=end is undefined behavior.
I also find it misleading to write if (i>=5) when you know i>5 is logically impossible - it makes the reader stop to think about whether it's possible, and if not, why you wrote it that way instead of if (i==5).
The answer is, it depends. If you have a loop like this:
for (int i = 0; i < 10; i++)
{
if (i == 5)
{
break;
}
}
Then there is no danger in i skipping 5.
On the other hand, if you have something like this:
volatile int i;
void interrupt(void)
{
i++;
}
void foo(void)
{
for (i = 0; i < 10; i++)
{
if (i == 5)
{
break;
}
}
}
Then i may change outside the normal flow of the program (e.g. because of an interrupt). In this case >= would be prudent.
Like could there ever be a case where the == is not evaluated
No, using == is safe. Integers represent all values within the range of the integer type used accurately. There are no surprises.
Should you then set the comparison to be >= instead of ==
As the result is the same, you can do both but I would find >= confusing. So for readability I prefer ==
is there any performance . . . that can make one choose between the ways to code
You can't tell by looking at the C code. It depends on the system (CPU) used. In general I would however doubt that you would experience any major difference.

C Integer range must meet 3 different conditions

If I wanted to limit the range of values to be assigned to an integer to three different conditions. eg; Must be between 9 and 95 and also be divisible by 5 would this be the correct way to accomplish this?
I've been told that i can have multiple conditions as long as they are separated by && but I am having little success with my code.
if (input >= 5 && input <= 95 && input %5)
Your code seems fine to me, except for this line.
if (input >= 5 && input <= 95 && input %5)
The expression input % 5 returns the remainder of input/5. You want input to be divisible by 5, which happens when input % 5 returns a remainder of 0. Since C interprets 0 as false, and pretty much all other integers as true, this expression will do exactly the opposite of what you want it to do. Try using
if (input >= 5 && input <= 95 && (input % 5 == 0))
That should do what you want it to do.
There are a number of issues with your code as it stands. First, the outright bugs:
The expression input % 5 will give you the remainder when divided by five. This means you will get zero if it is a multiple, non-zero otherwise. Unfortunately, zero is treated as false so this will only be true if input is not a multiple. The correct expression is (input % 5) == 0.
If you enter something that cannot be interpreted as an integer, the scanf will fail and input will be left at whatever value it was beforehand. This should be caught and acted upon, by checking the return value - this gives you the number of items successfully scanned so should be one.
Your code seems to return the value if okay but return nothing if it's invalid.
Next, while not bugs, these things are my personal preferences which can make code easier to read and maintain:
I prefer to explicitly separate sub-expressions so I never have to worry about precedence rules (provided it doesn't make the expression unreadable in the process). To that end, I would make the full if statement if ((input >= 5) && (input <= 95) && ((input % 5 == 0)).
I'm not a big fan of the if (condition) transferControl else ... construct since the else is totally superfluous.
I also prefer error catching to be done in a localised fashion at the start, catching problems early. Only after all checks are passed do you do the success portion.
A function (assuming it is a function, which seems likely) should generally do one thing, such as check if the value is valid. Writing issues to standard output is probably best left to the caller so that the function is truly re-usable. It would be better to have a function do the check and return some value to indicate whether or not there was a failure, along with the value if valid.
It's usually better to use puts("something") rather than printf("something\n"). The printf call is best left to where you actually need to do argument formatting.
Taking that all into account, the code that I would posit would be along the lines of:
#include <stdbool.h>
bool InputValidRangeAndMultiple(
unsigned *pValue,
unsigned minVal,
unsigned maxVal,
unsigned multVal
) {
unsigned input;
// If no unsigned int available, error.
if (scanf("%u", pValue) != 1) return false;
// If value invalid in any way (range or multiple), error.
if ((*pValue < minVal) || (*pValue > maxVal)) return false;
if ((*pValue % multVal) != 0) return false;
// Value is now deemed okay.
return true;
}
Calling that function can be done thus, with the prompts and errors handled outside the "input and check" function:
#include <stdio.h>
unsigned value;
puts("Enter Value.\nValue must be divisible by 5 and within 5 and 95...");
if (! InputValidRangeAndMultiple(&value, 5u, 95u, 5u)) {
puts("Invalid input...");
returnOrDoSomethingIntelligent();
}
// The 'value' variable is now valid.

Idiomatic way to check for non-zero

When I wish to check if a value is 0 in C, how is it idiomatically done?
if (!num)
if (num == 0)
While this is a matter of taste, I find it pretty much depends on intention. If the value is to be used as a boolean, ! is alright. If the value is counting something the equality makes more sense.
if (!isVisible) {...}
if (isVisible == 0) {...} // Intention not as clear as line above.
if (numberOfItems == 0) {...}
if (!numberOfItems) {...} // Intention not as clear as line above.
I always prefer the second way:
if (num == 0)
As num == 0 or ptr == NULL evaluates to a boolean which is the intent. The Java compiler enforces this form, but C/C++ compilers don't.
The worst example of this would be:
if (!strcmp(str, "something"))
Which really disguises its intent as the strcmp family of functions don't return boolean, they return positive, zero, or negative (as pointed out by #JoachimPileborg).
However if the int is being used to represent a boolean type, which C does not have a builtin type for, then this form is OK:
if (!b)
But this can be made self documenting by creating a custom type:
typedef int bool;
#define true 1
#define false 0
bool b = true;
if (!b)
{
... etc
}
Whatever the others told you WITH AN EXCEPTION!
Don't do it with float and double. IEEE 754 floats/doubles/long doubles (the most commonly used) often don't contain exact values, so comparing them directly with 0 is foolish (or doing if (!floatValue))
Example: http://ideone.com/PIUflA
float f = 0.3;
f -= 0.2;
f -= 0.1;
if (!f)
{
printf("zero\n");
}
else
{
printf("non zero\n");
}
if (f == 0)
{
printf("zero\n");
}
else
{
printf("non zero\n");
}
With unoptimized compilation can return (on ideone does)
non zero
non zero
(if you enable optimizations, the compiler could pre-compute some values in higher precision and round them to 0)
I think it depends on the context. If the variable refers to a boolean value is better first choice. Otherwise, the second is better.
It's done however you want it to be done, in terms of your style. I don't see it as mattering as long as your consistent and it's clear on what you're trying to do, or if you do it in cases where it may flow better in an English sentence and may put emphasis on what your doing.
For sake of clarity I usually have if (num == 0), since it takes less thinking to understand what I'm doing when I'm going over my code.
!!value will work too if you want to check if value is non-zero.
!value evaluates to 1 when value=0 and 0 when value≠0.
The second ! flips it, making !!value evaluate to 1 when value≠0 and 0 when value=0.
We may argue which way is better, but idiomatic, though for what I can tell by other answers archaic, would be if(!num).
For the compiler, it does not matter, of course. For the human reader it does. Since both forms are used by other human beings, you should get used to them and recognise them. Personally, I prefer the shortest form, which takes less time (less tokens, especially parenthesis) to read and understand. Especially:
if (!ptr) {}
if (!strcmp(a,b)) {}
are easyer to read than
if (ptr != NULL) {}
if (strcmp(a,b) == 0) {}
if (0 == strcmp()) {}
The last form makes me physically sick.

How to write "if x equals 5 or 4 or 78 or..." in C

I have a quick question about using logical operators in an if statement.
Currently I have an if statement that checks if x equals to 5 or 4 or 78:
if ((x == 5) || (x == 4) || (x == 78)) {
blah
}
And I was wondering if I could just condense all that to:
if (x == 5 || 4 || 78) {
blah
}
Sorry for such a basic question, I've just started learning C.
There is no shortcut, but you need to fix your equality operator.
if ((x == 5) || (x == 4) || (x == 78)) {
First, you're using assignments not equality tests in your ifs. The first method (with suitable substitutions for equality) is the best way to do the test, though if you have a lot of possible options, there might be better ways. The second way might compile, but it won't do what you want, it will always return true since both 4 and 78 evaluate to true and what you are doing is evaluating whether 5 (the result of assigning 5 to x) or 4 or 78 are true. A switch statement might be one possible alternative.
switch (x) {
case 4:
case 5:
case 78:
blah...
break;
default:
}
There's no shortcut for the if statement, but I suggest considering:
switch (x)
{
case 4:
case 5:
case 78:
/* do stuff */
break;
default:
/* not any of the above... do something different */
}
No you cannot and the test for equality is ==, not =
#uncle brad is spot on, but later you'll probably learn about something called a switch statement. It looks funky but is often used in these sorts of situations (where several possible values of a variable all have the same effect):
switch (x) {
case 4:
case 5:
case 78:
// ...
break;
}
Though you'd only want to use a switch statement when the meaning of an if statement is less clear--most compilers these days are smart enough to generate optimal machine code either way.
It's been answered in the time it took me to log in, but you could use the switch, and break it out into a function
int isValid(int toCheck) {
switch(toCheck) {
case 4:
case 5:
case 78:
return 1;
default:
return 0;
}
}
Then you would just call the method every time you needed to check the int against the established cases.
Admittedly, this example is rather silly, but for a bigger selection of cases, and ones that were evaluated repeatedly, you could do something like this to simplify and reuse some code.
No, sorry, you can't; you have to write all the expressions out. For very long lists of numbers to compare to, you could put the numbers in an array, and loop over the list; but you'd have to have a dozen numbers or so before that started to look like a good idea.
No, you cannot do this in C. Your first code sample is also incorrect. There is an important distinction between assignment (=) and equivalency (==) in C. When you wrote x = 5 in your expression, this will actually compile and evaluate to either 0 or 1 (false or true) before being logically OR'ed with the next part of the expression!
Your second code sample is also valid C, but it does not do what you want it to do. You read the statement as "(x is assigned to 5) or true or true". This is because any non-zero value in C is logically true. Thus, x will contain the value 5, and evaluate to true, making your if condition true. The rest of the expression does not matter since the || operator short-circuits.
One alternate thought: While there's no "shortcut", if you have a lot of numbers it may be easier for code length and typing sanity to put them all into an array and check against the array in a loop. If you have to compare against many numbers more than once, sort them in an array and use binary searching.
For 3 numbers, though, you have to do it the "long" way.
You can do the for loop if it's a long list, how ever that doesn't really handle the logical operators :-...
#include <stdio.h>
int forbiddenList[13] = {5, 4, 78, 34, 23, 56, 4, 7, 6, 4, 33, 2333, 0};
int length = 13;
int main() {
int mysteryNum;
printf("type a number: ");
scanf("%d",&mysteryNum);
int i;
for (i = 0; i <= length; i ++)
{
int target = forbiddenList[i];
if (mysteryNum == target)
{
printf("You have chosen of the forbidden list!\n");
printf("YOU SHALL NOT PASS!!\n");
}
}
return 0;
}
er... haven't done c... ever... you should take C++...
int preconditions[] = { 4,5,78 }; // it should be in most likely order
int i = 0;
for(;i<3;++i) {
if( x == preconditions[i] ) {
// code here.
}
}
The syntax is:
if(condition1 || condition2 || condition3 || ...){
// Do something
} else {
// Do something
}
Answer to your question:
if( (x == 5) || (x == 4) || (x == 78) ){
//do something
} else {
//do something
}

Resources