在 C++03 中,如何在没有重复代码的情况下在堆栈上创建非常量 C 字符串数组?
How to create an array of non-const C strings on the stack without repetitive code in C++03?
我正在为一个模块编写单元测试,该模块采用 C++03 中的可变 C 字符串数组 getopt
。函数参数是 char*const[]
。我想在函数内部的堆栈上创建参数。我目前的解决方案是:
char args_stor[][1024] = {
"parser",
"-o",
"SomeValue"
};
char* args[] = {
args_stor[0],
args_stor[1],
args_stor[2]
};
如何避免重复的部分,如果可能的话,将其转化为一条语句?
函数声明为:
int getopt(int argc, char* const argv[], const char *optstring);
因此用例是:
getopt(3, args, ":o:");
这个怎么样?
// Example program
#include <iostream>
#include <string>
int main()
{
char *what[] = {"asd", "asd", "asd"};
}
我会做类似的事情(我添加了一个 parse
函数来验证功能):
#include <stdio.h>
int parse(int argc, char* argv[], const char *optstring)
{
printf("optstring = %s\n", optstring);
printf("argv's:\n");
for (int i = 0; i < argc; ++i)
printf("\t%s\n", argv[i]);
return 0;
}
int main(void)
{
char args_arr[][1024] = {
"Hello",
"how",
"are",
"you?"
};
char *args[sizeof args_arr / sizeof *args_arr];
for (size_t i = 0; i < sizeof args / sizeof *args; ++i)
args[i] = args_arr[i];
parse(sizeof args / sizeof *args, args, ":o:");
return 0;
}
如果你想添加一些东西,你只需要将它添加到args_arr
声明中。 args
数组会相应改变
我认为你做不到。 getopt
需要 char * const[]
。您可以做的唯一改进是为每个参数使用单独的 char[]
声明而不是二维数组。这样你只分配需要的 space:
char opt1[] = "parser";
char opt2[] = "-o";
char *args[] = { opt1, opt2 };
从问题的评论来看,将 char 数组分配在堆栈上并不重要,重要的是它被正确且自动清理 使用后,这样我们就可以像这样调用函数:
void f(char *const *args);
int main()
{
const char *args[] = {
"parser",
"-o",
"SomeValue"
};
f(transform(args)->p);
}
要实现transform()
,我们可以return一个std::auto_ptr
到合适的结构:
#include <cstring>
#include <memory>
template<std::size_t N>
struct arg_list
{
char *s; // string storage
char *p[N+1]; // pointers into s
arg_list(const char *const *args)
: s(new char[total_len(args)]), p()
{
char *dest = s;
for (std::size_t i = 0; i < N; ++i) {
std::strcpy(p[i] = dest, args[i]);
dest += std::strlen(args[i]) + 1;
}
p[N] = 0;
}
~arg_list() { delete[] s; }
static std::size_t total_len(const char *const *args) {
std::size_t length = 0;
for (std::size_t i = 0; i < N; ++i)
length += std::strlen(args[i]) + 1;
return length;
}
};
template<std::size_t N>
std::auto_ptr< arg_list<N> > transform(const char *const (&args)[N])
{
return std::auto_ptr< arg_list<N> >(new arg_list<N>(args));
}
arg_list
对象将在main()
中调用f()
后销毁。我们可以使用这个例子 f()
:
来测试它
#include <iostream>
void f(char *const *args)
{
while (*args)
std::cout << *args++ << std::endl;
}
没有内存泄漏,也没有对无效内存进行访问:
valgrind --leak-check=full ./44908185
==23361== Memcheck, a memory error detector
==23361== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==23361== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==23361== Command: ./44908185
==23361==
parser
-o
SomeValue
==23361==
==23361== HEAP SUMMARY:
==23361== in use at exit: 0 bytes in 0 blocks
==23361== total heap usage: 3 allocs, 3 frees, 72,764 bytes allocated
对于较新的 C++ 标准,有更简单的版本,但这似乎是 C++03 中最简单的版本。
我正在为一个模块编写单元测试,该模块采用 C++03 中的可变 C 字符串数组 getopt
。函数参数是 char*const[]
。我想在函数内部的堆栈上创建参数。我目前的解决方案是:
char args_stor[][1024] = {
"parser",
"-o",
"SomeValue"
};
char* args[] = {
args_stor[0],
args_stor[1],
args_stor[2]
};
如何避免重复的部分,如果可能的话,将其转化为一条语句?
函数声明为:
int getopt(int argc, char* const argv[], const char *optstring);
因此用例是:
getopt(3, args, ":o:");
这个怎么样?
// Example program
#include <iostream>
#include <string>
int main()
{
char *what[] = {"asd", "asd", "asd"};
}
我会做类似的事情(我添加了一个 parse
函数来验证功能):
#include <stdio.h>
int parse(int argc, char* argv[], const char *optstring)
{
printf("optstring = %s\n", optstring);
printf("argv's:\n");
for (int i = 0; i < argc; ++i)
printf("\t%s\n", argv[i]);
return 0;
}
int main(void)
{
char args_arr[][1024] = {
"Hello",
"how",
"are",
"you?"
};
char *args[sizeof args_arr / sizeof *args_arr];
for (size_t i = 0; i < sizeof args / sizeof *args; ++i)
args[i] = args_arr[i];
parse(sizeof args / sizeof *args, args, ":o:");
return 0;
}
如果你想添加一些东西,你只需要将它添加到args_arr
声明中。 args
数组会相应改变
我认为你做不到。 getopt
需要 char * const[]
。您可以做的唯一改进是为每个参数使用单独的 char[]
声明而不是二维数组。这样你只分配需要的 space:
char opt1[] = "parser";
char opt2[] = "-o";
char *args[] = { opt1, opt2 };
从问题的评论来看,将 char 数组分配在堆栈上并不重要,重要的是它被正确且自动清理 使用后,这样我们就可以像这样调用函数:
void f(char *const *args);
int main()
{
const char *args[] = {
"parser",
"-o",
"SomeValue"
};
f(transform(args)->p);
}
要实现transform()
,我们可以return一个std::auto_ptr
到合适的结构:
#include <cstring>
#include <memory>
template<std::size_t N>
struct arg_list
{
char *s; // string storage
char *p[N+1]; // pointers into s
arg_list(const char *const *args)
: s(new char[total_len(args)]), p()
{
char *dest = s;
for (std::size_t i = 0; i < N; ++i) {
std::strcpy(p[i] = dest, args[i]);
dest += std::strlen(args[i]) + 1;
}
p[N] = 0;
}
~arg_list() { delete[] s; }
static std::size_t total_len(const char *const *args) {
std::size_t length = 0;
for (std::size_t i = 0; i < N; ++i)
length += std::strlen(args[i]) + 1;
return length;
}
};
template<std::size_t N>
std::auto_ptr< arg_list<N> > transform(const char *const (&args)[N])
{
return std::auto_ptr< arg_list<N> >(new arg_list<N>(args));
}
arg_list
对象将在main()
中调用f()
后销毁。我们可以使用这个例子 f()
:
#include <iostream>
void f(char *const *args)
{
while (*args)
std::cout << *args++ << std::endl;
}
没有内存泄漏,也没有对无效内存进行访问:
valgrind --leak-check=full ./44908185
==23361== Memcheck, a memory error detector
==23361== Copyright (C) 2002-2015, and GNU GPL'd, by Julian Seward et al.
==23361== Using Valgrind-3.12.0 and LibVEX; rerun with -h for copyright info
==23361== Command: ./44908185
==23361==
parser
-o
SomeValue
==23361==
==23361== HEAP SUMMARY:
==23361== in use at exit: 0 bytes in 0 blocks
==23361== total heap usage: 3 allocs, 3 frees, 72,764 bytes allocated
对于较新的 C++ 标准,有更简单的版本,但这似乎是 C++03 中最简单的版本。