Вы находитесь на странице: 1из 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 Safe Version of gets()
3.1.2 Read a Line of Text from the User
3.1.3 Read a Line of Text from the User, Discard Excess Characters
3.1.4 Read an Integer from the User, Part 1
3.1.5 Read an Integer from the User, Part 2
3.1.6 Read an Integer from the User, Part 3
3.1.7 Read a Floating-Point Value from the User, Part 1
3.1.8 Read a Floating-Point Value from the User, Part 2
3.1.9 Read a Floating-Point Value from the User, Part 3
3.2 Strings
3.2.1 Strings: Copying
3.2.2 Strings: Comparing
3.2.3 Strings: Comparing, Case-Insensitive
3.2.4 Strings: Reversal
3.2.5 Strings: Reversal, Part 2
3.2.6 Strings: Concatenation
3.2.7 Strings: Case Insensitive strstr
3.2.8 Strings: Finding a Substring
3.2.9 Strings: Search and Replace
3.3 Parsing
3.3.1 Parsing a String into Tokens Using sscanf
3.3.2 Parsing a String into Tokens Using strcspn 1
3.3.3 Parsing a String into Tokens Using strcspn, Part 2
3.3.4 Parsing a String into Tokens Using strcspn, Part 3
3.4 Bits
3.4.1 Binary to Decimal
3.4.2 Bits, Part 1
3.4.3 Bits, Part 2
3.4.4 Bits, Part 3
3.4.5 Bit Reversal
3.5 Time/Date
3.5.1 Time - Displaying Current
3.5.2 Time/Date Calculations
3.5.3 Days Since a Given Date
3.5.4 Days Remaining (30-Day Subscription)
3.6 Miscellaneous
3.6.1 Counting the Number of Lines in File
3.6.2 Reading a File Line By Line
3.6.3 Currency Converter
3.6.4 Currency: Making Change
3.6.5 Checking for Integer Overflow
3.6.6 Read and Write a Structure to a File in Binary Mode
3.6.7 Morse Code
3.6.8 Hex Dump
3.6.9 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 whitespace-
delimited -- 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 <iostream>
#include <vector>

using namespace std;

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

for (int i = 0; i < 5; ++i)


{
cout << v[i] << endl;
}
}

Or perhaps even this.


#include <iostream>
#include <vector>
#include <iterator>

using namespace std;

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

vector<int>::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 <iostream>
#include <vector>
#include <iterator>
#include <algorithm>

using namespace std;

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

