在C/C++中清空字符数组的最佳方式是什么?

5

我一直在寻找在C/C++中清空字符数组的方法。我想出了以下代码:

char testName[20];   

for(int i = 0; i < sizeof(testName); ++i)
{
  testName[i] = (char)0;
}  

它已经运行了一段时间,但是当我尝试使用strlen函数时,结果总是比输入的单词多两个字符。例如,我输入单词dog,输出将是五个字符。为什么会这样?我的字符数组没有被清除吗?

以下是我的代码:

char testName[20];
void loop()
{
  if(Serial.available())
  {
    Serial.println("Waiting for name...");
    index = 0;
    for(int i = 0; i < sizeof(testName); ++i)
    {
        testName[i] = (char)0;
    }
    while(Serial.available())
    {
      char character = Serial.read();
      testName[index] = character;
      index++;
    }
    Serial.print("Name received: ");
    Serial.println(testName);
    Serial.print("The sentence entered is ");
    Serial.print(strlen(testName));
    Serial.println(" long");
    delay(1000);
  } 
  delay(1000);
}

输出的截图:

输出的截图

文本输出:

Name received: dog

The sentence entered is 5 characters long

8
你确定输入的末尾没有\r\n吗? - Werner Henze
7
最好的方法是根本不使用字符数组,而是使用std::string。 - user2672107
5
请阅读memset的文档。 - user4581301
3
@manni66因为你的建议是完全错误的。 - SergeyA
3
@manni66,不行。字符数组经常比std::string更适合使用。此外,你的语言不太友好。 - SergeyA
显示剩余21条评论
4个回答

15

在现代 C++ 中不要使用 C 风格的数组。当你需要一个固定大小的数组时,请使用 std::array。从内存存储的角度来看,它们是相同的。

你可以用以下方法清空这个数组:myarray.fill('\0')


9
如果您定义“清空字符数组”的方式是将数组的所有元素设置为零,则可以使用std::memset。这将使您能够使用以下代码替代清除循环:
const size_t arraySize = 20;  // Avoid magic numbers!
char testName[arraySize];
memset(&(testName[0]), 0, arraySize);

关于strlen()的“奇怪”结果:strlen(str)返回"(...)指向str第一个元素的字符数组中不包括第一个空字符的字符数"。这意味着它会计算字符,直到找到零为止。
检查你传递给strlen()的字符串内容 - 你可能有一些空格字符(例如\r\n),它们是从输入流中读取的。
另外,建议使用std::string而不是普通的char数组。

小提示: memset() 经常被优化以获得更高的性能。如果这不是您的要求,您也可以使用 std::fill,它是一种更像 C++ 的方式来填充任何数组:

char testName[arraySize];
std::fill(std::begin(testName), std::end(testName), '\0');

std::begin()(和std::end)在编译时大小已知的数组上运行良好。


此外,为了回答 @SergeyA 的评论,我建议阅读 这篇SO帖子这个答案

3
@SergeyA 我已经在更新我的答案,使用了 std::fill 的版本。但是由于 std::memset 有着优化的实现方式,所以它肯定值得一提。我推荐使用 std::string,但是既然我们这里用的是普通的 char 数组,那么我认为错误使用 memset 是最不用担心的事情了。 - Mateusz Grzejek
4
如果任务是设置C风格的char数组的内容,我不会责怪建议使用memset的人。 - harper
2
@SergeyA,因为允许和“这个编译器是否做或不做”的意思是不同的,尤其是当它不做时。 - UKMonkey
3
@MichaelWalz 和 std::fill 要么对 POD 类型使用 memset 进行特化,要么优化器会识别模式并即时生成优化的代码。 - SergeyA
2
@SergeyA std::fillstd::memset 更不安全,因为你可能会意外传递指向不同范围的迭代器,导致未定义的行为。std::memset 没有这种错误的可能性。 - eerorika
显示剩余8条评论

2

另一种快速将数组初始化为零的方法:

char testName[20] = {};

同上

char testName[20] = {0};

由于这是C99功能,编译器可能会出现以下错误提示:

warning: ISO C forbids empty initializer braces

更多内容请点击这里

无论如何,看一下原始代码,没有必要初始化/填充数组,可以这样做得更好:

#define MAX_LENGTH 20
char testName[MAX_LENGTH];
void loop()
{
  if(Serial.available())
  {
    Serial.println("Waiting for name...");
    index = 0;
    while(Serial.available())
    {
      if(index < MAX_LENGTH) 
          testName[index++] = Serial.read();
    }
    if(index < MAX_LENGTH)
    {
      testName[index] = 0;
      Serial.print("Name received: ");
      Serial.println(testName);
      Serial.print("The sentence entered is ");
      Serial.print(strlen(testName));
      Serial.println(" long");
    }
    else
      Serial.print("Name too long ...");

    delay(1000);
  } 
  delay(1000);
} 

4
仅适用于定义的地方,不能在其他地方使用。 - doron
1
我会删除这个答案,因为它在这里真的没有意义,因为问题提出者显然想要在循环内部填充数组。 - Jabberwocky
@MichaelWalz 为什么这个答案是无意义的?这是有用的、相关的信息,那些使用原始缓冲区的人需要知道。 - Galik
1
问题说:在C++中清空字符数组,代码明确显示需要对已有的数组执行clear操作,而此答案的第一句话是:“(...) 快速地用零填充数组,仅在初始化中使用”。 - Mateusz Grzejek
3
根据发布的代码而言,我认为我的回答符合原帖提出的需求,而非问题本身。 - p-a-o-l-o
显示剩余6条评论

0

为了后人留存。OP的示例是一个Arduino草图。在此假设输入是通过Arduino IDE内置的串行监视器进行的。在串行监视器窗口底部有一个弹出菜单,可以选择行结尾:

  • 无行结尾
  • 换行符
  • 回车符
  • 两者都有

无论在此菜单中选择哪个选项,当单击“发送”按钮或按下回车键时,都会将其附加到在串行监视器顶部的框中输入的任何内容中。因此,额外的两个字符肯定是选择了“两者都有”菜单选项的结果。

选择“无行结尾”选项将解决问题。

顺便说一句,在此应用程序中,数组不需要“清除”。只需将'\0'字符附加到最后一个接收到的字符即可使其成为C字符串,一切都会很好。


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