C语言程序中不包含头文件

本文通过分析三个C语言程序示例,探讨了不包含头文件的情况下,如何依赖系统默认链接标准库和特定库来执行。其中,getchar()和putchar()的隐式链接,以及编译命令中-lm和-lpthread选项对于链接libm.so和libpthread.so库的重要性被详细解释,揭示了头文件在确保编译时检查和避免运行错误上的关键作用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

   《C陷阱与缺陷》第5章库函数有一个练习题:

#define EOF -1

main()

{

       register int c;

 

       while ((c = getchar()) != EOF)

           putchar(c);

}

   这个程序在许多系统中仍然能够运行,但是在某些系统运行起来却慢得多。这是为什么?

    我们把关注点放在该程序没有包含头文件而在许多系统仍然能够运行,而不去考虑为什么在某些系统运行起来却慢得多。

    先跑一下,看该程序能否正常运行。

[root@]# cat program.c 

#define EOF -1

int main()
{
    register int c;

    while ((c = getchar()) != EOF)
        putchar(c);
}
[root@]# gcc -o program program.c 
[root@]# ./program
hello,world
hello,world


hello,root
hello,root

[root@]#   

    经验证,该程序在CentOS 5.5下运行正常。

    再来看另外一个程序。

[root@]# cat program.c 

int main()
{
    double ret;

    ret = cos(30);
    printf("ret = %.2f\n", ret);
}
[root@]# gcc -o program program.c
program.c: In function ‘main’:
program.c:6: 警告:隐式声明与内建函数 ‘cos’ 不兼容
program.c:7: 警告:隐式声明与内建函数 ‘printf’ 不兼容
/tmp/ccYZtVM3.o: In function `main':
program.c:(.text+0x1c): undefined reference to `cos'
collect2: ld 返回 1
[root@]# gcc -o program program.c -lm
program.c: In function ‘main’:
program.c:6: 警告:隐式声明与内建函数 ‘cos’ 不兼容
program.c:7: 警告:隐式声明与内建函数 ‘printf’ 不兼容
[root@]# ./program
ret = 0.15
[root@]# 
    编译命令为:gcc -o program program.c时,出现cos未定义的错误。当编译选项相对第一个示例多一个-lm时,编译成功。但有两处告警。

    继续看第三个程序示例。

[root@]# cat program.c

void *thread_func(void *arg);
char message[] = "hello, thread";

int main()
{
    int ret;
    unsigned long a_thread;
    void *thread_ret;

    ret = pthread_create(&a_thread, 0, thread_func, (void *)message);
    if (ret != 0)
    {
        perror("Thread creation failed");
        exit(1);
    }

    printf("Waiting for thread to finish...\n");
    ret = pthread_join(a_thread, &thread_ret);
    if (ret != 0)
    {
        perror("Thread join failed");
        exit(1);
    }
    printf("Thread join, it returned %s\n", (char *)thread_ret);

    return 0;
}

void *thread_func(void *arg)
{
    printf("thread_func is running. Argument was %s\n", (char *)arg);
    pthread_exit("Thank you for the CPU time");
}
[root@]# gcc -o program program.c
program.c: In function ‘main’:
program.c:15: 警告:隐式声明与内建函数 ‘exit’ 不兼容
program.c:18: 警告:隐式声明与内建函数 ‘printf’ 不兼容
program.c:23: 警告:隐式声明与内建函数 ‘exit’ 不兼容
program.c: In function ‘thread_func’:
program.c:32: 警告:隐式声明与内建函数 ‘printf’ 不兼容
/tmp/ccAEXJyb.o: In function `main':
program.c:(.text+0x21): undefined reference to `pthread_create'
program.c:(.text+0x5f): undefined reference to `pthread_join'
collect2: ld 返回 1
[root@]# gcc -o program program.c -lpthread
program.c: In function ‘main’:
program.c:15: 警告:隐式声明与内建函数 ‘exit’ 不兼容
program.c:18: 警告:隐式声明与内建函数 ‘printf’ 不兼容
program.c:23: 警告:隐式声明与内建函数 ‘exit’ 不兼容
program.c: In function ‘thread_func’:
program.c:32: 警告:隐式声明与内建函数 ‘printf’ 不兼容
[root@]# ./program
Waiting for thread to finish...
thread_func is running. Argument was hello, thread
Thread join, it returned Thank you for the CPU time
[root@]# 

    结果发现,当编译命令为:gcc -o program program.c时,出现pthread_create()和pthread_join()未定义。当编译选项相对第一个示例多一个-lpthread时,编译成功,只是还有几处告警。

    接下来,分析这三个程序。

    第一个程序主要包含了库函数getchar()和putchar()。第二个程序主要包含了库函数cos()。第三个程序主要包含了pthread_create()和pthread_join()。

    再来看下三个编译命令:

    第一个程序:gcc -o program program.c

    第二个程序:gcc -o program program.c -lm

    第三个程序:gcc -o program program.c -lpthread

    第二个和第三个编译命令相对第一个多了-lm和-lpthread选项。该选项的作用是告诉编译器去链接共享库libm.so和libpthread.so。而函数cos()就在共享库libm.so里定义,pthread_create()和pthread_join()就在共享库libpthread.so里定义。故而,在分别链接了这两个共享库之后,出现的那些未定义错误就消失了。

    那第一个程序的getchar()和putchar()呢?该程序中并没有定义它们,但为何能正确执行呢?其实,我们知道getchar()和putchar()是标准库函数。而标准库函数的共享库名称是libc.so。gcc编译器在编译时,会自动链接该共享库,故而可以不用指定链接共享库的选项。

#include文件名

库路径名

所用的编译器选项

<math.h>

/usr/lib/libm.so

-lm

<stdio.h>

/usr/lib/libc.so

自动链接

<pthread.h>

/usr/lib/pthread.so

-lpthread

    头文件主要包含一些宏定义、类型定义及函数原型。包含函数原型的作用在于编译时及时发现错误,如参数的个数、类型匹配问题。

   


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值