copy(v.begin(), v.end(), ostream_iterator<int>(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) (sizeof(x)/sizeof(*(x)))
#define NELEM(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 <iostream>
#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 <iostream>
#include <fstream>

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 <iostream>
#include <fstream>

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 <iostream>
#include <fstream>

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 Floating-
Point 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 output
Enter an integer: one
Enter an integer:
Enter an integer: 42
value = 42

Enter an integer: 24f


value = 24

Enter an integer: 125 help


value = 125
*/

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 <ctype.h>

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 output
Enter an integer: one
Enter an integer:
Enter an integer: f123
Enter an integer: 123f
Enter an integer: 123
Enter an integer: 123
Enter an integer: 1.23
Enter an integer: -42
value = -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 <ctype.h>

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 output
Enter an integer: one
Enter an integer:
Enter an integer: f123
Enter an integer: 123f
Enter an integer: 123
Enter an integer: 123
Enter an integer: 1.23
Enter an integer: -42
value = -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 output
Enter a floating-point number: one
Enter a floating-point number:
Enter a floating-point number: f12.3
Enter a floating-point number: -45.67
value = -45.67

Enter a floating-point number: -12.3f


value = -12.3

Enter a floating-point number: 125 help


value = 125
*/

Return to Table of Contents


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 <ctype.h>

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 output
Enter a floating-point number: one
Enter a floating-point number:
Enter a floating-point number: f12.3
Enter a floating-point number: -45.67
value = -45.67

Enter a floating-point number: -12.3f


Enter a floating-point number: 125 help
Enter a floating-point number: 1.2.3
Enter a floating-point number: 1.23
value = 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 <ctype.h>

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 output
Enter a floating-point number: one
Enter a floating-point number:
Enter a floating-point number: f12.3
Enter a floating-point number: -45.67
value = -45.67

Enter a floating-point number: -12.3f


Enter a floating-point number: 125 help
Enter a floating-point number: 1.2.3
Enter a floating-point number: 1.23
value = 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", "world") = -15
mystrcmp("hello", "hello") = 0
mystrcmp("hello", "hell") = 111
mystrcmp("world", "hello") = 15
mystrcmp("world", "hell") = 15
mystrcmp("hello", "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 <ctype.h>

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", "world") = -15
mystricmp("hello", "Hello") = 0
mystricmp("hello", "hell") = 79
mystricmp("world", "Hello") = 15
mystricmp("world", "hell") = 15
mystricmp("Hello", "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 <ctype.h>

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 output
field = "2004/12/03 12:01:59"
field = "info1"
field = "info2"
field = "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] = "2"
token[ 1] = "31May2005"
token[ 2] = "23:59:00"
token[ 3] = "100.2.1.12"
token[ 4] = "log"
token[ 5] = "accept"
token[ 6] = ""
token[ 7] = "eth9"
token[ 8] = "inbound"
token[ 9] = "VPN-1"
line 2:
token[ 0] = "FireWall-1"
token[ 1] = ""
token[ 2] = "100.1.20.130"
token[ 3] = "172.30.7.114"
token[ 4] = "icmp"
token[ 5] = "191"
token[ 6] = ""
token[ 7] = ""
token[ 8] = "8"
token[ 9] = "0"
token[10] = ""
token[11] = ""
token[12] = ""
token[13] = ""
*/

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 : 00000000000000000000000000000000
~0U : 11111111111111111111111111111111
~0U >> 1 : 01111111111111111111111111111111
(~0U >> 1) + 1 : 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 <limits.h>

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 char uc = 0x5A;


unsigned short uh = 0x1234;
unsigned int ui = 0xABCD;
unsigned long ul = 0xDEADBEEF;
double d = 123.456;

printf("0x%-8X = \"%s\"\n", uc, bits_uchar (result, uc) );


printf("0x%-8hX = \"%s\"\n", uh, bits_ushort (result, uh) );
printf("0x%-8X = \"%s\"\n", ui, bits_uint (result, ui) );
printf("0x%-8lX = \"%s\"\n", ul, bits_ulong (result, ul) );
printf("0x%-8lX = \"%s\"\n", ul, bits_block(result, &ul, sizeof ul) );
printf("%-10g = \"%s\"\n", d, bits_block(result, &d, sizeof d) );

return 0;
}

/* my output
0x5A = "01011010"
0x1234 = "0001001000110100"
0xABCD = "00000000000000001010101111001101"
0xDEADBEEF = "11011110101011011011111011101111"
0xDEADBEEF = "11101111-10111110-10101101-11011110"
123.456 = "01110111-10111110-10011111-00011010-00101111-11011101-01011110-
01000000"
*/

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 <limits.h>

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, &value, sizeof value));
printf("value = 0x%lX: %s\n", value, bits_be(buffer, &value, sizeof value));
printf("d = %g: %s\n", d, bits_le(buffer, &d, sizeof d));
printf("d = %g: %s\n", d, bits_be(buffer, &d, sizeof d));
return 0;
}
/* 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 <limits.h>
#include <time.h>

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 = a.sbyte.u0;
b.sbyte.u6 = a.sbyte.u1;
b.sbyte.u5 = a.sbyte.u2;
b.sbyte.u4 = a.sbyte.u3;
b.sbyte.u3 = a.sbyte.u4;
b.sbyte.u2 = a.sbyte.u5;
b.sbyte.u1 = a.sbyte.u6;
b.sbyte.u0 = 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) >> 4;
value = (value & 0x33) << 2 | (value & 0xcc) >> 2;
value = (value & 0x55) << 1 | (value & 0xaa) >> 1;
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,
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) = 3A, elapsed time = 12.789
bar(5C) = 3A, elapsed time = 18.366
baz(5C) = 3A, elapsed time = 24.646
qux(5C) = 3A, elapsed time = 4.496
zot(5C) = 3A, elapsed time = 9.684
fum(5C) = 3A, elapsed time = 1.592
*/

