Posts

  • May 2020
  • PDF

This document was uploaded by user and they confirmed that they have the permission to share it. If you are author or own the copyright of this book, please report to us by using this DMCA report form. Report DMCA


Overview

Download & View Posts as PDF for free.

More details

  • Words: 19,278
  • Pages: 89
A Collection of Posts C and C++

Dave Sinkula

Rev 56, June 3, 2007

Table of Contents 1 About 2 Tutorials 2.1 User Input: Strings and Numbers [C] 2.2 C and C++ Timesaving Tips 2.3 Finding Array Size 2.4 Reading an Integer 2.5 Avoid Loop Control Using eof()

3 Snippets 3.1 User Input 3.1.1 3.1.2 3.1.3 3.1.4 3.1.5 3.1.6 3.1.7 3.1.8 3.1.9

Safe Version of gets() Read a Line of Text from the User Read a Line of Text from the User, Discard Excess Characters Read an Integer from the User, Part 1 Read an Integer from the User, Part 2 Read an Integer from the User, Part 3 Read a Floating-Point Value from the User, Part 1 Read a Floating-Point Value from the User, Part 2 Read a Floating-Point Value from the User, Part 3

3.2 Strings 3.2.1 3.2.2 3.2.3 3.2.4 3.2.5 3.2.6 3.2.7 3.2.8 3.2.9

Strings: Strings: Strings: Strings: Strings: Strings: Strings: Strings: Strings:

Copying Comparing Comparing, Case-Insensitive Reversal Reversal, Part 2 Concatenation Case Insensitive strstr Finding a Substring Search and Replace

3.3 Parsing 3.3.1 3.3.2 3.3.3 3.3.4

Parsing Parsing Parsing Parsing

a a a a

String String String String

into into into into

3.4 Bits 3.4.1 Binary to Decimal 3.4.2 Bits, Part 1

Tokens Tokens Tokens Tokens

Using Using Using Using

sscanf strcspn 1 strcspn, Part 2 strcspn, Part 3

3.4.3 Bits, Part 2 3.4.4 Bits, Part 3 3.4.5 Bit Reversal

3.5 Time/Date 3.5.1 3.5.2 3.5.3 3.5.4

Time - Displaying Current Time/Date Calculations Days Since a Given Date Days Remaining (30-Day Subscription)

3.6 Miscellaneous 3.6.1 3.6.2 3.6.3 3.6.4 3.6.5 3.6.6 3.6.7 3.6.8 3.6.9

Counting the Number of Lines in File Reading a File Line By Line Currency Converter Currency: Making Change Checking for Integer Overflow Read and Write a Structure to a File in Binary Mode Morse Code Hex Dump XOR Encryption

4 Rambles 4.1 Don't #define Your Array Size! 4.2 Much Ado About Bits 4.3 Silly Code

5 Additional Material 5.1 C Draft Standards 5.2 C++ Books

1 About This is a collection of stuff I've written mostly in response to a number questions frequently asked on C programming forums (such as Cprogramming.com and DaniWeb.com – many of these snippets originated in my my snippets at Daniweb). My intent is to leave this one pretty much as just a capture of a number of these posts. I would later like to develop this into a separate, more coherent version. The code was intended to be copied and pasted where possible. But in the PDF page breaks will screw that up. And unfortunately the indentation is lost as well. The colors used in the code are close to what I see in my editor. Sometimes I use color to highlight particular issues. A particular chunk of code may look like this: #include <stdio.h> #include <string.h> int main() { char text[20]; fputs("enter some text: ", stdout); fflush(stdout); if ( fgets(text, sizeof text, stdin) != NULL ) { char *newline = strchr(text, '\n'); /* search for newline character */ if ( newline != NULL ) { *newline = '\0'; /* overwrite trailing newline */ } printf("text = \"%s\"\n", text); } return 0; }

The “meanings” of particular colors is as follows: regular code: variables, operators, punctuation preprocessor keyword standard symbol (not a function) standard function user-defined function literal (string, number, character) comment

The consistency is not that great, the result of changing preferences over time. And some days I felt like commenting more that others. I am not going to make an effort to spruce up this version, that I'll save for the other one. For showing console input and I output, I color it like this: enter some text: 1234567890123456789 text = "1234567890123456789"

Right now, the organization is rather poor, stay tuned... Return to Table of Contents

2 Tutorials 2.1

User Input: Strings and Numbers [C]

Very early on in our attempt to learn programming in C we will do exercises that read in and print out strings and numbers. We might assume that C has a simple function that will get user input -- and it does -- but there are a couple of gotchas that will trip up those that are new to C. What Not To Do There are two things to vigorously avoid, and unfortunately they are all too common to see. char text[20]; gets(text); /* NEVER USE gets() */ scanf("%s", text); /* MANY HIDDEN GOTCHAS */

Look familiar? I hope not. And I hope it's not from any text or tutorial you've been following. How To Do It Right The short answer is to use fgets to read a line of text. But note that when you enter text, you press the [Enter] or [Return] key. This character does not just vanish, it remains in the input buffer. For example, #include <stdio.h> int main() { char text[20]; fputs("enter some text: ", stdout); fflush(stdout); /* http://c-faq.com/stdio/fflush.html */ fgets(text, sizeof text, stdin); printf("text = \"%s\"\n", text); return 0; }

A sample run: enter some text: hello world text = "hello world "

So when reading strings, we often want to remove this newline. But be careful -- it is not always there! Just enter 19 or more characters with that last bit of code to see. enter some text: 1234567890123456789 text = "1234567890123456789"

When the incoming text is less than (one less than) the size of the buffer, the newline is retained. We can search for the newline and overwrite it with a null terminator. --- skimmers start here ---

#include <stdio.h> #include <string.h> int main() { char text[20]; fputs("enter some text: ", stdout); fflush(stdout); if ( fgets(text, sizeof text, stdin) != NULL ) { char *newline = strchr(text, '\n'); /* search for newline character */ if ( newline != NULL ) { *newline = '\0'; /* overwrite trailing newline */ } printf("text = \"%s\"\n", text); } return 0; }

A sample run: enter some text: hello world text = "hello world"

There are many other ways to do this, but this is very frequently recommended and always correct. --- skimmers stop here --How To Do It Right With Numbers If you are reading a number, first read in the input string and then follow it with a call to sscanf or strtol to convert it to a number. #include <stdio.h> int main() { char text[20]; fputs("enter some number: ", stdout); fflush(stdout); if ( fgets(text, sizeof text, stdin) ) { int number; if ( sscanf(text, "%d", &number) == 1 ) { printf("number = %d\n", number); } } return 0; }

A sample run: enter some number: 42 number = 42

The newline does not need to be removed (if you choose not to) because it is whitespace and is not part of a valid number.

Listed below are related code snippets regarding input of numbers and text. •

Safe Version of gets()



Read a Line of Text from the User



Read a Line of Text from the User, Discard Excess Characters



Read an Integer from the User, Part 1



Read an Integer from the User, Part 2



Read an Integer from the User, Part 3



Read a Floating-Point Value from the User, Part 1



Read a Floating-Point Value from the User, Part 2



Read a Floating-Point Value from the User, Part 3

Why Not To Do What I Said Not To Do First, never use gets. It is included as a standard library function only as a holdover from pre-C89 code (the standards committee opted in the interest of not breaking existing code). But it is inherently and notoriously unsafe. Why? Because there is no way to tell it the size of the buffer into which data will be read. So it is always a risk for buffer overflow, which may be used as an exploit. If you ever want to be employed as a programmer, it's best not to ever let a potential employer see you use this function. And scanf? Well it is a truly complicated beast, so it is certainly not the first choice for someone starting out. And after you've learned many things, you'll probably avoid it anyway. So just avoid the middleman and save yourself a pile of headaches and avoid it from the start. One of the first issues with scanf("%s", str); is it suffers from a very similar problem as gets -- the the incoming buffer size is not specified. And if you didn't look up the description of %s, you may not have known that this directive is whitespacedelimited -- meaning that it if the user enters hello world, then str will be hello. Convinced yet to avoid scanf? Well there are more gotchas. If you are also using scanf to read integers, perhaps using scanf("%d", &i);, the same whitespace issue may bite you. Remember that when you enter text you press the [Enter] or [Return] key and it remains in the input buffer. This can cause issues when mixed with other input functions such a getchar. Or again, it is widely known by good programmers and potential employers that these are things to avoid. But if you still aren't convinced, listed below are many FAQs that say pretty much the same thing (mostly because I'm saying pretty much what they are saying).



http://c-faq.com/stdio/getsvsfgets.html



http://c-faq.com/stdio/scanfprobs.html



http://c-faq.com/stdio/scanfhang.html



http://c-faq.com/stdio/scanfinterlace.html



http://c-faq.com/stdio/scanfc.html



http://c-faq.com/stdio/scanfjam.html



http://faq.cprogramming.com/cgi-bin/...&id=1043284385

And there is another insidious issue that comes up when using scanf: the "need" to flush the input buffer. All too often we see fflush(stdin); used to "fix" this. But actually this one problem with scanf breeds another: •

http://c-faq.com/stdio/stdinflush.html



http://c-faq.com/stdio/stdinflush2.html



http://faq.cprogramming.com/cgi-bin/...&id=1043284351



http://faq.cprogramming.com/cgi-bin/...&id=1043284392

And don't think I presented a comprehensive list of gotchas associated with scanf, these were merely some common ones. Return to Table of Contents

2.2

C and C++ Timesaving Tips

One of my favorite discoveries in C++ regards displaying such a list. Initially one might do this. #include #include using namespace std; int main() { int a[] = {1,2,3,4,5}; vector v(a, a + 5);

}

for (int i = 0; i < 5; ++i) { cout << v[i] << endl; }

Or perhaps even this. #include #include #include using namespace std; int main() { int a[] = {1,2,3,4,5}; vector v(a, a + 5);

}

vector::const_iterator it, end = v.end(); for (it = v.begin(); it != end; ++it) { cout << *it << endl; }

But I happened upon the following that can make things even easier. #include #include #include #include



using namespace std; int main() { int a[] = {1,2,3,4,5}; vector v(a, a + 5); }

copy(v.begin(), v.end(), ostream_iterator(cout, "\n"));

The output for all of these is merely this.

1 2 3 4 5

Return to Table of Contents

2.3

Finding Array Size

Starting Out When first starting out, we might write some code that initializes an array, perhaps does some manipulation, and then displays the results. It might look something like this (contrived example): void foo(void) { int array[10]; int i; /* Initialize array. */ for ( i = 0; i < 10; ++i ) { array[i] = i; } /* Perform calculations. */ for ( i = 0; i < 10; ++i ) { array[i] *= 2; } /* Print array. */ for ( i = 0; i < 10; ++i ) { printf("%d,", array[i]); } putchar('\n'); }

We notice that if we want to change the array dimension, there are several lines of code to edit to make the change. Maintaining code like this is more difficult because in larger sets of code, finding all of the places that need to be changed becomes less trivial. Using a Symbolic Constant To avoid this, it is often recommended to use a symbolic constant instead. #define SIZE 10 void bar(void) { int array[SIZE]; int i; /* Initialize array. */ for ( i = 0; i < SIZE; ++i ) { array[i] = i; } /* Perform calculations. */ for ( i = 0; i < SIZE; ++i ) { array[i] *= 2; } /* Print array. */

}

for ( i = 0; i < SIZE; ++i ) { printf("%d,", array[i]); } putchar('\n');

Now we can see we only need to change the #define to make a change to the array size. Unfortunately, purveyors of this wisdom often stop there. The debugging hazard that this leads to often materializes itself in some header that is used to declare dozens of such constants. Some may like this, but I've found this to be a maintenance headache as well. For example, let's take a look at this code fragment. for ( i = 0; i < ASIZE; ++i ) { printf("%d,", array[i]); }

Is it correct? You can't tell from this alone. You have to find the declaration of the array and double check. That alone is probably not that big of a deal, except when you have data that may be declared far away from this section of code. Another Way A cure is such an old piece of code, it's hard to imagine more people aren't aware of it long before they've learned a struct. It works by dividing the total size of the array (in bytes) by the size of the first element of the array (in bytes); this gives the number of elements. This is calculated at compile time. void baz(void) { int array[10]; size_t i; /* Initialize array. */ for ( i = 0; i < sizeof array / sizeof *array; ++i ) { array[i] = i; } /* Perform calculations. */ for ( i = 0; i < sizeof array / sizeof *array; ++i ) { array[i] *= 2; } /* Print array. */ for ( i = 0; i < sizeof array / sizeof *array; ++i ) { printf("%d,", array[i]); } putchar('\n'); }

Now if you want to change the size of the array, you do it in only one place, and that place happens to be right where you define the array. If we saw the following fragment, is it correct?

for ( i = 0; i < sizeof array / sizeof *array; ++i ) { printf("%d,", array[i]); }

If array is an array in scope, yes. Further Considerations Some programmers may prefer to use a macro to make it easier to read; either of these are examples. #define ARRAYSIZE(x) #define NELEM(x)

(sizeof(x)/sizeof(*(x))) (sizeof(x)/sizeof(x[0]))

So another way to write this is as follows. void qux(void) { int array[10]; size_t i; /* Initialize array. */ for ( i = 0; i < ARRAYSIZE(array); ++i ) { array[i] = i; } /* Perform calculations. */ for ( i = 0; i < NELEM(array); ++i ) { array[i] *= 2; } /* Print array. */ for ( i = 0; i < ARRAYSIZE(array); ++i ) { printf("%d,", array[i]); } putchar('\n'); }

