第11章-字符串和字符串函数

第11章-字符串和字符串函数_第1张图片 


#include 
#define MSG "I am a symbolic string constant."
#define MAXLENGTH 81
int main(void) {
  char words[MAXLENGTH] = "I am a string in an array.";
  const char* pt1 = "Something is pointing at me.";
  puts("Here are some strings:");
  puts(MSG);
  puts(words);
  puts(pt1);
  words[8] = 'p';
  puts(words);
  return 0;
}
Here are some strings:
I am an old-fashioned symbolic string constant.
I am a string in an array.
Something is pointing at me.
I am a spring in an array.

Character String Literals(复合常量) (String Constants)

A string literal , also termed a string constant , is anything enclosed in double quotation marks. The enclosed characters, plus a terminating \0 character automatically provided by the compiler, are stored in memory as a character string. So "I am a symbolic string constant." , "I am a string in an array." , "Something is pointed at me." , and "Here are some strings:" all are string literals.

char greeting[50] = {"Hello, and"," how are"," you"," today!"};
is equivalent to this:
char greeting[50] = "Hello, and how are you today!";

Character string constants are placed in the static storage class, which means that if you use a string constant in a function, the string is stored just once and lasts for the duration of the program, even if the function is called several times. The entire quoted phrase acts as a pointer to where the string is stored. This action is analogous to the name of an array acting as a pointer to the array’s location.

/* strings as pointers */
#include 
int main(void) {
  printf("%s, %p, %c\n", "We", "are", *"space farers");
  return 0;
}
We, 0x100000f61, s

*"space farers" should produce the value to which the address points, which should be the first character of the string "space farers".

printf("%c", *("space farers"+1)); 输出:p

Character String Arrays and Initialization:

When you define a character string array, you must let the compiler know how much space is needed. One way is to specify an array size large enough to hold the string. The following declaration initializes the array m1 to the characters of the indicated string:

const char m1[40] = "Limit yourself to one line's worth.";

The const indicates the intent to not alter this string.

This form of initialization is short for the standard array initialization form:

const char m1[40] = {
'L', 'i', 'm', 'i', 't', ' ', 'y', 'o', 'u', 'r',
's', 'e', 'l', 'f', ' ', 't', 'o', ' ', 'o', 'n', 
'e', ' ', 'l', 'i', 'n', 'e', '\", 's', ' ', 'w',
'o', 'r', 't', 'h', '.', '\0' };

Note the closing null character. Without it, you have a character array, but not a string.

When you specify the array size, be sure that the number of elements is at least one more (that null character again) than the string length. Any unused elements are automatically initialized to 0 (which in char form is the null character, not the zero digit character).

第11章-字符串和字符串函数_第2张图片

int n = 8;
char cookies[1]; // valid
char cakes[2 + 5]; // valid, size is a constant expression
char pies[2*sizeof(long double) + 1]; // valid
char crumbs[n]; // invalid prior to C99, a VLA after C99

The name of a character array, like any array name, yields the address of the first element of the array. Therefore, the following holds:

char car[10] = "Tata";
car == &car[0], *car == 'T', and *(car+1) == car[1] == 'a'

Indeed, you can use pointer notation to set up a string. For example, using the following declaration:

const char * pt1 = "Something is pointing at me.";

This declaration is very nearly the same as this one:

const char ar1[] = "Something is pointing at me.";

The declarations amount to saying that both pt1 and ar1 are addresses of strings. In both cases, the quoted string itself determines the amount of storage set aside for the string.

Nonetheless, the forms are not identical.

Array Versus Pointer:

What is the difference, then, between an array and a pointer form? The array form ( ar1[] ) causes an array of 29 elements (one for each character plus one for the terminating '\0' ) to be allocated in the computer memory. Each element is initialized to the corresponding character of the string literal. Typically, what happens is that the quoted string is stored in a data segment that is part of the executable file; when the program is loaded into memory, so is that string. The quoted string is said to be in static memory . But the memory for the array is allocated only after the program begins running. At that time, the quoted string is copied into the array. ( Chapter 12 , “Storage Classes, Linkage, and Memory Management,” will discuss memory management more fully.) Note that, at this time, there are two copies of the string. One is the string literal in static memory, and one is the string stored in the ar1 array.

Hereafter, the compiler will recognize the name ar1 as a synonym for the address of the first array element, &ar1[0] . One important point here is that in the array form, ar1 is an address constant . You can’t change ar1 , because that would mean changing the location (address) where the array is stored. You can use operations such as ar1+1 to identify the next element in an array, but ++ar1 is not allowed. The increment operator can be used only with the names of variables (or, more generally, modifiable lvalues), not with constants.

The pointer form ( *pt1 ) also causes 29 elements in static storage to be set aside for the string. In addition, once the program begins execution, it sets aside one more storage location for the pointer variable pt1 and stores the address of the string in the pointer variable. This variable initially points to the first character of the string, but the value can be changed. Therefore, you can use the increment operator. For instance, ++pt1 would point to the second character ( o ).

A string literal is considered to be const data. Because pt1 points to that data, it should be declared as pointing to const data. This doesn’t mean you can’t change the value of pt1 (i.e., where it points), but it does mean you can’t use pt1 to change the data itself. If you copy a string literal to an array, on the other hand, you are free to change the data unless you choose to declare the array as const .

In short, initializing the array copies a string from static storage to the array, whereas initializing the pointer merely copies the address of the string. Listing 11.3 illustrates these points.

// addresses of strings
#define MSG "I'm special."
#include 
int main() {
    char ar[] = MSG;
    const char *pt = MSG;
    printf("address of \"I'm special\": %p \n", "I'm special");
    printf(" address ar: %p\n", ar);
    printf(" address pt: %p\n", pt);
    printf(" address of MSG: %p\n", MSG);
    printf("address of \"I'm special\": %p \n", "I'm special");
    return 0;
}

Here’s the output from one system: 

address of "I'm special": 0x100000f0c
              address ar: 0x7fff5fbff8c7
              address pt: 0x100000ee0
          address of MSG: 0x100000ee0
address of "I'm special": 0x100000f0c

What does this show? First, pt and MSG are the same address, while ar is a different address, just as promised(与前面讨论的内容一致). Second, although the string literal(字符串字面量) "I'm special." occurs twice in the printf() statements, the compiler chose to use one storage location, but not the same address as MSG . The compiler has the freedom to store a literal that’s used more than once in one or more locations. Another compiler might choose to represent all three occurrences of "I'm special." with a single storage location(另一个编译器可能在不同的位置储存3个"I'm special"。). Third, the part of memory used for static data is different from that used for dynamic memory, the memory used for ar(静态数据使用的内存与ar使用的动态内存不同。). Not only are the values different, but this particular compiler even uses a different number of bits to represent the two kinds of memory(不仅值不同,特定编译器甚至使用不同的位数表示两种内存。).

