switch statement using string in C [duplicate] - c

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
C/C++: switch for non-integers
C/C++ switch case with string
I am passing a string to a function get_band(char *str)
Then I am comparing the passed argument with a specific string
if(strcmp(str, "auto"))
{
//do something
}
My question is - since i have many number of strings to compare, how can i use switch statement instead of if/else, since switch statement supports only integral type.

Short answer: you can't.
Long answer: this question is a duplicate of C/C++ switch case with string.

You could do:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *words[] = {"one", "two", "four"}; // words is an array of pointers to char
void f(char *str);
int main(void) {
f("two");
return 0;
}
void f(char *str) {
int i;
for (i = 0; i < sizeof words/sizeof words[0]; i++) {
if (!strcmp(str, words[i])) {
/* Do something */
}
}
}

No, switch only works for integers.
If you want to optimize, you can use some data structure to determine if the string is any of the known strings. For example:
hash table
trie
some self-balancing binary search tree, like AVL tree or red-black tree
To make use of such a data structure, assign each known string a constant integer (e.g. define or enum); given a string, you can use the data structure to map it to some number of a known string (possibly with some number meaning "unknown"). Then you can use a switch on this number and do whatever you want for each case.
You can find an existing implementation of any of the above, but if you're new to C, it might be beneficial to implement it yourself.

A switch statement branches based on evaluating the following expression.
In your case, strcmp only promises to return less than zero, zero, or greater than zero. Even if you assume that means -1, 0, and 1 (respectively), that would mean you would only have 3 values you could choose from in your switch cases. [And that is not a safe assumption, by the way ...]
Instead, if your strings are taken from a closed set of strings (i.e. can only have one of set of values), you could tokenize the string. There are actually programs to do this for you, i.e. turns strings into tokens. (See flex(1).) This would mean that each string you expected would have some unique value, and you could switch on that value.
If this is too much trouble, just use the method you used in your question, i.e.:
if (strcmp("some string", str) == 0) {
// handle some string
} else if strcmp("some other string", str) == 0) {
// handle alternate case here
...
} else {
// handle error/default case here
}

Consider changing the logic of your program to use enums instead of strings. You can then use an array look up to map the integer enums to the corresponding string for display and logging purposes. The other options including hash etc have been covered.

The switch statement in C / C++ accepts only three case types : integers, characters and enumerated data types (which is effectively another way of saying integer). Characters also reduce to integers when evaluated as ascii codes.
#Nikolai N Fetissov made a good suggestion : switch on a hash of the string :
#define hashOfString1 <integer hash goes here>
#define hashOfString2 <integer hash goes here>
#define hashOfString3 <integer hash goes here>
. . .
switch (hashMyString("MyString")) {
case hashOfString1:
/* do this */
break;
case hashOfString2:
/* do that */
break;
case hashOfString3:
/* do other */
break;
default:
/* default behavior */
break;
}

Related

Comparing String if( "bonaparte"=="bonaparte" ) this gives true result?

Note: I know I can do this stuff by using strcmp() but I am just trying the equal to == operator.
Code 1:
#include<stdio.h>
int main()
{
char s1[]="bonaparte",s2[]="bonaparte";
if(s1==s2)
{
printf("True");
}
else
{
printf("Flase");
}
}
Output:
False
Code 2:
#include<stdio.h>
int main()
{
char s[]="bonaparte";
if(s=="bonaparte")
{
printf("True");
}
else
{
printf("Flase");
}
}
Output:
False
Code 3:
#include<stdio.h>
int main()
{
if("bonaparte"=="bonaparte")
{
printf("True");
}
else
{
printf("Flase");
}
}
Output:
True
So, in #1, we compare the base addresses of s1 and s2 and obviously they are going to different, so the output is "False".
In #2, we are comparing the base address of s and the base address of the constant string "bonaparte" and obviously it is again going to be different, so the output is "False".
However, in #3, we are comparing base addresses of one constant string "bonaparte" and another constant string "bonaparte". I think that the string names are same but they will get different locations in memory so, when we are trying to compare their base addresses, they should be different. But output is contradicting to above (what I think).
So what is exactly happening in the 3rd case?
When compiling your third code sample, the compiler is applying what is often called "string pooling" – that is, the compiler spots that two (or more) string literals are the same and, as such constants cannot be changed at runtime, optimizes the code by generating only one instance of the data, so the two literals end up with the same address.
Most compilers do this by default, but you may be able to disable it. For example, in Visual Studio with the MSVC compiler, you can set the /GF- option to disable it. With this set, your code above gives "Flase" (sic) as the output (Reference).

Better solution than if-else if?

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.

Variable length arithmetic calculator WITHOUT using strings?

