Skip to content

错误解决

编译和链接的错误

要想使用VS的Debug模式来调试程序,那么首要的前提是程序能够正常启动,也就是程序的问题不能出现在"C源文件 --> C可执行程序"的过程中。

我们都知道,在这个过程中,程序会经历编译(广义)和链接两大过程,所以在讲Debug模式之前,我们先给大家讲一下C程序启动时的编译错误和链接错误。

注意

VS的Debug模式用于调试运行时的错误,而不是编译或链接错误。编译和链接错误必须在程序运行之前解决。

编译错误

编译错误(Compile-time Errors)出现在代码的阶段,表示编译失败。,比如:

  1. 包含了错误的、不存在的头文件。(预处理阶段)
  2. 忘记在语句结尾加上分号。
  3. 小括号、中括号或大括号不匹配。
  4. 类型不匹配,尝试将一个字符串赋值给一个整数类型的变量等。
  5. 使用未声明的变量。
  6. ...

在VS当中运行下列代码:

cpp
int main(void) {
    a = 100;
    return 0;
}

这就是一个典型的编译报错。可以通过查看VS的错误列表窗口来检查这个错误:

VS-编译错误列表

当然,这个过程需要明确的是:

VS中的C语言代码编译是由内嵌的编译器MSVC完成的,编译错误的报错信息也是这个编译器给出的,然后通过VS的图形界面显示。

链接错误

链接错误(Linking Errors)出现在代码的链接阶段,表示链接失败。,比如:

  1. 调用函数时,把函数的名字写错了。比如想调用printf函数,但是写成了print。
  2. 忘记包含头文件。比如使用printf函数但忘记写#include语句。
  3. 没有定义一个函数却使用它。
  4. ...

在VS当中启动下列代码:

cpp
// 没有#include <stdio.h>语句
int main(void){
    printf("hello world!\n");

    return 0;
}

这就是一个典型的链接报错。可以通过查看VS的错误列表窗口来检查这个错误:

VS-链接错误列表

和编译报错信息是编译器给出的一样,链接错误的信息也是链接器给出的。

无论是编译还是链接错误,大多都是简单的语法问题、或主观疏忽导致的。熟练的程序员,程序中即便出现这样的问题,也往往能很快速的解决。

思考:为什么这些与函数调用相关的错误是链接器发出的链接错误呢?为什么不是编译错误呢?

首先我们先谈两个关于函数的概念:

指以;分号结尾的函数头。

比如:

cpp
int test();     // 函数的声明语法

指包含函数体的一个带有实现的,真正可以调用的函数。

比如:

cpp
int test() {}       // 函数的定义语法,只要带有{}就是一个带实现可调用的函数

接下来我们思考标题的问题,这实际上还是由C程序编译和链接,生成可执行文件的过程决定的。在上一节,我们已经讲解了下图:

编译和链接过程图

链接的过程允许将多个.c源文件合并成一个可执行程序,所以:

  1. 某个.c源文件中的代码完全可以调用其它.c源文件中实现的函数
  2. 某个.c源文件中的代码完全可以调用外部库函数

所以在编译的过程中,编译器并不会检查调用的某个函数是否真正定义(即函数是否有真正的实现),最多只会检查它是否存在声明。

而到了链接阶段,链接器会将源代码中所有可能存在的外部引用进行解析,此时如果:

  1. 调用了一个完全不存在的函数或者写错了函数名
  2. 忘记包含头文件
  3. ...

链接器就无法真正将函数的定义(实现)合并到目标文件中,此时链接器就会报错。(不报错不行了,再不报错程序就执行了,但此时代码显然无法执行)

Error C4996

由于微软在Visual Studio 2013中不建议再使用C的传统库函数scanfstrcpysprintf等,所以直接使用这些库函数会提⽰C4996错误:

VS建议采用带_s的函数,如scanf_sstrcpy_s,但这些并不是标准C函数。要想继续使用scanfstrcpysprintf等函数,有三种解决办法

  1. 在源文件中添加以下指令就可以避免这个错误提⽰:
cpp
#define _CRT_SECURE_NO_WARNINGS
//这个宏定义最好要放到.c文件的第一行
  1. 或者使用这个
cpp
#pragma warning(disable:4996)
  1. 在项目设置中取消安全检查