Use With Functions Finally, if you pass "the array" to a function, you are really only passing a pointer to the first element of the array. In such cases, you should also pass a size parameter to the function. void fum(int *array, size_t size) { size_t i; /* Initialize array. */ for ( i = 0; i < size; ++i ) { array[i] = i; } /* Perform calculations. */ for ( i = 0; i < size; ++i ) { array[i] *= 2; } /* Print array. */ for ( i = 0; i < size; ++i )

{

}

printf("%d,", array[i]); } putchar('\n');

int main(void) { int myarray[25]; foo(); bar(); baz(); qux(); fum(myarray, ARRAYSIZE(myarray)); return 0; }

Return to Table of Contents

2.4

Reading an Integer

This is in many FAQs elsewhere, so this is just my repackaging of it here. C Version #include <stdio.h> #include <stdlib.h> int main(void) { char response[32]; /* * Prompt for input. */ fputs ( "Enter your number: ", stdout ); fflush ( stdout ); /* * Read user response. */ if ( fgets ( response, sizeof response, stdin ) != NULL ) { /* * Attempt to convert user's response into an integer using strtol. The * second parameter to strtol allows for greater error checking if used. */ int value = strtol ( response, NULL, 10/* radix or base */ ); /* * No error checking was done, but attempt to display integer value. */ printf ( "value = %d\n", value ); } return 0; } /* my output Enter your number: 12345 value = 12345 */

C++ Version #include #include <string> #include <sstream> int main(void) { std::string response; /* * Prompt for input. */ std::cout << "Enter your number: "; /* * Read user response. */ std::cin >> response; /* * Attempt to convert user's response into an integer using a stringstream.

}

*/ std::istringstream convert(response); int value; if ( convert >> value ) { std::cout << "value = " << value << std::endl; } return 0;

/* my output Enter your number: 456879 value = 456879 */

Return to Table of Contents

2.5

Avoid Loop Control Using eof()

All over the 'net C++ programmers are taught to "read until end of file". This leads to the following (incorrect) idiom for loop control. #include #include int main() { std::ifstream file("file.txt"); if ( file ) { int i; while ( !file.eof() ) // bad!! { file >> i; std::cout << i << ' '; } std::cout << std:: endl; } return 0; }

Run the above code with the following text file, "file.txt". 1 2 3 4 5 6 7 8 9 10

Your output will be like this. 1 2 3 4 5 6 7 8 9 10 10

Note the repeated 10. What To Use Instead Change your thinking to "while successfully reading input from a file" and know that the state of the stream -- indicating successful input or a failure -- is available from the input operation. #include #include int main() { std::ifstream file("file.txt"); if ( file ) { int i; while ( file >> i ) { std::cout << i << ' '; } std::cout << std:: endl; } return 0; }

The output is now correct.

1 2 3 4 5 6 7 8 9 10

Then, for amusement, change "file.txt" to this. 1 2 3 4 5 6 7 8 9 1A

Then run each of the above programs. Another Example A similar thing can be done when reading text. #include #include int main() { std::ifstream file("file.txt"); if ( file ) { char line[80]; while ( file.getline(line, sizeof line) ) { std::cout << line << '\n'; } } return 0; }

Copy this source into "file.txt" and it will print out this code. Further Reading: •

http://www.parashift.com/c++-faq-lit....html#faq-15.2



http://www.parashift.com/c++-faq-lit....html#faq-15.3



http://www.parashift.com/c++-faq-lit....html#faq-15.4



http://www.parashift.com/c++-faq-lit....html#faq-15.5

Return to Table of Contents

3 Snippets 3.1

User Input

3.1.1

Safe Version of gets()

Never use the function gets. Why? It is unsafe. Since the maximum string size cannot be specified, it is always susceptible to buffer overflow. This snippet shows one way to do a safe version of gets. It reads characters from the stdin into a string up to the specified size and discards the trailing newline. It does not remove excess characters from the stdin either. See also Read a Line of Text from the User. #include <stdio.h> char *sgets(char *line, size_t size) { size_t i; for ( i = 0; i < size - 1; ++i ) { int ch = fgetc(stdin); if ( ch == '\n' || ch == EOF ) { break; } line[i] = ch; } line[i] = '\0'; return line; } int main(void) { int i; for ( i = 0; i < 3; ++i ) { char text[20] = ""; fputs("prompt: ", stdout); fflush(stdout); printf("text = \"%s\"\n", sgets(text, sizeof text)); } return 0; } /* my input/output prompt: 1234567890123456789012345 text = "1234567890123456789" prompt: text = "012345" prompt: hello world text = "hello world" */

Return to Table of Contents

3.1.2

Read a Line of Text from the User

Obtaining user input can be done in many surprisingly different ways. This code is somewhere in the middle: safer than gets(text) or scanf("%s", text), but not bulletproof. It is meant to be a simple but relatively safe demonstration. The function mygetline reads user input from the stdin into a string using fgets. Then it attempts to remove a trailing newline that fgets may leave in the string. It returns a pointer to the destination string. See also Safe Version of gets() and Read a Line of Text from the User, Discard Excess Characters. #include <stdio.h> #include <string.h> char *mygetline(char *line, int size) { if ( fgets(line, size, stdin) ) { char *newline = strchr(line, '\n'); /* check for trailing '\n' */ if ( newline ) { *newline = '\0'; /* overwrite the '\n' with a terminating null */ } } return line; } int main(void) { char text[20] = ""; fputs("prompt: ", stdout); fflush(stdout); printf("text = \"%s\"\n", mygetline(text, sizeof text)); return 0; } /* my input/output prompt: hello world text = "hello world" */

Return to Table of Contents

3.1.3

Read a Line of Text from the User, Discard Excess Characters

If the user tries to put 80 characters in a 20-character buffer, you may have issues. This snippet shows one way to cap the input and discard excess input. See also Safe Version of gets() and Read a Line of Text from the User. #include <stdio.h> char *mygettext(char *text, size_t size) { size_t i = 0; for ( ;; ) { int ch = fgetc(stdin); if ( ch == '\n' || ch == EOF ) { break; } if ( i < size - 1 ) { text[i++] = ch; } } text[i] = '\0'; return text; } int main(void) { int i; for ( i = 0; i < 3; ++i ) { char text[20]; fputs("prompt: ", stdout); fflush(stdout); printf("text = \"%s\"\n", mygettext(text, sizeof text)); } return 0; } /* my input/output prompt: 1234567890123456789012345 text = "1234567890123456789" prompt: hello world text = "hello world" prompt: goodbye text = "goodbye" */

Return to Table of Contents

3.1.4

Read an Integer from the User, Part 1

Obtaining user input can be done in many surprisingly different ways. This code is somewhere in the middle: safer than scanf("%d", &n), but not bulletproof. It is meant to be a simple but relatively safe demonstration. The function mygeti reads user input from the stdin into a string using fgets. Then it attempts to convert this string to an integer using sscanf. If both functions succeed, a 1 is returned; if either fails, a 0 is returned. Leading whitespace, and other trailing characters are some things not handled. Those issues are handled in Read an Integer from the User, Part 2. Read a FloatingPoint Value from the User, Part 1 can be done similarly. #include <stdio.h> int mygeti(int *result) { char buff [ 13 ]; /* signed 32-bit value, extra room for '\n' and '\0' */ return fgets(buff, sizeof buff, stdin) && sscanf(buff, "%d", result) == 1; } int main(void) { int value; do { fputs("Enter an integer: ", stdout); fflush(stdout); } while ( !mygeti(&value) ); printf("value = %d\n", value); return 0; } /* my Enter Enter Enter value

output an integer: one an integer: an integer: 42 = 42

Enter an integer: 24f value = 24 Enter an integer: value = 125 */

125 help

Return to Table of Contents

3.1.5

Read an Integer from the User, Part 2

Some issues, such as leading whitespace and trailing characters that cannot be part of a number, were not handled in Read an Integer from the User, Part 1. Here such issues receive lip service. #include <stdio.h> #include int mygeti(int *result) { char c, buff[13]; /* signed 32-bit value, extra room for '\n' and '\0' */ return fgets(buff, sizeof buff, stdin) && !isspace(*buff) && sscanf(buff, "%d%c", result, &c) == 2 && (c == '\n' || c == '\0'); } int main(void) { int value; do { fputs("Enter an integer: ", stdout); fflush(stdout); } while ( !mygeti(&value) ); printf("value = %d\n", value); return 0; } /* my Enter Enter Enter Enter Enter Enter Enter Enter value */

output an integer: an integer: an integer: an integer: an integer: an integer: an integer: an integer: = -42

one f123 123f 123 123 1.23 -42

/* note: this line in the above has a space character following the 123 Enter an integer: 123 */

Return to Table of Contents

3.1.6

Read an Integer from the User, Part 3

This is a strtol version of Read an Integer from the User, Part 2. #include <stdio.h> #include <stdlib.h> #include int mygeti(int *result) { char *end, buff [ 13 ]; fgets(buff, sizeof buff, stdin); *result = strtol(buff, &end, 10); return !isspace(*buff) && end != buff && (*end == '\n' || *end == '\0'); } int main(void) { int value; do { fputs("Enter an integer: ", stdout); fflush(stdout); } while ( !mygeti(&value) ); printf("value = %d\n", value); return 0; } /* my Enter Enter Enter Enter Enter Enter Enter Enter value */

output an integer: an integer: an integer: an integer: an integer: an integer: an integer: an integer: = -42

one f123 123f 123 123 1.23 -42

Return to Table of Contents

3.1.7

Read a Floating-Point Value from the User, Part 1

Obtaining user input can be done in many surprisingly different ways. This code is somewhere in the middle: safer than scanf("%lf", &n), but not bulletproof. It is meant to be a simple but relatively safe demonstration. Note also that there would be slight differences for using float instead of double. The function mygetd reads user input from the stdin into a string using fgets. Then it attempts to convert this string to a double using sscanf. If both functions succeed, a 1 is returned; if either fails, a 0 is returned. Leading whitespace, and other trailing characters are some things not handled. Those issues are handled in Read a Floating-Point Value from the User, Part 2 and Read a Floating-Point Value from the User, Part 3. #include <stdio.h> int mygetd(double *result) { char buff [ 32 ]; /* modify array size to suit your needs */ return fgets(buff, sizeof buff, stdin) && sscanf(buff, "%lf", result) == 1; } int main(void) { double value; do { fputs("Enter a floating-point number: ", stdout); fflush(stdout); } while ( !mygetd(&value) ); printf("value = %g\n", value); return 0; } /* my Enter Enter Enter Enter value

output a floating-point a floating-point a floating-point a floating-point = -45.67

number: one number: number: f12.3 number: -45.67

Enter a floating-point number: -12.3f value = -12.3 Enter a floating-point number: value = 125 */

Return to Table of Contents

125 help

3.1.8

Read a Floating-Point Value from the User, Part 2

Some issues, such as leading whitespace and trailing characters that cannot be part of a number, were not handled in Read a Floating-Point Value from the User, Part 1. Here such issues receive lip service. See also Read a Floating-Point Value from the User, Part 3. #include <stdio.h> #include int mygetd(double *result) { char c, buff [ 32 ]; return fgets(buff, sizeof buff, stdin) && !isspace(*buff) && sscanf(buff, "%lf%c", result, &c) == 2 && (c == '\n' || c == '\0'); } int main(void) { double value; do { fputs("Enter a floating-point number: ", stdout); fflush(stdout); } while ( !mygetd(&value) ); printf("value = %g\n", value); return 0; } /* my Enter Enter Enter Enter value

output a floating-point a floating-point a floating-point a floating-point = -45.67

Enter Enter Enter Enter value */

a a a a =

floating-point floating-point floating-point floating-point 1.23

number: one number: number: f12.3 number: -45.67 number: -12.3f number: 125 help number: 1.2.3 number: 1.23

Return to Table of Contents

3.1.9

Read a Floating-Point Value from the User, Part 3

This snippet uses strtod to be a little more stringent. #include <stdio.h> #include <stdlib.h> #include int mygetd(double *result) { char *end, buff [ 32 ]; return fgets(buff, sizeof buff, stdin) && !isspace(*buff) && (*result = strtod(buff, &end)), ( *end == '\n' || *end == '\0' ); } int main(void) { double value = -12.3; do { fputs("Enter a floating-point number: ", stdout); fflush(stdout); } while ( !mygetd(&value) ); printf("value = %g\n", value); return 0; } /* my Enter Enter Enter Enter value

output a floating-point a floating-point a floating-point a floating-point = -45.67

Enter Enter Enter Enter value */

a a a a =

floating-point floating-point floating-point floating-point 1.23

number: one number: number: f12.3 number: -45.67 number: -12.3f number: 125 help number: 1.2.3 number: 1.23

Return to Table of Contents

3.2

Strings

3.2.1

Strings: Copying

How might I write an implementation in C of the standard library function strcpy? Here's how I might. #include <stdio.h> char *mystrcpy(char *dst, const char *src) { char *start = dst; do { *dst++ = *src; } while ( *src++ ); return start; } int main(void) { char text[12]; puts(mystrcpy(text, "Hello world")); return 0; } /* my output Hello world */

Return to Table of Contents

3.2.2

Strings: Comparing

How might I write an implementation in C of the standard library function strcmp? Here's how I might. See also Strings: Comparing, Case-Insensitive. #include <stdio.h> int mystrcmp(const char *dst, const char *src) { for ( ; *dst && *src; ++src, ++dst ) { if ( *dst != *src ) { break; } } return *dst - *src; } int main(void) { const char *text[] = { "hello", "world", "hello", "hell" }; size_t i, j; for ( i = 0; i < sizeof text / sizeof *text; ++i ) { for ( j = i + 1; j < sizeof text / sizeof *text; ++j ) { printf("mystrcmp(\"%s\", \"%s\") = %d\n", text[i], text[j], mystrcmp(text[i], text[j])); } } return 0; } /* my output mystrcmp("hello", mystrcmp("hello", mystrcmp("hello", mystrcmp("world", mystrcmp("world", mystrcmp("hello", */