Are the differences between array and pointer representations of strings important(数组和指针表示字符串的区别是否很重要)? Often they are not, but it depends on what you try to do.(通常不太重要,但是这取决于想用程序做什么。). Let’s look further into the matter.

Array and Pointer Differences:

Let’s examine the differences between initializing a character array to hold a string and initializing a pointer to point to a string. (By “pointing to a string,” we really mean pointing to the first character of a string.) For example, consider these two declarations:

char heart[] = "I love Tillie!";
const char *head = "I love Millie!";

The chief difference is that the array name heart is a constant, but the pointer head is a variable. What practical difference does this make?

First, both can use array notation:

for (i = 0; i < 6; i++)
    putchar(heart[i]);
putchar('\n');
for (i = 0; i < 6; i++)
    putchar(head[i]));
I love
I love

Next, both can use pointer addition:

for (i = 0; i < 6; i++)
    putchar(*(heart + i));
putchar('\n');
for (i = 0; i < 6; i++)
    putchar(*(head + i));

Only the pointer version, however, can use the increment operator:

while (*(head) != '\0') /* stop at end of string */
    putchar(*(head++)); /* print character, advance pointer */
I love Millie!

Below show a difference: 

head = heart; /* head now points to the array heart */

heart = head; /* illegal construction */

The situation is analogous to x = 3; versus 3 = x;. The left side of the assignment statement must be a variable or, more generally, a modifiable lvalue , such as *p_int. Incidentally, head = heart; does not make the "Millie" string vanish(消失); it just changes the address stored in head. Unless you’ve saved the address of "I love Millie!" elsewhere, however, you won’t be able to access that string when head points to another location.(它还是在原来的地址对应的地方存储着,head指向了别处无法直接访问它而已)

There is a way to alter the heart message—go to the individual array elements:

heart[7]= 'M';
or
*(heart + 7) = 'M';

The elements of an array are variables (unless the array was declared as const ), but the name is not a variable.

Let’s go back to a pointer initialization that doesn’t use the const modifier:

char * word = "frame";

Can you use the pointer to change this string?

word[1] = 'l'; // allowed??

Your compiler may allow this, but, under the current C standard, the behavior for such an action is undefined. Such a statement could, for example, lead to memory access errors. The reason is that, as mentioned before, a compiler can choose to represent all identical string literals with a single copy in memory. For example, the following statements could all refer to a single memory location of string "Klingon":

char * p1 = "Klingon";
p1[0] = 'F'; // ok?
printf("Klingon");
printf(": Beware the %ss!\n", "Klingon");

That is, the compiler can replace each instance of "Klingon" with the same address. If the compiler uses this single-copy representation and allows changing p1[0] to 'F' , that would affect all uses of the string, so statements printing the string literal "Klingon" would actually display "Flingon" :

Flingon: Beware the Flingons!

In fact, in the past, several compilers did behave this rather confusing way, whereas others produced programs that abort(p1[0]='F';——>"EXC_BAD_ACCESS":应用程序无法访问该内存块). Therefore, the recommended practice for initializing a pointer to a string literal is to use the const modifier:

const char * pl = "Klingon"; // recommended usage

Initializing a non-const array with a string literal, however, poses no such problems, because the array gets a copy of the original string. In short, don’t use a pointer to a string literal if you plan to alter the string.

Arrays of Character Strings:

It is often convenient to have an array of character strings.Then you can use a subscript to access several different strings.

// array of pointers, array of strings
#include 
#define SLEN 40
#define LIM 5
int main(void) {
  const char* mytalents[LIM] = {
      "Adding numbers swiftly", "Multiplying accurately", "Stashing data",
      "Following instructions to the letter", "Understanding the C language"};

  char yourtalents[LIM][SLEN] = {"Walking in a straight line", "Sleeping",
                                 "Watching television", "Mailing letters",
                                 "Reading email"};
  int i;
  puts("Let's compare talents.");
  printf("%-36s %-25s\n", "My Talents", "Your Talents");
  for (i = 0; i < LIM; i++)
    printf("%-36s %-25s\n", mytalents[i], yourtalents[i]);
  printf("\nsizeof mytalents: %zd, sizeof yourtalents: %zd\n",
         sizeof(mytalents), sizeof(yourtalents));
  return 0;
}
Let's compare talents.
My Talents                           Your Talents             
Adding numbers swiftly               Walking in a straight line
Multiplying accurately               Sleeping                 
Stashing data                        Watching television      
Following instructions to the letter Mailing letters          
Understanding the C language         Reading email            

sizeof mytalents: 40, sizeof yourtalents: 200
//注: mytalents和mytalents[0]都是地址,对于64位系统,地址占8字节大小

In some ways, mytalents and yourtalents are much alike. Each represents five strings. When used with one index, as in mytalents[0] and yourtalents[0] , the result is a single string. And, just as mytalents[1][2] is 'l' , the third character of the second string represented by mytalents , yourtalents[1][2] is 'e' , the third character of the second string represented by yourtalents . Both are initialized in the same fashion.

But there are differences, too. The mytalents array is an array of five pointers, taking up 40 bytes on our system. But yourtalents is an array of five arrays, each of 40 char values, occupying 200 bytes on our system. So mytalents is a different type from yourtalents , even though mytalents[0] and yourtalents[0] both are strings. The pointers in mytalents point to the locations of the string literals used for initialization, which are stored in static memory(mytalents中的指针指向初始化时所用的字符串字面量的位置,这些字符串字面量被储存在静态内存中). The arrays in yourtalents , however, contain copies of the string literals, so each string is stored twice(yourtalents中的数组则储存着字符串字面量的副本,所以每个字符串都被储存了两次). Furthermore, the allocation of memory in the arrays is inefficient, for each element of yourtalents has to be the same size, and that size has to be at least large enough to hold the longest string(此外,为字符串数组分配内存的使用率较低。yourtalents中的每个元素的大小必须相同,而且必须是能储存最长字符串的大小).

One way of visualizing this difference is to think of yourtalents as a rectangular two-dimensional array, with each row being of the same length, 40 bytes, in this case. Next, think of mytalents as a ragged array, one in which the row length varies(我们可以把yourtalents想象成矩形二维数组,每行的长度都是40字节;把mytalents想象成不规则的数组,每行的长度不同。实际上,mytalents数组的指针元素所指向的字符串不必储存在连续的内存中,下图示只是为了强调两种数组的不同).

