这里的问题是您试图在 C 中处理字符串,就像在另一种语言(如 C++ 或 Java)中一样,它们是可调整大小的向量,您可以轻松地将任意数量的数据附加或读取到其中。
C字符串的级别要低得多。它们只是一个字符数组(或指向此类数组的指针;无论如何,数组可以被视为指向它们在 C 中的第一个元素的指针),并且字符串被视为该数组中的所有字符,直到第一个空字符. 这些数组是固定大小的;如果你想要一个任意大小的字符串,你需要自己分配它malloc()
,或者在堆栈上分配你想要的大小。
这里有点令人困惑的一件事是您使用的是非标准类型string
。鉴于上下文,我假设它来自您的cs50.h
, 并且只是一个 typedef 到char *
. 如果您实际使用char *
而不是,它可能会减少混淆string
;使用 typedef 掩盖了真正发生的事情。
让我们从第一个问题开始。
string word = strcat(argv[1], "\n");
strcat()
将第二个字符串附加到第一个字符串上;它从第一个字符串的空终止符开始,并用第二个字符串的第一个字符替换它,依此类推,直到它在第二个字符串中达到空值。为了使其工作,包含第一个字符串的缓冲区需要有足够的空间来容纳第二个字符串。如果没有,您可能会覆盖任意其他内存,这可能会导致您的程序崩溃或出现各种其他意外行为。
这是一个插图。假设argv[1]
包含单词hello
,并且缓冲区具有与其所需的空间一样多的空间。之后是其他一些数据;我已经填写other
了示例,虽然它实际上不是那样,它可以是任何东西,它可能重要也可能不重要:
+---+---+---+---+---+---+---+---+---+---+---+---+
| h | e | l | l | o | \0| o | t | h | e | r | \0|
+---+---+---+---+---+---+---+---+---+---+---+---+
现在如果你使用strcat()
append "\n"
,你会得到:
+---+---+---+---+---+---+---+---+---+---+---+---+
| h | e | l | l | o | \n| \0| t | h | e | r | \0|
+---+---+---+---+---+---+---+---+---+---+---+---+
你可以看到我们已经覆盖了other
之后的数据hello
。这可能会导致各种问题。要解决此问题,您需要将您的字符串复制argv[1]
到一个新字符串中,该字符串有足够的空间加上一个字符(并且不要忘记尾随的空值)。您可以调用strlen()
以获取字符串的长度,然后为 . 添加 1,\n
为结尾的 null 添加 1,以获得您需要的长度。
实际上,与其尝试将 a 添加\n
到您从命令行输入的单词中,我建议\n
您从输入的单词中删除 ,或者使用strncmp()
来比较除最后一个字符(the \n
)之外的所有字符。一般来说,最好在 C 中避免附加字符串,因为附加字符串意味着您需要分配内存并复制内容,这样做很容易出错,而且效率低下。高级语言通常会为您处理细节,使附加字符串变得更容易,尽管仍然同样低效。
编辑后,您将其更改为:
char* temp = argv[1];
char* word = strcat(temp, "\n");
然而,这也有同样的问题。Achar *
是指向字符数组的指针。您的temp
变量只是复制指针,而不是实际值;它仍然指向同一个缓冲区。这是一个插图;我为了演示的目的编了地址,在真机中这些东西之间会有更多的对象,但这对于演示的目的应该足够了。
+------------+---------+-------+
| name | address | value |
+------------+---------+-------+
| argv | 1000 | 1004 |-------+
| argv[0] | 1004 | 1008 | --+ <-+
| argv[1] | 1006 | 1016 | --|---+
| argv[0][0] | 1008 | 'm' | <-+ |
| argv[0][1] | 1009 | 'y' | |
| argv[0][2] | 1010 | 'p' | |
| argv[0][3] | 1011 | 'r' | |
| argv[0][4] | 1012 | 'o' | |
| argv[0][5] | 1013 | 'g' | |
| argv[0][6] | 1014 | 0 | |
| argv[1][0] | 1016 | 'w' | <-+ <-+
| argv[1][1] | 1017 | 'o' | |
| argv[1][2] | 1018 | 'r' | |
| argv[1][3] | 1019 | 'd' | |
| argv[1][4] | 1020 | 0 | |
+------------+---------+-------+ |
现在,当您创建temp
变量时,您所做的就是复制argv[1]
到一个新的char *
:
+------------+---------+-------+ |
| name | address | value | |
+------------+---------+-------+ |
| temp | 1024 | 1016 | --+
+------------+---------+-------+
作为旁注,你也不应该在argv[1]
不检查argc
大于 1 的情况下尝试访问。如果有人没有传入任何参数,那么argv[1]
它本身就无法访问。
我会继续下一个问题。
string c = "abc";
// ...
char* duh = fgets(c, 20, input);
在这里,您指的是静态字符串"abc"
。在源代码中出现的字符串,如"abc"
,进入程序内存的一个特殊的只读部分。记住我说的话;string
这里只是一种说法char *
。所以c
实际上只是指向这个只读内存部分的指针;并且它只有足够的空间来存储您在文本中提供的字符(4,用于abc
和终止字符串的空字符)。fgets()
它的第一个参数是一个存储正在读取的字符串的位置,第二个参数是它拥有的空间量。因此,您尝试将最多 20 个字节读取到一个只读缓冲区中,该缓冲区只有 4 个空间。
您需要在堆栈上分配用于读取的空间,例如:
char c[20];
或动态地,使用malloc()
:
char *c = malloc(20);