"world") = -15 "hello") = 0 "hell") = 111 "hello") = 15 "hell") = 15 "hell") = 111

Return to Table of Contents

3.2.3

Strings: Comparing, Case-Insensitive

How might I write an implementation in C of a case-insensitive version of the standard library function strcmp? Here's how I might. This this is often available as a nonstandard function that may be called stricmp, _stricmp, strcmpi, or strcasecmp. See also Strings: Comparing. #include <stdio.h> #include int mystricmp(const char *a, const char *b) { for ( ; *a && *b; ++a, ++b ) { if ( toupper(*a) != toupper(*b) ) { break; } } return toupper(*a) - toupper(*b); } int main(void) { const char *text[] = { "hello", "world", "Hello", "hell" }; size_t i, j; for ( i = 0; i < sizeof text / sizeof *text; ++i ) { for ( j = i + 1; j < sizeof text / sizeof *text; ++j ) { printf("mystricmp(\"%s\", \"%s\") = %d\n", text[i], text[j], mystricmp(text[i], text[j])); } } return 0; } /* my output mystricmp("hello", mystricmp("hello", mystricmp("hello", mystricmp("world", mystricmp("world", mystricmp("Hello", */

"world") = -15 "Hello") = 0 "hell") = 79 "Hello") = 15 "hell") = 15 "hell") = 79

Return to Table of Contents

3.2.4

Strings: Reversal

This is yet another example of reversing a string. This version modifies the original, reversing the string in place. See also Strings: Reversal, Part 2. #include <stdio.h> char *mystrrev(char *s) { char *t = s, *start = s; /* * Point 't' to the end of the string. */ while ( *t != '\0' ) { ++t; } /* * Swap the values at the beginning (pointed to by 's') and the end * (pointed to by 't'); 's' and 't' meet in the middle. */ for ( --t/* skip terminating null character */; s < t; ++s, --t ) { /* Just your run-of-the-mill swap here. */ char temp = *s; *s = *t; *t = temp; } return start; } int main(void) { char text[] = "Hello world"; puts(text); puts(mystrrev(text)); return 0; } /* my output Hello world dlrow olleH */

Return to Table of Contents

3.2.5

Strings: Reversal, Part 2

In Strings: Reversal, the code reversed the string in place. This version uses a source and a destination string. #include <stdio.h> char *mystrrev(char *dst, const char *src) { const char *end = src, *start = dst; /* * Point 'end' to the end of the 'src' string. */ while ( *end != '\0' ) { ++end; } --end; /* skip terminating null character */ /* * Copy starting the from the end of the 'src' and the beginning of 'dst'. */ while ( src <= end ) { *dst++ = *end--; } *dst = '\0'; /* null terminate the destination string */ return (char*)start; } int main(void) { const char text[] = "Hello world"; char result[sizeof text]; puts(text); puts(mystrrev(result, text)); return 0; } /* my output Hello world dlrow olleH */

Return to Table of Contents

3.2.6

Strings: Concatenation

How might I write an implementation in C of the standard library function strcat? Here's how I might. #include <stdio.h> char *mystrcat(char *dst, const char *src) { char *start = dst; while ( *dst ) { ++dst; } do { *dst++ = *src; } while ( *src++ ); return start; } int main (void) { char text[12] = "Hello", more[] = " world"; puts(text); puts(mystrcat(text, more)); return 0; } /* my output Hello Hello world */

Return to Table of Contents

3.2.7

Strings: Case Insensitive strstr

How might I write an implementation in C a case-insensitive version of the standard library function strstr? Here's how I might. But I need it to be case-sensitive? #include <stdio.h> #include const char *mystristr(const char *haystack, const char *needle) { if ( !*needle ) { return haystack; } for ( ; *haystack; ++haystack ) { if ( toupper(*haystack) == toupper(*needle) ) { /* * Matched starting char -- loop through remaining chars. */ const char *h, *n; for ( h = haystack, n = needle; *h && *n; ++h, ++n ) { if ( toupper(*h) != toupper(*n) ) { break; } } if ( !*n ) /* matched all of 'needle' to null termination */ { return haystack; /* return the start of the match */ } } } return 0; } int main(void) { const char text[] = "The quick brown fox jumps over the lazy dog."; const char word[] = "FoX"; const char *found = mystristr(text, word); if ( found ) { puts(found); } return 0; } /* my output fox jumps over the lazy dog. */

Return to Table of Contents

3.2.8

Strings: Finding a Substring

How might I write an implementation in C of the standard library function strstr? Here's how I might. What if I need it to be case-insensitive? #include <stdio.h> const char *mystrstr(const char *haystack, const char *needle) { if ( !*needle ) { return haystack; } for ( ; *haystack; ++haystack ) { if ( *haystack == *needle ) { /* * Matched starting char -- loop through remaining chars. */ const char *h, *n; for ( h = haystack, n = needle; *h && *n; ++h, ++n ) { if ( *h != *n ) { break; } } if ( !*n ) /* matched all of 'needle' to null termination */ { return haystack; /* return the start of the match */ } } } return 0; } int main(void) { const char text[] = "The quick brown fox jumps over the lazy dog."; const char word[] = "fox"; const char *found = mystrstr(text, word); if ( found ) { puts(found); } return 0; } /* my output fox jumps over the lazy dog. */

Return to Table of Contents

3.2.9

Strings: Search and Replace

Finding some text and replacing it with new text within a C string can be a little trickier than expected. Here is what I had come up with one day. #include <stdio.h> #include <stdlib.h> #include <string.h> /* * Description: * Find and replace text within a string. * * Parameters: * src (in) - pointer to source string * from (in) - pointer to search text * to (in) - pointer to replacement text * * Returns: * Returns a pointer to dynamically-allocated memory containing string * with occurences of the text pointed to by 'from' replaced by with the * text pointed to by 'to'. */ char *replace(const char *src, const char *from, const char *to) { /* * Find out the lengths of the source string, text to replace, and * the replacement text. */ size_t size = strlen(src) + 1; size_t fromlen = strlen(from); size_t tolen = strlen(to); /* * Allocate the first chunk with enough for the original string. */ char *value = malloc(size); /* * We need to return 'value', so let's make a copy to mess around with. */ char *dst = value; /* * Before we begin, let's see if malloc was successful. */ if ( value != NULL ) { /* * Loop until no matches are found. */ for ( ;; ) { /* * Try to find the search text. */ const char *match = strstr(src, from); if ( match != NULL ) { /* * Found search text at location 'match'. :)

* Find out how many characters to copy up to the 'match'. */ size_t count = match - src; /* * We are going to realloc, and for that we will need a * temporary pointer for safe usage. */ char *temp; /* * Calculate the total size the string will be after the * replacement is performed. */ size += tolen - fromlen; /* * Attempt to realloc memory for the new size. */ temp = realloc(value, size); if ( temp == NULL ) { /* * Attempt to realloc failed. Free the previously malloc'd * memory and return with our tail between our legs. :( */ free(value); return NULL; } /* * The call to realloc was successful. :) But we'll want to * return 'value' eventually, so let's point it to the memory * that we are now working with. And let's not forget to point * to the right location in the destination as well. */ dst = temp + (dst - value); value = temp; /* * Copy from the source to the point where we matched. Then * move the source pointer ahead by the amount we copied. And * move the destination pointer ahead by the same amount. */ memmove(dst, src, count); src += count; dst += count; /* * Now copy in the replacement text 'to' at the position of * the match. Adjust the source pointer by the text we replaced. * Adjust the destination pointer by the amount of replacement * text. */ memmove(dst, to, tolen); src += fromlen; dst += tolen; } else /* No match found. */ { /* * Copy any remaining part of the string. This includes the null * termination character. */ strcpy(dst, src);

break; }

} } return value; } void test(const char *source, const char *search, const char *repl) { char *after; after = replace(source, search, repl); printf("\nsearch = \"%s\", repl = \"%s\"\n", search, repl); if ( after != NULL ) { printf("after = \"%s\"\n", after); free(after); } } int main(void) { const char before[] = "the rain in Spain falls mainly on the plain"; printf("before = \"%s\"\n", before); test(before, "the", "THEE"); test(before, "the", "A"); test(before, "cat", "DOG"); test(before, "plain", "PLANE"); test(before, "ain", "AINLY"); return 0; } /* my output before = "the rain in Spain falls mainly on the plain" search = "the", repl = "THEE" after = "THEE rain in Spain falls mainly on THEE plain" search = "the", repl = "A" after = "A rain in Spain falls mainly on A plain" search = "cat", repl = "DOG" after = "the rain in Spain falls mainly on the plain" search = "plain", repl = "PLANE" after = "the rain in Spain falls mainly on the PLANE" search = "ain", repl = "AINLY" after = "the rAINLY in SpAINLY falls mAINLYly on the plAINLY" */

Return to Table of Contents

3.3

Parsing

3.3.1

Parsing a String into Tokens Using sscanf

Many times strtok is recommended for parsing a string; I don't care for strtok. Why? •

It modifies the incoming string, so it cannot be used with string literals or other constant strings.



The identity of the delimiting character is lost.



It uses a static buffer while parsing, so it's not reentrant.



It does not correctly handle "empty" fields -- that is, where two delimiters are back-to-back and meant to denote the lack of information in that field.

This snippet shows a way to use sscanf to parse a string into fields delimited by a character (a semicolon in this case, but commas or tabs or others could be used as well). Thanks to figo2476 for pointing out an issue with a previous version! Thanks to dwks for asking why not to use strtok. #include <stdio.h> int main(void) { const char line[] = "2004/12/03 12:01:59;info1;info2;info3"; const char *ptr = line; char field [ 32 ]; int n; while ( sscanf(ptr, "%31[^;]%n", field, &n) == 1 ) { printf("field = \"%s\"\n", field); ptr += n; /* advance the pointer by the number of characters read */ if ( *ptr != ';' ) { break; /* didn't find an expected delimiter, done? */ } ++ptr; /* skip the delimiter */ } return 0; } /* my field field field field */

output = "2004/12/03 12:01:59" = "info1" = "info2" = "info3"

Return to Table of Contents

3.3.2

Parsing a String into Tokens Using strcspn 1

Many times strtok is recommended for parsing a string; I don't care for strtok. Why? •

It modifies the incoming string, so it cannot be used with string literals or other constant strings.



The identity of the delimiting character is lost.



It uses a static buffer while parsing, so it's not reentrant.



It does not correctly handle "empty" fields -- that is, where two delimiters are back-to-back and meant to denote the lack of information in that field.

This snippet shows a way to use strcspn to parse a string into fields delimited by a character (a semicolon in this case, but commas or tabs or others could be used as well). See also Parsing a String into Tokens Using strcspn 2 and Parsing a String into Tokens Using strcspn 3. Thanks to dwks for asking why not to use strtok. #include <stdio.h> #include <string.h> int main(void) { static const char filename[] = "file.txt"; /* the name of a file to open */ FILE *file = fopen(filename, "r"); /* try to open the file */ if ( file ) { char line[BUFSIZ]; /* space to read a line into */ int k = 0; while ( fgets(line, sizeof line, file) ) /* read each line */ { int i; char *token = line; /* point to the beginning of the line */ printf("line %d:\n", ++k); for ( i = 0; *token; ++i ) /* loop through each character on the line */ { /* search for delimiters */ size_t len = strcspn(token, ";\n"); /* print the found text: use *.* in format to specify a maximum print size */ printf("token[%2d] = \"%*.*s\"\n", i, (int)len, (int)len, token); /* advance pointer by one more than the length of the found text */ token += len + 1; } } fclose(file); } return 0; } /* file.txt

2;31May2005;23:59:00;100.2.1.12;log;accept;;eth9;inbound;VPN-1 FireWall-1;;100.1.20.130;172.30.7.114;icmp;191;;;8;0;;;; */ /* my output line 1: token[ 0] token[ 1] token[ 2] token[ 3] token[ 4] token[ 5] token[ 6] token[ 7] token[ 8] token[ 9] line 2: token[ 0] token[ 1] token[ 2] token[ 3] token[ 4] token[ 5] token[ 6] token[ 7] token[ 8] token[ 9] token[10] token[11] token[12] token[13] */

= = = = = = = = = =

"2" "31May2005" "23:59:00" "100.2.1.12" "log" "accept" "" "eth9" "inbound" "VPN-1"

= = = = = = = = = = = = = =

"FireWall-1" "" "100.1.20.130" "172.30.7.114" "icmp" "191" "" "" "8" "0" "" "" "" ""

Return to Table of Contents

3.3.3

Parsing a String into Tokens Using strcspn, Part 2

This snippet is a variation of the code in Parsing a String into Tokens Using strcspn, Part 1. It uses canned strings rather than reading from a file. See also Parsing a String into Tokens Using strcspn, Part 3. #include <stdio.h> #include <string.h> int main(void) { const char *line[] = { "text1|text2|text3", "text1||text3\n", "" }; size_t j; for ( j = 0; j < sizeof line / sizeof *line; ++j ) { const char *token = line[j]; /* point to the beginning of the line */ printf("line %d:\n", j); for ( ;; ) { size_t len = strcspn(token, "|\n"); /* search for delimiters */ /* * Print the found text: use len with %.*s to specify field width. */ printf(" -> \"%.*s\"\n", (int)len, token); /* * Instead of the above, you could use sprint to copy the text into * a char array. */ token += len; /* advance pointer by the length of the found text */ if ( *token == '\0' ) { break; /* advanced to the terminating null character */ } ++token; /* skip the delimiter */ } } return 0; } /* my output line 0: -> "text1" -> "text2" -> "text3" line 1: -> "text1" -> "" -> "text3" -> "" line 2: -> "" */

