类似于初始化字符串字面值的方式,初始化字符数组

10

假设我有一个 char 数组的初始化如下:

char charArray[]={'h','e','l','l','o',' ','w','o','r','l','d'};

我还有一个字符串字面值的初始化:

char stringLiteral[]="hello world";
第一个数组和第二个字符串唯一的区别是,第二个字符串的末尾有一个空字符。
当初始化char数组时,是否有宏或其他方法可以让我们将初始化文本放置在两个双引号之间,但数组不会得到额外的空终止字符?
我无法理解的是,当不需要终止空字符时,我们应该使用第一种初始化语法,并为初始化器文本中的每个字符编写两个单引号以及逗号分隔符。
我应该补充说明的是,当我想要一个char数组时,也应该明显地表明,我不想将其与依赖于字符串字面值的函数一起使用,而且我不考虑使用字符串字面值所产生的任何特征。
感谢您的回答。

@Michael - 这是C和C++标准的要求。 - user180247
1
你面临的问题是,该语言要求“引用字符串”必须以NUL结尾。 - torak
5个回答

9
在C语言中,可以按照以下方式声明数组,这样将初始化它,而不会复制终止符'\0'
char c[3] = "foo";

但在C++中这是非法的。我不知道有什么技巧可以实现它。C++标准进一步说明:

原因:当这些非终止数组被标准字符串函数操作时,可能会导致严重的灾难。
对原始功能的影响:删除语义上定义良好的功能。
转换的难度:语义转换。这些数组必须声明为一个元素更大以包含字符串终止符“\0”
使用范围:很少。这种数组初始化方式被认为是糟糕的编码风格。


1
我觉得你的回答有点让人困惑。char c[4] = "foo"; 是会放置空字符的,对吗?这是否仍然被视为不良的编码风格?char[128] = ""; 呢?只是初始化缓冲区?这样可以吗?还是说char[128] = { '\0' }; 更好一些? - user1115652
根据Randal Albert的参考书,char c [4] ="foo"; 会为您放置一个空字符,并且根据该书建议将其编写为char [4]="foo";char c[3] ="foo"; 是非法的,因为我们需要在后面保留终止空字符的空间。 - user3437460
@user 值得阅读我完整的回答,其中提到“数组必须声明一个比字符串终止符'\0'多一个元素的空间。” - Johannes Schaub - litb

3

你想要的事情是无法实现的。初始化数组的第一种方法为每个字符指定单独的初始值,这允许明确省略 '\0'。第二种方法是从字符字符串初始化字符数组,在 C/C++ 中始终以空字符结尾。

编辑:更正为“字符数组”而非“字符指针”


2
实际上,我不相信第二种情况是将指针初始化为字符串。它创建了一个数组,并根据字符串的内容初始化其内容。而且,确实存在差异。 - torak
以前从未注意到“分离字符初始化器”的概念,谢谢。 - Pooria

1

litb 给出了技术上正确的答案

至于个人意见——我建议就让那多余的 '\0' 存在吧。很多 bug 都是由于代码期望有一个终止符号但实际上没有造成的(这条建议可能似乎与我前几天所提供的另一条建议直接相反,即不必费力将整个缓冲区全部清零。但我认为二者并不矛盾——我仍然主张在缓冲区中加入空字符以终止字符串)。

如果你因为处理的数据结构的某些语义原因而无法接受 '\0' 终止符,例如其可能是某个更大的紧凑结构的一部分,那么你可以自己初始化该数组(我认为这应该与编译器为你完成的操作不相上下):

#define MY_STRING_LITERAL "hello world"

char stringLiteral[sizeof(MY_STRING_LITERAL) - 1];

memcpy( stringLiteral, MY_STRING_LITERAL, sizeof(stringLiteral));

我认为您的方法对于具有自动存储类别的字符数组应该像编译器的方法一样有效,但是对于具有静态存储类别或全局作用域中的字符数组,我认为编译器不会执行任何运行时复制。 - Pooria
@garrett2011:对于const char[]类型的项,这很可能是正确的,但我认为大多数非const数组的初始值通常会从程序映像“text”(在大多数平台上是不可写的)复制到RAM中,在调用main()之前。只要memcpy()只执行一次,就效率而言,无论是由运行时还是由您的代码执行,都差不多。 - Michael Burr

0

我可能找到了一种实现我想要的功能的方法,虽然它不是我直接想要的,但它很可能会产生相同的效果。
首先考虑以下两个类:

template <size_t size>
class Cont{
 public:
  char charArray[size];
};
template <size_t size>
class ArrayToUse{
 public:
  Cont<size> container;
  inline ArrayToUse(const Cont<size+1> & input):container(reinterpret_cast<const Cont<size> &>(input)){}
};

在继续之前,您可能想要点击这里并查看常量表达式构造函数和初始化类型。
现在看一下以下代码:

const Cont<12> container={"hello world"};
ArrayToUse<11> temp(container);
char (&charArray)[11]=temp.container.charArray;

最后,初始化程序文本写在两个双引号之间。


请确保在该结构周围添加一些注释。正如您可能已经从其他答案和评论中猜到的那样,没有终止符'\0'是一个相当不寻常的概念,并应该配备适当的警告标志。 - DevSolar

0
基本答案是,绝大多数的字符数组都是字符串 - 在 C 中,字符串是以 null 结尾的。C++ 继承了这个惯例。即使不需要那个 null,大多数情况下把它留在那里也不会有问题。
宏不足以实现你想要的功能。模板可以,但它们没有任何编译时字符串处理。
通常,当人们想要在同一 char 数组序列中混合数字字节和字符串文字时,他们使用字符串文字,但使用十六进制字符转义,如 \xFF。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接