2013-02-22 60 views
0

我试图编写一个简单的shell接口,它接受用户输入(通过字符),并通过指针指向一个指针*(确切地说argv是如何工作的)。这里是我的代码:令牌化C中的用户输入(存储在** arg中)?

int i = 0; 
for (i = 0; i < wordCount; i++) 
    printf("Word %i: %s\n", i, argvInput[i]); 

argvInput的值[I]是什么最后输入分配是所有连接:通过argvInput

char input[100]; 
char **argvInput; 
char ch; 
int charLoop = 0; 
int wordCount = 0; 

argvInput = malloc(25 * sizeof(char *)); 

while((ch = getc(stdin))) { 
    if ((ch == ' ' || ch == '\n') && charLoop != 0) { 
     input[charLoop] = '\0'; 
     argvInput[wordCount] = malloc((charLoop + 1) * sizeof(char)); 
     argvInput[wordCount] = input; 
     charLoop = 0; 
     wordCount++; 

     if (ch == '\n') { 
      break; 
     } 

    } else if (ch != ' ' && ch != '\n') { 
      input[charLoop] = ch; 
      charLoop++; 
     } else { 
      break; 
     } 
    } 

如果我循环。所以,如果我输入: “幸福的日子即将到来”,环路的输出是:

Word 0: soon 
Word 1: soon 
Word 2: soon 
Word 3: soon 
Word 4: soon 

我不知所措。显然,每个循环覆盖以前的值,但我盯着屏幕,无法弄清楚为什么...

+0

你可能想看看进入['strtok'](http://en.cppreference.com/w/c/string/byte/ strtok)功能。如果您搜索,有很多示例可用。 – 2013-02-22 01:47:30

+0

这最终将成为一个shell脚本(随着学期的进展而发展)。由于它最终必须处理管道和报价,因此strtok不会长期工作。 – 2013-02-22 02:31:15

回答

4

此行是你的死穴:

argvInput[wordCount] = input; 

不要紧,你分配新的空间,如果你打算用另一个替换指针(即input)。

相反,使用strncpy来提取input的部分为argvInput[wordCount]

+3

或者只是使用argvInput [wordCount] = strdup(input); – Clyde 2013-02-22 01:48:05

+0

'strncpy'是背信弃义的,因为它_may_或_may不会终止目标字符串。跳过分配并做例如'strdup'可能更好(也更简单)。 – 2013-02-22 01:49:10

+0

@JoachimPileborg:他知道长度,他可以自己终止。事实上,他*是*自己终止它:'input [charLoop] ='\ 0''。所以这是对他的代码的最小改变,只要他自己分配东西。 – Amadan 2013-02-22 01:51:04

2

argvInput[wordCount] = input;仅使指针argvInput[wordCount]指向input的内存,而不是将输入的内容复制到新分配的内存中。您应该使用memcpy或strcpy来更正您的程序。

指针分配后的内存状态如下图所示。由malloc((charLoop + 1) * sizeof(char));分配的内存(图中灰色部分)无法再被您的程序访问,这会导致一些内存泄漏问题。请注意这一点。

enter image description here

0

我建议打印您argvInput指针与%P,而不是%S,找出这个问题:printf("Word %i: %p\n", i, (void *) argvInput[i]);

你注意到它打印出的值是什么?这与argv的行为有何不同?尝试打印argv的指针:for (size_t x = 0; x < argc; x++) { printf("Word %zu: %p\n", x, (void *) argv[x]); }

现在您已经观察到问题了,解释它可能会变得更容易。

此代码分配内存,并且存储一个指向在argvInput [的wordCount]认为存储器:argvInput[wordCount] = malloc((charLoop + 1) * sizeof(char));(顺便说一下,焦炭的sizeof是总是 1 C,所以你通过1不必要地相乘)。

这段代码用一个指向输入的指针替换指向已分配内存的指针:argvInput[wordCount] = input; ...因此,所有项目都包含一个指向同一个数组的输入:input,并且由于失去对它的引用而导致分配的内存泄漏。显然,这是有问题的路线;它不会做你最初认为它所做的事情。

有人建议您使用strdup调用替换您的malloc调用,并删除有问题的行。我不喜欢这个建议,因为strdup不在C标准中,所以不需要存在。

strncpy可以工作,但它不必要的复杂。由于目标数组的大小足以存储字符串,因此strcpy可以保证正常工作。因此,我建议用strcpy(argvInput[wordCount], input);替换有问题的行。

另一个没有详细解释的选项是strtok。看来这是目前最好的,尚未开发,因为它需要对代码进行太多的修改。

我有一块骨头可以用此代码挑选:char ch; ch = getc(stdin);是错误的。 getc返回一个int,原因是:任何成功的字符读取都将以unsigned char值的形式返回,这不可能是负数。如果getc遇到EOF或错误,它将返回一个负值。一旦您将返回值分配给ch,您如何区分错误和成功?

你有没有想过如果第一个字符是''会发生什么?目前,你的代码会跳出循环。这看起来像一个bug,如果你的代码是模仿常见的argv解析行为。适应这个代码来解决你的问题可能是一个好主意:

for (int c = getc(stdin); c >= 0; c = getc(stdin)) { 
    if (c == '\n') { 
     /* Terminate your argv array and break out of the loop */ 
    } 
    else if (c != ' ') { 
     /* Copy c into input */ 
    } 
    else if (charLoop != 0) { 
     /* Allocate argvInput[wordCount] and copy input into it, 
     * reset charLoop and increment wordCount */ 
    } 
} 
+0

是的,如果第一个字符是空格,它现在会打破循环。这根本不是理想的行为,但对于这份任务,他告诉我们假设没有人会放弃第一个角色。无论哪种情况,我都不喜欢我的默认处理方式,但它会随着时间的推移而变化......并且您的模板非常适合这样做。 – 2013-02-22 18:58:30

+0

是的,除了有不必要的malloc和strcpys。我在malloc外面添加了一个大缓冲区(比如1024字节),将输入设置为指向该缓冲区的起始字节,并增加每次读取的输入指针,以便这些字全部存储在一个缓冲区中,一个之后。当缓冲区已满时,我会使用realloc将其大小加倍并继续正常。该模板仍然是相同的,但反映它们的评论和代码会发生变化。限制每个字99个字节被删除,整个数据结构将变得更加缓存友好。 – Sebivor 2013-02-23 04:30:46

+0

我不能走那条路。这个想法是一个shell,因此,我发布的while循环处于永久循环。所以如果我保持加倍的缓冲区,最终它将成为一个内存猪。我每次malloc的原因是因为我实际上在每个shell命令运行后释放内存。 – 2013-02-24 17:33:16