MSVC 在简体中文 Windows 下的编码问题

前几天有人遇到了这样的问题:在编译 C 程序时,更改 extern 修饰的变量没有起作用,源文件大概如下:

// main.c
#include <stdint.h>
#include <stdio.h>

void f(); 
int x = 1;

int main()
{
	f();
	printf("%d", x);
	return 0;
}
// f.c
extern int x;

void f()
{
    // 此时这个语句将会如何执行?编译器会报错吗?链接器会报错吗?
    x = 0;
}

具体表现为程序运行输出 1 。我用 gcc 试了下,没有复现这个问题。继续追问,是在 MSVC 编译程序时出现了这个问题。我一试还真是这样!

为了研究哪里出了问题,我把程序整理成类似如上的最小单元,这个问题又神奇地消失了, amazing 。仔细看编译日志,发现了这样一行:

D:\a>cl f.c main.c
Microsoft (R) C/C++ Optimizing Compiler Version 19.34.31935 for x64
Copyright (C) Microsoft Corporation.  All rights reserved.

f.c
f.c(1): warning C4819: The file contains a character that cannot be represented in the current code page (936). Save the file in Unicode format to prevent data loss
main.c
Generating Code...
Microsoft (R) Incremental Linker Version 14.34.31935.0
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:f.exe
f.obj
main.obj

快速在 cmd 里验证一下:

D:\a>type f.c
// f.c
extern int x;

void f()
{
    // 姝ゆ椂杩欎釜璇彞灏嗕細濡備綍鎵ц锛熺紪璇戝櫒浼氭姤閿欏悧锛熼摼鎺ュ櫒浼氭姤閿欏悧锛?    x = -0.0;
}

果然是编码出了问题。从 Linux 下复制过来的时候换行符只有 LF ,这个时候在 CP936 里换行就消失了。与此对应的是, GB18030(CP54936) 和 UTF-8 (CP65001) 下都能正常工作。

CP936 是一个稍微混乱的东西,一般认为它对应的是 GBK 。详细可看 Code page 936 (Microsoft Windows)

解决方法:给 cl 打开 /utf-8 参数