CS50 再次来袭。
请注意,在使 C 更易于使用的过程中,CS50 库严重歪曲了字符串和字符串处理在 C 中的实际工作方式。string 类型不是原生 C 类型- 它是类型“指向char”(char *)的类型定义名称(别名)。 CS50 string 实际上并不存储 string(字符序列),它存储的是字符串第一个字符的 address。这意味着
string people[20];
实际上并不是一个字符串的数组,它是一个指针的数组:
char *people[20];
在 C 中,字符串是包含 0 值终止符的字符值序列 - 字符串 "hello" 表示为序列 {'h', 'e', 'l', 'l', 'o', 0}。所有字符串(包括字符串文字)都存储为字符类型的数组(char 用于 ASCII/EBCDIC/UTF-8,wchar_t 用于“宽”编码)。
在大多数情况下,数组表达式“衰减”为指针表达式1,所以大多数时候当我们处理字符串时,我们都在处理指针表达式,但指针不是字符串.
如果要将多个字符串一起追加成一个字符串,比如
"foo" + "bar" == "foobar"
那么您需要执行以下操作:
/**
* Create a target buffer large enough to store the final string. In
* this case we need a buffer 7 elements wide - 6 for the characters
* "foobar" plus 1 for the string terminator. The initial contents of
* this array are *indeterminate*:
*
* +---+---+---+---+---+---+---+
* | ? | ? | ? | ? | ? | ? | ? |
* +---+---+---+---+---+---+---+
*/
char target[7];
/**
* Copy the string "foo" to the target buffer. After this operation,
* the target buffer will contain:
*
* +---+---+---+---+---+---+---+
* |'f'|'o'|'o'| 0 | ? | ? | ? |
* +---+---+---+---+---+---+---+
*/
strcpy( target, "foo" );
/**
* Append "bar" to the end of the string in target - looks for the
* string terminator, and starts appending at that point. After
* this operation the target array should contain:
*
* +---+---+---+---+---+---+---+
* |'f'|'o'|'o'|'b'|'a'|'r'| 0 |
* +---+---+---+---+---+---+---+
*/
strcat( target, "bar" );
如果您想创建一个 array 字符串(这就是您所要求的),那么您将需要创建一个 char 的二维数组:
char strings[NUM_STRINGS][MAX_STRING_LENGTH+1];
然后使用strcpy复制字符串内容:
strcpy( strings[0], "foo" );
strcpy( strings[1], "bar" );
strcpy( strings[2], "bletch" );
给你类似的东西
+---+---+---+---+---+---+---+ +---+
strings[0] |'f'|'o'|'o'| 0 | ? | ? | ? | ... | ? |
+---+---+---+---+---+---+---+ +---+
strings[1] |'b'|'a'|'r'| 0 | ? | ? | ? | ... | ? |
+---+---+---+---+---+---+---+ +---+
strings[2] |'b'|'l'|'e'|'t'|'c'|'h'| 0 | ... | ? |
+---+---+---+---+---+---+---+ +---+
或者您将需要创建一个指针数组(这是您在代码中拥有的),然后存储每个字符串的地址:
char *strings[NUM_STRINGS];
strings[0] = "foo";
strings[1] = "bar";
strings[2] = "bletch";
这给了你类似的东西
+---+ +---+
strings[0]: | | ----------------------------> |'f'|
+---+ +---+ +---+
strings[1]: | | -------------------> |'b'| |'o'|
+---+ +---+ +---+ +---+
strings[2]: | | ----------> |'b'| |'a'| |'o'|
+---+ +---+ +---+ +---+
... |'l'| |'r'| | 0 |
+---+ +---+ +---+
|'e'| | 0 | ...
+---+ +---+
|'t'| ...
+---+
|'c'|
+---+
|'h'|
+---+
| 0 |
+---+
...
请注意,字符串文字可能不可写 - 尝试修改字符串文字内容的行为是未定义。您的代码可能崩溃,它可能会按预期运行,它可能会损坏其他数据,或者它可能会完全执行其他操作。如果你有类似的东西
char *strings[20];
...
strings[0] = "joe";
然后你可以设置strings[0] 指向不同的字符串字面量,像这样:
strings[0] = "bob";
但是如果你尝试修改strings[0]指向的文字的内容:
strcpy( strings[0], "bob" );
那么您的代码可能无法按预期运行。但是,如果您创建一个数组并使用字符串对其进行初始化:
char *strings[20];
char name[] = "joe";
strings[0] = name; // assigning the *address* of name[0] to strings[0]
strcpy( strings[0], "bob" );
那么这将按预期工作,因为strings[0] 指向name,这是一个可写缓冲区。但是name 最多只能存储 3 个字符的名称,所以如果你尝试做类似的事情
strcpy( strings[0], "aloysius" );
您将写到 names 数组的末尾并破坏它后面的任何内容。
是的,C 中的字符串处理是主要的 PITA,这就是 CS50 库试图掩盖它的原因。但同样,在这样做的过程中,他们歪曲了事情的实际运作方式,并且在学生必须自己处理 C 字符串时让他们陷入失败。
- 除非它是
sizeof 或一元& 运算符的操作数,或者是用于在声明中初始化字符类型数组的字符串文字,否则为表达式 “T 的 N 元素数组”类型的 em> 将被转换或“衰减”为“指向 T 的指针”类型的表达式,表达式的值将是第一个元素的地址数组。