====== C ====== ==== Strings ==== This took me forever to figure out, so I'm putting it here so it doesn't get lost. :) #include #include 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. }