C语言对于语法的检查没有C++那么严格,即使头文件中没有函数声明,编译的时候也只会报warning,但是依旧还是可以编译通过的。示例代码如下:
//myStrlen.c:
#include <stdio.h>
int myStrlen(char* myStr)
{
int len = 0, i = 0;
while(myStr[i] != '\0')
{
len++;
i++;
}
return len;
}
//main.c:
#include <stdio.h>
int main(int argc, char* argv[])
{
char* myStr = "ShiGuangluo";
int len = myStrlen(myStr);
printf("len = %d\n", len);
return 0;
}
程序编译及运行结果如下:
但是如果这套代码在C++编译器中是编译不过的,会直接报错,因为没有函数声明,结果如下:
修改main.cpp文件,在其中添加myStrlen函数的声明:
//mian.cpp:
#include <stdio.h>
extern int myStrlen(char* myStr); // 函数声明
int main(int argc, char* argv[])
{
char* myStr = (char*) "ShiGuangluo"; // C++中也需要强制类型转换 否则会报warning
int len = myStrlen(myStr);
printf("len = %d\n", len);
return 0;
}
这样编译就可以通过,不会报错了。
从这里我们可以看出:我们将函数定义放在一个文件中,在调用函数之前只需要声明一下就可以了。这里没有将函数声明写在头文件中,而是直接在用之前就临时声明一下。
那么在真正的项目开发过程中,我们为什么不直接在调用之前直接声明一下而是将函数声明写入头文件中呢?我们这么做就是为了编写代码方便,写入头文件中就不用重复的在各个.c/.cpp文件中去写函数声明。因此增加myStrlen.h头文件:
//myStrlen.h:
#include <stdio.h>
extern int myStrlen(char* myStr); // 将myStrlen函数写入头文件中用于声明
这样只需要在调用myStrlen的.c/.cpp中包含.h文件就可以了:
//main.cpp:
#include <stdio.h>
#include "myStrlen.h"
int main(int argc, char* argv[])
{
char* myStr = (char*) "ShiGuangluo"; // C++中也需要强制类型转换 否则会报warning
int len = myStrlen(myStr);
printf("len = %d\n", len);
return 0;
}
那我们能不能不写头文件直接包含实现了这个函数的.c或者.cpp文件呢?不可以,因为每次对包含的文件展开之后就会有一次函数实现,如果有好几个文件都调用了这个函数,预处理展开之后就相当于在好几个文件中都实现了一遍这个函数,因此函数就是重定义了,编译就会不通过。
那么还有一个问题啊,就是我们知道,函数和变量的定义只能有一次,但是函数的声明可以有多次。如果我在main.cpp中多次包含了同一个头文件会怎么样呢?比如下面的:
//main.cpp:
//#include <stdio.h> // 这里为了方便观察预处理之后的.i文件,我将stdio.h文件注销掉,因为他展开有一大堆东西
#include "myStrlen.h"
#include "myStrlen.h"
#include "myStrlen.h"
#include "myStrlen.h"
int main(int argc, char* argv[]) // main函数中的那些语句都不需要了 因为我主要就是观察头文件展开的结果
{
// char* myStr = (char*) "ShiGuangluo"; // C++中也需要强制类型转换 否则会报warning
// int len = myStrlen(myStr);
// printf("len = %d\n", len);
// return 0;
}
我通过g++ -E main.c -o main.i得到如下结果:
//main.i:
# 3 "myStrlen.h"
extern int myStrlen(char* myStr);
# 3 "main.cpp" 2
# 1 "myStrlen.h" 1
extern int myStrlen(char* myStr);
# 4 "main.cpp" 2
# 1 "myStrlen.h" 1
extern int myStrlen(char* myStr);
# 5 "main.cpp" 2
# 1 "myStrlen.h" 1
extern int myStrlen(char* myStr);
# 6 "main.cpp" 2
int main(int argc, char* argv[])
{
;
}
最后我们发现头文件展开之后还真的出现了四次声明,因此这也是不合理的。解决头文件重复包含的方法有两种:
1.在头文件中写上#pragma once;例如可以将myStrlen.h文件修改如下:
//myStrlen.h:
#pragma once
#include <stdio.h> // 所有头文件的包含都需要在#pragma once语句之后
extern int myStrlen(char* myStr);
2.在头文件中写上:
#ifndef _MYSTRLEN_H // _MYSTRLEN_H是根据头文件名来写的 都是大写字母
#define _MYSTRLEN_H
#endif
因此我们也可以使用这种方法来修改myStrlen.h(Qt中都是使用这种方式的):
//myStrlen.h:
#ifndef _MYSTRLEN_H
#define _MYSTRLEN_H
#include <stdio.h>
extern int myStrlen(char* myStr);
#endif