Return to Table of Contents

3.3.4

Parsing a String into Tokens Using strcspn, Part 3

Yet another angle at this same topic. See also Parsing a String into Tokens Using strcspn, Part 1 and Parsing a String into Tokens Using strcspn, Part 2. #include <stdio.h> #include <string.h> int main(void) { static const char filename[] = "file.txt"; /* the name of a file to open */ FILE *file = fopen(filename, "r"); /* try to open the file */ if ( file != NULL ) { char line[BUFSIZ]; /* space to read a line into */ /* * Create an array of strings: here each line may be broken up into up * to 20 strings of up to 32 characters. Try changing it to 10 to hit * the "no more room" message. */ char data[20][32]; while ( fgets(line, sizeof line, file) != NULL ) /* read each line */ { size_t i = 0, size; char *token = line; fputs(line, stdout); for ( ;; ) { size_t len = strcspn(token, ";\n"); /* search for delimiters */ /* * Use sprint to copy the text into a char array. */ sprintf(data[i], "%.*s", (int)len, token); token += len; /* advance pointer by the length of the found text */ if ( *token == '\0' ) { break; /* advanced to the terminating null character */ } ++token; /* skip the delimiter */ /* advance the index into the string array */ if ( ++i >= sizeof data / sizeof *data ) { puts("no more room"); break; } } /* * Display the results. */ for ( size = i, i = 0; i < size; ++i ) { printf("data[%d] = \"%s\"\n", (int)i, data[i]); } puts("---");

} fclose(file);

}

} else { perror(filename); /* why didn't the file open? */ } return 0;

/* file.txt 2;31May2005;23:59:00;100.2.1.12;log;accept;;eth9;inbound;VPN-1 FireWall-1;;100.1.20.130;172.30.7.114;icmp;191;;;8;0;;;; */ /* my output 2;31May2005;23:59:00;100.2.1.12;log;accept;;eth9;inbound;VPN-1 data[0] = "2" data[1] = "31May2005" data[2] = "23:59:00" data[3] = "100.2.1.12" data[4] = "log" data[5] = "accept" data[6] = "" data[7] = "eth9" data[8] = "inbound" data[9] = "VPN-1" --FireWall-1;;100.1.20.130;172.30.7.114;icmp;191;;;8;0;;;; data[0] = "FireWall-1" data[1] = "" data[2] = "100.1.20.130" data[3] = "172.30.7.114" data[4] = "icmp" data[5] = "191" data[6] = "" data[7] = "" data[8] = "8" data[9] = "0" data[10] = "" data[11] = "" data[12] = "" data[13] = "" */

Return to Table of Contents

3.4

Bits

3.4.1

Binary to Decimal

This is a very common task for a beginner programmer. Unfortunately many beginners do not seem to understand the question. First, know that binary, decimal, octal, hexadecimal, and others are simply representations of values. The value of 123 (decimal) still has the same value if it represented in hexadecimal (7B), octal (173), or binary (1111011). So the first thing to know is that you are not converting a binary value to a decimal value. What is the question then? You are likely being asked to convert the text representation of a value from binary notation into a value. So it is a conversion of text to an integral value. Like each digit in 123 has place value (1 is in the hundreds place, 2 is in the tens place, and 3 is in the units place), each bit in a binary number has place value as well. Starting from the least significant bit, each next significant bit has twice the place value. Okay, here's some code. #include <stdio.h> /* Convert the text representation of a binary number into an integral value. */ unsigned int btou(const char *bit) { unsigned int value = 0; while ( *bit != NULL ) /* loop through all of the text representation */ { value *= 2; /* double the previous result */ value += *bit - '0'; /* add current bit (0 or 1 from '0' or '1') */ ++bit; /* go on to the next bit */ } return value; } int main ( void ) { static const char *binary[] = { "0000","0001","0010","0011","0100","0101","0110","0111", "1000","1001","1010","1011","1100","1101","1110","1111", "1010010110100101", "11011110101011011011111011101111", }; size_t i; for ( i = 0; i < sizeof binary / sizeof *binary; ++i ) { unsigned int value = btou(binary[i]); printf("btou(\"%s\") = 0x%X = %u\n", binary[i], value, value); } return 0; }

/* my output btou("0000") = 0x0 = 0 btou("0001") = 0x1 = 1 btou("0010") = 0x2 = 2 btou("0011") = 0x3 = 3 btou("0100") = 0x4 = 4 btou("0101") = 0x5 = 5 btou("0110") = 0x6 = 6 btou("0111") = 0x7 = 7 btou("1000") = 0x8 = 8 btou("1001") = 0x9 = 9 btou("1010") = 0xA = 10 btou("1011") = 0xB = 11 btou("1100") = 0xC = 12 btou("1101") = 0xD = 13 btou("1110") = 0xE = 14 btou("1111") = 0xF = 15 btou("1010010110100101") = 0xA5A5 = 42405 btou("11011110101011011011111011101111") = 0xDEADBEEF = 3735928559 */ #if 0 /* of course, if you can use standard library functions, it would be simpler yet */ #include <stdlib.h> unsigned int btou(const char *bit) { return strtoul(bit, NULL, 2); } #endif

Return to Table of Contents

3.4.2

Bits, Part 1

It is often instructive to display the bits of a value. (This is similar to 'converting' an integer to binary.) The way I have done this in this snippet is to start at the most significant bit and work your way through all the rest of the bits. (Continued in Part 2.) #include <stdio.h> void bits_uint(unsigned int value) { unsigned int bit; for ( bit = /* msb */(~0U >> 1) + 1; bit > 0; bit >>= 1 ) { putchar(value & bit ? '1' : '0'); } putchar('\n'); } int main(void) { unsigned int u; /* * Using this code to explain itself a little -or- what is '(~0U >> 1) + 1'? * Short answer: this expression sets only the most significant bit (MSB) * of an unsigned integer. */ u = 0U ; printf(" 0U : "); bits_uint(u); u = ~0U ; printf(" ~0U : "); bits_uint(u); u = ~0U >> 1 ; printf(" ~0U >> 1 : "); bits_uint(u); u = (~0U >> 1) + 1; printf("(~0U >> 1) + 1 : "); bits_uint(u); return 0; } /* my output (spaces edited for display here) 0U ~0U ~0U >> 1 (~0U >> 1) + 1 */

: : : :

00000000000000000000000000000000 11111111111111111111111111111111 01111111111111111111111111111111 10000000000000000000000000000000

Return to Table of Contents

3.4.3

Bits, Part 2

In Part 1, I chose unsigned int as the type to use for displaying the bits of a value. The same general procedure can be done with other types as well. The biggest issue really is the expression which generates the most significant bit, for which there are a number of ways. Also, these snippets output to a string rather than print directly to the stdout. (Continued in Part 3.) #include <stdio.h> #include char *bits_uchar(char *dest, unsigned char d) { char *start = dest; unsigned char bit; for ( bit = 1 << (CHAR_BIT - 1); bit > 0; bit >>= 1 ) { *dest++ = d & bit ? '1' : '0'; } *dest = 0; return start; } char *bits_ushort(char *dest, unsigned short d) { char *start = dest; unsigned short bit; for ( bit = (((unsigned short)-1) >> 1) + 1; bit > 0; bit >>= 1 ) { *dest++ = d & bit ? '1' : '0'; } *dest = 0; return start; } char *bits_uint(char *dest, unsigned int d) { char *start = dest; unsigned int bit; for ( bit = (-1U >> 1) + 1; bit > 0; bit >>= 1 ) { *dest++ = d & bit ? '1' : '0'; } *dest = 0; return start; } char *bits_ulong(char *dest, unsigned long d) { char *start = dest; unsigned long bit; for ( bit = ULONG_MAX / 2 + 1; bit > 0; bit >>= 1 ) { *dest++ = d & bit ? '1' : '0'; } *dest = 0; return start; }

char *bits_block(char *dest, const void *object, size_t size) { char *start = dest; const unsigned char *byte; for ( byte = object; size-- > 0; ++byte ) { bits_uchar(dest, *byte); dest += CHAR_BIT; *dest++ = size > 0 ? '-' : 0; } return start; } int main(void) { char result[80]; unsigned unsigned unsigned unsigned double d

char uc = short uh = int ui = long ul = = 123.456;

0x5A; 0x1234; 0xABCD; 0xDEADBEEF;

printf("0x%-8X = \"%s\"\n", printf("0x%-8hX = \"%s\"\n", printf("0x%-8X = \"%s\"\n", printf("0x%-8lX = \"%s\"\n", printf("0x%-8lX = \"%s\"\n", printf("%-10g = \"%s\"\n", }

uc, uh, ui, ul, ul, d,

bits_uchar (result, uc) ); bits_ushort (result, uh) ); bits_uint (result, ui) ); bits_ulong (result, ul) ); bits_block(result, &ul, sizeof ul) ); bits_block(result, &d, sizeof d) );

return 0;

/* my output 0x5A 0x1234 0xABCD 0xDEADBEEF 0xDEADBEEF 123.456 01000000" */

= = = = = =

"01011010" "0001001000110100" "00000000000000001010101111001101" "11011110101011011011111011101111" "11101111-10111110-10101101-11011110" "01110111-10111110-10011111-00011010-00101111-11011101-01011110-

Return to Table of Contents

3.4.4

Bits, Part 3

In Part 2, you may have noticed that the bits representing the value of an unsigned int did not appear to match the bits representing the object. This is because of the endianness of my implementation. When it comes to displaying the bits of objects that take more space that a char, this issue will come into play. #include <stdio.h> #include char *bits_le(char *dst, const void *object, size_t size) { char *start = dst; const unsigned char *byte = object; for ( byte += size - 1; size > 0; --size, --byte ) { unsigned char bit; for ( bit = 1 << (CHAR_BIT * sizeof *byte - 1); bit; bit >>= 1 ) { *dst++ = (*byte & bit) ? '1' : '0'; } } *dst = '\0'; return start; } char *bits_be(char *dst, const void *object, size_t size) { char *start = dst; const unsigned char *byte = object; for ( /* no additional initialization */; size > 0; --size, ++byte ) { unsigned char bit; for ( bit = 1 << (CHAR_BIT * sizeof *byte - 1); bit; bit >>= 1 ) { *dst++ = (*byte & bit) ? '1' : '0'; } } *dst = '\0'; return start; } int main(void) { unsigned long value = 0x12345678; double d = 123.456; char buffer [ sizeof d * CHAR_BIT + 1 ]; printf("value = 0x%lX: %s\n", value, bits_le(buffer, printf("value = 0x%lX: %s\n", value, bits_be(buffer, printf("d = %g: %s\n", d, bits_le(buffer, &d, sizeof printf("d = %g: %s\n", d, bits_be(buffer, &d, sizeof return 0; }

&value, sizeof value)); &value, sizeof value)); d)); d));

/* my output value = 0x12345678: 00010010001101000101011001111000 value = 0x12345678: 01111000010101100011010000010010 d = 123.456: 0100000001011110110111010010111100011010100111111011111001110111 d = 123.456: 0111011110111110100111110001101000101111110111010101111001000000 */

Return to Table of Contents

3.4.5

Bit Reversal

Several methods of reversing the bits of a byte are presented here. Note that not all of the methods are portable. #include <stdio.h> #include #include unsigned char foo(unsigned char value) { unsigned char mask = 1 << (CHAR_BIT - 1), result = 0; while ( value ) /* skip most significant bits that are zero */ { if ( value & 1 ) /* replace mod (machine dependency) */ { result |= mask; } mask >>= 1; value >>= 1; } return result; } unsigned char bar(unsigned char num) { unsigned char byte = 0; int i = 0; do { if ( num % 2 ) { byte = byte << 1; byte |= 0x01; } else byte = byte << 1; num = num >> 1; i++; } while ( i & 7 ); return byte; } unsigned char baz(unsigned char value) { union byteu { unsigned char byte; struct { unsigned int u0:1; unsigned int u1:1; unsigned int u2:1; unsigned int u3:1; unsigned int u4:1; unsigned int u5:1; unsigned int u6:1;

unsigned int u7:1; } sbyte; } a, b; a.byte = value; b.sbyte.u7 b.sbyte.u6 b.sbyte.u5 b.sbyte.u4 b.sbyte.u3 b.sbyte.u2 b.sbyte.u1 b.sbyte.u0 }

= = = = = = = =

a.sbyte.u0; a.sbyte.u1; a.sbyte.u2; a.sbyte.u3; a.sbyte.u4; a.sbyte.u5; a.sbyte.u6; a.sbyte.u7;

return b.byte;

