How can I use ranges in a switch case statement in C? - c

My logic is:
if number is between 1 to 10, execute first case statement
if number is from 20 to 30, execute second case statement
is there a solution other than the one below?
case '1' ... '10':
case '20' ... '30':

The GCC compiler supports, as a language extension, case ranges like:
switch(i) {
case 0 ... 9: return true;
default: return false;
}
This language extension is also accepted by Clang/LLVM. So use it if you can afford restricting your code to GCC & Clang compilers.
See also this.
I have no idea why this extension was not included in C11 standard.
Notice also that GCC accepts computed or indirect goto and labels as values. There are cases (in particular in
generated C code) where these features are useful. Examples could include some efficient bytecode interpreter. Some implementations of the Ocaml virtual machine are a good example.

void SwitchDemo(int value)
{
switch(value / 10)
{
case 0: ...; break; // 0 - 9
case 1: ...; break; // 10 - 19
...
}
}
or, specific to the question ranges:
void SwitchDemo(int value)
{
switch((value-1) / 10)
{
case 0: ...; break; // 1 - 10
case 1: ...; break; // 11 - 20
...
}
}

Option 1: use case 0 for 0-9, case 1 for 11-20 and so on.
Option 2: use if
Option 3:
Another shabby way is using fall through cases like this:
#include <stdio.h>
int main(void) {
int i=1;
for(i=1;i<=25;i++)
{
switch(i)
{
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
case 8:
case 9:
case 10:
printf("%d is in between 1-10\n", i);
break;
case 11:
case 12:
case 13:
case 14:
case 15:
case 16:
case 17:
case 18:
case 19:
case 20:
printf("%d is in between 11-20\n", i);
break;
default:
printf("%d is above 20\n", i);
}
}
return 0;
}
Output:
1 is in between 1-10
2 is in between 1-10
3 is in between 1-10
4 is in between 1-10
5 is in between 1-10
6 is in between 1-10
7 is in between 1-10
8 is in between 1-10
9 is in between 1-10
10 is in between 1-10
11 is in between 11-20
12 is in between 11-20
13 is in between 11-20
14 is in between 11-20
15 is in between 11-20
16 is in between 11-20
17 is in between 11-20
18 is in between 11-20
19 is in between 11-20
20 is in between 11-20
21 is above 20
22 is above 20
23 is above 20
24 is above 20
25 is above 20
https://ideone.com/Cw6HDO

C doesn't support case values other than single integers (or integer-like things -- characters, enumeration values). So your options are:
As suggested by pzaenger in a now-deleted comment: transform the number you're working with into something you can switch on (in this case, divide by 10).
Multiple case statements (taking advantage of fallthrough): case 1: case 2: case 3: ... case 10: do_something();
Use if rather than case.

