疑难解答(1)
疑难解答(1)
学了一段时间的C语言,我向哈基米问了几个问题
Q:我现在正在使用visual studio 2026,为什么我使用scanf的时候会显示scanf不安全?
A:
Visual Studio 给你的报错通常是 C4996,这并不是说 scanf 这个函数坏了,而是因为它有一个先天缺陷:不检查边界。
1. 缓冲区溢出(Buffer Overflow)
想象一下,你申请了一个只能装 5 个字符的数组 char a[5],但是你在 scanf("%s", a) 的时候,用户如果不听话,输入了 100 个字符。
scanf也是个“老实人”,用户给多少它就往内存里塞多少。- 结果:多出来的字符会溢出,覆盖掉数组后面原本不属于它的内存区域。这可能导致程序崩溃,甚至被黑客利用(这就是著名的“缓冲区溢出攻击”)。
2. 微软的解决方案
微软为了安全,推出了 scanf_s 等一系列带 _s (Secure) 后缀的函数。它们要求你明确告诉它“这块内存有多大”,如果输入超过这个大小,函数就会报错而不是让程序崩溃。
Q:为什么
1 | |
不会报错
为什么
1 | |
换一个顺序就又报错了?
A:
这是一个关于 C 语言预处理(Preprocessing) 机制的超级硬核知识点。
你要明白,#include <stdio.h> 并不是一句咒语,它的真实作用是:把 stdio.h 这个文件里的几千行代码,在编译前原封不动地“复制粘贴”到你的代码里。
场景 1:正确写法(#define 在前)
1 | |
发生了什么? 在 stdio.h 的源文件深处(其实是在 Microsoft 的 corecrt.h 里面),写着类似这样的逻辑:
1 | |
因为你在引入头文件之前就已经打好了招呼(定义了宏),所以头文件里的开关就被正确关闭了。
场景 2:错误写法(#include 在前)
1 | |
发生了什么? 晚了! 当你定义这个宏的时候,stdio.h 里的代码已经被处理过了,报错开关已经被打开了。这就好比你都要进门被保安拦下了,你才想起来去办通行证,那肯定来不及了。
Q:为什么一定要叫 int main(){},不能叫 int Hello(){}?
A:
1. 约定俗成的入口
想象一下,操作系统(Windows)就像是一个邮递员,你的程序就像是一栋大楼。
- 当你想运行程序时,Windows 会去你的代码里找一个特定的门牌号,这个门牌号在 C 语言标准里被规定为 **
main**。 - 如果你把函数名改成了
Hello,邮递员(操作系统)拿着信(执行权)转了一圈,发现找不到main这个门牌号,它就不知道该从哪里开始执行你的代码。
2. 链接器报错
如果你强行把 main 改成 Hello,VS 会报一个 LNK2019 或 LNK1120 错误,提示类似: unresolved external symbol _main (无法解析的外部符号 main) 这就意思是:编译器找了一圈,没找到程序的启动按钮。
3. 特殊情况(稍微超纲一点点)
其实是可以改的,但不是给初学者用的。 在做 嵌入式开发 或者 写操作系统内核 时,程序员可以通过修改“链接器脚本”来指定程序的入口是 Start() 或者是 Reset_Handler()。但在标准的 Windows 应用程序开发中,请死死守住 int main() 这个入口。