unsigned char qux(unsigned char value) { unsigned char result = 0; if ( value & 0x80 ) result |= 0x01; if ( value & 0x40 ) result |= 0x02; if ( value & 0x20 ) result |= 0x04; if ( value & 0x10 ) result |= 0x08; if ( value & 0x08 ) result |= 0x10; if ( value & 0x04 ) result |= 0x20; if ( value & 0x02 ) result |= 0x40; if ( value & 0x01 ) result |= 0x80; return result; } unsigned char zot(unsigned char value) { value = (value & 0x0f) << 4 | (value & 0xf0) >> value = (value & 0x33) << 2 | (value & 0xcc) >> value = (value & 0x55) << 1 | (value & 0xaa) >> return value; } unsigned char fum(unsigned char value) { static const unsigned char table[256] = { 0x00,0x80,0x40,0xC0,0x20,0xA0,0x60,0xE0, 0x10,0x90,0x50,0xD0,0x30,0xB0,0x70,0xF0, 0x08,0x88,0x48,0xC8,0x28,0xA8,0x68,0xE8, 0x18,0x98,0x58,0xD8,0x38,0xB8,0x78,0xF8, 0x04,0x84,0x44,0xC4,0x24,0xA4,0x64,0xE4, 0x14,0x94,0x54,0xD4,0x34,0xB4,0x74,0xF4, 0x0C,0x8C,0x4C,0xCC,0x2C,0xAC,0x6C,0xEC, 0x1C,0x9C,0x5C,0xDC,0x3C,0xBC,0x7C,0xFC, 0x02,0x82,0x42,0xC2,0x22,0xA2,0x62,0xE2, 0x12,0x92,0x52,0xD2,0x32,0xB2,0x72,0xF2, 0x0A,0x8A,0x4A,0xCA,0x2A,0xAA,0x6A,0xEA, 0x1A,0x9A,0x5A,0xDA,0x3A,0xBA,0x7A,0xFA, 0x06,0x86,0x46,0xC6,0x26,0xA6,0x66,0xE6, 0x16,0x96,0x56,0xD6,0x36,0xB6,0x76,0xF6, 0x0E,0x8E,0x4E,0xCE,0x2E,0xAE,0x6E,0xEE,

4; 2; 1;

0x1E,0x9E,0x5E,0xDE,0x3E,0xBE,0x7E,0xFE, 0x01,0x81,0x41,0xC1,0x21,0xA1,0x61,0xE1, 0x11,0x91,0x51,0xD1,0x31,0xB1,0x71,0xF1, 0x09,0x89,0x49,0xC9,0x29,0xA9,0x69,0xE9, 0x19,0x99,0x59,0xD9,0x39,0xB9,0x79,0xF9, 0x05,0x85,0x45,0xC5,0x25,0xA5,0x65,0xE5, 0x15,0x95,0x55,0xD5,0x35,0xB5,0x75,0xF5, 0x0D,0x8D,0x4D,0xCD,0x2D,0xAD,0x6D,0xED, 0x1D,0x9D,0x5D,0xDD,0x3D,0xBD,0x7D,0xFD, 0x03,0x83,0x43,0xC3,0x23,0xA3,0x63,0xE3, 0x13,0x93,0x53,0xD3,0x33,0xB3,0x73,0xF3, 0x0B,0x8B,0x4B,0xCB,0x2B,0xAB,0x6B,0xEB, 0x1B,0x9B,0x5B,0xDB,0x3B,0xBB,0x7B,0xFB, 0x07,0x87,0x47,0xC7,0x27,0xA7,0x67,0xE7, 0x17,0x97,0x57,0xD7,0x37,0xB7,0x77,0xF7, 0x0F,0x8F,0x4F,0xCF,0x2F,0xAF,0x6F,0xEF, 0x1F,0x9F,0x5F,0xDF,0x3F,0xBF,0x7F,0xFF, }; return table[value]; } #define CYCLES 100000000 int main(void) { const char *name[] = { "foo", "bar", "baz", "qux", "zot", "fum" }; unsigned char (*const function[])(unsigned char) = { foo, bar, baz, qux, zot, fum }; size_t i,j; fflush(stdout); for ( i = 0; i < sizeof(function)/sizeof(*function); ++i ) { clock_t end, start; printf("%s(%X) = %X", name [ i ], 0x5C, function [ i ] (0x5C)); fflush(stdout); start = clock(); for ( j = 0; j < CYCLES; ++j ) { function [ i ] (j); } end = clock(); printf(", elapsed time = %g\n", (end - start) / (double)CLOCKS_PER_SEC); } return 0; } /* my output (Borland C++ 5.5.1 for Win32) foo(5C) bar(5C) baz(5C) qux(5C) zot(5C) fum(5C) */

= = = = = =

3A, 3A, 3A, 3A, 3A, 3A,

elapsed elapsed elapsed elapsed elapsed elapsed

time time time time time time

Return to Table of Contents

= = = = = =

12.789 18.366 24.646 4.496 9.684 1.592

3.5

Time/Date

3.5.1

Time - Displaying Current

This snippet shows how to use the functions time, localtime, and strftime to pick off only the time parts for display. #include <stdio.h> #include int main(void) { time_t now; /* * The next line attempts to get the current time. * The result is stored in 'now'. * If the call to time() fails, it returns (time_t)(-1); verify success * before continuing on. */ if ( time(&now) != (time_t)(-1) ) { /* * Declare a variable 'mytime' and initialize it to a pointer to the * result of calling localtime(). * The localtime() function converts the calendar time in 'now' into a * broken-down time, expressed as local time. * This is needed for the call to strftime(). */ struct tm *mytime = localtime(&now); /* * If localtime() fails, it returns a null pointer; verify success before * continuing on. */ if ( mytime ) { char buffer [ 32 ]; /* * Use strftime() to create a string representation of the broken-down * time. * If strftime() fails, zero is returned and the contents of the array * are indeterminate; verify success before continuing on. */ if ( strftime(buffer, sizeof buffer, "%X", mytime) ) { /* * We were able to get the current time, we were able to convert * this value to local time and point to its structure, we were able * to create a string in our chosen format, so let's print it. * The double quotes help show the full contents of the string we * have created. */ printf("buffer = \"%s\"\n", buffer); } } } return 0; }

/* my output buffer = "09:09:26" */

Return to Table of Contents

3.5.2

Time/Date Calculations

Some issues with date/time calculations. [Perhaps I ought to edit this one day and better describe it.] #include <stdio.h> #include const char *dtstamp(int days, const char *format) { static const char *errmsg[] = { "the calendar time is not available", "the specified time cannot be converted to local time", "the calendar time cannot be represented", "strftime conversion failure" }; static char result [ 80 ]; time_t t; struct tm *local; /* * Try to obtain the implementation's best approximation to the current * calendar time. */ if ( time ( &t ) == (time_t)(-1) ) { return errmsg[0]; } /* * Try to convert the calendar time pointed into a broken-down time. */ local = localtime ( &t ); if ( !local ) { return errmsg[1]; } /* * It might be prudent to check for integer over/underflow here, but I'll * live dangerously and just adjust by the offset. */ local->tm_mday += days; /* * Try to convert the adjusted broken-down time into a calendar time value. */ t = mktime ( local ); if ( t == (time_t)(-1) ) { return errmsg[2]; } /* * Try to convert the adjusted calendar time pointed into a broken-down time. */ local = localtime ( &t ); if ( !local ) { return errmsg[1]; }

}

/* * Try to created a formatted output string. */ if ( !strftime ( result, sizeof result, format, local ) ) { return errmsg[3]; } return result;

int main(void) { static const char Iso8601[] = "%Y-%m-%dT%X"; int offset; offset = -10; printf("%d days from today is %s\n", offset, dtstamp(offset, Iso8601)); offset = 10; printf("%d days from today is %s\n", offset, dtstamp(offset, Iso8601)); return 0; } /* my output -10 days from today is 2005-05-06T22:31:11 10 days from today is 2005-05-26T22:31:11 */

Return to Table of Contents

3.5.3

Days Since a Given Date

This snippet shows one way to calculate the number of days since a given date until present time. Note that this code will not work for dates outside of the current epoch, which typically begins on January 1, 1970. #include <stdio.h> #include int days_diff(int y, int m, int d) { time_t now, then; struct tm date = {0}; date.tm_year = y - 1900; date.tm_mon = m - 1; date.tm_mday = d;

}

if ( time ( &now ) != (time_t)(-1) ) { then = mktime ( &date ); if ( then != (time_t)(-1) ) { fputs(ctime(&now), stdout); fputs(ctime(&then), stdout); return difftime(now, then) / (24 * 60 * 60); } } return 0;

int main(void) { printf("days = %d\n", days_diff(1970, 3, 8)); return 0; } /* my output Mon Aug 22 11:51:06 2005 Sun Mar 08 00:00:00 1970 days = 12951 */

Return to Table of Contents

3.5.4

Days Remaining (30-Day Subscription)

This snippet started with this question. Essentially it put some of the pieces previously mentioned together in a little different way – because it was doing something different. So I guess it was to further expand on some of the date/time issues or handling. But when I was new to standard date and time functions, I found it quite confusing. So I figured another example wouldn't hurt. #include <stdio.h> #include int main(void) { char buffer[BUFSIZ]; double diff; time_t begin, end, now; struct tm *local, stop, start = {0}; /* * Obtain start date. Here I'll fake it (May 10, 2007). */ start.tm_year = 2007 - 1900; start.tm_mon = 5 - 1; start.tm_mday = 10; /* * Make this into a "real" time. */ begin = mktime(&start); if ( begin == (time_t)-1 ) { return 0; } /* * Then localize it. */ local = localtime(&begin); if ( !local ) { return 0; } start = *local; /* start is now normalized and localized */ /* * This far we've just been dealing with the start date. The normalization * and localization is to get things in their proper ranges and such. That * is, no such thing as May 119th, for example. * * Now we get to pretty much repeat the process with the end date. */ stop = start; /* a copy of the start */ stop.tm_mday += 30; /* add 30 days */ /* * Now normalize the end date. */ end = mktime(&stop); if ( end == (time_t)-1 ) { return 0; } /*

* And let's find out the current date and time. */ now = time(NULL); if ( now == (time_t)-1 ) { return 0; } /* * All righty, let's find out how much time in seconds is between now and * the end. */ diff = difftime(end, now); /* * Then convert seconds into days, and then lop off the fraction. */ diff /= 60 * 60 * 24; diff = (int)diff; printf("diff = %g\n", diff); /* only for demonstration purposes, remove */ /* * But let's dress this up a bit. Let's localize the end. */ local = localtime(&end); if ( !local ) { return 0; } /* * And bear with me, let's localize the current time as well. */ local = localtime(&now); if ( !local ) { return 0; } /* * Now we can use strftime on both the start and the end to make nice * messages. */ strftime(buffer, sizeof buffer, "%A %B %d, %Y", &start); printf("Your subscription began %s\n", buffer); strftime(buffer, sizeof buffer, "%A %B %d, %Y", &stop); printf("Your subscription will end %s\n", buffer); strftime(buffer, sizeof buffer, "%A %B %d, %Y", local); printf("Today is %s. You have %g days remaining.\n", buffer, diff); /* * And we're done! */ return 0; } /* my output diff = 17 Your subscription began Thursday May 10, 2007 Your subscription will end Saturday June 09, 2007 Today is Tuesday May 22, 2007. You have 17 days remaining. */

Return to Table of Contents

3.6

Miscellaneous

3.6.1

Counting the Number of Lines in File

This snippet shows one way to count the number of lines in a file. It will count the last line of a file even if the last line does not end in a newline. #include <stdio.h> int main(int argc, const char *const *argv) { if ( argc > 1 ) { FILE *file = fopen(argv[1], "r"); /* Get filename from command line. */ if ( file ) { int ch, prev = '\n' /* so empty files have no lines */, lines = 0; while ( (ch = fgetc(file)) != EOF ) /* Read all chars in the file. */ { if ( ch == '\n' ) { ++lines; /* Bump the counter for every newline. */ } prev = ch; /* Keep a copy to later test whether... */ } fclose(file); if ( prev != '\n' ) /* ...the last line did not end in a newline. */ { ++lines; /* If so, add one more to the total. */ } printf("lines = %d\n", lines); } else { perror(argv[1]); } } return 0; }

Usage: C:\>linecnt linecnt.c lines = 32

Return to Table of Contents

3.6.2

Reading a File Line By Line

While not terribly difficult, first timers may not know the functions to look up and use. Here is some basic code for reading a file one line at a time. The contents of the file are printed to the stdout. #include <stdio.h> int main ( void { static const FILE *file = if ( file != { char line

) char filename[] = "file.txt"; fopen ( filename, "r" ); NULL ) [ 128 ]; /* or other suitable maximum line size */

while ( fgets ( line, sizeof line, file ) != NULL ) /* read a line */ { fputs ( line, stdout ); /* write the line */ } fclose ( file ); } else { perror ( filename ); /* why didn't the file open? */ } return 0; } /* file.txt This text is the contents of the file named, "file.txt". It is being used with some example code to demonstrate reading a file line by line. More interesting things could be done with the output, but I'm trying to keep this example very simple. */ /* my output This text is the contents of the file named, "file.txt". It is being used with some example code to demonstrate reading a file line by line. More interesting things could be done with the output, but I'm trying to keep this example very simple. */

Return to Table of Contents

3.6.3

Currency Converter

This problem is a typical homework question. The code presented here may contain some elements that may be a little "ahead of the game" in coursework, but it presents some of the basic elements that might be used in such an exercise. #include <stdio.h> #include <string.h> const char Description[] = "Currency Conversion As of June 11, 2003 2:35am GMT"; void convert(double amount, const char *from, const char *to) { static const struct { const char *name; double rate; } Xchg[] = /* US Dollar conversion factor */ { { "USD", 1.0 }, { "Lire", 0.000603049 }, { "Yen", 0.008464602 }, { "Pound", 1.65123 }, { "Peso", 0.0940910 }, { "Canadian", 0.734061 }, }; size_t i,j; /* * Search for the name of the currency you want to convert from. */ for ( i = 0; i < sizeof Xchg / sizeof *Xchg; ++i ) { if ( strcmp(Xchg[i].name, from) == 0 ) { /* * Found the 'from' currency name; 'i' is the index of it. * Search for the name of the currency you want to convert to. */ for ( j = 0; j < sizeof Xchg / sizeof *Xchg; ++j ) { if ( strcmp(Xchg[j].name, to) == 0 ) { /* * Found the 'to' currency name; 'j' is the index of it. * Calculate the new value: multiply by the 'from' rate and * divide by the 'to' rate. */ double value = amount * Xchg[i].rate / Xchg[j].rate; printf("%g %s = %g %s\n", amount, from, value, to); return; } } printf("Cannot convert to \"%s\"\a\n", to); /* 'to' name not found */ return; } } printf("Cannot convert from \"%s\"\a\n", from); /* 'from' name not found */

} int main(void) { puts(Description); convert( 20.0, "USD", convert(100.0, "Yen", convert( 20.0, "Euro", convert( 20.0, "USD", return 0; }

"Pound"); "Lire" ); "Krone"); "Krone");