In the C programming language the case statement used in a switch() statement must specify a value that the compiler can turn into a constant in some way. Each of the values used in the case statements must be unique within the scope of the switch(). The default keyword indicates the default if none of the case statements match the expression in the switch() statement.
As an aside, check out Duff's Device to show an interesting use of switch() and case. See How does Duff's device work?
So the following shows several examples of proper case statements in a switch():
#define XXVAL 2
#define CASETEST(x) (x + 5)
int iValue;
// set the value of the variable iValue at some point
switch (iValue) {
case 0:
// do the case if iValue == 0
break;
case XXVAL:
// do the case if iValue == XXVAL
break;
case CASETEST(3):
// do the case if iValue == CASETEST(3)
// works because preprocessor generates the source text which is
// then compiled and the expression can be resolved to a constant
break;
case CASETEST(5) * 2:
// do the case if iValue == CASETEST(5) * 2
// works because preprocessor generates the source text which is
// then compiled and the expression can be resolved to a constant
break;
default:
break;
}
What you can do if you still want to use a switch() with ranged case statements is to provide some mechanism to fold the expression into one or more specific constant values.
So in a simple, trivial example you could do something like the following. This is a trivial case to show the technique which ends up making the logic of the simple if statements opaque. This technique can be useful for complex decisions and classification that can be folded into a simple set of constants.
int foldit (int iValue)
{
if (iValue < 5000) return 0;
else if (iValue < 10000) return 1;
else if (ivalue < 20000) return 2;
else return 9999; // triggers the default part of the switch
}
switch (foldit(iValue)) {
case 0:
// do what is needed for up to but not including 5000
break;
case 1:
// do what is needed for 5000 up to but not including 10000
break;
case 2:
// do what is needed for 10000 up to but not including 20000
break;
default:
// handle anything else
break;
}
Where the fold approach can be helpful is when you have several different results perhaps using a filter to try to classify a data item.
#define type1 0x00001
#define type2 0x00002
#define type3 0x00004
#define type4 0x00008
struct datatype {
int iVal;
int jVal;
};
unsigned long is_a_type1(struct datatype * thing)
{
unsigned long retVal = 0; // initialize to not a type1, set to type1 if turns out to be
// do checks for the type and if so set retVal to type1 if it matches
return retVal;
}
unsigned long is_a_type2(struct datatype * thing)
{
unsigned long retVal = 0; // initialize to not a type2, set to type2 if turns out to be
// do checks for the type and if so set retVal to type2 if it matches
return retVal;
}
unsigned long is_a_type3(struct datatype * thing)
{
unsigned long retVal = 0; // initialize to not a type3, set to type3 if turns out to be
// do checks for the type and if so set retVal to type3 if it matches
return retVal;
}
unsigned long is_a_type4(struct datatype * thing)
{
unsigned long retVal = 0; // initialize to not a type4, set to type4 if turns out to be
// do checks for the type and if so set retVal to type4 if it matches
return retVal;
}
unsigned long classify (struct datatype *thing)
{
unsigned long ulTestResult = 0;
// test to see if this is a type1 thing
ulTestResult |= is_a_type1(thing);
// test to see if this is a type2 thing
ulTestResult |= is_a_type2(thing);
// test to see if this is a type3 thing
ulTestResult |= is_a_type3(thing);
// test to see if this is a type4 thing
ulTestResult |= is_a_type4(thing);
return ulTestResult;
}
int main ()
{
struct datatype myThing;
// other source code then
switch (classify(&myThing)) {
case type1 | type2 | type3:
// do stuff if this is a type1, type2, and type3 but not type4
// that is classify() determined that myThing matched all three types.
break;
case type1:
// do stuff if type1 which includes stuff you do for type2 as well under
// special values of myThing.
if (myThing.iVal < 50) {
case type2:
// at this point we have type2 case stuff that we do. Code above is skipped
// and the switch () will jump straight to here if classify() is type2.
//
// Also stuff we do if type1 and myThing.iVal < 50
// in other words this code is execute if classify(&myThing) is type2 or
// if classify(&myThink) is type1 and there is a special processing for myThing.iVal < 50
break; // if classify() type2 or if classify() type1 and myThing.ival < 50
}
// do stuff if only type1 and myThing.iVal >= 50
break;
case type2 | type3:
// do stuff if type2 and type3 matched but none of the others.
break;
default:
// any other case
break;
}
return 0;
}

Switch statements in c can only operate on a constant expression, the case statements cannot include dynamic comparisons.
Example of something which is, and is not, a "Constant Expression" in C?
For something this simple an if/else structure could be clearer and simpler, depending on the compiler your case statement may be translated into a series of branching comparison statements anyways.

Related

Alternative function to (switch without break)

