如果用户C未输入,则编辑argv[1]

hjzp0vay  于 2023-11-16  发布在  其他
关注(0)|答案(9)|浏览(102)

如果用户没有输入任何东西,是否可以在argv[1]中获得空字符串?如果用户没有输入argv[1],我需要能够使用它,因为它是空字符串。
我尝试将空字符串分配给argv[1],但它会打印分割错误

nkkqxpd9

nkkqxpd91#

您可以使用argc来了解argv中有多少个元素,并相应地做出响应。第一个值是程序的名称,所有后续值都是用户输入。

int main(int argc, char * argv[]){
  
  char * str;

  if (argc < 2){
     // .... create your empty string
     str = "";
  } else {
     // .... assign argv[1] to your string
     str = argv[1];
  }
  // ... do stuff with string

  return 0;
}

字符串

c90pui9n

c90pui9n2#

Standard并没有说要修改argv引用的指针数组(不是argv本身和这个数组的元素引用的C字符串)。你需要:

char **newargv = malloc((argc + 2) * sizeof(*newargv));
    memcpy(newargv, argv, (argc) * sizeof(*newargv));
    newargv[argc] = "Hello";
    newargv[argc + 1] = NULL;
    argc++;
    argv = newargv;

字符串

rggaifut

rggaifut3#

作弊!你的编译器会很高兴

// rename your main as
int oldmain(int argc, char **argv) {
    // your code
}

int main(int argc, char **argv) {
    if (argc == 1) {
        return oldmain(2, (char*[]){argv[0], "-----------------------", NULL});
    } else {
        return oldmain(argc, argv);
    }
}

字符串

wa7juj8i

wa7juj8i4#

如果用户没有输入任何东西,那么argv[1]就不存在,因为在这种情况下argc==1argv[1]将是越界访问。
您可以做的是创建一个工作副本,以满足您在argv为空的情况下的需求。例如:

#include <stdio.h>

#define ARGV_MIN 2

int main (int argc, char* argv[]) 
{
  char* argv_workcopy[argc > 1 ? argc : ARGV_MIN];
  argv_workcopy[1] = "";

  for(int i=0; i<argc; i++)
  {
    argv_workcopy[i] = argv[i];
  }

  // do something with the string:
  puts(argv_workcopy[1]);
}