Return to Table of Contents


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 <time.h>

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 <time.h>

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 <time.h>

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 <time.h>

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 char filename[] = "file.txt";
FILE *file = fopen ( filename, "r" );
if ( file != NULL )
{
char line [ 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", "Pound");
convert(100.0, "Yen", "Lire" );
convert( 20.0, "Euro", "Krone");
convert( 20.0, "USD", "Krone");
return 0;
}

/* 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 <limits.h>

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 int
* 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 int
* 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 int
* 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 int
* divided by the other negative value 'a' (negative quotient).
*/
if ( b < INT_MAX / a )
{
return -1;
}
}
*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", INT_MAX);
printf("INT_MIN = %d\n", INT_MIN);
for ( a = 10, b = 5; a < INT_MAX / 10; a *= 10, b *= 10 )
{
bar(a,b);
}
for ( a = 10, b = -5; a < INT_MAX / 10; a *= 10, b *= 10 )
{
bar(a,b);
}
for ( a = -10, b = 5; a > INT_MIN / 10; a *= 10, b *= 10 )
{
bar(a,b);
}
for ( a = -10, b = -5; a > INT_MIN / 10; a *= 10, b *= 10 )
{
bar(a,b);
}
return 0;
}

/* 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 = 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 = -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 = -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 <ctype.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 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 <ctype.h>

static const struct


{
const char letter, *morse;
} Code[] =
{
{ 'A', ".- " },{ 'B', "-... " },{ 'C', "-.-. " },{ 'D', "-.. " },
{ 'E', ". " },{ 'F', "..-. " },{ 'G', "--. " },{ 'H', ".... " },
{ 'I', ".. " },{ 'J', ".--- " },{ 'K', ".-.- " },{ 'L', ".-.. " },
{ 'M', "-- " },{ 'N', "-. " },{ 'O', "--- " },{ 'P', ".--. " },
{ 'Q', "--.- " },{ 'R', ".-. " },{ 'S', "... " },{ 'T', "- " },
{ 'U', "..- " },{ 'V', "...- " },{ 'W', ".-- " },{ 'X', "-..- " },
{ 'Y', "-.-- " },{ 'Z', "--.. " },{ ' ', " " },
};

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');
}
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 <ctype.h>

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.
+0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +A +B +C +D +E +F Text
00000000 23 69 6E 63 6C 75 64 65 20 3C 73 74 64 69 6F 2E #include <stdio.
00000010 68 3E 0D 0A 23 69 6E 63 6C 75 64 65 20 3C 63 74 h>..#include <ct
00000020 79 70 65 2E 68 3E 0D 0A 0D 0A 73 69 7A 65 5F 74 ype.h>....size_t
00000030 20 68 65 78 64 75 6D 70 28 46 49 4C 45 20 2A 66 hexdump(FILE *f
00000040 69 6C 65 29 0D 0A 7B 0D 0A 20 20 20 75 6E 73 69 ile)..{.. unsi
00000050 67 6E 65 64 20 63 68 61 72 20 64 61 74 61 5B 31 gned char data[1
00000060 36 5D 3B 0D 0A 20 20 20 73 69 7A 65 5F 74 20 69 6];.. size_t i
00000070 2C 20 6A 2C 20 73 69 7A 65 2C 20 63 6F 75 6E 74 , j, size, count
00000080 20 3D 20 30 3B 0D 0A 20 20 20 2F 2A 0D 0A 20 20 = 0;.. /*..
00000090 20 20 2A 20 48 65 61 64 69 6E 67 0D 0A 20 20 20 * Heading..
000000A0 20 2A 2F 0D 0A 20 20 20 66 70 75 74 73 28 22 20 */.. fputs("
000000B0 20 20 20 20 20 20 20 20 22 2C 20 73 74 64 6F 75 ", stdou
000000C0 74 29 3B 20 2F 2A 20 73 6B 69 70 20 61 64 64 72 t); /* skip addr
000000D0 65 73 73 20 61 72 65 61 20 2A 2F 0D 0A 20 20 20 ess area */..
000000E0 66 6F 72 20 28 20 69 20 3D 20 30 3B 20 69 20 3C for ( i = 0; i <
000000F0 20 73 69 7A 65 6F 66 20 64 61 74 61 20 2F 20 73 sizeof data / s
00000100 69 7A 65 6F 66 20 2A 64 61 74 61 3B 20 2B 2B 69 izeof *data; ++i
00000110 20 29 0D 0A 20 20 20 7B 0D 0A 20 20 20 20 20 20 ).. {..
00000120 70 72 69 6E 74 66 28 22 2B 25 6C 58 20 22 2C 20 printf("+%lX ",
00000130 28 6C 6F 6E 67 20 75 6E 73 69 67 6E 65 64 29 69 (long unsigned)i
00000140 29 3B 0D 0A 20 20 20 7D 0D 0A 20 20 20 70 75 74 );.. }.. put
00000150 73 28 22 54 65 78 74 22 29 3B 0D 0A 20 20 20 2F s("Text");.. /
00000160 2A 0D 0A 20 20 20 20 2A 20 42 6F 64 79 0D 0A 20 *.. * Body..
00000170 20 20 20 2A 2F 0D 0A 20 20 20 64 6F 0D 0A 20 20 */.. do..
00000180 20 7B 0D 0A 20 20 20 20 20 20 2F 2A 20 52 65 61 {.. /* Rea
00000190 64 20 73 6F 6D 65 20 64 61 74 61 2E 20 2A 2F 0D d some data. */.
000001A0 0A 20 20 20 20 20 20 73 69 7A 65 20 3D 20 66 72 . size = fr
000001B0 65 61 64 28 64 61 74 61 2C 20 73 69 7A 65 6F 66 ead(data, sizeof
000001C0 20 2A 64 61 74 61 2C 20 73 69 7A 65 6F 66 20 64 *data, sizeof d
000001D0 61 74 61 20 2F 20 73 69 7A 65 6F 66 20 2A 64 61 ata / sizeof *da
000001E0 74 61 2C 20 66 69 6C 65 29 3B 0D 0A 20 20 20 20 ta, file);..
000001F0 20 20 69 66 20 28 20 73 69 7A 65 20 29 0D 0A 20 if ( size )..
00000200 20 20 20 20 20 7B 0D 0A 20 20 20 20 20 20 20 20 {..
00000210 20 2F 2A 20 50 72 69 6E 74 20 74 68 65 20 62 61 /* Print the ba
00000220 73 65 20 61 64 64 72 65 73 73 2E 20 2A 2F 0D 0A se address. */..
00000230 20 20 20 20 20 20 20 20 20 70 72 69 6E 74 66 28 printf(
00000240 22 25 30 38 6C 58 20 22 2C 20 28 6C 6F 6E 67 20 "%08lX ", (long
00000250 75 6E 73 69 67 6E 65 64 29 63 6F 75 6E 74 29 3B unsigned)count);
00000260 0D 0A 20 20 20 20 20 20 20 20 20 63 6F 75 6E 74 .. count
00000270 20 2B 3D 20 73 69 7A 65 3B 20 2F 2A 20 61 64 6A += size; /* adj
00000280 75 73 74 20 74 68 65 20 62 61 73 65 20 61 64 64 ust the base add
00000290 72 65 73 73 20 2A 2F 0D 0A 20 20 20 20 20 20 20 ress */..
000002A0 20 20 2F 2A 20 50 72 69 6E 74 20 74 68 65 20 63 /* Print the c
000002B0 68 61 72 61 63 74 65 72 73 27 20 68 65 78 20 76 haracters' hex v
000002C0 61 6C 75 65 73 2E 20 2A 2F 0D 0A 20 20 20 20 20 alues. */..
000002D0 20 20 20 20 66 6F 72 20 28 20 69 20 3D 20 30 3B for ( i = 0;
000002E0 20 69 20 3C 20 73 69 7A 65 3B 20 2B 2B 69 20 29 i < size; ++i )
000002F0 0D 0A 20 20 20 20 20 20 20 20 20 7B 0D 0A 20 20 .. {..
00000300 20 20 20 20 20 20 20 20 20 20 70 72 69 6E 74 66 printf
00000310 28 22 25 30 32 58 20 22 2C 20 64 61 74 61 5B 69 ("%02X ", data[i
00000320 5D 29 3B 0D 0A 20 20 20 20 20 20 20 20 20 7D 0D ]);.. }.
00000330 0A 20 20 20 20 20 20 20 20 20 2F 2A 20 50 61 64 . /* Pad
00000340 20 74 68 65 20 68 65 78 20 76 61 6C 75 65 73 20 the hex values
00000350 61 72 65 61 20 69 66 20 6E 65 63 65 73 73 61 72 area if necessar
00000360 79 2E 20 2A 2F 0D 0A 20 20 20 20 20 20 20 20 20 y. */..
00000370 66 6F 72 20 28 20 2B 2B 69 3B 20 69 20 3C 3D 20 for ( ++i; i <=
00000380 73 69 7A 65 6F 66 20 64 61 74 61 20 2F 20 73 69 sizeof data / si
00000390 7A 65 6F 66 20 2A 64 61 74 61 3B 20 2B 2B 69 20 zeof *data; ++i
000003A0 29 0D 0A 20 20 20 20 20 20 20 20 20 7B 0D 0A 20 ).. {..
000003B0 20 20 20 20 20 20 20 20 20 20 20 66 70 75 74 73 fputs
000003C0 28 22 20 20 20 22 2C 20 73 74 64 6F 75 74 29 3B (" ", stdout);
000003D0 0D 0A 20 20 20 20 20 20 20 20 20 7D 0D 0A 20 20 .. }..
000003E0 20 20 20 20 20 20 20 2F 2A 20 50 72 69 6E 74 20 /* Print
000003F0 74 68 65 20 63 68 61 72 61 63 74 65 72 73 2E 20 the characters.
00000400 2A 2F 0D 0A 20 20 20 20 20 20 20 20 20 66 6F 72 */.. for
00000410 20 28 20 6A 20 3D 20 30 3B 20 6A 20 3C 20 73 69 ( j = 0; j < si
00000420 7A 65 3B 20 6A 2B 2B 20 29 0D 0A 20 20 20 20 20 ze; j++ )..
00000430 20 20 20 20 7B 0D 0A 20 20 20 20 20 20 20 20 20 {..
00000440 20 20 20 70 75 74 63 68 61 72 28 69 73 70 72 69 putchar(ispri
00000450 6E 74 28 64 61 74 61 5B 6A 5D 29 20 3F 20 64 61 nt(data[j]) ? da
00000460 74 61 5B 6A 5D 20 3A 20 27 2E 27 29 3B 0D 0A 20 ta[j] : '.');..
00000470 20 20 20 20 20 20 20 20 7D 0D 0A 20 20 20 20 20 }..
00000480 20 20 20 20 70 75 74 63 68 61 72 28 27 5C 6E 27 putchar('\n'
00000490 29 3B 0D 0A 20 20 20 20 20 20 7D 0D 0A 20 20 20 );.. }..
000004A0 7D 20 77 68 69 6C 65 20 28 20 73 69 7A 65 20 3D } while ( size =
000004B0 3D 20 73 69 7A 65 6F 66 20 64 61 74 61 20 2F 20 = sizeof data /
000004C0 73 69 7A 65 6F 66 20 2A 64 61 74 61 20 29 3B 20 sizeof *data );
000004D0 2F 2A 20 62 72 65 61 6B 20 6F 6E 20 70 61 72 74 /* break on part
000004E0 69 61 6C 20 62 75 66 66 65 72 20 2A 2F 0D 0A 20 ial buffer */..
000004F0 20 20 72 65 74 75 72 6E 20 63 6F 75 6E 74 3B 0D return count;.
00000500 0A 7D 0D 0A 0D 0A 69 6E 74 20 6D 61 69 6E 28 76 .}....int main(v
00000510 6F 69 64 29 0D 0A 7B 0D 0A 20 20 20 73 74 61 74 oid)..{.. stat
00000520 69 63 20 63 6F 6E 73 74 20 63 68 61 72 20 66 69 ic const char fi
00000530 6C 65 6E 61 6D 65 5B 5D 20 3D 20 5F 5F 46 49 4C lename[] = __FIL
00000540 45 5F 5F 3B 20 2F 2A 20 6D 61 79 20 6E 6F 74 20 E__; /* may not
00000550 77 6F 72 6B 20 6F 6E 20 65 76 65 72 79 77 68 65 work on everywhe
00000560 72 65 20 2A 2F 0D 0A 20 20 20 46 49 4C 45 20 2A re */.. FILE *
00000570 66 69 6C 65 20 3D 20 66 6F 70 65 6E 28 66 69 6C file = fopen(fil
00000580 65 6E 61 6D 65 2C 20 22 72 62 22 29 3B 0D 0A 20 ename, "rb");..
00000590 20 20 69 66 20 28 20 66 69 6C 65 20 21 3D 20 4E if ( file != N
000005A0 55 4C 4C 20 29 0D 0A 20 20 20 7B 0D 0A 20 20 20 ULL ).. {..
000005B0 20 20 20 70 72 69 6E 74 66 28 22 25 6C 75 20 62 printf("%lu b
000005C0 79 74 65 73 5C 6E 22 2C 20 28 6C 6F 6E 67 20 75 ytes\n", (long u
000005D0 6E 73 69 67 6E 65 64 29 68 65 78 64 75 6D 70 28 nsigned)hexdump(
000005E0 66 69 6C 65 29 29 3B 0D 0A 20 20 20 20 20 20 66 file));.. f
000005F0 63 6C 6F 73 65 28 66 69 6C 65 29 3B 0D 0A 20 20 close(file);..
00000600 20 7D 0D 0A 20 20 20 65 6C 73 65 0D 0A 20 20 20 }.. else..
00000610 7B 0D 0A 20 20 20 20 20 20 70 65 72 72 6F 72 28 {.. perror(
00000620 66 69 6C 65 6E 61 6D 65 29 3B 0D 0A 20 20 20 7D filename);.. }
00000630 0D 0A 20 20 20 72 65 74 75 72 6E 20 30 3B 0D 0A .. return 0;..
00000640 7D 0D 0A }..
1603 bytes

Return to Table of Contents


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
<limits.h> 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
<limits.h>. 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 <limits.h>

#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 our TOP9 macro then, this string of expressions may look ugly to the
compiler after the precompiler gets finished with it, but it all resolves into a nice
little constant value calculated by the poor compiler at compile time. And on a
garden-variety 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

Вам также может понравиться