很多从 Java / Kotlin / Android 转到 C++ 的同学,第一次看到这种代码都会懵:
void JNIMethod(double, double, int, int) {
}
心里第一反应是:
这怎么能编译通过?参数名呢???
因为在 Java 里,方法一定要写参数名:
void f(int a, int b) {}
但在 C++ 中,参数名不是必须的。
这是语言设计上的一个根本差异。
一、结论先行(一句话记住)
C++ 函数调用只依赖“参数类型”,不依赖“参数名字”。
参数名只是给程序员看的,用于函数体内部引用;
函数签名的核心是类型、顺序、数量。
二、函数签名是什么?
函数签名(Function Signature)指的是:
- 返回类型
- 函数名
- 参数类型列表(顺序也算)
例如:
void f(int, int);
这里已经足够让编译器知道:
- 这是个 void 函数
- 名字叫 f
- 接收两个 int
即使没有参数名,函数也可以被正确调用。
三、为什么机器不需要参数名?
我们来看函数调用本质:
f(1, 2);
在底层发生的是:
CPU 根本不知道你在源码里把参数叫 a、b 还是 x、y。
对机器来说:
参数只是“值在第几个位置”,不是“名字”。
四、参数名到底有什么用?
参数名只在函数体内有意义,相当于“局部变量别名”。
有参数名
void f(int x) {
x += 1;
}
无参数名
void f(int) {
// 不能使用参数,因为没有变量名
}
参数依然存在,但你不能引用它。
五、为什么系统源码里经常省略参数名?
主要有三个原因:
1. 回调接口只关心签名
系统要求函数必须长这样,但实现里用不到参数。
void callback(int, int) {}
2. 占位实现
先写接口,后续补逻辑。
3. 避免 unused parameter 警告
写了名字却不用,编译器会报警告。
六、函数签名 vs 函数指针(最容易混淆的点)
很多人会误以为:
“这是不是函数指针?”
其实不是。
普通函数
void f(int, int);
函数指针
void (*p)(int, int);
区别在于:
| void f(int,int) | 函数定义 |
| void (*p)(int,int) | 指向函数的指针 |
函数签名是“规则”,
函数指针是“工具”。
七、C++ 函数机制四层模型(终极版)
| 函数签名 | 钥匙孔形状 | int f(int,int); | 规定函数“长什么样” |
| 函数指针类型 | 钥匙制造图纸 | using F = int(*)(int,int); | 规定钥匙“怎么造” |
| 函数指针变量 | 一把具体钥匙 | F p; | 能存函数地址、能调用 |
| 函数实现 | 门 | int f(int a,int b){} | 真正执行逻辑 |
四者关系图(从抽象到具体)
函数签名 → 决定能不能匹配
函数实现 → 提供真实能力
函数指针类型 → 规定指针长什么样
函数指针变量 → 指向某个实现并调用
用一段完整代码串起来
#include <iostream>
using namespace std;
// 1. 钥匙孔形状(函数签名)
int add(int, int);
// 2. 钥匙制造图纸(函数指针类型)
using Calc = int(*)(int,int);
// 3. 门(函数实现)
int add(int a, int b) {
return a + b;
}
int main() {
// 4. 一把具体钥匙(函数指针变量)
Calc c = add;
// 开门
cout << c(3,4) << endl;
}
为什么这套模型最稳?
因为它把两个最容易混的点拆开了:
容易混的点 1
函数签名 vs 函数指针类型
-
签名是“函数的规则”
-
指针类型是“变量的规则”
就像:
int → 整数类型
int a → 一个整数变量
容易混的点 2
函数指针类型 vs 函数指针变量
-
类型不占内存
-
变量占内存、能存地址
和 Java 的对齐(辅助记忆)
| interface 方法签名 | 函数签名 |
| 接口类型 | 函数指针类型 |
| 接口变量 | 函数指针变量 |
| 实现类方法 | 函数实现 |
最终记忆口诀
签名看形状,
类型看规格,
变量才是钥匙,
实现才是门。
八、和 Java 的类比
| interface 方法签名 | 函数签名 |
| interface 引用 | 函数指针 |
| implements | 函数实现 |
| 回调 | 指针调用 |
区别在于:
-
Java 接口是“类级别”
-
C++ 这里是“函数级别”
九、JNI 场景最典型
JNI native 方法通常必须带:
- JNIEnv*
- jobject 或 jclass
但有时实现里用不到它们,就会写:
void method(JNIEnv*, jobject, int x) {
// 只用 x
}
这是最常见的真实场景。
十、最终记忆口诀
参数类型决定函数能否调用
参数名字决定函数能否使用
一句话终极总结
C++ 允许只写参数类型不写名字,是因为:
函数调用依赖的是“参数位置和类型”,
参数名只是给程序员看的标签,而不是语义的一部分。
当你看到这种写法时,可以直接理解为:
“这是在声明函数规则,而不是在使用参数。”
这在系统源码、JNI、HAL、Binder、回调机制中非常常见。


