当尝试多次使用
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==
设置
optind=1
一开始似乎有效。但是,如果您针对一个错误的选项测试代码,然后针对一个好的选项进行测试,结果仍然很糟糕。至少对于使用 glibc v2.35 的 Linux 系统来说,getopt() 的某些内部状态没有正确重置。经过搜索后,我从这篇文章中找到了一些线索,并在 getopt() 之前添加此行进行了测试:
选择= 0;
摘自 Michael Kerrisk 撰写的帖子:
- 将 optind 重置为 0 会导致调用 glibc 特定的 初始化例程,扫描 optstring 中的 glibc 扩展('+' 和 '-' 在字符串的开头)并重新检查 POSIXLY_CORRECT。重置为 如果您想使用这些扩展,则需要 0。