有没有办法“重置”getopt 以供非全局使用?

问题描述 投票:0回答:2

当尝试多次使用

getopt
时,我在 valgrind 中遇到的错误是
Invalid read of size 1
。仅当执行以下操作时才会出现错误:

ls -a -b
ls -a -b

因此我假设问题在于重用

getopt
函数。

命令.h

class Command {
    protected:
            // for use in getopt
        int c = 0;
            // name of command
        char* name;
    public:
        Command(const char* nname) : name((char*)nname) { }
        virtual ~Command() { };
        virtual void process_args(int argc, char* argv[]) = 0;
        virtual char* get_name() const {
            return name;
        }
};

ls.h 只是将 this 封装在一个类中:

class ls : public Command {
    public:
        ls() : Command("ls") { }
        ~ls() { }
        void process_args(int input_argc, char* input_argv[]) {
            int verbose_flag = 0;

            struct option long_options[] =
            {
                    /* These options set a flag. */
                    {"verbose", no_argument,       &verbose_flag, 1},
                    {"brief",   no_argument,       &verbose_flag, 0},
                    /* These options don't set a flag.
                        We distinguish them by their indices. */
                    {"add",     no_argument,       0, 'a'},
                    {"append",  no_argument,       0, 'b'},
                    {"delete",  required_argument, 0, 'd'},
                    {"create",  required_argument, 0, 'c'},
                    {"file",    required_argument, 0, 'f'},
                    {0, 0, 0, 0}
            };

            while (1) {
                      // removed static and moved struct outside
                      // everything else is the same
                    }
        }
};

主.cpp

std::vector<std::unique_ptr<Command>> commands;
commands.push_back(std::unique_ptr<Command>(new ls()));
commands.push_back(std::unique_ptr<Command>(new shibe()));

while (true) {
    std::string input;
    std::getline(std::cin, input);
    if (input == "quit")
        break;
    std::istringstream iss(input);
    std::vector<std::string> args;
    std::copy(std::istream_iterator<std::string>(iss), std::istream_iterator<std::string>(), std::back_inserter(args));
    int input_argc = args.size();
    char* input_argv[input_argc];
    for (int i = 0; i < args.size(); i++) {
        input_argv[i] = (char*)args[i].c_str();
    }

    for (int i = 0; i < commands.size(); i++) {
        if (strcmp(input_argv[0], commands[i]->get_name()) == 0) {
            commands[i]->process_args(input_argc, input_argv);
            break;
        }
    }
}

Valgrind 输出为:

ls -a -b
--30624-- REDIR: 0x375e8812d0 (strlen) redirected to 0x480155c (_vgnU_ifunc_wrap                                                                                                 per)
--30624-- REDIR: 0x375e87f810 (__GI_strchr) redirected to 0x4a07b30 (__GI_strchr                                                                                                 )
option -a
option -b
ls -a -b
==30624== Invalid read of size 1
==30624==    at 0x375E8CDFDC: _getopt_internal_r (in /lib64/libc-2.12.so)
==30624==    by 0x375E8CF1EA: _getopt_internal (in /lib64/libc-2.12.so)
==30624==    by 0x375E8CF2D2: getopt_long (in /lib64/libc-2.12.so)
==30624==    by 0x401E1C: ls::process_args(int, char**) (ls.h:31)
==30624==    by 0x4019CB: main (main.cpp:36)
==30624==  Address 0x513e5da is 26 bytes inside a block of size 27 free'd
==30624==    at 0x4A05FD6: operator delete(void*) (vg_replace_malloc.c:480)
==30624==    by 0x4CCADFE: std::basic_string<char, std::char_traits<char>, std::                                                                                                 allocator<char> >::~basic_string() (basic_string.h:538)
==30624==    by 0x403AA5: void std::_Destroy<std::string>(std::string*) (stl_con                                                                                                 struct.h:93)
==30624==    by 0x403855: void std::_Destroy_aux<false>::__destroy<std::string*>                                                                                                 (std::string*, std::string*) (stl_construct.h:103)
==30624==    by 0x403466: void std::_Destroy<std::string*>(std::string*, std::st                                                                                                 ring*) (stl_construct.h:126)
==30624==    by 0x402DE6: void std::_Destroy<std::string*, std::string>(std::str                                                                                                 ing*, std::string*, std::allocator<std::string>&) (stl_construct.h:151)
==30624==    by 0x402878: std::vector<std::string, std::allocator<std::string> >                                                                                                 ::~vector() (stl_vector.h:415)
==30624==    by 0x401A03: main (main.cpp:26)
==30624==
--30624-- REDIR: 0x375e8846b0 (mempcpy) redirected to 0x4a09f80 (mempcpy)
non-option input_argv-elements: s b
quit
--30624-- REDIR: 0x375e87b6d0 (free) redirected to 0x4a06369 (free)
==30624==
==30624== HEAP SUMMARY:
==30624==     in use at exit: 0 bytes in 0 blocks
==30624==   total heap usage: 36 allocs, 36 frees, 916 bytes allocated
==30624==
==30624== All heap blocks were freed -- no leaks are possible
==30624==
==30624== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 6 from 6)
==30624==
c++ valgrind getopt
2个回答
20
投票

Linux 上的

getopt()
手册页清楚地说明了如何重置
getopt()
(不幸的是,它使用了许多全局变量来与调用者通信并维护状态):

变量 optind 是 argv 中下一个要处理的元素的索引。系统将此值初始化为 1。调用者可以将其重置为 1 以重新开始扫描相同的 argv,或者在扫描新的参数向量时。


0
投票

设置

optind=1
一开始似乎有效。但是,如果您针对一个错误的选项测试代码,然后针对一个好的选项进行测试,结果仍然很糟糕。至少对于使用 glibc v2.35 的 Linux 系统来说,getopt() 的某些内部状态没有正确重置。经过搜索后,我从这篇文章中找到了一些线索,并在 getopt() 之前添加此行进行了测试:

选择= 0;

摘自 Michael Kerrisk 撰写的帖子:

  • 将 optind 重置为 0 会导致调用 glibc 特定的 初始化例程,扫描 optstring 中的 glibc 扩展('+' 和 '-' 在字符串的开头)并重新检查 POSIXLY_CORRECT。重置为 如果您想使用这些扩展,则需要 0。
© www.soinside.com 2019 - 2024. All rights reserved.