第11章-字符串和字符串函数_第3张图片

char fruit_one[3][7]={"Apple","Pear","Orange"};const char * fruit_two[3]={"Apple","Pear","Orange"};区别:一个是直接存储的字符串字面量,一个是存储的指针。所以,它们占用的内存大小是不一样的。

The upshot is that, if you want to use an array to represent a bunch of strings to be displayed, an array of pointers is more efficient than an array of character arrays. There is, however, a catch(但是,指针数组也有自身的缺点). Because the pointers in mytalents point to string literals, these strings shouldn’t be altered. The contents of yourtalents , however, can be changed. So if you want to alter strings or set aside space for string input(为字符串输入预留空间), don’t use pointers to string literals.

Pointers and Strings:

Perhaps you noticed an occasional reference to pointers in this discussion of strings. Most C operations for strings actually work with pointers(你可能已经注意到了,在讨论字符串时或多或少会涉及指针。实际上,字符串的绝大多数操作都是通过指针完成的).

/* pointers and strings */
#include 
int main(void) {
  const char* mesg = "Don't be a fool!";
  const char* copy;
  copy = mesg;
  printf("%s\n", copy);
  printf("mesg = %s; &mesg = %p; value = %p\n", mesg, &mesg, mesg);
  printf("copy = %s; © = %p; value = %p\n", copy, ©, copy);
  return 0;
}
Don't be a fool!
mesg = Don't be a fool!; &mesg = 0x0012ff48; value = 0x0040a000
copy = Don't be a fool!; © = 0x0012ff44; value = 0x0040a000

First, mesg and copy are printed as strings ( %s ). No surprises here; all the strings are "Don't be a fool!" .

The next item on each line is the address of the specified pointer. For this particular run, the two pointers mesg and copy are stored in locations 0x0012ff48 and 0x0012ff44, respectively.

Now notice the final item, the one we called value . It is the value of the specified pointer. The value of the pointer is the address it contains. You can see that mesg points to location 0x0040a000 , and so does copy . Therefore, the string itself was never copied. All that copy = mesg; does is produce a second pointer pointing to the very same string.

Why all this pussyfooting around? Why not just copy the whole string? Well, ask yourself which is more efficient: copying one address or copying, say, 50 separate elements? Often, the address is all that is needed to get the job done. If you truly require a copy that is a duplicate, you can use the strcpy() or strncpy() function, discussed later in this chapter.

String Input:

If you want to read a string into a program, you must first set aside space to store the string and then use an input function to fetch the string.

Creating Space:

The first order of business is setting up a place to put the string after it is read(要做的第一件事是分配空间,以储存稍后读入的字符串). As mentioned earlier, this means you need to allocate enough storage to hold whatever strings you expect to read(意味着必须要为字符串分配足够的空间). Don’t expect the computer to count the string length as it is read and then allot space for it(不要指望计算机在读取字符串时顺便计算它的长度). The computer won’t (unless you write a function to do so). For example, suppose you try something like this:

char *name; // 声明了一个“野指针”
scanf("%s", name);

It will probably get by the compiler, most likely with a warning, but when the name is read, the name might be written over data or code in your program(name可能改写掉程序中的数据或代码), and it might cause a program abort. That’s because scanf() copies information to the address given by the argument, and in this case, the argument is an uninitialized pointer; name might point anywhere(野指针).

The simplest course is to include an explicit array size in the declaration(最简单的方法是,在声明时显式指明数组的大小):

char name[81];

Now name is the address of an allocated block of 81 bytes(现在name是一个已分配块(81字节)的地址).

The Unfortunate gets() Function:

/* using gets() and puts() */
#include 
#define STLEN 81
int main(void) {
  char words[STLEN];
  puts("Enter a string, please.");
  gets(words);
  printf("Your string twice:\n");
  printf("%s\n", words);
  puts(words);
  puts("Done.");
  return 0;
}
Enter a string, please.
|I want to learn about string theory!
Your string twice:
I want to learn about string theory!
I want to learn about string theory!
Done.

You'll get a warning: this program uses gets(),which is unsafe.

So what’s the problem? The problem is that gets() doesn’t check to see if the input line actually fits into the array. Given that its only argument here is words , gets() can’t check.

The Alternatives to 【gets()】:

The fgets()-fputs() Function:

/* using fgets() and fputs() */
#include 
#define STLEN 14
int main(void) {
  char words[STLEN];
  puts("Enter a string, please.");
  fgets(words, STLEN, stdin);
  printf("Your string twice (puts(), then fputs()):\n");
  puts(words);
  fputs(words, stdout);
  puts("Enter another string, please.");
  fgets(words, STLEN, stdin); // 如果缓存里有数据,则从缓存取;否则,才从键盘获取
  printf("Your string twice (puts(), then fputs()):\n");
  puts(words);
  fputs(words, stdout);
  puts("Done.");
  return 0;
}
Enter a string, please.
|apple pie
Your string twice (puts(), then fputs()):
apple pie

apple pie
Enter another string, please.
|strawberry shortcake
Your string twice (puts(), then fputs()):
strawberry sh
strawberry shDone.
——————————————————————————————————————————
Enter a string, please.
|abcdefghijklmnopq
Your string twice (puts(), then fputs()):
abcdefghijklm
abcdefghijklmEnter another string, please.
Your string twice (puts(), then fputs()):
nopq

nopq
Done.

fgets() differs from gets():

■ It takes a second argument indicating the maximum number of characters to read. If this argument has the value N , fgets() reads up to N-1 characters or through the newline character, whichever comes first(fgets()将读入N-1个字符或者读到遇到的第一个换行符为止).

■ If fgets() reads the newline, it stores it in the string, unlike gets() , which discards it.

■ It takes a third argument indicating which file to read. To read from the keyboard, use stdin (for standard input ) as the argument; this identifier is defined in stdio.h.

gets() 丢弃末尾回车换行符 fgets() 获取所有键盘输入字符(包括回车换行符)
puts() 添加末尾回车换行符 fputs() 所有字符原样输出

Because the fgets() function includes the newline as part of the string (assuming the input line fits), it’s often paired with fputs() , which works like puts() , except that it doesn’t automatically append a newline. It takes a second argument to indicate which file to write to. For the computer monitor we can use stdout (for standard output) as an argument.

The first input, apple pie, is short enough that fgets() reads the whole input line and stores apple pie\n\0 in the array. So when puts() displays the string and adds its own newline to the output, it produces a blank output line after apple pie . Because fputs() doesn’t add a newline, it doesn’t produce a blank line.

