GCC/Java 编译问题解决指南
最近很多次碰到 GCC/Javac 向我提示编译错误(Compile-time Error) ,特地来总结一下如何更好的理解 GCC/Javac 等高级编译器的表述。
基本概念
Symbol 符号
大家可以暂且认为“符号”就是变量的意思。
声明(Declaration) 是指在程序中指明下文所需要的一个符号,但是这个符号具体的存储位置并不确定,需要编译器见到定义后才能确定。
定义 (Definition)是指 在程序中指定这个变量的实际存储位置,并确定的指明这个变量(可包含初始值)。
Type 类型
主要有下列几种Type
1. 系统自带的标量类型
整数型(1bit/8bit/16bit/32bit/64bit/128bit) 以及有符号、无符号的组合。
浮点型 float/double/long double 主要遵循IEEE 754 定义。
复数型 umm, 这个可能用的比较少,提及一下。
BOOL 型(类似整数1bit型)真/假
空 (VOID) 这个其实也是一种类型
2. 结构类型 / UNION 类型
3. 数组类型
4. 指针类型
5. 函数类型(其实函数也是一种类型)
0. 文件相关
Cannot locate .....
请你确认你的Include Directory 列表里面有没有包含这个能追溯到这个文件的路径。
譬如 如果我的 #include <openssl/rsa.h> 意思为我的 Include Directory 列表里面不应包含 openssl 这个目录,而是应该包含openssl 所在的这个目录。
1. 变量相关
Undefined symbol
这个未定义的”符号“是指我们使用了一个未定义的符号。注意这里的提示是未定义的符号,这里有两种情况:
1. 有可能这个符号已经被声明,但是没有发现他的定义。
2. 这个符号根本不存在,(连声明都没有。)
2. 函数调用/定义
Parameter Mismatch
Ambiguious Call / Ambiguious Function / Ambiguious Reference
函数的声明和对应的实际传入的参数可能不符,这里需要注意两种情况。
1 : 函数可能存在多态(Polymorphism), 即多个函数名称一致,但参数不一致。这种情况下有可能产生编译器无法自动推断出我们想要调用的对应函数
举个栗子:
我们定义了几个函数
foo (int a);
foo (long b);
但实际上我们调用的时候写的是
void bar () {
foo (0.3);
}
编译器看到这里就傻眼了,WHAT?这两个定义让我如何选择?
这里需要注意到的是这种情况比较容易发生在变量的实际参数需要经过一些Casting (强制转换)才能满足定义要求的时候,这种时候我们就需要手动指定需要用哪个,(加上强制转换)即可。
void bar () {
foo ((int) 0.3);
}
2. 函数可能在某个 .h 文件中,而我们没有引用这个.h 文件
#include <一个你没有的文件.h>
3. 函数定义可能被包裹在某个 #ifdef / #if 控制的这种可选择的部分内,然而这些宏开关并没有打开,这个时候我们就需要去仔细查看是不是我们的配置文件/Makefile/CMakeLists 选项,参数 出现了一些问题。
#ifdef SOME_THING_SHOULD_BE_OFF
int myvar;
#endif
3. 类型、结构、运算
Type mismatch
1. 仔细检查是否有 某个类型 T 和 T* , T& 混淆的情况
void foo (int &p) {
...
}
void bar () {
int samp;
foo (&samp); // 这里的 &samp 的类型是 int *, 不符合上面的定义
}
void far () {
int samp;
foo (samp); // 这里的 samp 的类型是 int &, 符合上面的定义
}
2. 确认可否进行强制转换
Java 当中注意 bool 和整数、浮点数不可以进行转换
Java 之中标量转换仅限于 (byte,char,short,int,long,float,double) 注意char是无符号,其余有符号哦。
所有有精度损失的地方在Java 中最好加上显式强制转换,以告知编译器你充分了解你在做什么。
C/C++/Java 注意浮点数整数转换的精度损失。
3. 是不是 Unsigned 和 Signed 的问题
Incomplete Type
1. 请检查是否存在这个类型的定义、声明,大部份情况都是没有定义类型或者拼写错误。
2. Forwarding 相关: 我们在一些情况下使用Type的时候,编译器要求必须有可见的定义(声明是不足够的)
这些情况例如 嵌套的结构、结构数组,某些强制转换 等等,这个时候注意不要出现循环定义。
A包含B,B包含A,编译器会直接当场晕死。
4. 链接与运行时问题
Undefined Symbol / Unresolved external symbol
这个真的遇到无数次,主要有下面几种情况
譬如我有一个变量 在 a.cpp , 然鹅使用的地方在 b.cpp
我就会在 b.cpp 写一个 EXTERN 的变量声明
a.cpp
int somevar;
b.cpp
extern int somevar;
这种情况下 b.cpp 可以进行单文件编译,但是编译之后的代码肯定没有办法通过链接(因为有 somevar 这个变量没有定义,只有声明)这种时候就需要在链接时将两个Symbol归一化,然鹅 如果我们在运行 LD (连接器)的时候没有带上 a.cpp 生成的 a.o, 那么就会出现 Undefined Symbol 这个问题,
在MSVC 上面这个叫做LNK2001, Unresolved external symbol.