** argv包含许多字符而非预期

时间:2018-01-16 22:29:40

标签: c

首先,我需要用system()执行两个命令,例如,我收到一个字符串并用文本编辑器打开这个字符串,如下所示:

$ ./myprogram string1

输出应该是这样的命令:

$ vim string1

但是,我找不到像这个伪代码那样做的方法:

system("vim %s",argv[1]); //Error:

test.c:23:3: error: too many arguments to function 'system'
   system("vim %s",argv[1]);

因此,我的解决方案是将argv [1]存储在已经用四个字符初始化的char数组中,如下所示:

char command[strlen(argv[1])+4];
command[0] = 'v'; command [1] = 'i'; command[2] = 'm'; command[3] = ' ';

将argv [1]分配给我的新char数组:

for(int i = 0; i < strlen(argv[1]) ; i++)
    command[i+4] = argv[1][i];

最后:

system(command);

但是,如果给我的程序的参数少于3个字符,它的工作正常,但如果没有,我不期望的一些奇怪的字符出现在输出中,如下所示:

./myprogramg 1234

输出是:

$ vim 12348�M�

我该如何解决这个错误,为什么会这样?

完整的代码是:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 int main (int argc,char **argv) {

    char command[strlen(argv[1])+4];
    command[0] = 'v'; command [1] = 'i'; command[2] = 'm'; command[3] = ' ';

    for(int i = 0; i < strlen(argv[1]) ; i++)
        command[i+4] = argv[1][i];

        system(command);

    return 0;
}

4 个答案:

答案 0 :(得分:4)

您需要NUL终止您的C风格字符串,其中包括分配足够的内存来保存NUL

你的数组是一个字节短(必须char command[strlen(argv[1])+4+1];NUL留出空间),你应该只使用像sprintf这样的东西来填充它,例如:

 sprintf(command, "vim %s", argv[1]);`

这比手动循环更简单,它也为你填写NUL

你看到的垃圾是由于搜索NUL字节(终止字符串)而导致在缓冲区之后发生的不相关(和未定义的)内存引起的。

答案 1 :(得分:3)

您遇到问题的原因是您没有使用command终止NULL字符串。但你真的想用sprintf(甚至更好地使用snprintf)来做这样的事情。它与printf的工作方式类似,但输出到内存而不是stdout,并为您处理终止NULL。 E.g:

char cmd[80];

snprintf(cmd, 80, "vim %s", argv[1])
system(cmd);

正如@VTT指出的那样,这个简化的代码假定argv[1]中的值小于75个字符(对于“vim”,减去4减去4,对于NULL字符减去1)。一个更安全的选择是首先验证这个假设,如果不是这样,则抛出错误。为了更灵活,您可以动态分配cmd缓冲区:

char *cmd = "vim ";
char *buf = malloc(strlen(argv[1]) + strlen(cmd) + 1);

sprintf(buf, "%s%s", cmd, argv[1]);
system(buf);
free(buf);

当然你也应该检查以确保argc&gt; 1。

答案 2 :(得分:0)

我知道这里已经有了很好的答案,但我想稍微扩展一下。

我经常看到这种代码

system("vim %s",argv[1]); //Error:

初学者经常想知道,为什么这不起作用。

原因是"%s", some_string 是C的一个功能 语言,字符序列%s没有特殊含义,实际上它是 与序列mickey mouse一样有意义。

这与printf(和其他成员)一起使用的原因 printf - 家庭)是因为printf 被设计为来替换像 %s,其值作为参数传递。使printf特殊的%s, 不是C语言。

正如您可能已经注意到的那样,"hallo" + " world"没有做字符串 级联。 C没有像C ++那样的本机字符串类型 std::string或Python String。在C中,字符串只是一个序列 碰巧在末尾有一个值为0的字节的字符(也称为 '\0' - 终止字节。)

这就是为什么你传递printf格式作为第一个参数的原因。它说 printf除非找到%,否则应逐个字符打印, 告诉printf下一个字符 1 是特殊的 必须用作为printf的后续参数传递的值替换它们。

%x被称为转化说明符和documentation of printf 将列出所有这些以及如何使用它们。

scanf家族等其他功能使用了类似的策略,但并非如此 意味着C中所有期望字符串的函数都将以相同的方式工作。在 事实上绝大多数期望字符串的C函数都不起作用 方式。

  

man system

#include <stdlib.h>

int system(const char *command);

在这里,您可以看到system是一个只需要一个参数的函数。 这就是你的编译器抱怨这样一行的原因:system("vim %s",argv[1]);。 那些像sprintfsnprintf这样的功能可以派上用场。

1 如果您查看printf文档,您会看到它 转换说明符和长度修饰符可以长于1 字符。

答案 3 :(得分:-1)

您需要处理缓冲区分配,不要忘记终止null:

char const * const psz_original_command = argv[1];
size_t const original_command_length = strlen(psz_original_command);
char const * const psz_prefix = "vim ";
size_t const prefix_length = strlen(psz_prefix);
char * new_command = (char *) malloc(prefix_length + original_command_length + 1); /* +1 for terminating null */
if(new_command)
{
    memcpy(new_command, psz_prefix, prefix_length); 
    memcpy(new_command + prefix_length, psz_original_command, original_command_length + 1); /* +1 for terminating null */
    system(new_command);
    free(new_command);
    new_command = NULL;
}
return 0;