The second input line, strawberry shortcake, exceeds the size limit, so fgets() reads the first 13 characters and stores strawberry sh\0 in the array. Again, puts() adds a newline to the output and fputs() doesn’t.

The fgets() function returns a pointer to char. If all goes well, it just returns the same address that was passed to it as the first argument. If the function encounters end-of-file, however, it returns a special pointer called the null pointer. This is a pointer guaranteed not to point to valid data so it can be used to indicate a special case. In code it can be represented by the digit 0 or, more commonly in C, by the macro NULL.

/* using fgets() and fputs() */
#include 
#define STLEN 10
int main(void) {
  char words[STLEN];
  puts("Enter strings (empty line to quit):");
  while (fgets(words, STLEN, stdin) != NULL && words[0] != '\n')
    fputs(words, stdout);
  puts("Done.");
  return 0;
}
Enter strings (empty line to quit):
|By the way, the gets() function
By the way, the gets() function
|also returns a null pointer if it
also returns a null pointer if it
|encounters end-of-file.
encounters end-of-file.

Done.

Interesting—even though STLEN is 10, the program seems to have no problem processing input lines much longer than that. What’s happening is that, in this program, fgets() reads in input STLEN – 1 (i.e., 9) characters at a time. So it begins by reading “By the wa”, storing it as By the wa\0. Then fputs() displays this string and does not advance to the next output line. Next, fgets() resumes where it left off on the original input, that is, it reads “y, the ge” and stores it as y, the ge\0. Then fputs() displays it on the same line it used before. Then fgets() resumes reading the input, and so on, until all that’s left is “tion\n”; fgets() stores tion\n\0, fputs() displays it, and the embedded newline character moves the cursor to the next line.

The system uses buffered I/O. This means the input is stored in temporary memory (the buffer) until the Return key is pressed; this adds a newline character to the input and sends the whole line on to fgets() . On output, fputs() sends characters to another buffer, and when a newline is sent, the buffer contents are sent on to the display.

The fact that fgets() stores the newline presents a problem and an opportunity. The problem is that you might not want the newline as part of the string you store. The opportunity is the presence or absence of a newline character in the stored string can be used to tell whether the whole line was read. If it wasn’t, then you can decide what to do with the rest of the line.

First, how can you get rid of(丢弃) a newline? One way is to search the stored string for a newline and to replace it with a null character:

while (words[i] != '\n') // assuming \n in words
    i++;
words[i] = '\0';

Second, what if there are still characters left in the input line? One reasonable choice if the whole line doesn’t fit into the destination array is to discard the part that doesn’t fit:

while (getchar() != '\n') // read but don't store
    continue; // input including \n
/* using fgets() */
#include 
#define STLEN 10
int main(void) {
    char words[STLEN];
    int i;
    puts("Enter strings (empty line to quit):");
    while (fgets(words, STLEN, stdin) != NULL && words[0] != '\n') {
        i = 0;
        while (words[i] != '\n' && words[i] != '\0')
            i++;
        if (words[i] == '\n')
            words[i] = '\0';
        else // 读到最大字符数都没有读到换行符(words[i] != '\n' && words[i] == '\0')
            while (getchar() != '\n') // 对于abcdefghijklm\n,words=abcdefghi\0
                continue;             // 要丢弃的缓存数据: jklm\n,通过getchar()以\n为
        puts(words);                  // 结束判断符(因为\n只可能是回车键输入的),清空缓存
    }
    puts("done");
    return 0;
}

The loop:

while (words[i] != '\n' && words[i] != '\0')
    i++;

marches through the string until reaching a newline or null character, whichever comes first. If that character is a newline, the following if statement replaces it with a null character. Otherwise, the else part disposes of the rest of the input line.

Here is sample run:

Enter strings (empty line to quit):
|This
This
|program seems
program s
|unwilling to accept long lines.
unwilling
|But it doesn't get stuck on long
But it do
|lines either.
lines eit

done

Null and null:

Null character and null pointer both appear before. Conceptually, these two nulls are different from one another. The null character, or '\0' , is the character used to mark the end of a C string. It’s the character whose code is zero. Because that isn’t the code of any character, it won’t show up accidentally in some other part of the string.

The null pointer, or NULL , has a value that doesn’t correspond to a valid address of data. It’s often used by functions that otherwise return valid addresses to indicate some special occurrence, such as encountering end-of-file or failing to perform as expected.

So the null character is an integer type, while the null pointer is a pointer type. What sometimes causes confusion is that both can be represented numerically by the value 0. But, conceptually, they are different types of 0. Also, while the null character, being a character, is one byte, the null pointer, being an address, typically is four bytes.

The gets_s() Function:

C11’s optional gets_s() function, like fgets() , uses an argument to limit the number of characters read. Given the same definitions used in the codes above, the following code would read a line of input into the words array providing the newline shows up in the first 9 characters of input:

gets_s(words, STLEN);

The three main differences from fgets() are these:

gets_s() just reads from the standard input, so it doesn’t need a third argument.

■ If gets_s() does read a newline; it discards it rather than storing it.

■ If gets_s() reads the maximum number of characters and fails to read a newline, it takes several steps. It sets the first character of the destination array to the null character. It reads and discards subsequent input until a newline or end-of-file is encountered. It returns the null pointer. It invokes an implementation-dependent “handler” function (or else one you’ve selected), which may cause the program to exit or abort.(如果gets_s()读到最大字符数都没有读到换行符,会执行以下几步。首先,先把目标数组中的首字符设置为空字符,读取并丢弃随后的输入直至读到换行符或文件结尾,然后返回空指针。接着,调用依赖实现的“处理函数”(或你选择的其他函数),可能会中止或退出程序。)

#include 
int main(void) {
	char buffer[9];
	gets_s(buffer, 9); // C11,一旦输入超过9-1=8字节(末尾的'\0'占去1字节)就会出错
	printf("Your input was %s\n", buffer);
	return 0;
}
|12345678
Your input was 12345678

第11章-字符串和字符串函数_第4张图片

Let’s compare the suitability of gets() , fgets() , and gets_s(). If the input line fits into the target storage, all three work fine. But fgets() does include the newline as part of the string, and you may need to provide code to replace it with a null character.

What if the input line doesn’t fit(如果输入行太长会怎样)? Then gets() isn’t safe; it can corrupt your data and compromise security(它会擦写现有数据,存在安全隐患). The gets_s() function is safe, but, if you don’t want the program to abort or otherwise exit, you’ll need to learn how to write and register special “handlers.” Also, if you manage to keep the program running, gets_s() disposes of the rest of the input line whether you want to or not. The fgets() function is the easiest to work with if the line doesn’t fit, and it leaves more choices up to you. If you want the program to process the rest of the input line, you can do as the codes shows above. If, instead, you want to dispose of the rest of the input line, you can do that, too, as codes shows above. So gets_s() , when input fails to meet expectations, is less convenient and flexible than fgets(). Perhaps that’s one reason that gets_s() is just an optional extension of the C library. And given that gets_s() is optional, using fgets() usually is the better choice.

