<<

Strings in

======Section #1: Strings in C (p.665) ======

We all know what strings are in programming, right? We use strings to represent text. And what is text but a of individual characters. Hmm, sounds like an array, right? And that’s precisely how strings are stored in C, they’re stored in arrays!

There are different ways to determine where strings end in different languages, but in C, the end of a string is marked by a special character called “the null character.” Characters are stored internally in memory using ASCII values, and the null character has an ASCII integer value of zero. So, another way to view strings in C, is that they are null-terminated (or “zero-terminated”) arrays of chars. In the context of a character array, zero has special meaning, it means “end of the string.” This allows us to create strings of any arbitrary length.

Let’s create a few strings from the stack and see what we get:

auto char str1[] = {‘a’, ‘b’, ‘c’, 0};

Here is an array without an explicit dimension. Remember, you’re allowed to do that but only if you provide a complete initialization list. Since the initialization list has four initializers, and since str1 is an array where each array element is a char, that means a contiguous of four chars will be allocated for the array. It’ll look something like this:

str1 [0x120] +-----+-----+-----+-----+ | ‘a’ | ‘b’ | ‘c’ | 0 | +-----+-----+-----+-----+

But that’s not the only way to do it, here are a couple of others:

auto char str2[] = “abc”;

str2 [0x130] +-----+-----+-----+-----+ | ‘a’ | ‘b’ | ‘c’ | 0 | +-----+-----+-----+-----+

auto char str3[4] = “abc”;

str3 [0x140] +-----+-----+-----+-----+ | ‘a’ | ‘b’ | ‘c’ | 0 | +-----+-----+-----+-----+

Notice that str2 and str3 are being initialized with string literals. How is that possible? It’s possible because string literals are null-terminated character arrays! When you write a (which is a sequence of characters enclosed in a pair of double-quotes), you’re actually creating an unnamed character array that’s allocated from the data section at compile time.

C depends entirely upon static type-checking, which means that all data must have some of that is determined at compile-time. So, what is the data type of a string literal? That data is sitting in the data section somewhere, which means it must have a , but what is its type?

The answer is that it’s a const pointer to a char. Huh? That’s right! In other words, when you write a string literal, it forms an expression that evaluates to the base address of the character array in the data section. Recall that the base address of an array is the address of the first array element, and another word for “address” is “pointer”, and since the first array element is a char, that means a pointer to a char. What happens when you do this?

printf(“%p\n”, “Hello world!”);

The “%p” is used to display a memory address, so who’s address is going to get displayed? It’s going to be the base address of the unnamed character array in the data section that contains “Hello world!” (Try it and see for yourself!)

Okay, if that’s the case, what happens here?

auto char *str4 = “abc”;

Can you visualize that? str4 is a stack variable because it has the auto storage class. It’s a pointer to a char, so it can only store the address of a char. What is it being initialized with? It’s being initialized with the base address of the unnamed string “abc” that’s sitting out in the data section. I hope you already drew a picture of that to make it more concrete, if not, I’ll give you a minute or two (don’t look below this sentence)…

Okay, got it? Let’s compare notes:

STACK DATA

[x0x560] +------+ +-----+-----+-----+-----+ str4 [0x150] | 0x560 | ------> | ‘a’ | ‘b’ | ‘c’ | 0 | +------+ +-----+-----+-----+-----+

See how the stack pointer str4 is a variable with its own address, but the value it contains is just the base address of the unnamed string sitting in the heap? Wow!

Are you ready for more? If you recall the “golden rule”:

a[i] == *(a+i)

Look familiar? Indexing into an array with the subscript operator is the same thing as taking the base address of the array to derive an address, then dereferencing that address to get what’s sitting there. Since str4 contains an address, that means you could apply the subscript operator to it to index into the characters of the string literal:

str4[0] == *(str4+0) == ‘a’ or this:

str4[2] == *(str4+2) == ‘c’

Pretty slick stuff! BTW, I just want to quickly mention that there are different ways to indicate the null character:

1) you can write it as a character constant, like this: ‘\0’ 2) you can write it using the predefined constant: NULL 3) you can write it using a simple integer value of zero: 0

======Section #2: Reading Strings from stdin ======

Okay, we know how to create string literals from the data section using double quotes, and also how to use them to initialize character arrays. What if you want to read a string from the keyboard and store it in a character array? How would you do that?

There are a few ways to do it, and each has tradeoffs. You might try this:

auto char myStr[100];

myStr [0x200] +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | ‘?’ | ‘?’ | ‘?’ | ? | ? | ? | ? | ? | ? | ? | ? | <-- undefined content +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ [0] [1] [2] ...... [99]

printf(“Please enter a string of text: “); scanf(“%s”, myStr);

Here the scanf function is being used to read a string from stdin and store it in the character array. Notice that we don’t need to use the address operator for myStr because that’s an array identifier without a subscript operator, which evaluates to the base address, which is a pointer to the first char in the array of chars. But if the user sees the prompt and types “C is fun!” what do we get? We get this:

myStr [0x200] +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | ‘C’ | 0 | ‘?’ | ? | ? | ? | ? | ? | ? | ? | ? | +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ [0] [1] [2] ...... [99]

What? Where did the “fun” go? The problem here is that scanf sees whitespace as a . In other words, it uses whitespace to know when one piece of data ends and the next one begins. Since the user entered “C is fun!”, scanf figured it was done extracting the string from stdin as soon as it reached that blank space, so it just left us with a string that contains “C”.

How can we get an entire string, including embedded whitespace? There’s a function in the called fgets, it will read an entire string, whitespace and all. Well, almost… The fgets function sees the as the delimiter, so it will copy all of your characters up to and including the newline character. Here’s how it can be used:

printf(“Please enter a string of text: “); fgets(myStr, 100, stdin);

With that function call, you’ll get this:

myStr [0x200] +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ | ‘C’ | ‘ ’ | ‘i’ | ‘s’ | ‘ ’ | ‘f’ | ‘u’ | ‘n’ | ‘!’ |‘\n’ | 0 | ‘?’ | ‘?’ | +-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+-----+ [0] [1] [2] ...... [99]

Notice that fgets copies the newline character into the character array. Also, notice that the function call gets three pieces of from our arguments:

1) the base address of the character array

2) the capacity of the array

3) the input to read from (stdin)

This way fgets will read at most 100 characters and store them in your character array, always leaving room for the null character. This is a much safer function to use than scanf because fgets won’t exceed beyond the allocated capacity of the destination array, assuming you gave it the correct capacity argument. The scanf doesn’t have that information, so if you create an array of ten chars and then use scanf to read a string of 100 chars, scanf will just keep writing those chars to memory, far exceeding the allocated boundary of your array argument.

Something else to note about the fgets function, it returns a pointer to the character array argument if all goes well. However, if an error occurs, or if EOF is read, then the fgets function will return a NULL pointer instead. This is how you can tell if you successfully read a string when calling the function, you’ll get a pointer returned to you that’s either NULL or non-NULL.

There you have it, that’s what strings are like in C. Now you can see why, in C++, when null-terminated character arrays are used, they’re called cstrings, because that’s where they came from!