1 函数的声明和定义

  • 声明的意义在于告诉编译器程序单元的存在。只是告诉编译器它存在但是不在声明这里定义,有可能在当前文件中的其他地方或者其他文件中定义。如果在它还没有被定义之前就使用它,会导致编译错误
  • 定义则明确表示程序单元的意义
  • C语言中,通过关键字extern进行程序单元的声明。现在的编译器可以省略不写,大多时候都是直接省略。

注意:在C语言中,当有多个源文件的时候,编译器共同编译这些源文件的顺序是不确定的。有可能先编译A文件,也有可能先编译B文件。这一点在下面的代码中会有体现。

1.1 代码分析

  • 代码 lyy.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; // (struct Test*)malloc(sizeof(struct Test));
    
    printf("p = %p\n", p);
    
    //g_var = 10;
    
    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);
}

分析:

上述代码中,g_var和Test都是在global.c中定义的。在lyy.c中只是声明。上述代码编译运行会是正确的结果。

  • 问题1:

但是,如果将g_var在global.c中的定义改为:float g_var = 10;的话,再编译运行上述代码的结果就是打印g_var的值是一个很大的随机值。这是为什么呢?

因为g_var 的定义是float类型,float类型在内存的存储方式与int类型在内存的存储方式是不一样。具体参见float的内存存储方式:【C语言进阶深度学习记录】三 浮点数(float) 在内存中的表示方法 .所以最终以int的方式打印g_var的时候,由于它本身在内存的存储方式导致打印出一个很大的数。

  • 问题2:

如果将lyy.c中的这一行:struct Test* p = NULL;改为:struct Test* p = (struct Test*)malloc(sizeof(struct Test)); 那么再次编译程序就会报错:

这个错误是说Test是不完整类型。为什么会这样呢?在本文的刚开始已经说明:多个源文件一起编译,各个源文件的编译顺序是不确定的。在这里,很明显,在编译lyy.c的13行的时候,使用了sizeof求解Test结构体的大小,但是Test结构体此时由于编译器还没有编译到global.c,那么Test就是未定义的,根本无法使用sizeof求解它的大小所以编译器报错。

2 总结

  • 声明和定义是不同的