The s_gets() Function:

char * s_gets(char * st, int n) {
    char * ret_val;
    int i = 0;
    ret_val = fgets(st, n, stdin);
    if (ret_val) // encounter end-of-file or error
    {
        while (st[i] != '\n' && st[i] != '\0')
            i++;
        if (st[i] == '\n')
            st[i] = '\0';
        else // input is longer than n
            while (getchar() != '\n')
                continue;
    }
    return ret_val;
}

If fgets() returns NULL , indicating end-of-file or a read error, s_gets() skips the rest of the processing. Otherwise, it imitates the codes above, replacing the newline character with a null character if the former is present in the string, and discarding the rest of the line otherwise. It then returns the same value fgets() returned. We’ll use this function in later examples.(如果fgets()返回 NULL,说明读到文件结尾或出现读取错误,s_gets()函数跳过了这个过程。它模仿上面程序清单的处理方法,如果字符串中出现换行符,就用空字符替换它;如果字符串中出现空字符,就丢弃该输入行的其余字符,然后返回与fgets()相同的值。我们在后面的示例中将讨论函数。)

Perhaps you are wondering what’s the rationale for discarding the rest of a too-long line. The problem is that if the remainder of the line is left in place, it becomes the input for the next read statement. This can, for example, cause the program to crash if the next read statement is looking for a type double value. Discarding the rest of the line keeps the read statements synchronized with the keyboard input. Our s_gets() function isn’t perfect. Its most serious flaw is that it is silent about encountering input that doesn’t fit. It discards the extra input with neither the program nor the user being informed, thus closing off other options, such as having the user try again or finding more storage space. Another flaw is that it doesn’t cope with misuse such as being passed a size of 1 or less. But it’s good enough to serve as a gets() substitute for our examples.(也许你想了解为什么要丢弃过长输入行中的余下字符。这是因为,输入行中多出来的字符会被留在缓冲区中,成为下一次读取语句的输入。例如,如果下一条读取语句要读取的是 double 类型的值,就可能导致程序崩 溃。丢弃输入行余下的字符保证了读取语句与键盘输入同步。我们设计的s_gets()函数并不完美,它最严重的缺陷是遇到不合适的输入时毫无反应。它丢弃多余的字符时,既不通知程序也不告知用户。但是,用来替换前面程序示例中的gets()足够了。)

The Do-It-Yourself Option:

You aren’t limited to the standard C library options for input and output. If you don’t have these options or don’t like them, you can prepare your own versions, building on getchar() and putchar() . Suppose you want a function like puts() that doesn’t automatically add a newline.

/* prints a string without adding \n */
#include 
void put1(const char * string) /* string not altered */
{
    while (*string != '\0')
        putchar(*string++);
}
/* prints a string and counts characters */
#include 
int put2(const char * string) {
    int count = 0;
    while (*string) /* common idiom */
    {
        putchar(*string++);
        count++;
    }
    putchar('\n'); /* newline not counted */
    return(count);
}

The following call prints the string pizza :

put1("pizza");

The next call also returns a character count that is assigned to num (in this case, the value 5 ):

num = put2("pizza");
// user-defined output functions
#include 
void put1(const char *);
int put2(const char *);
int main(void) {
    put1("If I'd as much money");
    put1(" as I could spend,\n");
    printf("I count %d characters.\n", put2("I never would cry old chairs to mend."));
    return 0;
}
If I'd as much money as I could spend,
I never would cry old chairs to mend.
I count 37 characters.

String Functions:

The C library supplies several string-handling functions; ANSI C uses the string.h header file to provide the prototypes. We’ll look at some of the most useful and common ones: strlen() , strcat() , strncat() , strcmp() , strncmp() , strcpy() , and strncpy() . We’ll also examine sprintf() , supported by the stdio.h header file.

The strlen() Function:

描述

C 库函数 size_t strlen(const char *str) 计算字符串 str 的长度,直到空结束字符,但不包括空结束字符。

声明

size_t strlen(const char *str)

参数

  • str -- 要计算长度的字符串。

返回值

该函数返回字符串的长度。

void fit(char *string, unsigned int size) {
    if (strlen(string) > size)
        string[size] = '\0';
}

This function does change the string, so the function header doesn’t use const in declaring the formal parameter string.

/* try the string-shrinking function */
#include 
#include  /* contains string function prototypes */
void fit(char*, unsigned int);
int main(void) {
  char mesg[] = "Things should be as simple as possible, but not simpler.";
  puts(mesg);
  fit(mesg, 38);
  puts(mesg);
  puts("Let's look at some more of the string.");
  puts(mesg + 39);
  return 0;
}
void fit(char* string, unsigned int size) {
  if (strlen(string) > size)
    string[size] = '\0';
}
Things should be as simple as possible, but not simpler.
Things should be as simple as possible
Let's look at some more of the string.
but not simpler.

The fit() function placed a '\0' character in the 39th element of the array, replacing a comma. The puts() function stops at the first null character and ignores the rest of the array.However, the rest of the array is still there, as shown by calling: puts(mesg + 39);.

第11章-字符串和字符串函数_第5张图片

The strcat() Function:

描述

C 库函数 char *strcat(char *dest, const char *src) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾。

声明

char *strcat(char *dest, const char *src)

参数

  • dest -- 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串。
  • src -- 指向要追加的字符串,该字符串不会覆盖目标字符串。

返回值

该函数返回一个指向最终的目标字符串 dest 的指针。

/* joins two strings */
#include 
#include  /* declares the strcat() function */
#define SIZE 80
char* s_gets(char* st, int n);
int main(void) {
  char flower[SIZE];
  char addon[] = "s smell like old shoes.";
  puts("What is your favorite flower?");
  if (s_gets(flower, SIZE)) {
    strcat(flower, addon);// argument type: pointer-to-char
    puts(flower);
    puts(addon);
  } else {
    puts("End of file encountered!");
  }
  puts("bye");
  return 0;
}
char* s_gets(char* st, int n) {
  char* ret_val;
  int i = 0;
  ret_val = fgets(st, n, stdin);
  if (ret_val) {
    while (st[i] != '\n' && st[i] != '\0')
      i++;
    if (st[i] == '\n')
      st[i] = '\0';
    else
      while (getchar() != '\n')
        continue;
  }
  return ret_val;
}
What is your favorite flower?
|wonderflower
wonderflowers smell like old shoes.
s smell like old shoes.
bye