/* my output Currency Conversion As of June 11, 2003 2:35am GMT 20 USD = 12.1122 Pound 100 Yen = 1403.63 Lire Cannot convert from "Euro" Cannot convert to "Krone" */

Return to Table of Contents

3.6.4

Currency: Making Change

Break down an amount into units of dollars and cents. #include <stdio.h> void breakdown(double amount) { size_t i; struct currency { int cents; const char *name; } Unit[] = { { 10000, "hundreds" }, { 5000, "fifties" }, { 2000, "twenties" }, { 1000, "tens" }, { 500, "fives" }, { 100, "ones" }, { 25, "quarters" }, { 10, "dimes" }, { 5, "nickels" }, { 1, "pennies" }, }; int cents = amount * 100 + 0.5; /* add 0.5 to round */ printf("$%.2f : ", amount); for ( i = 0; i < sizeof Unit / sizeof *Unit; ++i ) { int count = cents / Unit[i].cents; if ( count ) { printf("%d %s, ", count, Unit[i].name); } cents %= Unit[i].cents; } putchar('\n'); } int main() { double cost = 20.21, tendered = 50.00, change = tendered - cost; breakdown(cost); breakdown(tendered); breakdown(change); return 0; } /* my output $20.21 : 1 twenties, 2 dimes, 1 pennies, $50.00 : 1 fifties, $29.79 : 1 twenties, 1 fives, 4 ones, 3 quarters, 4 pennies, */

Return to Table of Contents

3.6.5

Checking for Integer Overflow

To check for integer overflow, you can't simply check the outcome of an operation, you need to check before the operation. So how do you do that? This example checks for overflow with multiplication. #include <stdio.h> #include int foo(int a, int b, int *result) { if ( a > 0 && b > 0 ) { /* * 'a' is positive, 'b' is positive, product will be positive. * See if one positive value 'b' is more than the most positive * divided by the other positive value 'a' (positive quotient). */ if ( b > INT_MAX / a ) { return -1; } } else if ( a > 0 && b < 0 ) { /* * 'a' is positive, 'b' is negative, product will be negative. * See if the negative value 'b' is less than the most negative * divided by the positive value 'a' (negative quotient). */ if ( b < INT_MIN / a ) { return -1; } } else if ( a < 0 && b > 0 ) { /* * 'a' is positive, 'b' is negative, product will be negative. * See if the negative value 'a' is less than the most negative * divided by the positive value 'b' (negative quotient). */ if ( a < INT_MIN / b ) { return -1; } } else /* if (a < 0 && b < 0) */ { /* * 'a' is negative, 'b' is negative, product will be positive. * See if one negative value 'b' is less than the most positive * divided by the other negative value 'a' (negative quotient). */ if ( b < INT_MAX / a ) { return -1; }

int

int

int

int

} *result = a * b; return 0; } void bar(int a, int b) { int product; printf("a = %d, b = %d, ", a, b); if ( foo(a, b, &product) < 0 ) { puts("overflow warning"); } else { printf("a * b = %d\n", a * b); } } int main(void) { int a, b; printf("INT_MAX = %d\n", printf("INT_MIN = %d\n", for ( a = 10, b = 5; a { bar(a,b); } for ( a = 10, b = -5; a { bar(a,b); } for ( a = -10, b = 5; a { bar(a,b); } for ( a = -10, b = -5; a { bar(a,b); } return 0; }

INT_MAX); INT_MIN); < INT_MAX / 10; a *= 10, b *= 10 )

< INT_MAX / 10; a *= 10, b *= 10 )

> INT_MIN / 10; a *= 10, b *= 10 )

> INT_MIN / 10; a *= 10, b *= 10 )

/* my output INT_MAX = 2147483647 INT_MIN = -2147483648 a = 10, b = 5, a * b = 50 a = 100, b = 50, a * b = 5000 a = 1000, b = 500, a * b = 500000 a = 10000, b = 5000, a * b = 50000000 a = 100000, b = 50000, overflow warning a = 1000000, b = 500000, overflow warning a = 10000000, b = 5000000, overflow warning a = 100000000, b = 50000000, overflow warning a = 10, b = -5, a * b = -50 a = 100, b = -50, a * b = -5000 a = 1000, b = -500, a * b = -500000 a = 10000, b = -5000, a * b = -50000000 a = 100000, b = -50000, overflow warning a = 1000000, b = -500000, overflow warning a = 10000000, b = -5000000, overflow warning

a a a a a a a a a a a a a a a a a */

= = = = = = = = = = = = = = = = =

100000000, b = -50000000, overflow warning -10, b = 5, a * b = -50 -100, b = 50, a * b = -5000 -1000, b = 500, a * b = -500000 -10000, b = 5000, a * b = -50000000 -100000, b = 50000, overflow warning -1000000, b = 500000, overflow warning -10000000, b = 5000000, overflow warning -100000000, b = 50000000, overflow warning -10, b = -5, a * b = 50 -100, b = -50, a * b = 5000 -1000, b = -500, a * b = 500000 -10000, b = -5000, a * b = 50000000 -100000, b = -50000, overflow warning -1000000, b = -500000, overflow warning -10000000, b = -5000000, overflow warning -100000000, b = -50000000, overflow warning

Return to Table of Contents

3.6.6

Read and Write a Structure to a File in Binary Mode

This snippet asks the user to fill in a structure and saves each record to the file. It uses append mode to add each new record to the end of the file. After all input is taken, it prints out the contents of the file. Disclaimer: fread and fwrite may not work portably between different implementations. Meaning, results may vary from platform to platform, compiler to compiler, or even with the same compiler with a different set of options or a different version. Better code on the way(?). #include <stdio.h> #include <string.h> #include char *mygetline(char *line, int size) { if ( fgets(line, size, stdin) ) { char *newline = strchr(line, '\n'); /* check for trailing '\n' */ if ( newline ) { *newline = '\0'; /* overwrite the '\n' with a terminating null */ } } return line; } int mygeti(int *result) { char buff [ 13 ]; /* signed 32-bit value, extra room for '\n' and '\0' */ return fgets(buff, sizeof buff, stdin) && sscanf(buff, "%d", result) == 1; } struct record { char name[30]; int age; }; void record_write(const char *filename) { FILE *file = fopen(filename, "ab"); /* append to the file in binary mode */ if ( file != NULL ) { struct record data; /* * Read a string from the user. Put it into the structure. */ printf("Enter name: "); fflush(stdout); mygetline(data.name, sizeof data.name); /* * Read a string from the user. Put it into the structure. */

do {

}

printf("Enter age: "); fflush(stdout); } while ( !mygeti(&data.age) ); /* * Write the data to the file. */ fwrite(&data, sizeof data, 1, file); fclose(file);

} void record_readall(const char *filename) { FILE *file = fopen(filename, "rb"); /* read from the file in binary mode */ if ( file != NULL ) { struct record data; while ( fread(&data, sizeof data, 1, file) == 1 ) /* read all records */ { printf("%s,%d\n", data.name, data.age); /* print each */ } fclose(file); } } int main() { static const char filename[] = "output.dat"; /* * Loop as long as the user wants to add records. */ for ( ;; ) { char line[10]; fputs("Add record (Y/N)? ", stdout); fflush(stdout); mygetline(line, sizeof line); if ( tolower(*line) == 'n' ) { break; } record_write(filename); } /* * Show all records in the file. */ record_readall(filename); return 0; } /* my output C:\Test>test Add record (Y/N)? y Enter name: Dave Enter age: 35 Add record (Y/N)? N

Dave,35 C:\Test>test Add record (Y/N)? Y Enter name: Vlad Enter age: 39 Add record (Y/N)? y Enter name: Jose Enter age: 32 Add record (Y/N)? n Dave,35 Vlad,39 Jose,32 C:\Test>test Add record (Y/N)? n Dave,35 Vlad,39 Jose,32 C:\Test>test Add record (Y/N)? y Enter name: Bill Enter age: four Enter age: Enter age: 40 Add record (Y/N)? n Dave,35 Vlad,39 Jose,32 Bill,40 */

Return to Table of Contents

3.6.7

Morse Code

This code uses a structure to map a letter to an equivalent representation of Morse code. It simply loops through either a string to encode or decode looking for text to substitute with a replacement string or character. Output is presented to the stdout. #include <stdio.h> #include <string.h> #include static const struct { const char letter, } Code[] = { { 'A', ".- " },{ { 'E', ". " },{ { 'I', ".. " },{ { 'M', "-- " },{ { 'Q', "--.- " },{ { 'U', "..- " },{ { 'Y', "-.-- " },{ };

*morse; 'B', 'F', 'J', 'N', 'R', 'V', 'Z',

"-... " "..-. " ".--- " "-. " ".-. " "...- " "--.. "

},{ },{ },{ },{ },{ },{ },{

'C', 'G', 'K', 'O', 'S', 'W', ' ',

"-.-. " "--. " ".-.- " "--- " "... " ".-- " " "

},{ },{ },{ },{ },{ },{ },

void encode(const char *s) { size_t i, j; for ( i = 0; s[i]; ++i ) { for ( j = 0; j < sizeof Code / sizeof *Code; ++j ) { if ( toupper(s[i]) == Code[j].letter ) { printf("%s", Code[j].morse); break; } } } putchar('\n'); }

'D', 'H', 'L', 'P', 'T', 'X',

"-.. " ".... " ".-.. " ".--. " "- " "-..- "

}, }, }, }, }, },

