从常量初始化char数组

时间:2019-01-30 21:56:08

标签: c++

我从以下代码开始:

void func1() {
  char tmpfile[] = "/tmp/tmpXXXXXX";
  mkstemp(tmpfile);  // Note: mkstemp modifies the char array, cannot be const
  ...
}

void func2() {
  char tmpfile[] = "/tmp/tmpXXXXXX";
  mkstemp(tmpfile);  // Note: mkstemp modifies the char array, cannot be const
  ...
}

我想对其进行重构以提取共享的"/tmp/tmpXXXXXX"常量。这是一个尝试:

constexpr char kTmpfile[] = "/tmp/tmpXXXXXX";

void func1() {
  char tmpfile[] = kTmpfile;
  mkstemp(tmpfile);  // Note: mkstemp modifies the char array, cannot be const
  ...
}

void func2() {
  char tmpfile[] = kTmpfile;
  mkstemp(tmpfile);  // Note: mkstemp modifies the char array, cannot be const
  ...
}

但是,这不能编译。将tmpfile[]更改为tmpfile[sizeof(kTmpfile)]也不起作用。

下面的确实有效,但是它使用了我的公司的样式指南(基于Google Style Guide)不建议使用的宏。

#define TMPFILE "/tmp/tmpXXXXXX"

void func1() {
  char tmpfile[] = TMPFILE;
  mkstemp(tmpfile);  // Note: mkstemp modifies the char array, cannot be const
  ...
}

void func2() {
  char tmpfile[] = TMPFILE;
  mkstemp(tmpfile);  // Note: mkstemp modifies the char array, cannot be const
  ...
}

有什么办法写得“漂亮”吗?不必使用宏或硬编码大小?还是宏是可读性和可维护性的最佳选择?

4 个答案:

答案 0 :(得分:4)

这是三种方法。值得一提的是@πάνταῥεῖ,@ PSkocik和@Asu提出的建议,我只是打了一下。

方法1a

constexpr auto kTmpfile = "/tmp/tmpXXXXXX";

void func1() {
  std::string tmpfile = kTmpfile;
  mkstemp(tmpfile.data());
  ...
}

优势:

  • 更少的代码/更易读

缺点:

    仅限
  • C ++ 17,因为std::string::data在C ++ 14和更早的版本中返回const char*(当然,您可以在C ++ 14中使用const_cast,但这是也不好)
  • 可能会更慢,因为char数组可能分配在堆上而不是堆栈上

方法1b

constexpr auto kTmpfile = "/tmp/tmpXXXXXX";

void func1() {
  std::string tmpfile = kTmpfile;
  mkstemp(&tmpfile[0]);
  ...
}

优势:

缺点:

  • 可能会更慢,因为char数组可能分配在堆上而不是堆栈上

方法2

constexpr char kTmpfile[] = "/tmp/tmpXXXXXX";

void func1() {
  char tmpfile[sizeof(kTmpfile)];
  memcpy(tmpfile, kTmpfile, sizeof(kTmpfile));
  mkstemp(tmpfile);
  ...
}

优势:

  • 仅使用堆栈,不使用堆
  • 与C ++ 14和更早版本兼容

缺点:

  • 更冗长/更难以理解

答案 1 :(得分:2)

您可以使用std::array和一些 template magic 来确定数组大小;

$table_query

要获取#include <array> #include <algorithm> constexpr char kTmpfile[] = "/tmp/tmpXXXXXX"; template<typename T, size_t N> constexpr size_t array_size(T(&)[N]) { return N; } void func1() { std::array<char, array_size(kTmpfile)> var; std::copy(std::begin(kTmpfile), std::end(kTmpfile), var.begin()); mkstemp(var.data()); //... } 中的数据,可以调用函数data()

答案 2 :(得分:2)

只要char数组是本地的,就可以替换

char tmpfile[] = STR_LITERAL; 

char tmpfile[sizeof kTmpfile]; memcpy(tmpfile,kTmpfile,sizeof tmpfile); 

理论上没有效率损失。

例如,下面的代码片段中的compiles both func1 and func2会在x86_64上变成相同的说明:

#include <stdlib.h>

int func1() {
  char tmpfile[] = "/tmp/tmpXXXXXX";
  mkstemp(tmpfile);
}

#include <string.h>
const char kTmpfile[] = "/tmp/tmpXXXXXX";
int func2() {
  char tmpfile[sizeof kTmpfile]; 
  memcpy(&tmpfile,kTmpfile,sizeof tmpfile);

  mkstemp(tmpfile);
}

组装输出:

func1(): # @func1()
  subq $24, %rsp
  movabsq $24866934413088880, %rax # imm = 0x58585858585870
  movq %rax, 15(%rsp)
  movabsq $8101259051807896623, %rax # imm = 0x706D742F706D742F
  movq %rax, 8(%rsp)
  leaq 8(%rsp), %rdi
  callq mkstemp
func2(): # @func2()
  subq $24, %rsp
  movabsq $24866934413088880, %rax # imm = 0x58585858585870
  movq %rax, 15(%rsp)
  movabsq $8101259051807896623, %rax # imm = 0x706D742F706D742F
  movq %rax, 8(%rsp)
  leaq 8(%rsp), %rdi
  callq mkstemp

这种无需使用宏即可重构重复的初始化字符串的解决方案在纯C语言中也适用。

使用std::string,尽管它通常会使用堆,这比堆栈要贵很多,但也不会在这里造成太大的损失,因为您可以预期文件创建将至少花费一微秒,并且从而主导了堆分配和字符串复制。

答案 3 :(得分:1)

另一种方法,C ++ 11(如果您编写构造函数,则为C ++ 03):

struct KTmpfile { char value[...] = "/tmp/tmpXXXXXX"; };

void func1() {
    KTmpfile tmpfile;
    mkstemp(tmpfile.value);
    ...
}

仅堆栈,func*用户中可能的最小代码。

请注意,您当然必须指定value的大小(或重复文字以找出大小)。


在我看来,这是最好的方法,因为您真正想要的是要获取多个实例的类型-您知道其大小和初始值,因此请为其创建一个合适的类型

并且,在这一点上,由于您有一个类型,因此您可以从类型本身(甚至在构造函数中)调用mkstemp,然后简单地说:

void func1() {
    KTmpfile tmpfile;
    ...
}