I'm trying to create a calculator that solves arithmetic expressions of different lengths (e.g. 2+3/4 or 7*8/2+12-14), and I was wondering if it was possible to do so without the use of strings.
I've found countless tutorials explaining how to make a simple calculator with only two numbers and an operator, and I've also found examples using sscanf and strings to get the input.
However, my question is: Is there a way (is it even possible) to get variable length inputs without using strings?
At first I thought i could simply add more specifiers:
int num1 , num2, num3;
char op1, op2;
printf("Please enter your equation to evaluate: ");
scanf("%d%c%d%c%d", &num1, &op1, &num2, &op2, &num3);
but obviously, that doesn't work for equations longer than 3 numbers or less than 3 numbers.
I was also thinking of perhaps using some sort of recursive function, but I'm not sure how I would do that if I need to ask for the entire equation up front?
If you intend to read ASCII from user input, or from command line arguments, then you're pretty inescapably in the world of strings. What you can do is convert them into something else as early as possible.
You could abandon ASCII altogether and define a binary file format.
For example, you might say that each pair of two bytes is a token. The first byte is an element type ( signed integer, unsigned integer, float, operator), the second byte is the value.
Pseudocode:
while(!done) {
int type = read(f);
int value = read(f);
switch(type) {
case TYPE_INTEGER:
push_to_stack(value);
break;
case TYPE_FLOAT:
push_to_stack_as_float(value);
break;
case TYPE_OPERATOR:
execute_operator(value);
break;
}
}
Quite why you would force yourself down this route, I don't know. You'd probably find yourself wanting to write a program to convert ASCII input into your binary file format; which would use strings. So why did you run away from strings in the first place?
you can create a list of struct, every struct will have to contains a value OR a sub list, an operator (char?) and a reference to the next (and or before) char.
Then you just ask the user for a number (or "("/")"), and a operator sign. Every number + operator is a new element in list, every ( is a sub list, every ) is a return to superior list (you can even don't create a sublist, but elaborate it on the fly and return just the result, like a recursive function)
Also struct and code can be elaborated to support multiple parameter.

How to store array element in an array

I want to store an array to element. So i used 2-D array, but seems it is not working. In this case i am getting 5 IEEE address which is array of 8 bytes. I want to store one by one according to which address in coming first
char a[5][8];
int i = 0;
if(data)
{
a[i] = es->ieee;
i++;
}
//it will receive 5 different address
I need to compare those IEEE address with already store IEEE address,for example there are 5 IEEE address
char First[8] = "0x32441";
char Second[8] = "0x42421";
if(a[1] == First)
{
printf("it is matching in First");
}
Can any one help me out
Let's call these "strings", since that seems to be how you think of them, despite that you say otherwise.
In C, a string is an array of char that ends with a char whose value is 0.
Strings are "assigned" (copied) with strcpy(), since you cannot directly assign an entire array, usually.
This would mean that your initial example probably should be something like:
if(data)
strcpy(a[i++], es->ieee);
it's hard to be sure, since you don't tell us what es is.
Also, strings are compared with the strcmp() function, you cannot use == to compare strings directly (since they are arrays, and arrays cannot be compared with == either).
This means that the second snippet should be:
if(strcmp(a[1], First) == 0)
printf("it is matching First\n");
C does not have array assignment. You need to use memcpy (or possibly strncmp, this seems one of the rare cases where it is actually appropriate function):
char a[5][8];
int i = 0;
if(data)
{
memcpy(a[i], es->ieee, 8);
i++;
}
And C does not support comparing arrays with operators either, so use memcmp function (or possibly strncmp, depending if you have null terminated strings or not):
char First[8] = "0x32441";
char Second[8] = "0x42421";
if(memcmp(a[1], First, 8) == 0)
{
printf("it is matching in First");
}

expand [x-y] and {a,b,c} in strings -- like glob(3) but without filename matching

I'm looking for a canned C routine that does what glob(3) does, except without matching the results against filenames, e.g.
input: "x[1-4]y"
output: "x1y", "x2y", "x3y", "x4y"
regardless of whether any files with those names happen to exist. EDIT: This doesn't need to produce the list all at once; in fact it would be better if it had an iterator-style "give me the next name now" API, as the list could be enormous.
Obviously this cannot support * and ?, but that's fine; I only need the [a-z] notation. Support for the {foo,bar,baz} notation would be nice too.
Best option is telling me the name of a routine that is already in everybody's C library that does this. Second best would be a pointer to a chunk of BSD-licensed (or more permissively) code. GPL code would be awkward, but I could live with it.
cURL (the command line tool, not the library) contains code that does this job, which is relatively easy to extract:
https://github.com/bagder/curl/blob/master/src/tool_urlglob.c
https://github.com/bagder/curl/blob/master/src/tool_urlglob.h
They'll have to be edited to remove some dependencies on the guts of cURL that are not part of the public library interface. The API is a little confusing, so here's some wrapper code I wrote:
#include "tool_urlglob.h"
struct url_iter
{
char **upats;
URLGlob *uglob;
int nglob;
};
static inline struct url_iter
url_prep(char **upats)
{
struct url_iter it;
it.upats = upats;
it.uglob = NULL;
it.nglob = -1;
return it;
}
static char *
url_next(struct url_iter *it)
{
char *url;
if (!it->uglob) {
for (;;) {
if (!*it->upats)
return 0;
if (!glob_url(&it->uglob, *it->upats, &it->nglob, stderr))
break;
it->upats++;
}
}
if (glob_next_url(&url, it->uglob))
abort();
if (--it->nglob == 0) {
glob_cleanup(it->uglob);
it->uglob = 0;
it->upats++;
}
return url;
}
Pass an array of strings to url_prep, call url_next on the result until it returns NULL. Strings returned from url_next must be deallocated with free when you're done with them.
Here's a sketch of how I'd write the iterator:
Count the instances of [ in the string. This will be the number of "dimensions" you iterate over.
For each dimension, establish a range of values based on the number of characters in the bracket expression.
Simply iterate an n-tuple of integers over these ranges, and use the resulting values as indices into the bracket expressions to expand the string based on the values.

Resources