C

Strings

This took me forever to figure out, so I'm putting it here so it doesn't get lost. :)

#include <stdio.h>
#include <string.h>
 
int main() {
 
	// References:
	// http://www.programiz.com/c-programming/c-strings
	// http://www.c-faq.com/charstring/index.html
	// http://www.c-faq.com/charstring/assign.html
 
	// STRINGS ARE ARRAYS OF CHARACTERS *WITH* A NULL TERMINATOR
	// Remember that, because that makes sense when you try to copy strings
	// from one place to another.  It becomes much clearer when you realize
	// you're only dealing with *arrays*.
	//
	// Consider an example in PHP:
	// $arr_chars() = array();
	// You can't say $arr_chars = 'hello'; because they aren't even the
	// same type.  You could however say:
	// $arr_chars = array('H', 'e', 'l', 'l', 'o');  This example also
	// illustrates the length of each value in an array -- one character.
	//
	// A STRING, then, it just an array of characters with a NULL
	// terminator as the last value.
	//
	// In the same example, $arr_chars has 5 values: one for each letter.
	// However, in C, a "string" needs to have a NULL terminator.  So
	// if using the same PHP syntax, the new array would look like this:
	// $arr_chars = array('H', 'e', 'l', 'l', 'o', '\0'); and it has
	// six values again.
	//
	// When copying strings around, remember that you only want parts of
	// the array.  You can initialize an array, and have a null terminator,
	// which makes it a string, but that is still not the END of possible
	// values in the array.  You would still need to resize it to the
	// correct size.
	//
	// So, for example, using PHP syntax as an example, let's say you
	// wanted to create an array of 6 characters ('Hello\0') and create
	// a new one with only the first two *string* characters ('He').
	//
	// The size of the first array needs to b 6 -- 5 characters and one
	// null terminator.
	//
	// Creating a string like this automatically adds the terminator:
	char *hello = "Hello";
	// Likewise, create a new array with a specific number of characters:
	// two, plus one for the null terminator.
	char hi[3];
	// Use the strncpy function to copy *contents of the array* from one
	// to another.
	//
	// strncpy() is a good function for two main reasons:
	// 1. It allows you to set a fixed amount of characters (array
	//    elements).  This is a good feature because it limits the
	//    amount you want to copy.
	//
	//    I was trying to think of a good example to use of why this is
	//    helplful, but I couldn't really come up with anyhting, mostly
	//    because it's this second feature that really makes it shine:
	//
	// 2. It automatically adds a null terminator **if** the size of the
	//    source is at least one smaller than the destination.  This is
	//    where the real power of this function comes into play.  Since
	//    it's used for strings, who need null terminators, it simply
	//    takes care of managing one for you.
	//
	// Here are some specific walkthroughs.  First, let's start with two
	// words that we can trim down and they'll both be full words.  Let's
	// use "hope" and "hop".  Since they are only one character apart, that
	// will make it simpler.
	//
	// Create the first string.  It will be an array of characters with
	// four of them being letters, and the fifth a null terminator.
	//
	// For the sake of verbosity, we'll set each value directly.
	char str[5] = {'H','o','p','e','\0'};
	//
	// If we check the string length, then it will return 4.  The strlen
	// function counts all the letters *up until* the null terminator. So
	// even though the array has 5 characters, there are only 4 actual
	// lettters.
	printf("string length: %lu\n", strlen(str));
	//
	// At the same time, if we looked at the *real* size of the array, it
	// will say that there are 5 characters.
	printf("sizeof array: %lu\n", sizeof(str));
	//
	// Once you understand that, you can see how strings are really just
	// a quick check to see if there's a null terminator or not.
	//
	// That's it!  Pretty simple when you look at it that way.  The
	// string length is always one less than the size of the actual array.
	//
	// Now, some IMPORTANT THINGS TO REMEMBER about strncpy():
	//
	// 1. "Warning:  If  there  is no null byte among the first n bytes of
	//    src, the string placed in dest will not be null-terminated."
	//
	// You don't really have to worry about this if your source array
	// wasn't a string anyway, because you'd probably know what you were
	// doing anyway.
	//
	// An example:
	char abc[3] = {'a','b','c'};
	// Is this a string?  No, it is not, because it doesn't have a null
	// terminator on the end.  It's an array with three characters.  Now
	// if you want to copy that array to another one of the same size, then
	// you can use strncpy.  But since there's no null terminator, it won't
	// gracefully add one for you, because you didn't set that expectation
	// in the first place;
	//
	// Moving on, create a second array with 3 possible values.
	char abc_clone[3];
	// And use strncpy to copy the original in to the new one:
	strncpy(abc_clone, abc, 3);
	// There's no null terminator added here, so checking the size will
	// return a 3.
	printf("sizeof abc_clone: %lu\n", sizeof(abc_clone));
	//
	// Now let's do the same thing, but this time with a string.
	char str_abc[4] = {'a','b','c','\0'};
	// This array has a null terminator plus three characters, so it counts
	// as a string.  Let's use strncpy again to copy it directly to another
	// string:
	char str_abc_clone[4];
	strncpy(str_abc_clone, str_abc, 4);
	// Strangely enough, strncpy isn't doing anything special here, because
	// it's copying all the array values.  Having a null terminator on the
	// source doesn't make a difference to the destination, because all
	// four values are being copied anyway.
	//
	// To show some of the power of strncpy, we'll create an array with
	// one more additional value, and then compare sizes after using strncpy.
	//
	// Here, we set another array:
	char five_ton[5];
	// If we checked the size of the array, it would return 5 -- just as
	// expected, because that is how we declared it.
	//
	// Now, what happens when you take a string (three letters + one null
	// terminator) and add it to an array that is larger than it?
	//
	// First, let's go ahead and copy ALL the characters that the first array
	// has, including its null terminator:
	strncpy(five_ton, str_abc, 4);
	// Remember that 'str_abc' is an array of four characters (three letters
	// plus one null terminator).  The *size* of the array of five_ton has
	// not changed at all -- it's still five characters:
	printf("size of five_ton: %lu\n", sizeof(five_ton));
	// However, if we checked the length of the string, it's going to stop
	// at the first null terminator, so it will say it is 3 instead of 5.
	printf("string length of five_ton: %lu\n", strlen(five_ton));
	//
	// In that first example, we told strncpy to pass FOUR of the FOUR
	// characters in our 'abc' string, which also put the null terminator
	// in there, because it was a part of that array.
	//
	// This is where the magic of strncpy comes into play -- if you only
	// passed those *three* letters, and the new array can hold the original
	// size (three) plus one more, it will automatically add a null
	// terminator for you -- thus making strncpy essentially a "copy from
	// an array to a string" function. :)
	//
	// Here's another example:
	char five_more_tons[5];
	strncpy(five_more_tons, str_abc, 3);
	// Now the size of the array is still going to be 5, because that's
	// how many characters we set it to (allocated memory for).  The string
	// length, however, is only going to be 3 -- the same string length
	// of the 'str_abc' array.  You can again verify it using sizeof() and
	// strlen():
	printf("size of five_more_tons: %lu\n", sizeof(five_more_tons));
	printf("string length of five_more_tons: %lu\n", strlen(five_more_tons));
 
	// That's it for now!! There's still a lot of questions I have, such as:
	// - How do you "trim" a string -- make the size of the array smaller?
	//   Seems to me that maybe making a new array is easiest, and using
	//   strlen() to get the total size, plus one, and then strncpy() to
	//   put it in there.  Example?
	char fun[16];
	fun[0] = 'f';
	fun[1] = 'u';
	fun[2] = 'n';
	fun[3] = '\0';
	// The array length is still 16, and has 12 more characters it can hold.
	// Get the array size of the new one -- the length of the string, plus
	// an extra one for the null character:
	int i = strlen(fun) + 1;
	char less_fun[i];
	// Then go ahead and strncpy like normal!
	// Since the first array has a null terminator already, and the second
	// array has room for it, then *either* one of these strncpy functions
	// would work, and return the same result:
	//
	// strncpy adds its own null terminator:
	strncpy(less_fun, fun, 3);
	// strncpy copies all the characters, including the null terminator:
	strncpy(less_fun, fun, 4);
	// That's still not a real "fix" to shortening a string, but it does
	// make it possible to just start over.  Is there a way to make it go
	// a little faster?  That is, in less code ..
	char str_testing[strlen("testing") + 1];
	strncpy(str_testing, "testing", strlen("testing"));
	printf("testing: %s\n", str_testing);
	// ... or in that case, just more complex.
 
}