The strncat() Function:

描述

C 库函数 char *strncat(char *dest, const char *src, size_t n) 把 src 所指向的字符串追加到 dest 所指向的字符串的结尾,直到 n 字符长度为止。

声明

char *strncat(char *dest, const char *src, size_t n)

参数

  • dest -- 指向目标数组,该数组包含了一个 C 字符串,且足够容纳追加后的字符串,包括额外的空字符。
  • src -- 要追加的字符串。
  • n -- 要追加的最大字符数。

返回值

该函数返回一个指向最终的目标字符串 dest 的指针。

/* joins two strings, check size first */
#include 
#include 
#define SIZE 30
#define BUGSIZE 13
char* s_gets(char* st, int n);
int main(void) {
  char flower[SIZE];
  char addon[] = "s smell like old shoes.";
  char bug[BUGSIZE];
  int available;
  puts("What is your favorite flower?");
  s_gets(flower, SIZE);
  if ((strlen(addon) + strlen(flower) + 1) <= SIZE)
    strcat(flower, addon);
  puts(flower);
  puts("What is your favorite bug?");
  s_gets(bug, BUGSIZE);
  available = BUGSIZE - strlen(bug) - 1;
  strncat(bug, addon, available);
  puts(bug);
  return 0;
}
char* s_gets(char* st, int n) {
  char* ret_val;
  int i = 0;
  ret_val = fgets(st, n, stdin);
  if (ret_val) {
    while (st[i] != '\n' && st[i] != '\0')
      i++;
    if (st[i] == '\n')
      st[i] = '\0';
    else
      while (getchar() != '\n')
        continue;
  }
  return ret_val;
}
What is your favorite flower?
|Rose
Roses smell like old shoes.
What is your favorite bug?
|Aphid
Aphids smell

You may have noticed that strcat() , like gets() , can lead to buffer overflows. Why, then, doesn’t the C11 standard dump strcat() and just offer strncat() ? One reason may be that gets() exposes a program to dangers from those who use the program, while strcat() exposes the program to the dangers of a careless programmer. You can’t control what some user will do in the future, but you can control what goes in your program. The C philosophy of trust the programmer brings with it the responsibility of recognizing when you can use strcat() safely.

The strcmp() Function:

描述

C 库函数 int strcmp(const char *str1, const char *str2) 把 str1 所指向的字符串和 str2 所指向的字符串进行比较。

声明

int strcmp(const char *str1, const char *str2)

参数

  • str1 -- 要进行比较的第一个字符串。
  • str2 -- 要进行比较的第二个字符串。

返回值

  • 如果返回值 < 0,则表示 str1 小于 str2。
  • 如果返回值 > 0,则表示 str2 小于 str1。
  • 如果返回值 = 0,则表示 str1 等于 str2。

Suppose you want to compare someone’s response to a stored string

Who is buried in Grant's tomb?
Grant
No, that's wrong. Try again.

The comparison try != ANSWER doesn’t check to see whether the two strings are the same. Rather, it checks to see whether the two strings have the same address. Because ANSWER and try are stored in different locations, the two addresses are never the same, and the user is forever told that he or she is wrong.

/* this will work */
#include 
#include   // declares strcmp()
#define ANSWER "Grant"
#define SIZE 40
char* s_gets(char* st, int n);
int main(void) {
  char try[SIZE];
  puts("Who is buried in Grant's tomb?");
  s_gets(try, SIZE);
  while (strcmp(try, ANSWER) != 0) {
    puts("No, that's wrong. Try again.");
    s_gets(try, SIZE);
  }
  puts("That's right!");
  return 0;
}
Who is buried in Grant's tomb?
Grant
That's right!
/* strcmp returns */
#include 
#include 
int main(void) {
  printf("strcmp(\"A\", \"A\") is ");
  printf("%d\n", strcmp("A", "A"));
  printf("strcmp(\"A\", \"B\") is ");
  printf("%d\n", strcmp("A", "B"));
  printf("strcmp(\"B\", \"A\") is ");
  printf("%d\n", strcmp("B", "A"));
  printf("strcmp(\"C\", \"A\") is ");
  printf("%d\n", strcmp("C", "A"));
  printf("strcmp(\"Z\", \"a\") is ");
  printf("%d\n", strcmp("Z", "a"));
  printf("strcmp(\"apples\", \"apple\") is ");
  printf("%d\n", strcmp("apples", "apple"));
  return 0;
}

Here is the output on one system: 

strcmp("A", "A") is 0
strcmp("A", "B") is -1
strcmp("B", "A") is 1
strcmp("C", "A") is 1
strcmp("Z", "a") is -1
strcmp("apples", "apple") is 1

The ANSI standard says that strcmp() returns a negative number if the first string comes before the second alphabetically, returns 0 if they are the same, and returns a positive number if the first string follows the second alphabetically.

/* beginning of some program */
#include 
#include 
#define SIZE 80
#define LIM 10
#define STOP "quit"
char* s_gets(char* st, int n);
int main(void) {
  char input[LIM][SIZE];
  int ct = 0;
  printf("Enter up to %d lines (type quit to quit):\n", LIM);
  while (ct < LIM && s_gets(input[ct], SIZE) != NULL &&
         strcmp(input[ct], STOP) != 0) { // 二维数组可以这种方式赋值!
    ct++;
  }
  printf("%d strings entered\n", ct);
  return 0;
}
Enter up to 10 lines (type quit to quit):
|a
|b
|c
|quit
3 strings entered

--or(run again)--

Enter up to 10 lines (type quit to quit):
|1
|2
|3
|4
|5
|6
|7
|8
|9
|10
10 strings entered

The strncmp() Variation:

描述

C 库函数 int strncmp(const char *str1, const char *str2, size_t n) 把 str1 和 str2 进行比较,最多比较前 n 个字节。

声明

int strncmp(const char *str1, const char *str2, size_t n)

参数

  • str1 -- 要进行比较的第一个字符串。
  • str2 -- 要进行比较的第二个字符串。
  • n -- 要比较的最大字符数。

返回值

  • 如果返回值 < 0,则表示 str1 小于 str2。
  • 如果返回值 > 0,则表示 str2 小于 str1。
  • 如果返回值 = 0,则表示 str1 等于 str2。