void decode(const char *morse) { size_t i, j; for ( i = 0; morse[i]; ) { for ( j = 0; j < sizeof Code / sizeof *Code; ++j ) { size_t size = strlen(Code[j].morse); if ( memcmp(Code[j].morse, &morse[i], size) == 0 ) { putchar(Code[j].letter); i += size; break; } } } putchar('\n'); } int main(void) { const char text[] = "Hello world"; const char test[] = ".... . .-.. .-.. --encode(text); decode(test); return 0; } /* my output .... . .-.. .-.. --HELLO WORLD */

.-- --- .-. .-.. -..

Return to Table of Contents

.-- --- .-. .-.. -.. ";

3.6.8

Hex Dump

This is a quick and dirty example of a hex dump utility. Not much in the way of special features -- it just dumps the contents of a hard-coded filename in hex and also shows the characters. An example that can show the basics of making your own. #include <stdio.h> #include size_t hexdump(FILE *file) { unsigned char data[16]; size_t i, j, size, count = 0; /* * Heading */ fputs(" ", stdout); /* skip address area */ for ( i = 0; i < sizeof data / sizeof *data; ++i ) { printf("+%lX ", (long unsigned)i); } puts("Text"); /* * Body */ do { /* Read some data. */ size = fread(data, sizeof *data, sizeof data / sizeof *data, file); if ( size ) { /* Print the base address. */ printf("%08lX ", (long unsigned)count); count += size; /* adjust the base address */ /* Print the characters' hex values. */ for ( i = 0; i < size; ++i ) { printf("%02X ", data[i]); } /* Pad the hex values area if necessary. */ for ( ++i; i <= sizeof data / sizeof *data; ++i ) { fputs(" ", stdout); } /* Print the characters. */ for ( j = 0; j < size; j++ ) { putchar(isprint(data[j]) ? data[j] : '.'); } putchar('\n'); } } while ( size == sizeof data / sizeof *data ); /* break on partial buffer */ return count; }

int main(void) { static const char filename[] = __FILE__; /* may not work on everywhere */ FILE *file = fopen(filename, "rb"); if ( file != NULL ) { printf("%lu bytes\n", (long unsigned)hexdump(file)); fclose(file); } else { perror(filename); } return 0; }

The result of feeding this to itself, in this case by using __FILE__, is as follows. 00000000 00000010 00000020 00000030 00000040 00000050 00000060 00000070 00000080 00000090 000000A0 000000B0 000000C0 000000D0 000000E0 000000F0 00000100 00000110 00000120 00000130 00000140 00000150 00000160 00000170 00000180 00000190 000001A0 000001B0 000001C0 000001D0 000001E0 000001F0 00000200 00000210 00000220 00000230 00000240 00000250 00000260 00000270 00000280 00000290 000002A0 000002B0 000002C0 000002D0

+0 23 68 79 20 69 67 36 2C 20 20 20 20 74 65 66 20 69 20 70 28 29 73 2A 20 20 64 0A 65 20 61 74 20 20 20 73 20 22 75 0D 20 75 72 20 68 61 20

+1 69 3E 70 68 6C 6E 5D 20 3D 20 2A 20 29 73 6F 73 7A 29 72 6C 3B 28 0D 20 7B 20 20 61 2A 74 61 20 20 2F 65 20 25 6E 0A 2B 73 65 20 61 6C 20

+2 6E 0D 65 65 65 65 3B 6A 20 2A 2F 20 3B 73 72 69 65 0D 69 6F 0D 22 0A 20 0D 73 20 64 64 61 2C 69 20 2A 20 20 30 73 20 3D 74 73 2F 72 75 20

+3 63 0A 2E 78 29 64 0D 2C 30 20 0D 20 20 20 20 7A 6F 0A 6E 6E 0A 54 20 2A 0A 6F 20 28 61 20 20 66 20 20 61 20 38 69 20 20 20 73 2A 61 65 20

+4 6C 23 68 64 0D 20 0A 20 3B 48 0A 20 2F 61 28 65 66 20 74 67 20 65 20 2F 20 6D 20 64 74 2F 66 20 20 50 64 20 6C 67 20 73 74 20 20 63 73 66

+5 75 69 3E 75 0A 63 20 73 0D 65 20 20 2A 72 20 6F 20 20 66 20 20 78 20 0D 20 65 20 61 61 20 69 28 7B 72 64 20 58 6E 20 69 68 2A 50 74 2E 6F

+6 64 6E 0D 6D 7B 68 20 69 0A 61 20 20 20 65 69 66 2A 20 28 75 20 74 20 0A 20 20 20 74 2C 73 6C 20 0D 69 72 20 20 65 20 7A 65 2F 72 65 20 72

+7 65 63 0A 70 0D 61 20 7A 20 64 20 20 73 61 20 20 64 7B 22 6E 7D 22 2A 20 20 64 73 61 20 69 65 73 0A 6E 65 20 22 64 20 65 20 0D 69 72 2A 20

+8 20 6C 0D 28 0A 72 73 65 20 69 66 22 6B 20 3D 64 61 0D 2B 73 0D 29 20 20 20 61 69 2C 73 7A 29 69 20 74 73 20 2C 29 20 3B 62 0A 6E 73 2F 28

+9 3C 75 0A 46 20 20 69 2C 20 6E 70 2C 69 2A 20 61 74 0A 25 69 0A 3B 42 20 20 74 7A 20 69 65 3B 7A 20 20 73 70 20 63 20 20 61 20 74 27 0D 20

+A 73 64 73 49 20 64 7A 20 2F 67 75 20 70 2F 30 74 61 20 6C 67 20 0D 6F 64 2F 61 65 73 7A 6F 0D 65 20 74 2E 72 28 6F 20 2F 73 20 20 20 0A 69

+B 74 65 69 4C 20 61 65 63 2A 0D 74 73 20 0D 3B 61 3B 20 58 6E 20 0A 64 6F 2A 2E 20 69 65 66 0A 20 20 68 20 69 6C 75 63 2A 65 20 74 68 20 20

+C 64 20 7A 45 75 74 5F 6F 0D 0A 73 74 61 0A 20 20 20 20 20 65 20 20 79 0D 20 20 3D 7A 6F 20 20 29 20 65 2A 6E 6F 6E 6F 20 20 20 68 65 20 3D

+D 69 3C 65 20 6E 61 74 75 0A 20 28 64 64 20 69 2F 2B 20 22 64 70 20 0D 0A 52 2A 20 65 66 2A 20 0D 20 20 2F 74 6E 74 75 61 61 20 65 78 20 20

+E 6F 63 5F 2A 73 5B 20 6E 20 20 22 6F 64 20 20 20 2B 20 2C 29 75 20 0A 20 65 2F 66 6F 20 64 20 0A 20 62 0D 66 67 29 6E 64 64 20 20 20 20 30

+F 2E 74 74 66 69 31 69 74 20 20 20 75 72 20 3C 73 69 20 20 69 74 2F 20 20 61 0D 72 66 64 61 20 20 20 61 0A 28 20 3B 74 6A 64 20 63 76 20 3B

Text #include <stdio. h>..#include ....size_t hexdump(FILE *f ile)..{.. unsi gned char data[1 6];.. size_t i , j, size, count = 0;.. /*.. * Heading.. */.. fputs(" ", stdou t); /* skip addr ess area */.. for ( i = 0; i < sizeof data / s izeof *data; ++i ).. {.. printf("+%lX ", (long unsigned)i );.. }.. put s("Text");.. / *.. * Body.. */.. do.. {.. /* Rea d some data. */. . size = fr ead(data, sizeof *data, sizeof d ata / sizeof *da ta, file);.. if ( size ).. {.. /* Print the ba se address. */.. printf( "%08lX ", (long unsigned)count); .. count += size; /* adj ust the base add ress */.. /* Print the c haracters' hex v alues. */.. for ( i = 0;

000002E0 20 000002F0 0D 00000300 20 00000310 28 00000320 5D 00000330 0A 00000340 20 00000350 61 00000360 79 00000370 66 00000380 73 00000390 7A 000003A0 29 000003B0 20 000003C0 28 000003D0 0D 000003E0 20 000003F0 74 00000400 2A 00000410 20 00000420 7A 00000430 20 00000440 20 00000450 6E 00000460 74 00000470 20 00000480 20 00000490 29 000004A0 7D 000004B0 3D 000004C0 73 000004D0 2F 000004E0 69 000004F0 20 00000500 0A 00000510 6F 00000520 69 00000530 6C 00000540 45 00000550 77 00000560 72 00000570 66 00000580 65 00000590 20 000005A0 55 000005B0 20 000005C0 79 000005D0 6E 000005E0 66 000005F0 63 00000600 20 00000610 7B 00000620 66 00000630 0D 00000640 7D 1603 bytes

69 0A 20 22 29 20 74 72 2E 6F 69 65 0D 20 22 0A 20 68 2F 28 65 20 20 74 61 20 20 3B 20 20 69 2A 61 20 7D 69 63 65 5F 6F 65 69 6E 20 4C 20 74 73 69 6C 7D 0D 69 0A 0D

20 20 20 25 3B 20 68 65 20 72 7A 6F 0A 20 20 20 20 65 0D 20 3B 20 20 28 5B 20 20 0D 77 73 7A 20 6C 72 0D 64 20 6E 5F 72 20 6C 61 69 4C 20 65 69 6C 6F 0D 0A 6C 20 0A

3C 20 20 30 0D 20 65 61 2A 20 65 66 20 20 20 20 20 20 0A 6A 20 20 70 64 6A 20 20 0A 68 69 65 62 20 65 0A 29 63 61 3B 6B 2A 65 6D 66 20 70 73 67 65 73 0A 20 65 20

20 20 20 32 0A 20 20 20 2F 28 6F 20 20 20 20 20 20 63 20 20 6A 7B 75 61 5D 20 70 20 69 7A 6F 72 62 74 0D 0D 6F 6D 20 20 2F 20 65 20 29 72 5C 6E 29 65 20 20 6E 20

73 20 20 58 20 20 68 69 0D 20 66 2A 20 20 22 20 20 68 20 3D 2B 0D 74 74 20 20 75 20 6C 65 66 65 75 75 0A 0A 6E 65 2F 6F 0D 3D 2C 28 0D 69 6E 65 29 28 20 20 61 72

Return to Table of Contents

69 20 20 20 20 20 65 66 0A 2B 20 64 20 20 2C 20 20 61 20 20 2B 0A 63 61 3A 20 74 20 65 6F 20 61 66 72 69 7B 73 5B 2A 6E 0A 20 20 20 0A 6E 22 64 3B 66 20 20 6D 65

7A 20 20 22 20 20 78 20 20 2B 64 61 20 20 20 20 2F 72 20 30 20 20 68 5B 20 20 63 20 20 66 2A 6B 66 6E 6E 0D 74 5D 20 20 20 66 22 66 20 74 2C 29 0D 69 65 20 65 74

65 20 20 2C 20 20 20 6E 20 69 61 74 20 20 73 20 2A 61 20 3B 29 20 61 6A 27 7D 68 20 28 20 64 20 65 20 74 0A 20 20 6D 65 20 6F 72 69 20 66 20 68 0A 6C 6C 20 29 75

3B 20 20 20 20 20 76 65 20 3B 74 61 20 20 74 20 20 63 20 20 0D 20 72 5D 2E 0D 61 20 20 64 61 6F 72 63 20 20 63 3D 61 76 20 70 62 6C 20 28 28 65 20 65 73 70 3B 72

20 20 70 64 20 2F 61 63 20 20 61 3B 20 20 64 20 50 74 20 6A 0A 20 28 29 27 0A 72 7D 73 61 74 6E 20 6F 6D 20 68 20 79 65 46 65 22 65 7B 22 6C 78 20 29 65 65 0D 6E

2B 7B 72 61 20 2A 6C 65 20 69 20 20 20 66 6F 7D 72 65 20 20 20 20 69 20 29 20 28 0D 69 74 61 20 2A 75 61 20 61 5F 20 72 49 6E 29 20 0D 25 6F 64 20 3B 0D 72 0A 20

2B 0D 69 74 20 20 75 73 20 20 2F 2B 7B 70 75 0D 69 72 20 3C 20 20 73 3F 3B 20 27 0A 7A 61 20 70 2F 6E 69 73 72 5F 6E 79 4C 28 3B 21 0A 6C 6E 75 20 0D 0A 72 20 30

69 0A 6E 61 20 50 65 73 20 3C 20 2B 0D 75 74 0A 6E 73 66 20 20 20 70 20 0D 20 5C 20 65 20 29 61 0D 74 6E 74 20 46 6F 77 45 66 0D 3D 20 75 67 6D 20 0A 20 6F 20 3B

20 20 74 5B 7D 61 73 61 20 3D 73 69 0A 74 29 20 74 2E 6F 73 20 20 72 64 0A 20 6E 20 20 2F 3B 72 0A 3B 28 61 66 49 74 68 20 69 0A 20 20 20 20 70 20 20 20 72 20 0D

29 20 66 69 0D 64 20 72 20 20 69 20 20 73 3B 20 20 20 72 69 20 20 69 61 20 20 27 20 3D 20 20 74 20 0D 76 74 69 4C 20 65 2A 6C 20 4E 20 62 75 28 66 20 20 28 7D 0A

i < size; ++i ) .. {.. printf ("%02X ", data[i ]);.. }. . /* Pad the hex values area if necessar y. */.. for ( ++i; i <= sizeof data / si zeof *data; ++i ).. {.. fputs (" ", stdout); .. }.. /* Print the characters. */.. for ( j = 0; j < si ze; j++ ).. {.. putchar(ispri nt(data[j]) ? da ta[j] : '.');.. }.. putchar('\n' );.. }.. } while ( size = = sizeof data / sizeof *data ); /* break on part ial buffer */.. return count;. .}....int main(v oid)..{.. stat ic const char fi lename[] = __FIL E__; /* may not work on everywhe re */.. FILE * file = fopen(fil ename, "rb");.. if ( file != N ULL ).. {.. printf("%lu b ytes\n", (long u nsigned)hexdump( file));.. f close(file);.. }.. else.. {.. perror( filename);.. } .. return 0;.. }..

3.6.9

XOR Encryption

This snippet is an example of using XOR encryption to encrypt/decrypt a file. It uses command-line input parameters as follows: argv[1] - name of input file to encrypt/decrypt argv[2] - name of output file argv[3] - crypt key

For example, an input file called "main.c" is used to create an output file "file.bin" with the encryption key "key" by doing this: H:\Forum>xorcrypt main.c file.bin key

Performing the reverse on the encrypted file "file.bin" will restore the original file contents into another output file "file.txt" using the same "key". H:\Forum>xorcrypt file.bin file.txt key

If the code shown here was in "main.c", you should find that the second output "file.txt" matches "main.c". #include <stdio.h> int main(int argc, char *const *argv) { if ( argc == 4 ) { FILE *input = fopen(argv[1], "rb"); FILE *output = fopen(argv[2], "wb"); if ( input != NULL && output != NULL ) { unsigned char buffer[BUFSIZ]; size_t count, i, j = 0; do { count = fread(buffer, sizeof *buffer, sizeof buffer, input); for ( i = 0; i < count; ++i ) { buffer[i] ^= argv[3][j++]; if ( argv[3][j] == '\0' ) { j = 0; /* restart at the beginning of the key */ } } fwrite(buffer, sizeof *buffer, count, output); } while ( count == sizeof buffer ); fclose(input); fclose(output); } } return 0; }

Return to Table of Contents

4 Rambles 4.1

Don't #define Your Array Size!

In a number of text and other references, the author stops short of recommending good practice, and plenty of code and its programmers suffer for it. I took a stab at explaining the basics once before, but I felt like revisiting it again. Since I abandoned the use of a #define macro to define an array size, I find I don't even miss it and my code is to me simpler and easier to follow, as well as being more correct. I just want to proselytize a bit more. I must mention, however that I am against this type of macro: #define SIZE 100

I am wholeheartedly in favor of something like this: #define CPU_SPEED 100000 #define TIMER_BASE ((CPU_SPEED)/1000)

But as you see, that's different from declaring an array size. Now as much as we'd all love to believe that the software documentation we are supposed to write is supposed to tell us how everything works, these docs go largely unread and we all head straight to the source code; after all, this is always the actual current state of the code. What happens in larger projects, at least in my experience, is that this little "shortcut" gets put in some header somewhere. Now it wouldn't be bad if it were just one simple lookup, but too often in my experience it's been too much of a hunt. Let's say I've got a line of code that I am examining: Rx.Buffer[Rx.Index++] = uart0; if ( Rx.Index >= BUFFER_SIZE ) { Rx.Index = 0; }

This all looks dandy, but I have to do some searches to verify that this is doing what is intended. First I look up Rx. RXTYPE Rx;

Okay. What's RXTYPE? (And don't get me started on typedefs, that'll be an article on its own someday.) typedef struct { unsigned char Buffer[RX_BUFFER_SIZE]; int index; } RXTYPE;

Okay, now what's RX_BUFFER_SIZE? #define RX_BUFFER_SIZE 40 // must be smaller than normal

