【C语言-函数-内部函数和外部函数】内部函数和外部函数——你家门口的东西,别人能不能翻墙进来拿?

开发技术 作者: 精灵星空 SKY315.COM 更新于: 浏览: 254
码字发文不易,转载请注明出处【精灵星空 SKY315.COM】

前面的课咱们把函数的基本用法都讲透了。但还有一个事儿一直没聊——这个函数是不是谁都能调?我写的函数只能在当前文件里用,还是别的文件也能用?

这就涉及到今天的话题:内部函数和外部函数。

这两个概念说起来特简单,就一句话的事:内部函数只能在当前文件里用,外部函数可以被别的文件调用。

先看外部函数。

实际上,你之前写的所有函数,默认都是外部函数。啥意思?就是别的文件如果想调用你的函数,是可以的,只要人家知道你的函数长啥样。

你想啊,printf、scanf这些函数,它们不在你写的代码里,但你照样能调用它们。为啥?因为它们就是外部函数——它们定义在C标准库里,通过#include <stdio.h>把头文件引进来,你就能用了。

你自定义的函数,默认也是外部函数:

// file1.c
int add(int a, int b) {    // 默认就是外部函数
    return a + b;
}
// file2.c
#include <stdio.h>

int add(int a, int b);   // 声明一下:这个函数在别处

int main() {
    int z = add(3, 5);   // 能调用到file1.c里的add
    printf("%d\n", z);
    return 0;
}

你只要在file2.c里声明一下add函数的存在,编译器在编译file2.c的时候知道“哦有这号人,具体的链接的时候再说”。链接的时候把两个文件拼在一起,就找到了file1.c里的add。

外部函数的关键词是extern,不过你写不写都一样,因为函数默认就是extern的。你也可以明确写出来:

extern int add(int a, int b) {   // 跟不写extern效果一样
    return a + b;
}

那什么是内部函数?

内部函数就是只允许当前文件使用的函数。别的文件看不到它,也调不了它。想实现这个效果,就在函数定义前面加个static。

// file1.c
static int helper(int x) {   // 内部函数,只能在本文件用
    return x * 2;
}

int public_add(int a, int b) {
    return helper(a) + helper(b);   // 可以调用,因为在一个文件里
}
// file2.c
#include <stdio.h>

int helper(int x);   // 声明也没用,链接的时候找不到

int main() {
    int z = helper(5);   // 链接错误!找不到helper
    return 0;
}

你看,file2.c里明明声明了helper,但链接的时候找不到它的定义,因为file1.c里的helper被static修饰了,它锁在了file1.c里面,外面看不见。

这就是内部函数的作用:把函数封装在文件内部,防止被别的文件乱调。

你可能要问:我啥时候需要把函数声明成内部函数呢?

前面咱们学存储类别的时候讲过static修饰全局变量,是为了“封装”,不让别的文件乱动。static修饰函数也是同样的道理。

在实际开发中,你通常会这么组织代码:你写了一个模块(比如一个数学工具包),对外只暴露几个核心函数让别人用,但内部有一些辅助函数(比如一些计算的子步骤)就不想让人知道,也不想让人乱调。这时候那些辅助函数你就加上static,把它们“藏”起来。

这样有几个好处:

第一,接口清晰。别人看你这个文件,只看那些没加static的函数就知道“哦,这些是能用的”,那些static的就不用管。

第二,防止命名冲突。如果你的内部函数名字跟别的文件里的函数重名了,因为它加了static,只在当前文件可见,所以不会跟别的文件冲突。

第三,封装性好。你内部怎么实现的,外面不用管,外面只调用你暴露的接口就行。

你在实际项目里看一些开源代码,里面很多static函数。这可不是为了“神秘”,而是为了“干净”。

那“内部”和“外部”到底对应咱们之前学过的啥?其实就是看有没有static:

  • 外部函数:没有static修饰。默认就是外部。作用域是整个程序,别的文件通过extern声明就能调用。

  • 内部函数:有static修饰。作用域只限当前文件,别的文件调不了。

你看下面这段代码,猜猜编译会不会报错?

// a.c
#include <stdio.h>

static void say_hello() {
    printf("Hello from a.c\n");
}

void call_hello() {
    say_hello();   // 可以,同一个文件里
}
// b.c
#include <stdio.h>

void say_hello();   // 声明

int main() {
    say_hello();   // 编译能过,但链接会报错
    return 0;
}

结果:编译两个文件的时候,a.c和b.c各自编译成目标文件都没问题。但链接的时候,b.c里调用了say_hello,链接器在全局符号表里找不到它,因为a.c里的say_hello加了static,它只存在于a.c内部,不暴露给链接器。所以链接报错。

这就是内部函数的效果。

你还可以把函数的声明也加上static,这样声明和定义都限制在当前文件:

// file1.c
static int helper(int x);   // 声明成内部函数

static int helper(int x) {  // 定义也是内部
    return x * 2;
}

int public_add(int a, int b) {
    return helper(a) + helper(b);
}

这样写更一致,让人一看就明白“这是个内部函数,别想着外面调”。

外部函数还有个情况你可能碰到:你在file1.c里定义了一个函数void print_msg(),在file2.c里想用。你通常会在file2.c里写extern void print_msg();来声明它。但其实你写不写extern都行,函数声明默认就是extern的。

// file2.c
void print_msg();   // 不加extern,也是外部声明,因为函数默认外部
int main() {
    print_msg();
    return 0;
}

不过为了更清晰地表达“这个函数定义在别的文件里”,我建议你还是写上extern,这样看代码的人一目了然。

最后总结一下这节课的核心:

  • 外部函数:没加static,默认。整个程序可见,别的文件可以调用。

  • 内部函数:加static,只在当前文件可见,别的文件调不了。

  • 内部函数的好处:封装内部实现、防止命名冲突、让接口更清晰。

  • 函数默认是外部的,你想让它变成内部的才要加static。

实际开发中,建议你把那些不打算让外界调用的辅助函数全都加上static,这算是个好习惯。

好了,同学们回去之后自己建两个.c文件,写一个内部函数和一个外部函数,分别试试能不能跨文件调用。