#include <stdio.h>
void main(void)
{
int price;
scanf("%d", &price);
switch (price)
{
case 1000: // what i want - case pay >= 1000
// code (no break - intentional)
case 500: // ....... - case pay >= 500
// code
default:
break;
}
}
I'm new to this. Is there any alternative to switch without break and also able to use comparisons, not constant, in a switch?
Just series of if statements then :
if (price >= 1000) {
// do something
}
if (price >= 500) {
// do some more stuff
}
You may also want to use #define for those magic numbers (1000, 500 ...). Or Enumerate them using enum.
Although switch() looks like a function, it's a statement block.
As others have said, this problem is better suited to consecutive if blocks than to a switch statement (where the use of deliberate fall-through is frowned-upon by many in the C programming world).
However, if you want to divide a number into ranges of fixed-size blocks (in your case, that size is 500), then you can divide your number by that block-size and use the result as the switch variable. Also, note that the default case doesn't have to be the final case – it can be anywhere, even first.
In the below example, because of the fall-through (i.e. no break; statements in any of the case blocks), the case 0: code will be run for any input; for inputs less than 500 (i.e. the integer division will result in 0), only that case will execute. For numbers in the range 500 thru 999 (division will give 1), the case 1 and case 0 code will run; and, for numbers >= 1000, the default case will run, followed by the other two blocks. (This code requires a positive value for price!)
#include <stdio.h>
int main(void)
{
int price = 0;
printf("Price: ");
int s = scanf("%d", &price);
if (s != 1 || price < 0) {
printf("Invalid price!\n");
}
else {
switch (price / 500) {
default:
printf("Price is >= 1000!\n");
case 1:
printf("Price is >= 500\n");
case 0:
printf("Price is anything\n");
}
}
return 0;
}
As I have said, I would generally not recommend using a switch in cases (poor pun) like this; but, that advice notwithstanding, it is then actually quite easy to add extra blocks/conditions, such as code for prices in the 1000 - 1499 range, where you could just insert a case 2: block to the code. Furthermore, when the number of 'ranges' becomes sufficiently large (say, more than 3), then it does arguably become clearer to use such a switch, rather than a chain of if statements/blocks.
Note: For added clarity – if your compiler supports it (clang and gcc do but I'm not aware of an equivalent for MSVC) – then you can add __attribute__((fallthrough)); statements wherever you are relying on any (implied) fall-though from one case block to the next. In the code shown above, such statements would be added immediately before the case 1: and case 0: labels.
Use chained if … else statements:
if (1000 <= price)
{
// Things for price 1000 or more.
}
else if (500 <= price)
{
// Things for price 500 or more.
}
else
{
// Things for other cases.
}

Exercise in C with if and switch statements: Write code to convert 2 digit numbers into words

There's this exercise I'm trying to figure out. The assignment asks to convert a two-digit number in words, the output should be something like this :
Enter a two-digit number:45
You entered the number forty-five.
I'm still a total beginner to programming. I'm at this chapter in this C programming book, in the exercise section about the switch and if statements. The exercise suggests to use two switch statements, One for the tens and the other one for units, but numbers within 11 and 19 require special treatment.
The problem is that I'm trying to figure out what should I do for numbers between 11 and 19, I was thinking to use the if statement but then the second switch function would include in the output and it would turn into something like You've entered the number eleven one.
This is the program I've been writing so far (incomplete):
int digits;
printf("Enter a two-digit number:");
scanf("%d", &digits);
printf("You entered the number ");
switch (digits / 10) {
case 20:
printf("twenty-");break;
case 30:
printf("thirty-");break;
case 40:
printf("forty-");break;
case 50:
printf("fifty-");break;
case 60:
printf("sixty-");break;
case 70:
printf("seventy-");break;
case 80:
printf("eighty-");break;
case 90:
printf("ninety-");break;
}
switch (digits % 10) {
case 1:
printf("one.");break;
case 2:
printf("two.");break;
case 3:
printf("three.");break;
case 4:
printf("four.");break;
case 5:
printf("five."); break;
case 6:
printf("six.");break;
case 7:
printf("seven.");break;
case 8:
printf("eight.");break;
case 9:
printf("nine.");break;
}
return 0;
Before the switch use these if then else statements.
if (digits == 11) {
printf("eleven");
}
else if (digits == 12) {
printf("twelve");
}
else if (... ) {// similar statements for 13 14 15 16 17 18 19
}
// now figure out where do the switch statements have to be inserted
else {
// here?
}
// or here?
The above solution assumes you haven't learnt arrays yet. If you have learnt or know to use arrays - there are ways to improve this code.
This is such a good problem for a learner, do not want to give much away
// Pseudo code
int print_two_digits(int tens, int ones) {
if (tens 2 or more)
print tens_place(tens) // use `tens` to index an array of strings.
if (ones == 0) return;
print -
else
ones += tens*10;
print ones_text(ones) // ones is 0-19 at this point. Index an string array with `ones`
}
How to index a string array and print?
// index: valid for 0,1,2
void print rgb(int index) {
const char *primary[] = { "red", "green", "blue" };
puts(primary[index]);
}
Trivia: English numbers in text 10-99 are mostly big-endian, most significant first as in "forty-two", except for [11-19] which has the smaller digit first as in "fourteen".
I would suggest you write a function with 3 switches instead of 2:
a first switch to handle special cases such as 0 and the numbers from 10 to 19 and return directly.
a second switch to handle tens from 20 and print the word without a trailing dash,
an if statement where you test if you need a dash,
a third switch where you handle units.

What is the fastest way to reverse a power of two in C?

In the equation :
What is the fastest way in C language to find x with a given power of two value (a) ?
Edit :
The mathematical exact solution is :
As (a) is a positive integer and a power of two (no rational number, no equal to zero), this problem can be simplified as "looking for position of set bit".
This post is focused on lite embedded CPU systems. For example : ARM CORTEX M4.
a to x results :
a | x
-------
1 | 0
2 | 1
4 | 2
8 | 3
16 | 4
32 | 5
64 | 6
128 | 7
256 | 8
512 | 9
...
Option 1 : The dirty loop
unsigned int get_power_of_two_exponent(unsigned int value)
{
unsigned int x = 0;
while( ( 1 << x ) != value)
{
x ++;
}
return x;
}
Option 2 : The weird trick
#include <stdint.h>
#if defined(__GNUC__)
static int highest_bit_set(uint32_t value)
{
if (sizeof (unsigned int) == sizeof value)
return 31 - __builtin_clz(value);
else
if (sizeof (unsigned long) == sizeof value)
return 31 - __builtin_clzl(value);
else
exit(127); /* Weird architecture! */
}
#endif
Any faster options ?
Fastest in C is almost always look-up tables, at the expense of memory use. Assuming that the value is always exactly a power of 2, you can make a look-up table like this:
uint8_t get_exponent (uint8_t val)
{
static const uint8_t byte[256] =
{
[1] = 0,
[2] = 1,
[4] = 2,
[8] = 3,
[16] = 4,
[32] = 5,
[64] = 6,
[128] = 7,
};
return byte[val & 0xFF];
}
It will return 0 in case you pass a value which isn't a power of 2.
This can be expanded further either by looping through for example the 4 bytes of a uint32_t and do 4 table-lookups. Or by making even bigger look-up tables.
On x86 I get the above to boil down to this tiny, branch-free machine code:
get_exponent:
movzx edi, dil
movzx eax, BYTE PTR byte.2173[rdi]
ret
(Swapping to uint_fast8_t gives identical code in this case.)
This answer is in dispute - see comment.
The fastest way, somewhat facetiously1, is to write
switch (a)
{
case 1: return 0;
case 2: return 1;
case 4: return 2;
...
Clearly there are as many labels as there are bits in the type, but this is still O(1).
You could even truncate a to a power of two using the idiom a ^ (a & (a - 1)), at the expense of portability given that only works if a is a 2's complement type.
1Although in C++ you could get the compiler to build the table with constexpr and metaprogramming techniques.
The best performances (on my embedded ARM CORTEX M4 CPU core) are obtained with :
Builtin CLZ solution (Count Leading Zero’s)
Moreover, the CLZ solution is by far much more memory efficient than the lookup table method which take the second place.
Often, the LookUp table method still less efficient than the Builtin CLZ because the table is stored in RAM like a DDR for example. Thus, it can takes a dozen of cycle to access the data in this kind of RAM. In this example, this is amplified by the fact that the instruction cache is enabled but not the data cache. Besides, having this huge table stored in cache would not have been very appropriate.
It depends how big values you would like to search, and if there's the biggest possible input defined.
If x can be, for example, 100, searching from beginning (x = 0) with step x++, isn't elegant and optimized (100 checks). You can set step x+=5. If the result is lower than searched value, x+=5. If bigger - step back with x-- (max 4 Times). Size of step you can adjust to your needs.
If there's a "top-limit", you can create an array of possible x and implement binary search.
#Lundin's answer seems the best in terms of speed (just 3 assembly instructions!), but it may not be a good option for your embedded system. If huge LUTs are not an option:
The weird trick seems to be the a fast option, I guess (you should benchmark each option and see actual results, though). You could use that one in case it exists, and fallback to the usual shifting otherwise:
#include <stdint.h>
static int get_pow2_exp(uint32_t value)
{
#if defined(__GNUC__)
if (sizeof(unsigned int) == sizeof(value))
return 31 - __builtin_clz(value);
if (sizeof(unsigned long) == sizeof(value))
return 31 - __builtin_clzl(value);
#endif
int x;
for (x = -1; value; value >>= 1)
x++;
return x;
}
If you want to ensure that it is a power of two, you may use popcnt. Your while loop is an infinite loop in case the input is not a power of two, while mine just gives a solution based on the highest bit (which may be incorrect, depending on your needs).
2^x = a is the equation
Assuming 32 bit architecture and 'a' & 'x' as integers.
Here is my approach
uint32_t x;
uint8_t *ptr ;
uint8_t ByteNo,BitNo,i;
void My_Function(uint32_t a)
{
ByteNo = BitNo = 9;//some random number
ptr = (uint8_t*)&a;//Assuming points to LSB in variable a
for(i=0;i<4;i++)
{
switch(*ptr)
{
case 0x01: BitNo=0;break;
case 0x02: BitNo=1;break;
case 0x04: BitNo=2;break;
case 0x08: BitNo=3;break;
case 0x10: BitNo=4;break;
case 0x20: BitNo=5;break;
case 0x40: BitNo=6;break;
case 0x80: BitNo=7;break;
case 0x00: BitNo=9;break;
default : break;//take care error condition
}
if(9 != BitNo)
{
break;
}
else
{
ptr++;
}
}//for loop
ByteNo = i;
x = (BitNo) + (ByteNo*8);
}//My_Function
Another approach:
switch(a)
{
case 0x00000001: x=0; break;
case 0x00000002: x=1; break;
case 0x00000004: x=2; break;
case 0x00000008: x=3; break;
case 0x00000010: x=4; break;
case 0x00000020: x=5; break;
case 0x00000040: x=6; break;
case 0x00000080: x=7; break;
case 0x00000100: x=8; break;
case 0x00000200: x=9; break;
case 0x00000400: x=10; break;
case 0x00000800: x=11; break;
case 0x00001000: x=12; break;
case 0x00002000: x=13; break;
case 0x00004000: x=14; break;
case 0x00008000: x=15; break;
case 0x00010000: x=16; break;
case 0x00020000: x=17; break;
case 0x00040000: x=18; break;
case 0x00080000: x=19; break;
case 0x00100000: x=20; break;
case 0x00200000: x=21; break;
case 0x00400000: x=22; break;
case 0x00800000: x=23; break;
case 0x01000000: x=24; break;
case 0x02000000: x=25; break;
case 0x04000000: x=26; break;
case 0x08000000: x=27; break;
case 0x10000000: x=28; break;
case 0x20000000: x=29; break;
case 0x40000000: x=30; break;
case 0x80000000: x=31; break;
default: break;//error condition
}

Hexadecimal Number convertor

i trying to write a Recursion function that get a decimal number and print the value in hexadecimal
but i dont know what worng in my code
please help me i still new in Recursion and dont get it complitly...
my code:
void PrintHexadecimalNumber(int n)
{
if (n%16==0)
return ;
PrintHexadecimalNumber(n/16);
switch ((n % 16))// A-F cases
{
case (10):
printf("A");
break;
case (11):
printf("B");
break;
case (12):
printf("C");
break;
case (13):
printf("D");
break;
case (14):
printf("E");
break;
case (15):
printf("F");
break;
}
if ((n%16)<10)
printf(n%16);
}
Your code isn't going to work because of the test you've written to stop the recursion.
if (n%16==0)
If you have the number (in hex) 10, the function will stop immediately because n % 16 will be 0. What you actually want to test for is when n is 0. Like this
if (n==0)
Your switch statement should also be expanded out to include the values 0 to 9 rather than having a separate if statement after it. You can group them together by leaving out the break
switch(n % 16)
{
/* existing case statements go here */
case 0:
case 1:
case 2:
...
case 9:
printf("%d",n % 16); // also fixing this as per comments above.
break;
}
Or you could just use printf("%x",n % 16) and do away with the switch completely if what you're trying to do is explore how recursion works.

Is there a C compiler that fails to compile this?

I was hanging out in my profiler for a while trying to figure out how to speed up a common log parser which was bottlenecked around the date parsing, and I tried various algorithms to speed things up.
The thing I tried that was fastest for me was also by far the most readable, but potentially non-standard C.
This worked quite well in GCC, icc, and my really old and picky SGI compiler. As it's a quite readable optimization, where doesn't it do what I want?
static int parseMonth(const char *input) {
int rv=-1;
int inputInt=0;
int i=0;
for(i=0; i<4 && input[i]; i++) {
inputInt = (inputInt << 8) | input[i];
}
switch(inputInt) {
case 'Jan/': rv=0; break;
case 'Feb/': rv=1; break;
case 'Mar/': rv=2; break;
case 'Apr/': rv=3; break;
case 'May/': rv=4; break;
case 'Jun/': rv=5; break;
case 'Jul/': rv=6; break;
case 'Aug/': rv=7; break;
case 'Sep/': rv=8; break;
case 'Oct/': rv=9; break;
case 'Nov/': rv=10; break;
case 'Dec/': rv=11; break;
}
return rv;
}
Solaris 10 - SPARC - SUN Compiler.
Test code:
#include <stdio.h>
static int parseMonth(const char *input) {
int rv=-1;
int inputInt=0;
int i=0;
for(i=0; i<4 && input[i]; i++) {
inputInt = (inputInt << 8) | input[i];
}
switch(inputInt) {
case 'Jan/': rv=0; break;
case 'Feb/': rv=1; break;
case 'Mar/': rv=2; break;
case 'Apr/': rv=3; break;
case 'May/': rv=4; break;
case 'Jun/': rv=5; break;
case 'Jul/': rv=6; break;
case 'Aug/': rv=7; break;
case 'Sep/': rv=8; break;
case 'Oct/': rv=9; break;
case 'Nov/': rv=10; break;
case 'Dec/': rv=11; break;
}
return rv;
}
static const struct
{
char *data;
int result;
} test_case[] =
{
{ "Jan/", 0 },
{ "Feb/", 1 },
{ "Mar/", 2 },
{ "Apr/", 3 },
{ "May/", 4 },
{ "Jun/", 5 },
{ "Jul/", 6 },
{ "Aug/", 7 },
{ "Sep/", 8 },
{ "Oct/", 9 },
{ "Nov/", 10 },
{ "Dec/", 11 },
{ "aJ/n", -1 },
};
#define DIM(x) (sizeof(x)/sizeof(*(x)))
int main(void)
{
size_t i;
int result;
for (i = 0; i < DIM(test_case); i++)
{
result = parseMonth(test_case[i].data);
if (result != test_case[i].result)
printf("!! FAIL !! %s (got %d, wanted %d)\n",
test_case[i].data, result, test_case[i].result);
}
return(0);
}
Results (GCC 3.4.2 and Sun):
$ gcc -O xx.c -o xx
xx.c:14:14: warning: multi-character character constant
xx.c:15:14: warning: multi-character character constant
xx.c:16:14: warning: multi-character character constant
xx.c:17:14: warning: multi-character character constant
xx.c:18:14: warning: multi-character character constant
xx.c:19:14: warning: multi-character character constant
xx.c:20:14: warning: multi-character character constant
xx.c:21:14: warning: multi-character character constant
xx.c:22:14: warning: multi-character character constant
xx.c:23:14: warning: multi-character character constant
xx.c:24:14: warning: multi-character character constant
xx.c:25:14: warning: multi-character character constant
$ ./xx
$ cc -o xx xx.c
$ ./xx
!! FAIL !! Jan/ (got -1, wanted 0)
!! FAIL !! Feb/ (got -1, wanted 1)
!! FAIL !! Mar/ (got -1, wanted 2)
!! FAIL !! Apr/ (got -1, wanted 3)
!! FAIL !! May/ (got -1, wanted 4)
!! FAIL !! Jun/ (got -1, wanted 5)
!! FAIL !! Jul/ (got -1, wanted 6)
!! FAIL !! Aug/ (got -1, wanted 7)
!! FAIL !! Sep/ (got -1, wanted 8)
!! FAIL !! Oct/ (got -1, wanted 9)
!! FAIL !! Nov/ (got -1, wanted 10)
!! FAIL !! Dec/ (got -1, wanted 11)
$
Note that the last test case still passed - that is, it generated a -1.
Here's a revised - more verbose - version of parseMonth() which does work the same under both GCC and Sun C compiler:
#include <stdio.h>
/* MONTH_CODE("Jan/") does not reduce to an integer constant */
#define MONTH_CODE(x) ((((((x[0]<<8)|x[1])<<8)|x[2])<<8)|x[3])
#define MONTH_JAN (((((('J'<<8)|'a')<<8)|'n')<<8)|'/')
#define MONTH_FEB (((((('F'<<8)|'e')<<8)|'b')<<8)|'/')
#define MONTH_MAR (((((('M'<<8)|'a')<<8)|'r')<<8)|'/')
#define MONTH_APR (((((('A'<<8)|'p')<<8)|'r')<<8)|'/')
#define MONTH_MAY (((((('M'<<8)|'a')<<8)|'y')<<8)|'/')
#define MONTH_JUN (((((('J'<<8)|'u')<<8)|'n')<<8)|'/')
#define MONTH_JUL (((((('J'<<8)|'u')<<8)|'l')<<8)|'/')
#define MONTH_AUG (((((('A'<<8)|'u')<<8)|'g')<<8)|'/')
#define MONTH_SEP (((((('S'<<8)|'e')<<8)|'p')<<8)|'/')
#define MONTH_OCT (((((('O'<<8)|'c')<<8)|'t')<<8)|'/')
#define MONTH_NOV (((((('N'<<8)|'o')<<8)|'v')<<8)|'/')
#define MONTH_DEC (((((('D'<<8)|'e')<<8)|'c')<<8)|'/')
static int parseMonth(const char *input) {
int rv=-1;
int inputInt=0;
int i=0;
for(i=0; i<4 && input[i]; i++) {
inputInt = (inputInt << 8) | input[i];
}
switch(inputInt) {
case MONTH_JAN: rv=0; break;
case MONTH_FEB: rv=1; break;
case MONTH_MAR: rv=2; break;
case MONTH_APR: rv=3; break;
case MONTH_MAY: rv=4; break;
case MONTH_JUN: rv=5; break;
case MONTH_JUL: rv=6; break;
case MONTH_AUG: rv=7; break;
case MONTH_SEP: rv=8; break;
case MONTH_OCT: rv=9; break;
case MONTH_NOV: rv=10; break;
case MONTH_DEC: rv=11; break;
}
return rv;
}
static const struct
{
char *data;
int result;
} test_case[] =
{
{ "Jan/", 0 },
{ "Feb/", 1 },
{ "Mar/", 2 },
{ "Apr/", 3 },
{ "May/", 4 },
{ "Jun/", 5 },
{ "Jul/", 6 },
{ "Aug/", 7 },
{ "Sep/", 8 },
{ "Oct/", 9 },
{ "Nov/", 10 },
{ "Dec/", 11 },
{ "aJ/n", -1 },
{ "/naJ", -1 },
};
#define DIM(x) (sizeof(x)/sizeof(*(x)))
int main(void)
{
size_t i;
int result;
for (i = 0; i < DIM(test_case); i++)
{
result = parseMonth(test_case[i].data);
if (result != test_case[i].result)
printf("!! FAIL !! %s (got %d, wanted %d)\n",
test_case[i].data, result, test_case[i].result);
}
return(0);
}
I wanted to use MONTH_CODE() but the compilers did not cooperate.
if ( !input[0] || !input[1] || !input[2] || input[3] != '/' )
return -1;
switch ( input[0] )
{
case 'F': return 1; // Feb
case 'S': return 8; // Sep
case 'O': return 9; // Oct
case 'N': return 10; // Nov
case 'D': return 11; // Dec;
case 'A': return input[1] == 'p' ? 3 : 7; // Apr, Aug
case 'M': return input[2] == 'r' ? 2 : 4; // Mar, May
default: return input[1] == 'a' ? 0 : (input[2] == 'n' ? 5 : 6); // Jan, Jun, Jul
}
Slightly less readable and not so much validating, but perhaps even faster, no?
You're just computing a hash of those four characters. Why not predefine some integer constants that compute the hash in the same way and use those? Same readability and you're not depending on any implementation specific idiosyncrasies of the compiler.
uint32_t MONTH_JAN = 'J' << 24 + 'a' << 16 + 'n' << 8 + '/';
uint32_t MONTH_FEB = 'F' << 24 + 'e' << 16 + 'b' << 8 + '/';
...
static uint32_t parseMonth(const char *input) {
uint32_t rv=-1;
uint32_t inputInt=0;
int i=0;
for(i=0; i<4 && input[i]; i++) {
inputInt = (inputInt << 8) | (input[i] & 0x7f); // clear top bit
}
switch(inputInt) {
case MONTH_JAN: rv=0; break;
case MONTH_FEB: rv=1; break;
...
}
return rv;
}
I only know what the C Standard says about this (C99):
The value of an integer character
constant containing more than one
character (e.g., 'ab'), or containing
a character or escape sequence that
does not map to a single-byte
execution character, is
implementation-defined. If an integer
character constant contains a single
character or escape sequence, its
value is the one that results when an
object with type char whose value is
that of the single character or escape
sequence is converted to type int.
(6.4.4.4/10 taken from a draft)
So it's implementation defined. Meaning it is not guaranteed it works the same everywhere, but the behavior must be documented by the implementation. For example if int is only 16 bits wide in a particular implementation, then 'Jan/' can't be represented anymore like you intend it (char must be at least 8 bits, while a character literal is always of type int).
char *months = "Jan/Feb/Mar/Apr/May/Jun/Jul/Aug/Sep/Oct/Nov/Dec/";
char *p = strnstr(months, input, 4);
return p ? (p - months) / 4 : -1;
There are at least 3 things that keep this program from being portable:
Multi-character constants are implementation-defined so different compilers may handle them differently.
A byte can be more than 8 bits, there is plenty of hardware where the smallest addressable unit of memory is 16 or even 32 bits, you often find this in DSPs for example. If a byte is more than 8 bits then so will char since char is by definition one byte long; your program will not function properly on such systems.
Lastly, there are many machines where int is only 16-bits (which is the smallest size allowed for int) including embedded devices and legacy machines, your program will fail on these machines as well.
National Instrument's CVI 8.5 for Windows compiler fails on your original code with multiple warnings:
Warning: Excess characters in multibyte character literal ignored.
and errors of the form:
Duplicate case label '77'.
It succeeds on Jonathan's code.
I get warnings, but no errors (gcc). Seems to compile and operate fine. May not work for big-endian systems, though!
I wouldn't suggest this method, though. Perhaps you can xor instead of or-shift, to create a single byte. Then use the case statement on a byte (or, faster, use a LUT of the first N bits).
The fact that a four character constant is equivalent to an particular 32-bit integer is a non-standard feature often seen on compilers for MS Windows and Mac computers (and PalmOS, AFAICR).
On theses systems a four character string is commonly used as a tag for identifying chunks of data files, or as an application / data-type identifier (e.g. "APPL").
It's a convenience then for the developer that they can store such a string into various data-structures without worrying about zero-byte termination, pointers, etc.
Comeau compiler
Comeau C/C++ 4.3.10.1 (Oct 6 2008 11:28:09) for ONLINE_EVALUATION_BETA2
Copyright 1988-2008 Comeau Computing. All rights reserved.
MODE:strict errors C99
"ComeauTest.c", line 11: warning: multicharacter character literal (potential
portability problem)
case 'Jan/': rv=0; break;
^
"ComeauTest.c", line 12: warning: multicharacter character literal (potential
portability problem)
case 'Feb/': rv=1; break;
^
"ComeauTest.c", line 13: warning: multicharacter character literal (potential
portability problem)
case 'Mar/': rv=2; break;
^
"ComeauTest.c", line 14: warning: multicharacter character literal (potential
portability problem)
case 'Apr/': rv=3; break;
^
"ComeauTest.c", line 15: warning: multicharacter character literal (potential
portability problem)
case 'May/': rv=4; break;
^
"ComeauTest.c", line 16: warning: multicharacter character literal (potential
portability problem)
case 'Jun/': rv=5; break;
^
"ComeauTest.c", line 17: warning: multicharacter character literal (potential
portability problem)
case 'Jul/': rv=6; break;
^
"ComeauTest.c", line 18: warning: multicharacter character literal (potential
portability problem)
case 'Aug/': rv=7; break;
^
"ComeauTest.c", line 19: warning: multicharacter character literal (potential
portability problem)
case 'Sep/': rv=8; break;
^
"ComeauTest.c", line 20: warning: multicharacter character literal (potential
portability problem)
case 'Oct/': rv=9; break;
^
"ComeauTest.c", line 21: warning: multicharacter character literal (potential
portability problem)
case 'Nov/': rv=10; break;
^
"ComeauTest.c", line 22: warning: multicharacter character literal (potential
portability problem)
case 'Dec/': rv=11; break;
^
"ComeauTest.c", line 1: warning: function "parseMonth" was declared but never
referenced
static int parseMonth(const char *input) {
^
Machine word size issues aside, your compiler may promote input[i] to a negative integer which will just set the upper bits of inputInt with or operation, so I suggest you to be explicit about signedness of char variables.
But since in US, no one cares about the 8th bit, it is probably a non-issue for you.
I'd sure love to see the profiling that shows this is your most significant bottleneck, but in any case if you're going to pull something like this, use a union instead of 50 instructions looping and shifting. Here's a little example program, I'll leave it to you to fit it into your program.
/* union -- demonstrate union for characters */
#include <stdio.h>
union c4_i {
char c4[5];
int i ;
} ;
union c4_i ex;
int main (){
ex.c4[0] = 'a';
ex.c4[1] = 'b';
ex.c4[2] = 'c';
ex.c4[3] = 'd';
ex.c4[4] = '\0';
printf("%s 0x%08x\n", ex.c4, ex.i );
return 0;
}
Here's example output:
bash $ ./union
abcd 0x64636261
bash $
As mentioned by others, that code throws a bunch of warnings and is probably not endian-safe.
Was your original date parser hand-written as well? Have you tried strptime(3)?

Resources