Okay, does that match? One more lookup... What was the question again? Oh, yeah. #define BUFFER_SIZE 100 // used for most buffers

Aha! There's the bug. What's a simple fix that is guaranteed to be correct? Rx.Buffer[Rx.Index++] = uart0; if ( Rx.Index >= sizeof Rx.Buffer / sizeof *Rx.Buffer ) { Rx.Index = 0; }

What did the #define by me? A lot of indirection and hunting through code. But now with the use of sizeof for a replacement, I don't actually need to look anywhere, I can tell by the idiom sizeof x / sizeof *x that it is correct for an array. I don't even need to care how big the actual array is. So as the places in the code that once had these magic macros disappear, you're left with about two places left where the macro exists: the definition itself and the array definition. Now why in the world should these two related values be separate? Hm. No reason, delete the macro and put the actual size at the array definition. Now, less code, more correct, less hunting: easier to maintain code. But feel free to ignore my advice and take the macro approach. Many feel quite comfortable with what I just railed against. But the next time you are searching through a half-dozen or more files chasing a bug like this, do have a distant memory of reading this article. And if this is the case, spread the news. Return to Table of Contents

4.2

Much Ado About Bits

Many years ago I was wandering through some SNMP code trying to port it to a Rabbit 2000. I'd guess that not many are familiar with this device -- it's an 8-bitter, and I had no operating system or dynamic memory. So there were several issues in addition to the Dynamic C dialect I had to use to program it. I seem to remember that one of the things that came up was that the 9 most significant bits were needed to mask off a value. I was just beginning my swing toward writing portable code and this got me thinking: how would you create a compile-time mask for the top 9 bits of some integer? Well, if you could have some macro MSB that produced just the one most significant bit, then it would be possible to create another macro that could slam 9 of them together. #define TOP9 (MSB | (MSB >> 1) | (MSB >> 2) | (MSB >> 3) | (MSB >> 4) \ | (MSB >> 5) | (MSB >> 6) | (MSB >> 7) | (MSB >> 8))

So this started me poking around at various ways to define a macro MSB. Now you'd think this would be simple and obvious. Well, I guess it is, but along the way I found a lot of cracks and holes with some of the things I first tried. Setting the High Bit of an Integer Type So let’s say we want the high bit of an int; and of course when talking about bits we really mean the unsigned variety. It’s 0x80000000, right? No. Well, yes and no, I suppose. It might be, if our int has 32 bits, but if an int has 16 bits or 64 bits it’s not right. Aha! We can use our friend sizeof like this, right? #define MSB (1U << (sizeof msb * 8 - 1))

Nope. Here it is assumed that there are 8 bits in a byte. Which is good enough for most folks, but I have happened to work on platforms where CHAR_BIT is not 8. So we use CHAR_BIT instead! #define MSB (1U << (sizeof msb * CHAR_BIT - 1))

Well, that’s good enough for most people, I suppose, but not for the pedant. The standard leaves open the possibility for holes. That’s right, unused padding bits may be part of an integer! So all the bits of the unsigned int may not be bits that contribute to the value. Another way to see this is that we may be setting a bit such that the value is bigger that INT_MAX. Now why would those standard writers have done such a thing? Well, I can only speculate. Let’s remember that C was born in an environment in which 8-bit bytes were one of several possibilities; hardware used many different methods of being fast and useful before 8-bit became de facto standard (but not quite C or C++ standard). But let’s not put this in yesterday’s persepective, because all of us with our 20/20

hindsight will say that today’s garden-variety byte is so obvious. Instead, let’s pretend we’re working on the platform of tomorrow. Example: A Made-Up Platform Let’s say that in this world that everyone uses a GUI and graphics are key; cool things like 3D rendering are expected. So hardware guys come up with a processor that is floating-point oriented. This mythical platform uses an 80-bit floating point register. But the integers that use this register only occupy 64 of these 80 bits. And let’s assume that CHAR_BIT is 16. So sizeof(int) is 5, and the topmost 16 bits are padding. Can you now see some holes in the attempts thus far? All of them would fail to set only the high bit of the integer. So is it even possible to get it right? Of course. And along the way of finding this out for myself, I also began to realize that C and C++ are more geared to values and their ranges than the sizes of the objects that contain them. Perhaps that’s why has all values and has very little to say about sizes, merely giving us CHAR_BIT. How Can You Get Only The High Bit? pppppppppppppppp0000000000000000000000000000000000000000000000000000000000000000

In fact, there are a number of ways to set only the high bit of an integer, and in general it deals with values. First, let’s take a look at the value 0; it has all bits zero. Then let’s take a look at ~0; it has all bits one. But since 0 has type int, there is a little bit of ugliness that rears its head: the result of the operator ~ has “implementation-defined and undefined aspects for signed types”. So let’s avoid that and go with 0U – hey an opportunity to use an integer suffix! But along the way I also found that if you subtract 1 from an unsigned zero, it rolls to the highest integer value – a value with all bits set. So this leads us to 0U - 1, or slightly smaller but odd-looking -1U. This isn’t a negative unsigned value, it is unary negation that has the same effect as subtracting 1 from 0U. So now we’ve got a couple of funny-looking values that set all bits. Continuing with the example, we’d have a 0U that looks something like this. And our –1U would look like this. pppppppppppppppp1111111111111111111111111111111111111111111111111111111111111111

Obviously we’ve got more than the high bit set, but if you’d right shift these bits by one, you would have this. pppppppppppppppp0111111111111111111111111111111111111111111111111111111111111111

That looks like just the opposite of what we want. If we added one to this value, or inverted the bits, we would have this. pppppppppppppppp1000000000000000000000000000000000000000000000000000000000000000

And that’s what we want.

Putting It All Together So now it boils down to personal preference as much as anything, but there are just 3 key things to do in an expression to obtain this result. 1. Get a value with all value bits set to 1. 2. Shift a zero into the top bit and move the rest down. 3. Flip these bits. There happen to be several ways to do each of these, so that deserves a little discussion as well. For the all-bits-one value, you can use the Utype_MAX macros in . Note that this is a little type-specific (unsigned char, unsigned short, unsigned int, or unsigned long). You can use a cast: (unsigned type)-1. Again, this is a little type-specific. You can use the -1U or ~0U, but you can see that unsigned char, unsigned short, or unsigned long may feel a little left out. And for our friend unsigned char, which has a guarantee that all bits are value bits, we could even use 1 << (CHAR_BIT – 1). The shifting may be accomplished by either using a bitwise shift (>>1) or by dividing by two. Flipping the bits may be done with the operator ~ or by adding 1. For those who might mention issues with the three possible integer representations – ones complement, twos complement, or sign-magnitude – remember that we’re dealing with unsigned integers. So there are a couple of portable ways to set the high bit. Mix and match your favorite methods for each of the 3 steps! Here are a couple of snippets: •

Bits, Part 1



Bits, Part 2

More Good News And finally, note that since these constructs all result in compile-time constant values, even though it looks like a calculation is occurring, there is no run-time math going on: the bottom line is a value with only the most significant bit set. #include <stdio.h> #include #define MSB ((-1U >> 1) + 1) #define TOP9 (MSB | (MSB >> 1) | (MSB >> 2) | (MSB >> 3) | (MSB >> 4) \ | (MSB >> 5) | (MSB >> 6) | (MSB >> 7) | (MSB >> 8)) int main(void) { unsigned int msb = MSB, mask = TOP9; printf("msb = 0x%X\n", msb); printf("mask = 0x%X\n", mask); return 0; }

/* my output msb = 0x80000000 mask = 0xFF800000 */

Going back to compiler after little constant garden-variety

our TOP9 macro then, this string of expressions may look ugly to the the precompiler gets finished with it, but it all resolves into a nice value calculated by the poor compiler at compile time. And on a PC of today with a 32-bit int, this macro looks merely becomes this:

11111111100000000000000000000000

But if we took the same code and went to the strange little processor I used as an example, it should instead automagically change to this. pppppppppppppppp1111111110000000000000000000000000000000000000000000000000000000

Return to Table of Contents

4.3

Silly Code

If you've never heard of the following inane code, do a search for "Rob Pike rules". (Or just do that anyway, it'll be good for you.)



There is a famously bad comment style: i=i+1;

/* Add one to i */

and there are worse ways to do it: /********************************** * * * Add one to i * * * **********************************/



i=i+1;

Don't laugh now, wait until you see it in real life. Since that kinda thing is so well known, we don't actually see this kind of stuff in real life, right? #define ZERO 0

Don't laugh. enum { ZERO, ONE, TWO, THREE, FOUR, FIVE, SIX, SEVEN, EIGHT, NINE, TEN };

Um, okay. So that's like so someday you can change FIVE to have a value of 7? What does it actually provide other than a maintenance headache? Similarly, why would anyone programming in C want to do this? typedef enum { false, true } bool;

Sure it makes the code look like C++ and it is a fairly common idiom. But why should it be? Why couldn't programmers back from the time of C's start not have told others simply to use zero or nonzero values and stop there. Or is the point just to see if your code could withstand the rigors of doing this? typedef enum { true, false } bool;

I would say less than one in a million programs that rigorously use true and false could actually withstand a reversal. And if that's the case, why waste the time throughout the code being so rigorous? Hindsight is 20/20, but even with that, there's 99% of us programmers that will still get it wrong. Return to Table of Contents

5 Additional Material 5.1

C Draft Standards

For those who are interested, here is a current draft of the C standard, containing C99: •

A working paper containing the Current ISO C standard incl TC1 and TC2 is available (pdf)

The above document was found at "the international standardization working group for the programming language C": •

JTC1/SC22/WG14 - C

Related information may also be found here: •

C - Approved standards

You may be able to find a copy of the C99 draft standard here: •

http://web.archive.org/web/200502070...c99-draft.html

You may be able to find a copy of the C90 draft standard here: •

http://web.archive.org/web/200502070...c89-draft.html

Return to Table of Contents

5.2

C++ Books

With regard to C++ books, I'll just echo the advice here.



The following books are recommended; read them in mostly the order listed. •

“Accelerated C++” Andrew Koenig & Barbara Moo



“The C++ Standard Library” Nicolai Josuttis --- a “must have”



“Effective C++”, “More Effective C++”, “Effective STL” Scott Meyers



“Exceptional C++”, “More Exceptional C++” Herb Sutter



“The C++ Programming Language”, 3rd edition or later Bjarne Stroustrup



“Modern C++ Design” Andrei Alexandrescu



“C++ Templates” Vandevoorde & Josuttis



“Standard C++ IOStreams and Locales” Langer & Kreft



Proper credit: vawjr, who now has this posted. The following is also recommended (hat tip: Daved).



C++ Coding Standards : 101 Rules, Guidelines, and Best Practices Herb Sutter, Andrei Alexandrescu Consider adding C++ Coding Standards to that list. It is by Sutter and Alexandrescu, and has an excellent compilation and mini-discussion of many of the topics discussed in greater detail in the other books. It is not a coding standards book, but rather a guide to best practices in C++ code and would fit perfectly as an introduction or summary of many of the other books on that list (after Accelerated C++).

Book reviews can also be found at www.accu.org: Beginner's C++. Return to Table of Contents



5.3

Style and Comments

1.1.1

Brian Kernighan's Programming Style Tips

Here is a summary of the very important programming style tips from Brian Kernighan's 1994 guest CS50 lecture. 1. Say what you mean, simply and directly. 2. Use the “telephone test” for readability. 3. Write clearly - don't be too clever. 4. Don't use conditional expressions as a substitute for a logical expression. 5. Parenthesize to avoid ambiguity. 6. Each time you make a test, do something. 7. Follow each decision as closely as possible with its associated action. 8. Use the good features of a language; avoid the bad ones. 9. Capture regularity in control flow, irregularity in data. 10.Each module should do one thing well. 11.Make sure comments and code agree. 12.Don't just echo the code with comments - make every comment count. 13.Don't comment bad code - rewrite it. 14.Use symbolic constants for magic numbers. 15.Watch out for side effects and order of evaluation. 16.Macros are not functions. 17.Watch out for off-by-one errors. 18.Test programs at their boundaries. 19.Program defensively. 20.Make sure input cannot violate the limits of the program. 21.Make it right before you make it faster. 22.Keep it right when you make it faster. 23.Don't sacrifice clarity for small gains in “efficiency.” 24.Don't stop with your first draft. [From The Elements of Programming Style, Kernighan & Plauger, McGraw-Hill, 1978]

1.1.2

Rob Pike's Rules Regarding Complexity

Most programs are too complicated - that is, more complex than they need to be to solve their problems efficiently. Why? Mostly it's because of bad design, but I will skip that issue here because it's a big one. But programs are often complicated at the microscopic level, and that is something I can address here. 1. You can’t tell where a program is going to spend its time. Bottlenecks occur in surprising places, so don’t try to second guess and put in a speed hack until you’ve proven that’s where the bottleneck is. 2. Measure. Don’t tune for speed until you’ve measured, and even then don’t unless one part of the code overwhelms the rest. 3. Fancy algorithms are slow when n is small, and n is usually small. Fancy algorithms have big constants. Until you know that n is frequently going to be big, don’t get fancy. (Even if n does get big, use Rule 2 first.) 4. Fancy algorithms are buggier than simple ones, and they’re much harder to implement. Use simple algorithms as well as simple data structures. 5. Data dominates. If you’ve chosen the right data structures and organized things well, the algorithms will almost always be self-evident. Data structures, not algorithms, are central to programming. 6. There is no Rule 6. Return to Table of Contents

Related Documents

Posts
May 2020 7
Posts
May 2020 8
Posts
May 2020 7
Blog Posts
November 2019 19
Goiit Posts
May 2020 3
All Posts Name
June 2020 1