/* use strncmp() */
#include 
#include 
#define LISTSIZE 6
int main() {
  const char* list[LISTSIZE] = {"astronomy", "astounding", "astrophysics",
                                "ostracize", "asterism",   "astrophobia"};
  int count = 0;
  int i;
  for (i = 0; i < LISTSIZE; i++)
    if (strncmp(list[i], "astro", 5) == 0) {
      printf("Found: %s\n", list[i]);
      count++;
    }
  printf("The list contained %d words beginning with astro.\n", count);
  return 0;
}
Found: astronomy
Found: astrophysics
Found: astrophobia
The list contained 3 words beginning with astro.

The strcpy() and strncpy() Functions:

We’ve said that if pts1 and pts2 are both pointers to strings, the expression

pts2 = pts1;

copies only the address of a string, not the string itself. Suppose, though, that you do want to copy a string. Then you can use the strcpy() function.

描述

C 库函数 char *strcpy(char *dest, const char *src) 把 src (字符串包含结束符'\0')所指向的字符串复制(以覆盖的方式)到 dest (以dest开始的地址空间)。

声明

char *strcpy(char *dest, const char *src)

参数

  • dest -- 指向用于存储复制内容的目标数组。
  • src -- 要复制的字符串。

返回值

该函数返回一个指向最终的目标字符串 dest 的指针。

Codes below asks the user to enter words beginning with q. The program copies the input into a temporary array, and if the first letter is a q, the program uses strcpy() to copy the string from the temporary array to a permanent destination. The strcpy() function is the string equivalent of the assignment operator.

/* strcpy() demo */
#include 
#include   // declares strcpy()
#define SIZE 40
#define LIM 5
char* s_gets(char* st, int n);
int main(void) {
  char qwords[LIM][SIZE];
  char temp[SIZE];
  int i = 0;
  printf("Enter %d words beginning with q:\n", LIM);
  while (i < LIM && s_gets(temp, SIZE)) {
    if (temp[0] != 'q') // <=>if (strncmp(temp, "q", 1) != 0)
      printf("%s doesn't begin with q!\n", temp);
    else {
      strcpy(qwords[i], temp);
      i++;
    }
  }
  puts("Here are the words accepted:");
  for (i = 0; i < LIM; i++)
    puts(qwords[i]);
  return 0;
}
Enter 5 words beginning with q:
|quackery
|quasar
|quilt
|quotient
|no more
no more doesn't begin with q!
|quiz
Here are the words accepted:
quackery
quasar
quilt
quotient
quiz

You can remember the order of the arguments by noting that it is the same as the order in an assignment statement (the target string is on the left):

char target[20];
int x;
x = 50; /* assignment for numbers */
strcpy(target, "Hi ho!"); /* assignment for strings */
target = "So long"; /* syntax error:Array type 'char [20]' is not assignable */

It is your responsibility to make sure the destination array has enough room to copy the source.The following is asking for trouble:

char * str;
strcpy(str, "The C of Tranquility"); // a problem

The function will copy the string "The C of Tranquility" to the address specified by str, but str is uninitialized, so the copy might wind up anywhere!

In short, strcpy() takes two string pointers as arguments. The second pointer, which points to the original string, can be a declared pointer, an array name, or a string constant. The first pointer, which points to the copy, should point to a data object, such as an array, roomy enough to hold the string. Remember, declaring an array allocates storage space for data; declaring a pointer only allocates storage space for one address.

The strcpy() function has two more properties that you might find useful. First, it is type char *. It returns the value of its first argument—the address of a character. Second, the first argument need not point to the beginning of an array;this lets you copy just part of an array.

/* strcpy() demo with more properties*/
#include 
#include   // declares strcpy()
#define WORDS "beast"
#define SIZE  40
int main(void) {
    const char *orig = WORDS;
    char copy[SIZE] = "Be the best that you can be.";
    char *ps;
    puts(orig);
    puts(copy);
    ps = strcpy(copy + 7, orig);
    puts(copy);
    puts(ps);
    puts(copy + 13);
    return 0;
}
beast
Be the best that you can be.
Be the beast
beast
hat you can be.

第11章-字符串和字符串函数_第6张图片

Note that strcpy() copies the null character from the source string. In this example, the null character overwrites the first t in that in copy so that the new string ends with beast.

The Careful Choice: strncpy()

描述

C 库函数 char *strncpy(char *dest, const char *src, size_t n) 把 src 所指向的字符串复制到 dest,最多复制 n个字符。当 src 的长度小于 n 时,dest 的剩余部分将用空字节填充。

声明

char *strncpy(char *dest, const char *src, size_t n)

参数

  • dest -- 指向用于存储复制内容的目标数组。
  • src -- 要复制的字符串。
  • n -- 要从源中复制的字符数。

返回值

该函数返回最终复制的字符串。

Codes below, using strncpy() instead of strcpy(). To illustrate what happens if the source string is too large, it uses a rather small size (seven elements, six characters) for the target strings.

/* strncpy() demo */
#include 
#include  /* declares strncpy() */
#define SIZE 40
#define TARGSIZE 7
#define LIM 5
char* s_gets(char* st, int n);
int main(void) {
  char qwords[LIM][TARGSIZE];
  char temp[SIZE];
  int i = 0;
  printf("Enter %d words beginning with q:\n", LIM);
  while (i < LIM && s_gets(temp, SIZE)) {
    if (temp[0] != 'q')
      printf("%s doesn't begin with q!\n", temp);
    else {
      strncpy(qwords[i], temp, TARGSIZE - 1);
      qwords[i][TARGSIZE - 1] = '\0';
      i++;
    }
  }
  puts("Here are the words accepted:");
  for (i = 0; i < LIM; i++)
    puts(qwords[i]);
  return 0;
}
Enter 5 words beginning with q:
|quack
|quadratic
|quisling
|quota
|quagga
Here are the words accepted:
quack
quadra
quisli
quota
quagga

The sprintf() Function:

The sprintf() function is declared in stdio.h instead of string.h. It works like printf(), but it writes to a string instead of writing to a display. Therefore, it provides a way to combine several elements into a single string. The first argument to sprintf() is the address of the target string. The remaining arguments are the same as for printf()—a conversion specification string followed by a list of items to be written.

/* format a string */
#include 
#define MAX 20
char* s_gets(char* st, int n);
int main(void) {
  char first[MAX];
  char last[MAX];
  char formal[2 * MAX + 10];
  double prize;
  puts("Enter your first name:");
  s_gets(first, MAX);
  puts("Enter your last name:");
  s_gets(last, MAX);
  puts("Enter your prize money:");
  scanf("%lf", &prize);
  sprintf(formal, "%s, %-19s: $%6.2f\n", last, first, prize);
  puts(formal);
  return 0;
}
Enter your first name:
|Annie
Enter your last name:
|von Wurstkasse
Enter your prize money:
|25000
von Wurstkasse, Annie        : $25000.00

