【问题标题】:Why does the memory behave differently after applying strcpy() and initialising an array with a string literal? [closed]为什么在应用 strcpy() 并使用字符串文字初始化数组后,内存的行为会有所不同? [关闭]
【发布时间】:2020-09-16 08:25:33
【问题描述】:

如果我使用strcpy 为数组分配字符串字面量,我可以轻松访问超出数组边界的那些字节:

char sequence[4];
strcpy(sequence, "String");
printf("&sequence == %p\n", &sequence);
printf("&sequence[3] == %p\n", &sequence[3]);
printf("The out-of-range values are '%c' and '%c'\n", *(char*)0x000000000022FE50, *(char*)0x000000000022FE51);

&sequence == 000000000022FE4C
&sequence[3] == 000000000022FE4F
The out-of-range values are 'n' and 'g'

但是如果我只是在原地初始化数组就做同样的事情,这些内存字节是空的或者有一些意想不到的值:

char sequence[4] = "String";    
printf("&sequence == %p\n", &sequence);
printf("&sequence[3] == %p\n", &sequence[3]);
printf("The out-of-range values are '%c' and '%c'\n", *(char*)0x000000000022FE50, *(char*)0x000000000022FE51);

&sequence == 000000000022FE4C
&sequence[3] == 000000000022FE4F
The out-of-range values are ' ' and 'r'

为什么strcpy和直接存储有这么大的区别?

免责声明: 我知道这个程序不正确。我知道什么是未定义的行为,我知道这是不好的,不应该被视为解决方案。问题仅与机制有关,仅寻求教育目的。我永远不会将此应用于任何实际任务(如果有人担心的话)。

【问题讨论】:

  • char sequence[4] 的初始化正好写入 4 个字节。函数调用strcpy(sequence, "whatever") 写入与"whatever" 中一样多的字节(调用UB)
  • @pmg strcpy 不接受数组作为指针吗?也许这就是答案
  • strcpy()而言,它只接收指针。调用者发送的数组被编译过程自动转换为指针
  • 你用的是什么编译器?不是抱怨char sequence[4] = "String";吗?!?!这是违反约束的!
  • @AnttiHaapala 一些编译器可能会发出警告。实际上没有像 Java 那样的边界检查,人们应该始终注意它们的数组边界

标签: arrays c memory


【解决方案1】:

如果我使用 strcpy 为数组分配字符串字面量,我可以轻松访问超出数组边界的那些字节:

不,你不能。这是未定义的行为。它可能会起作用。它可能不会。不要那样做。每当您“可以”访问越界元素时,您就做错了。

你可以通过strncpy(sequence, "String", sizeof(sequence))避免这种情况

为什么strcpy和直接存储有这么大的区别?

strcpy 不知道您传递给它的数组的大小,但编译器在编译过程中知道这一点。 strcpy 的实现如下所示:

char * strcpy ( char * destination, const char * source ) {
    char *ret = destination;
    while(source) {
        *destination = *source;
        destination++;
        source++;
    }
    return ret;
}

如果你初始化一个数组,这就是C standard 所说的:

字符类型的数组可以由字符串文字或 UTF-8 字符串文字初始化,可选用大括号括起来。字符串文字的连续字节(如果有空间或数组大小未知,则包括终止的空字符)初始化数组的元素。

如果字符串文字太长,剩余的字符将被忽略。请注意,这很可能会导致字符串没有零终止。所以这不是一个有效的字符串,打印它会调用未定义的行为。但是,这段代码是可以的,即使它会发出警告:

#include <stdio.h>
int main(void)
{
    char str[8]="Hi!\0This initializer is too long";
    printf("%s\n", str);
    for(int i=0; i<sizeof(str); i++)
        putchar(str[i]);
    putchar('\n');
}

它将打印:

Hi!
Hi!This

【讨论】:

猜你喜欢
  • 1970-01-01
  • 2015-01-31
  • 2020-05-04
  • 2023-01-25
  • 1970-01-01
  • 2013-04-13
  • 2019-03-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多