字符串
在本例中,argv_workcopy是一个指针的可变长度数组(VLA),它的大小可能与argc一样大,或者在没有任何命令行参数的情况下,它的大小为2
默认值输入为argv_workcopy[1] = "";。这是一个只读的空字符串,但请注意,你也可以让这个点读/写内存,如malloc调用返回的内存。
如果有argv字符串传递,它们将覆盖默认值。如果没有,将使用默认值。因此,如果我将此程序称为myprog hello world,那么它将打印hello,但如果我将其称为myprog,它将打印一个空行。
(Also这个程序不会在argv的末尾添加一个NULL sentinel,所以如果你需要这个功能,你需要增加ARGV_MIN并手动分配一个NULL。

wsewodh2

wsewodh25#

程序加载器的使命是在程序开始时管理argcargv

int main(int argc, char** argv);

字符串
这些都是声明的,一个int和一个char**。但是argc可以是unsignedargv可以是char*[],或者它们可以被声明为const。它只是一个函数和它的参数。* 特殊的事情 * 发生在程序执行之前和之后。所以你可以按照你的想法使用它。
审议这个

示例

这是示例的输出(需要5个参数)对于某些参数,没有参数或很少参数。仅举例子,如图所示生成并插入缺少的参数,并在退出时插入free。第一个参数是程序名,数组argvNULL指针结束,如果需要,会生成一个新的argv数组,并替换初始数组,这样程序就可以继续运行,而不需要知道命令行中是否提供了参数。

输出

C:\SO_  ./p 1 2 3 4 last
argument(s) ok    5 arguments on command line:
        #1:                        1
        #2:                        2
        #3:                        3
        #4:                        4
        #5:                     last
C:\SO_ 
C:\SO_  ./p
    5 argument (s) missing
    argv[1] created as "(Generated) argv[1]"
    argv[2] created as "(Generated) argv[2]"
    argv[3] created as "(Generated) argv[3]"
    argv[4] created as "(Generated) argv[4]"
    argv[5] created as "(Generated) argv[5]"
    5 arguments on command line:
        #1:      (Generated) argv[1]
        #2:      (Generated) argv[2]
        #3:      (Generated) argv[3]
        #4:      (Generated) argv[4]
        #5:      (Generated) argv[5]
    free argv[1]
    free argv[2]
    free argv[3]
    free argv[4]
    free argv[5]
C:\SO_  ./p arg1 arg2
    3 argument (s) missing
    argv[1] is present: "arg1"
    argv[2] is present: "arg2"
    argv[3] created as "(Generated) argv[3]"
    argv[4] created as "(Generated) argv[4]"
    argv[5] created as "(Generated) argv[5]"
    5 arguments on command line:
        #1:                     arg1
        #2:                     arg2
        #3:      (Generated) argv[3]
        #4:      (Generated) argv[4]
        #5:      (Generated) argv[5]
    free argv[3]
    free argv[4]
    free argv[5]


一般来说,如果你需要修改这些参数,只修改它们比在程序中创建和使用新的参数数组更方便。它可以只是旧程序中的一个修复,所以到处去修改是很无聊的。
示例中的此函数打印参数数组,并且不关心它们是否被篡改:

int print_args(int argc, char** argv, const char* msg)
{
    if (msg != NULL) printf("%s", msg);
    printf("    %d arguments on command line:\n", argc-1);
    for (int i = 1; i < argc; i += 1)
        printf("\t#%d:\t%20s\n", i, argv[i]);
    return 0;
}


在程序结束时,生成的参数应该是free 'd,这是一个很好的实践。

示例代码

#define EXPECTED 6

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

int print_args(int argc, char** argv, const char*);

int main(int argc, char** argv)
{
    const char* prefix = "(Generated) argv[";
    // if there at least EXPECTED we are good to go
    if (argc == EXPECTED)
    {
        print_args(argc, argv, "argument(s) ok");
        return 0;
    }
    int missing = EXPECTED - argc;
    // missing at least 1
    printf("    %d argument (s) missing\n", missing);
    char** nd_argv = malloc((1 + EXPECTED) * sizeof(char*));
    nd_argv[0]     = argv[0];  // the program name
    nd_argv[EXPECTED] = NULL;  // the sentinel pointer
    for (int i = 1; i < EXPECTED; i += 1)
    {
        if (i < argc)
        {
            printf(
                "    argv[%d] is present: \"%s\"\n", i,
                argv[i]);
            nd_argv[i] = argv[i];
            continue;
        }
        char* one_arg = malloc(strlen(prefix) + 3);
        sprintf(one_arg, "%s%d]", prefix, i);
        printf(
            "    argv[%d] created as \"%s\"\n", i, one_arg);
        nd_argv[i] = one_arg;
    }
    argc = EXPECTED;
    argv = nd_argv;
    print_args(argc, argv, NULL);
    for (int i = argc - missing; i < argc; i += 1)
    {
        printf("    free argv[%d]\n", i);
        free(argv[i]);
    }
    free(argv[EXPECTED]);  // free the sentinel
    free(nd_argv);         // and the array itself
    return 0;
}

int print_args(int argc, char** argv, const char* msg)
{
    if (msg != NULL) printf("%s", msg);
    printf("    %d arguments on command line:\n", argc - 1);
    for (int i = 1; i < argc; i += 1)
        printf("\t#%d:\t%20s\n", i, argv[i]);
    return 0;
}

gev0vcfq

gev0vcfq6#

C标准有很多规则管理argcargv
参见C11 5.1.2.2.1p2

  • argv[argc]应该是空指针。
  • [...]
  • 参数argcargv以及argv数组指向的字符串应可由程序修改,并在程序启动和程序终止之间保留其最后存储的值。

因此,在argc == 1的情况下,您可以将NULL更改为其他内容:

#include <stdlib.h>
#include <string.h>

int main(int argc, char **argv) {
    if (argc == 1) {
        argv[1] = malloc(7);
        if (!argv[1]) exit(EXIT_FAILURE);
        strcpy(argv[1], "foobar");
    }

    // rest of code that keeps `argc` unchanged
    // also do not pass argc and argv to some code
    //     that reasonably expects argv[argc] to be NULL

    if (argc == 1) {
        free(argv[1]);
        // return to the initial situation in case the library code needs it
        argv[1] = NULL;
    }
}

字符串
请注意,在这种情况下,argv[2]不存在。您无法更改它。
还要注意的是,虽然argv[0]是一个普通的旧的常规字符串(可以是任何其他argv元素),但您不知道底层数组的大小,只知道它至少是strlen(argv[0]) + 1

size_t len0 = strlen(argv[0]);
argv[0][len0] = 'X'; // argv[0] is no longer a string
argv[0][len0+1] = 0; // possible UB: that byte may not belong to the array

bakd9h0s

bakd9h0s7#

如果用户没有在程序名后输入任何命令行参数,则main函数接收值为1argcargv指向一个2 char *的数组,第一个可能指向一个带有程序名的字符串,第二个是空指针,而不是空字符串。
如果你试图用strcpy(argv[1], "default")覆盖argv[1]中缺少的参数,你肯定会调用undefined行为,因为argv[1]是一个空指针,这是你观察到的分段错误的一个很好的解释。
C标准在5.1.2.2.1程序启动中规定,

5.1.2.2.1程序启动

1 在程序启动时调用的函数名为main。该实现没有声明此函数的原型。它应使用int的返回类型定义,并且没有参数:

int main(void) { /* ... */ }

字符串
或者使用两个参数(这里称为argcargv,尽管可以使用任何名称,因为它们对于声明它们的函数是局部的):

int main(int argc, char *argv[]) { /* ... */ }


或等同物或以某种其它实现定义的方式。
2 如果声明了它们,则main函数的参数应遵守以下约束:

  • argc的值应为非负值。
  • argv[argc]应该是空指针。
  • 如果argc的值大于零,则数组成员argv[0]argv[argc-1](含)应包含指向字符串的指针,它们被赋予了实现-在程序启动之前由宿主环境定义的值。目的是向程序提供在程序启动之前从宿主环境中的其他地方确定的信息。如果宿主环境不能如果提供字符串同时包含字母和小写字母,则实现应确保字符串以小写字母接收。
  • 如果argc的值大于零,则argv[0]指向的字符串表示程序名称;如果主机环境中没有程序名称,则argv[0][0]应为空字符。如果argc的值大于1,则argv[1]argv[argc-1]指向的字符串表示程序参数。
  • 参数argcargv以及argv数组指向的字符串应可由程序修改,并在程序启动和程序终止之间保留其最后存储的值。
    因此,您不应该尝试修改argv[1]以使其将缺少的参数值指向argv[1] = "default",尽管这可能在大多数目标上都能正常工作。相反,您可以修改argv参数本身以使其指向另一个具有正确内容的数组:
#include <stdio.h>

int main(int argc, char *argv[]) {
    char *new_args[3];
    
    if (argc < 2) {
        new_args[0] = argv[0];
        new_args[1] = "world";
        new_args[2] = NULL;
        argv = new_argv;
        argc = 2;
    }
    printf("Hello %s\n", argv[1]);
    return 0;
}


这种方法有点非常规:一种更经典的方法是使用命名变量和默认值作为可选的命令行参数:

#include <stdio.h>

int main(int argc, char *argv[]) {
    char *name = "world";
    
    if (argc > 1) {
        name = argv[1];
    }
    printf("Hello %s\n", name);
    return 0;
}

uxh89sit

uxh89sit8#

撤销我以前的答案,在这个愚蠢的探索中,一个简单的解决方案,我相信,还没有被建议,但具体回答了OP的愿望,当命令行上没有提供时,将argv[1]设置为默认值。

#include <stdio.h>

int main( int argc, char *argv[] ) {
    if( argc == 1 ) {
        char arg1[] = "argument #1"; // mutable!!
        char *argvX[] = {
            argv[0],
            arg1,
            NULL,
        };
        return main( 2, argvX ); // RECURSION!!!
    }

    // Note the use of 'argc' and 'argv' as requested.
    for( int i = 0; i <= argc; i++ ) 
        printf( "[%d] \"%s\"\n", i, argv[i] );

    return 0;
}

字符串
输出量:

[0] "demo"
[1] "argument #1"
[2] "(null)"   // printf() can be kind, printing a NULL ptr


没有malloc/free的烦恼。没有VLA。便携式。可扩展。KISS...

kqlmhetl

kqlmhetl9#

您将得到由索引越界异常引起的segfault
在C中解析args时,你应该检查getopt和argp:

相关问题