C语言中的.h头文件的作用

举报
yd_221104950 发表于 2020/12/02 22:54:27 2020/12/02
【摘要】 首先C语言是一门面向过程的编程语言,它是由一系列的函数组成的。函数的使用必须遵守: 先声明后使用的原则 举个例子说明一下,下面是一个test.c的源程序: #include <stdio.h&...

首先C语言是一门面向过程的编程语言,它是由一系列的函数组成的。函数的使用必须遵守:

先声明后使用的原则

举个例子说明一下,下面是一个test.c的源程序:

#include <stdio.h>

int main(){
        sayHello();
        return 0;
}
void sayHello(){
        printf("Tom says:%s\n","Hello!");
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

我们在terminal上用gcc编译一下这个程序:

~$ gcc test.c -o test

  
 
  • 1

然后就报了如下的错误:

test.c: In function ‘main’:
test.c:4:2: warning: implicit declaration of function ‘sayHello’; did you mean ‘ftello’? [-Wimplicit-function-declaration]
  sayHello();
  ^~~~~~~~
  ftello
test.c: At top level:
test.c:7:6: warning: conflicting types for ‘sayHello’
 void sayHello(){
      ^~~~~~~~
test.c:4:2: note: previous implicit declaration of ‘sayHello’ was here
  sayHello();
  ^~~~~~~~

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

正确的做法是在调用sayHello()函数前,先声明这个函数。这里解释一下,为什么在函数调用前需要先声明,这主要是由编译器的编译过程决定的:

  1. 编译器看到了一个不认识的函数调用:sayHello()函数。编译器此时不会报错,它认为在该源文件后面会找到这个函数的详细信息,所以它记录下这个函数,随后会在文件中查找函数。
  2. 编译器需要知道函数的返回类型,因此在它记录下这个函数后,它会假设它返回int。
  3. 等到编译器看到实际函数时,发现返回的不是int,而是void,所以它就会报出“conflicting types for ‘sayHello’的错误,就是说函数的返回类型冲突了。如果sayHello的返回值是int,它就不会报错了,顶多给个警告。如果在调用前,就进行了函数声明,那么编译器在函数调用的地方就可以清楚地知道函数的返回类型,它就不用自己假设成int了,也就不会有后面的错误了,而这种假设通常很危险。这就是为什么在函数调用前必须先声明的原因。

test.c的完整且正确的程序如下。

下面这种是在源文件开头添加函数声明:

#include <stdio.h>

void sayHello();  //函数声明

int main(){
        sayHello();
        return 0;
}
void sayHello(){
        printf("Tom says:%s\n","Hello!");
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

或是下面这种调整main函数与sayHello函数的位置:

#include <stdio.h>
    
    void sayHello(){
            printf("Tom says:%s\n","Hello!");
    }
    
    int main(){
            sayHello();
            return 0;
    }

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

再次编译运行,就完全没有问题了:

~$ gcc test.c -o test
~$ ./test
Tom says:Hello!

  
 
  • 1
  • 2
  • 3

补充:运行目标程序时要加上./,为什么呢?因为在类Unix系统上,必须指定可执行文件所在的目录,除非目录已添加到PATH环境变量中。./表示当前目录下。

其中调整函数的顺序其实是一件很痛苦的事情,尤其在加入新入的函数的时候,特别容易出现上述错误。如果写一些相互递归调用的函数,它们互相调用对方,那么总有一个函数在定义 前被调用了,那如何是好?有没有一种方法既不用调整代码,又能让编译器知道函数返回类型呢?有,将函数声明都写在源文件的开头或用头文件的形式将声明与定义进行分离。我们来说说用头文件的方式。

将函数声明放在一个.h的头文件中,然后再把.h文件包含进来就可以了。
函数声明只是一个函数签名,一条包含函数名、形参类型、返回类型的记录。我们将上述函数的声明放到一个叫tong.h的头文件中去:

void sayHello();

  
 
  • 1

在test.c引入tong.h头文件

#include <stdio.h>
#include "tong.h"  //引入头文件

int main(){
        sayHello();
        return 0;
}
void sayHello(){
        printf("Tom says:%s\n","Hello!");
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

这样一来,编译器在一开始就知道函数的返回类型,就不用稍后再找了,而且防止了编译器假设函数的返回类型,可以显式地告诉它了。这种告诉编译器函数会返回什么类型的语句就叫函数声明。如果代码中有很多函数,而你又不想管它们在文件中的顺序,那么可以在源文件开头列出函数声明,甚至可以把这些函数声明放到一个头文件中,再通过include指令包含进来。
小知识:
头文件的名称用双引号括起来和用尖括号括起来的区别:
1.用双引号括起来的文件名,编译器就会在本地查找文件,如果是加上了目录的文件名,编译器就会在相对路径下查找头文件;
2.用尖括号括起来的文件名,编译器就会在标准库里找,gcc知道在哪里。在类Unix系统中,头文件一般都放在/usr/local/include 、/usr/include这些地方。

以上谈到的头文件作用是为了能够调整函数之间的顺序,以下将介绍的头文件作用是为了让其他程序知道某个函数,从而达到共享这个函数的目的:

例如我们要共享如下这个加密函数,那么我们需要想办法让其他程序知道它,这时就可以用头文件了:

void encrypt(char *message){
        while(*message){
                *message = *message ^ 31;
                message++;
        }       
}  

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

1.创建一个encrypt.h的头文件:

void encrypt(char *message);

  
 
  • 1

2.在encrypt.c中包含这个头文件,这样可以让其他程序知道encrypt()函数:

#include "encrypt.h"
void encrypt(char *message){
        while(*message){
                *message = *message ^ 31;
                message++;
        }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3.在test1.c程序中使用这个函数,只需要包含这个头文件就可以了:

#include <stdio.h>
#include "encrypt.h"

int main(){
       char c[] = "hello world";
       encrypt(c);
       printf("%s\n",c);
       return 0;
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

这样一来在主程序test1.c引入了encrypt.h,编译器就可以知道encrypt()函数,这样才能编译代码。最后,为了把所有东西编译到一起,只需要把源文件都传给gcc:

~$ gcc test1.c encrypt.c -o testEncrypt
~$ ./testEncrypt
wzssp?hpms{

  
 
  • 1
  • 2
  • 3

这样把加密程序放到了一个单独文件中,就可以在任何程序中使用它了。

至此.h头文件的第二个作用也讲完了,谢谢大家的支持。

文章来源: blog.csdn.net,作者:WongKyunban,版权归原作者所有,如需转载,请联系作者。

原文链接:blog.csdn.net/weixin_40763897/article/details/87181359

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。