【问题标题】:C pointer with char[], const keyword ignored?带有 char[] 的 C 指针,忽略 const 关键字?
【发布时间】:2020-10-20 21:58:06
【问题描述】:

我在 C 中创建了一个小函数,它复制了一个 char 数组。我不想source被改变,所以我把const放到函数声明和函数体中,但是它改变了。

main.c

#include "copy_char.h"
#include <stdio.h>

int main(void)
{
  char source[] = { 'h', 'e', 'l', 'l', 'o', 0 };
  char dest[] = { 'a', 'b', 'c', 0 };

  printf("%s\n", source); // hello
  printf("%s\n", dest); // abc

  copy_char(source, dest);
  
  printf("%s\n", source); // o (?) - why did this change? 
  printf("%s\n", dest); // hello
}

copy_char.c

#include "copy_char.h"

void copy_char(const char* source, char* dest)
{
  while(*source != 0)
  {
    *dest++ = *source++; 
  }

  *dest = 0;
}

copy_char.h

#ifndef COPYCHAR_H
#define COPYCHAR_H

void copy_char(const char* source, char* dest);

#endif /* COPYCHAR_H */

谁能给我解释一下,为什么我的source char 数组从hello 变成了o

【问题讨论】:

  • 您有未定义的行为,因为您溢出了dest 缓冲区。

标签: c pointers char


【解决方案1】:

缓冲区溢出。字符串source 比分配给dest 的内存要长,所以当您将source 复制到dest 时,您会占用您不打算的内存。然后会发生奇怪的事情(未定义的行为)。

您可以通过声明dest 的大小足以容纳“hello”来使dest 足够大:

char dest[6] = { 'a', 'b', 'c', 0 };

【讨论】:

    【解决方案2】:

    我不想更改源代码,所以我将 const 放入函数声明和函数体中。但是,它改变了

    const 限定符并不能神奇地保护您免于在不该写的地方写字。这只是你和编译器之间的契约,上面写着“相信我,我不会修改这个指针指向的内存”。

    在您的函数中,您正在写入超过dest 末尾的数据,这是未定义的行为。您应该:

    1. 将最大长度传递给函数:

      void copy_char(const char* source, char* dest, size_t maxlen)
      {
        size_t i;
      
        for (i = 0; i < maxlen && source[i]; i++)
        {
          dest[i] = source[i];
        }
      
        dest[i] = 0;
      }
      
    2. 停在*source == 0*dest == 0

      void copy_char(const char* source, char* dest)
      {
        while(*source != 0 && *dest != 0)
        {
          *dest++ = *source++; 
        }
      
        *dest = 0;
      }
      
    3. 创建一个足够大的dest 数组并保持函数不变。

    函数的语义会根据您选择的选项而变化,哪个是正确的取决于您。

    在您的情况下,最有可能发生的情况是 source 恰好位于 dest 的末尾,因此您将 dest 的内容覆盖为 'h','e','l','l',然后是第一个字符source'o',然后是第二个 '\0'。幸运的是,您的程序不会崩溃,并且您最终会在source 中得到一个有效字符串,即"o"。尽管如此,您应该永远不要依赖未定义的行为,无论您的程序是否能神奇地工作。

    【讨论】:

    • 确实如此。我只想指出,永远不要依赖上一段中描述的行为。我认为描述导致观察到的行为的原因是绝对正确的,但它可能会随着不同的编译器、编译器版本、编译器选项(例如优化)等而改变。未定义的行为毕竟是未定义的。
    • @FredLarson 确实如此。为了清楚起见,让我声明一下。
    • @JinLim 声明的顺序无关紧要,编译器可以根据需要重新排列堆栈上的变量。事实上,以相反的声明顺序看到它们是最常见的。
    • @Marco 也许是因为它会在stack 内存上?后进先出。
    • @JinLim 是的,我认为这就是它们通常被颠倒的原因,但正如我所说,你不应该假设它们总是如此,由编译器决定如何在堆栈。
    猜你喜欢
    • 1970-01-01
    • 2013-06-12
    • 2016-06-10
    • 2016-06-23
    • 2023-03-03
    • 2021-03-30
    • 1970-01-01
    • 1970-01-01
    • 2011-12-05
    相关资源
    最近更新 更多