c语言中warning:hex escape sequence out of range的探究

起因:

在和 clownmemcpy 函数拷贝字符串是不会被 \x00 截断这个结论之前,为了避免翻车(尽管我有九成九的把握),我自己写了个代码进行验证,结果发现了一些奇怪的事情…

探究

#include<stdio.h>
#include<string.h>
#include<unistd.h>
int main()
{
const char *str="abcd\x00efgklm";
char buf[0x100];
memcpy(buf,str,12);
write(1,buf,12);
return 0;
}

先将一个字符串常量中夹杂一个空字符 \x00 ,然后用 memcpy 函数进行拷贝到新的数组中,最终用 write 函数进行输出,这样就能清楚的判断 \x00 会不会造成截断效果

运行起来的效果如下

image-20231005215620318

看到 \x00 后面的字符串 gklm 成功输出后,我就去告诉了 clown \x00 并不会截断 memcpy 函数的结论,正准备收工时,我突然发现字符 ef 没了😮

本着不放弃小细节的原则,我并不打算得过且过。

先使用了 gdb 进行调试

image-20231005220602193

查看 memcpy 函数的第二个参数要拷贝的字符串为 abcd\357gklm ,这个 \357 我第一时间没反应过来,以为这里是出问题了?😨 不过联想到这里的数据原本是字符 ef ,我猜测可能是被解析成十六进制数据了,用计算器查看了一下发现 357 是八进制,表示为十六进制是 \xef

上网查资料,得知编译器看到 \x 时,它会尝试解析后面的所有十六进制数字,直到遇到一个非十六进制数字。在上面的代码中,它看到了 \x00ef 将其解析为一个单一的字符 \xef ,而并非是我希望的空字符 \x00 和字符 e f

我又好奇如果给的数据为 \x01ef ,那么在内存中这个值会如何表示?因为之前的高位 00 是可以省略的,但这个高位的 \x01 无法省略

#include<stdio.h>
#include<string.h>
#include<unistd.h>
int main()
{
const char *str="abcd\x01efgklm";
char buf[0x100];
memcpy(buf,str,12);
write(1,buf,12);
return 0;
}

image-20231005221525192

发现在编译时就已经给了 warning: hex escape sequence out of range ,这表示十六进制转义序列超出范围,在 \x 之后的第三个字符如果依然可以表示十六进制(0~9 a~f),会一起转义,这里的 \x01ef 超过了 ASCII 码的范围

image-20231005222408394

在使用 gdb 调试时,只能看到 \xef ,高位的 \x01 已经不见了。这因为十六进制转义序列的长度虽然没有限制,但在实际使用中,一个字符只能包含8个比特(即1个字节),所以这里是直接舍弃了高位的数据,即使写成 \x11223344 最后留下的也只有 \x44

如何达到我想要结果?

image-20231005223445247

在下面的参考文章中对这个进行了说明,只需要将字符串拆开就可以正常来显示了

#include<stdio.h>
#include<string.h>
#include<unistd.h>
int main()
{
const char *str="abcd\x00" "efgklm";
char buf[0x100];
memcpy(buf,str,12);
write(1,buf,12);
return 0;
}

image-20231005223606998

image-20231005223659788

如此不但验证了上面 memcpy 函数不会被 \x00 截断拷贝的结论,并且还发现了两个有意思的现象

  1. \x0061 实际上为 \x61 因为编译器会尝试解析后面的所有十六进制数字,直到遇到一个非十六进制数字,此处的 \x00 为高位可以被省略,属于正常代码
  2. \x0161 实际上还是 \x61,因为 \x0161 尝试被解析为一个十六进制的数据,导致超出最大的转义序列范围,只保留了最低位并且编译器给了 warning

参考文章

https://learn.microsoft.com/zh-cn/cpp/c-language/octal-and-hexadecimal-character-specifications?view=msvc-170