【C语言进阶剖析】43、函数的声明和定义

1 声明和定义

  • 声明的意思在于告诉编译器程序单元的存在,定义则明确指示程序单元的意义
  • C 语言中通过 extern 进行程序单元的声明
  • 一些程序单元在声明时可以省略 extern

注意:在 C 语言中,当有多个源文件的时候,编译器编译文件的顺序是不确定的。所以不能编写依赖于编译顺序的代码。

2 实例分析

// 43-1.c
#include<stdio.h>
#include<malloc.h>
extern int g_var;
extern struct Test;
int main()
{
    extern void f(int i, int j);
    extern int g(int x);
    struct Test* p = NULL;
    printf("%p\n", p);
    printf("g_var = %d\n", g_var);
    f(1, 2);
    printf("g(3) = %d\n", g(3));
    free(p);
    return 0;
}
// global.c
#include<stdio.h>
int g_var = 10;
struct Test
{
    int x;
    int y;
};
void f(int i, int j)
{
    printf("i + j = %d\n", i + j);
}
int g(int x)
{
    return (int)(2 * x + g_var);
}

编译运行:

$ gcc 43-1.c global.c -o 43-1
43-1.c:5:15: warning: useless storage class specifier in empty declaration
 extern struct Test;
               ^~~~
$ ./43-1
(nil)
g_var = 10
i + j = 3
g(3) = 16

可以看到,变量 g_var,结构体 struct Test,函数 f(int i, int j) 和函数 int g(int x) 在文件 global.c 中定义,语句 extern struct Test 之前的 extern 可以去掉。

  • 问题1:将文件 global.c 中的 int g_var = 10; 改成 float g_var = 10; 重新编译运行。
gcc 43-1.c global.c -o 43-1
$ ./43-1
(nil)
g_var = 1092616192
i + j = 3
g(3) = 16

发现打印出来的 g_var 时一个很大的数字,这是为什么呢?
因为 g_var 的定义是 float 类型,float 类型在内存的存储方式与 int 类型在内存的存储方式是不一样。以float 存储的数据再以 int 方式打印,当然取出的不是我们想要的数据了。

  • 问题2:将 43-1.c 中的第 10 行的 struct Test* p = NULL; 改为:struct Test* p = (struct Test*)malloc(sizeof(struct Test)); 再次编译
$ gcc 43-1.c global.c -o 43-1
43-1.c: In function ‘main’:
43-1.c:11:50: error: invalid application of ‘sizeof’ to incomplete type ‘struct Test’
     struct Test* p = (struct Test*)malloc(sizeof(struct Test));
                                                  ^~~~~~

编译器告诉我们这时候的 sizeof 中的 struct Test 是不完整的,为什么呢?
在多个源文件一起编译,我们不能确定每个文件的编译顺序,在编译文件 43-1.c 时,使用了 sizeof(struct Test),此时编译器并不知道结构体 struct Test 是什么样子的,所以不能求解出内存大小,无法分配空间。

!!!注意:绝对不能编写依赖于编译顺序的程序。

发布了248 篇原创文章 · 获赞 115 · 访问量 9万+

猜你喜欢

转载自blog.csdn.net/happyjacob/article/details/103809675