The sprintf() command took the input and formatted it into a standard form, which it then stored in the string formal.

Other String Functions:

The ANSI C library has more than 20 string-handling functions, and the following list summarizes some of the more commonly used ones:

■ char *strcpy(char * restrict s1, const char * restrict s2);

This function copies the string (including the null character) pointed to by s2 to the location pointed to by s1 . The return value is s1 .

char *strncpy(char * restrict s1, const char * restrict s2, size_t n);

This function copies to the location pointed to by s1 no more than n characters from the string pointed to by s2 . The return value is s1 . No characters after a null character are copied and, if the source string is shorter than n characters, the target string is padded with null characters. If the source string has n or more characters, no null character is copied. The return value is s1.

char *strcat(char * restrict s1, const char * restrict s2);

The string pointed to by s2 is copied to the end of the string pointed to by s1. The first character of the s2 string is copied over the null character of the s1 string. The return value is s1.

char *strncat(char * restrict s1, const char * restrict s2, size_t n);

No more than the first n characters of the s2 string are appended to the s1 string, with the first character of the s2 string being copied over the null character of the s1 string. The null character and any characters following it in the s2 string are not copied, and a null character is appended to the result. The return value is s1.

int strcmp(const char * s1, const char * s2);

This function returns a positive value if the s1 string follows the s2 string in the machine collating sequence, the value 0 if the two strings are identical, and a negative value if the first string precedes the second string in the machine collating sequence.

int strncmp(const char * s1, const char * s2, size_t n);

This function works like strcmp() , except that the comparison stops after n characters or when the first null character is encountered, whichever comes first.

char *strchr(const char * s, int c);

This function returns a pointer to the first location in the string s that holds the character c. (The terminating null character is part of the string, so it can be searched for.) The function returns the null pointer if the character is not found.

char *strpbrk(const char * s1, const char * s2);

This function returns a pointer to the first location in the string s1 that holds any character found in the s2 string. The function returns the null pointer if no character is found.

char *strrchr(const char * s, int c);

This function returns a pointer to the last occurrence of the character c in the string s. (The terminating null character is part of the string, so it can be searched for.) The function returns the null pointer if the character is not found.

char *strstr(const char * s1, const char * s2);

This function returns a pointer to the first occurrence of string s2 in string s1. The function returns the null pointer if the string is not found.

size_t strlen(const char * s);

This function returns the number of characters, not including the terminating null character, found in the string s.

 

Example:First, use strchr()(shorthand for:string_char) to find the newline, if any. If the function finds the newline, it returns the address of the newline, and you then can place a null character at that address:

char line[80];
char * find;
fgets(line, 80, stdin);
find = strchr(line, '\n'); // look for newline
if (find)                  // if the address is not NULL,
    *find = '\0';          // place a null character there

A String Example: Sorting Strings:

/* reads in strings and sorts them */
#include 
#include 
#define SIZE 81                       /* string length limit, including \0 */
#define LIM 20                        /* maximum number of lines to be read */
#define HALT ""                       /* null string to stop input */
void stsrt(char* strings[], int num); /* string-sort function */
char* s_gets(char* st, int n);
int main(void) {
  char input[LIM][SIZE]; /* array to store input */
  char* ptstr[LIM];      /* array of pointer variables */
  int ct = 0;            /* input count */
  int k;                 /* output count */
  printf("Input up to %d lines, and I will sort them.\n", LIM);
  printf("To stop, press the Enter key at a line's start.\n");
  while (ct < LIM && s_gets(input[ct], SIZE) != NULL && input[ct][0] != '\0') {
    ptstr[ct] = input[ct]; /* set ptrs to strings */
    ct++;
  }
  stsrt(ptstr, ct); /* string sorter */
  puts("\nHere's the sorted list:\n");
  for (k = 0; k < ct; k++)
    puts(ptstr[k]); /* sorted pointers */
  return 0;
}
/* string-pointer-sorting function */
void stsrt(char* strings[], int num) {
  char* temp;
  int top, seek;
  for (top = 0; top < num - 1; top++)
    for (seek = top + 1; seek < num; seek++)
      if (strcmp(strings[top], strings[seek]) > 0) {
        temp = strings[top];
        strings[top] = strings[seek];
        strings[seek] = temp;
      }
}
Input up to 20 lines, and I will sort them.
To stop, press the Enter key at a line's start.
|O that I was where I would be,
|Then would I be where I am not;
|But where I am I must be,
|And where I would be I can not.

Here's the sorted list:

And where I would be I can not.
But where I am I must be,
O that I was where I would be,
Then would I be where I am not;

Sorting Pointers Instead of Strings:

第11章-字符串和字符串函数_第7张图片

The ctype.h Character Functions and Strings:

/* modifies a string */
#include 
#include 
#include 
#define LIMIT 81
void ToUpper(char*);
int PunctCount(const char*);
int main(void) {
  char line[LIMIT];
  char* find;
  puts("Please enter a line:");
  fgets(line, LIMIT, stdin);
  find = strchr(line, '\n');  // look for newline
  if (find)                   // if the address is not NULL,
    *find = '\0';             // place a null character there
  ToUpper(line);
  puts(line);
  printf("That line has %d punctuation characters.\n", PunctCount(line));
  return 0;
}
void ToUpper(char* str) {
  while (*str) {
    *str = toupper(*str);
    str++;
  }
}
int PunctCount(const char* str) {
  int ct = 0;
  while (*str) {
    if (ispunct(*str))
      ct++;
    str++;
  }
  return ct;
}
Please enter a line:
|Me? You talkin' to me? Get outta here!
ME? YOU TALKIN' TO ME? GET OUTTA HERE!
That line has 4 punctuation characters.

Command-Line Arguments:

第11章-字符串和字符串函数_第8张图片

argc=argument_count,argv=argument_value。注意参数类型char * argv[],即argv是一个指针数组。

Key Concepts:

Many programs deal with text data. A program may ask you to enter your name, a list of corporations, an address, the botanical name for a type of fern, the cast of a musical, or...well, because we interact with the world using words, there’s really no end to examples using text. And strings are the means a C program uses to handle strings. A C string—whether it be identified by a character array, a pointer, or a string literal—is stored as a series of bytes containing character codes, and the sequence is terminated by the null character. C recognizes the usefulness of strings by providing a library of functions for manipulating them, searching them, and analyzing them. In particular, keep in mind that you should use strcmp() instead of relational operators when comparing strings, and you should use strcpy() or strncpy() instead of the assignment operator to assign a string to a character array.

 

你可能感兴趣的:(《C,Primer,Plus》读书笔记)