Differences
This shows you the differences between two versions of the page.
— |
c [2014/04/11 21:41] (current) beandog created |
||
---|---|---|---|
Line 1: | Line 1: | ||
+ | ====== C ====== | ||
+ | ==== Strings ==== | ||
+ | |||
+ | This took me forever to figure out, so I'm putting it here so it doesn't get lost. :) | ||
+ | |||
+ | <code c> | ||
+ | #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. | ||
+ | |||
+ | } | ||
+ | </code> |