谁删了服务器?谈VC源码字符集和回车换行注意事项

在Windows Visual C/C++编程时,经常需要从Github或者其他既有项目中“借鉴”(呵呵哒)一些代码过来。这个时候,就要格外注意字符集和回车换行带来的编译问题。

这类编译问题造成的错误千奇百怪,可能在编译时、链接时,甚至运行时造成困扰。尤其是运行时,很难发现,在极其倒霉的时候,会造成删库跑路或者惨痛的事故。

1. 源代码文件的规格

源代码文件存储为可见文本,但各个平台、工具链上具体的编码规格是略有区别的。对含有中文等非ASCII标准字符的源代码,会遇到编码/字符集的概念。对跨越Linux、Windows环境的源代码,会涉及换行符的概念。

  • 编码/字符集: windows下,Visual Studio创建的C语言源文件一般存储为 本地字符集(如中文windows就是GB2312)。Linux下,默认是UTF-8的。
  • 换行符: windows下,Visual Studio创建的C语言源文件每行代码后面是两个不可见字符,即\015\012,也就是\n\r,而Linux默认只有\n.

下表是几类C++ IDE/编辑器常用(默认配置或常用配置)例子。

操作系统编辑器/IDE编码/字符集回车/换行
WindowsVisual StudioANSI(GB2312系)\n\r
WindowsVisual Studio CodeUTF-8\n\r
WindowsQt CreatorUTF-8\n\r
WindowsCodeBlocksANSI(GB2312系)\n\r
WindowsNotepadANSI(GB2312系)\n\r
Linux大部分编辑器/IDEUTF-8\n

因此,如果引用的代码原本就是Linux下的,那它十有八九是UTF-8+\n的配置组合。如果引用的是既往合作企业的Visual C++头文件,那通常都是GB2312+\n\r的组合。

2. Visual C++编译器踩坑

Visual C++编译器默认处理的是 ANSI本地字符集,若用的是中文Windows,则默认.c、.h文件里都是GB2312编码的汉字+\n\r换行。如果直接引用了非默认格式的代码,则会有很多现象。

汉字编码回车换行编程工序可能故障规避方法
GB2312\n\r默认
GB2312\n一般没问题
UTF-8\n\r编译嵌入式注释导致编译报错

1.首末汉字前后插入多个空格

2.独立注释,后续间隔空行

UTF-8\n编译嵌入式注释导致编译报错

1.首末汉字前后插入多个空格

2.独立注释,后续间隔多个空行

UTF-8\n链接导出符号名称不正确或含有奇怪字符转换源码为\n\r存储
UTF-8运行乱码

转换源码为GB2312存储

Qt用QString::from编码临时解决

Qt用英文界面+翻译解决

UTF-8运行崩溃数字节和数字符得到的数字不一致,也不是x2关系。UTF-8存在3字节汉字。转换源码为GB2312存储
UTF-8运行意外的结果多字节汉字导致if等语句被吃掉了。转换源码为GB2312存储

3. 原因分析

UTF-8编码通过首字节连续1的个数来确定当前字符使用几个字节来表示。其原理引用网络截图如下表
在这里插入图片描述编译器用GB2312的定长2字符来处理非ASCII可见字符,而3字节以上的汉字会打破这种奇偶属性。因此,如果错误的用GB2312来解释UTF-8,对3字节以上的编码,后续的字节会“粘”到下一个正常ASCII字符的起点上去:
比如下面这个字符:

1110xxxx 10xxxxxx 10xxxxxx 00010111

本应是1个汉字+1个字母,却被解释为2个汉字。

为什么插入空格有用: 错误的UTF-8译码带来的误差,在尾部存在多个空白字符时可能被抵消,因此拥有很多尾部空格的汉字一般不会造成编译错误。

为什么\n换行比\n\r换行错误更古怪: 因为\n\r相当于2个字符,可以比\n一个字符纠正更多的解析误差。

3.1 隐晦的链接错误

隐晦的错误是多行函数定义里的注释:

/**!...巴拉巴拉...导出DLL
一堆Doxygen注释
*/
DLL_EXP_API  
/*返回值修改巴拉巴拉 2021-02*/
int 
STDCALL fun (
	int p1, /*bala*/
	int p2, /*bala*/
)
{
	//...
}

曾经出现过,因为相邻两个注释块因为字节数误差连成一体,变成了:

/**!...巴拉巴拉...
#$%^&**%乱七八糟L_-02*/
int 
STDCALL fun (
	int p1, /*bala*/
	int p2, /*bala*/
)
{
	//...
}

结果死活报链接错误!因为这个符号压根没有导出。

3.2 危险的运行时错误

被吃掉的if:

//...巴拉巴拉...
//一顿注释猛如虎
if(safe(root))/*这安全了吧*/
	if(auto_del && partion_size(root_id) >= MAX_C)
		deleteAll(root);/*一堆IF绝对安全*/

变成了:

//...巴拉巴拉...
//一顿%$全了吧*/
	if(auto_del && partion_size(root_id) >= MAX_C)
		deleteAll(root);/*芭比Q了,时怎么断到这里的?!*/

这种错误在测试时也是比较难发现的,尤其是当MAX_C相对较大的时候。
Babiq

4. 建议操作-使用Git批量维护差异

既然使用Visual Studio 作为开发工具,建议还是尽可能使用默认的编码,除非Visual Studio 向 MinGW 等第三方编译器,或者Linux平台输出库,此时建议使用UTF-8编码。复用代码要注意这些事情,有没有什么比较简单和普适的批处理方法呢?

有的,可以使用Git服务作为中转。Windows的Git可以设置编码自动转换:

user@Dev283$ git config --global core.autocrlf true

设置后,在克隆和签出等操作中,会把代码转换为windows换行符;在哈希、提交时,会转换回Linux换行符。

  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丁劲犇

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值