-
2020-01-29 17:33:40
JavaScript函数参数缺省值
本文介绍Es6中如何处理函数的缺省参数。
1. 实参与形参(Argument vs. Parameter)
通常我们不区分实参与形参(Argument vs. Parameter),形参(Parameter)是函数定义时声明的参数,而实参(Argument)是调用函数时传入的参数。
请看下面add()函数:
function add(x,y) { return x + y; } add(100,200);
x,y是形参,100,200是实参。
2. 函数形式参数缺省值
JavaScript中,形式参数默认缺省值为
undefined
,即如果不传入实参给函数,则取缺省值为undefined
。请看示例:function foo(bar) { console.log(bar); } foo(); // undefined
foo()函数带bar形式参数。因调用时没有传入任何实际参数,故bar的实际值为
undefined
。
假设给bar参数设置缺省值为10,为了测试缺省值可以传入undefined
。function foo(bar) { bar = typeof bar !== 'undefined' ? bar : 10; console.log(bar); } foo(); // 10
上面示例没有给bar任何值,因此值为
undefined
。然而在函数内部检测如果值为undefined
则赋值为10,因此输出10.ES6提供更简单方法设置参数缺省值,语法如下:
function function_name(param1=value1,param2=value2,..) { }
我们看到,使用
=
给参数设置缺省值。请看示例:function foo(bar = 10) { console.log(bar); } foo(); // 10 foo(undefined); // 10 foo(20); //20
说明如下:
- 第一次函数调用,给函数 foo()的参数任何值 , 因此bar的缺省值为 10.
- 第二次调用, 给 foo() 函数传入
undefined
, 因此bar的缺省值仍为 10. - 第三次调用, 给函数 foo()的参数值设置为20 , 因此bar的缺省值为 20.
3. 示例说明参数缺省值
本节通过几个示例看看设置缺省值的不同方式。
- 传入 undefined
下面 createDiv() 函数在文档中创建div元素,参数都带默认值:
function createDiv(height = '100px', width = '100px', border = 'solid 1px red') { let div = document.createElement('div'); div.style.height = height; div.style.width = width; div.style.border = border; document.getElementsByTagName('body')[0].appendChild(div); return div; }
下面调用使用缺省参数:
createDiv();
如果想使用height和width的缺省参数,仅传入border参数。这时你需要传入
undefined
值给height和width:createDiv(undefined,undefined,'solid 5px blue');
- 评估缺省值
JavaScript引擎在函数调用时评估缺省值,请看示例:
function put(toy, toyBox = []) { toyBox.push(toy); return toyBox; } console.log(put('Toy Car')); // -> ['Toy Car'] console.log(put('Teddy Bear')); // -> ['Teddy Bear'], not ['Toy Car','Teddy Bear']
该函数参数带缺省值,即函数返回结果值。
再看另一个示例:
function date(d = today()) { console.log(d); } function today() { return (new Date()).toLocaleDateString("en-US"); } date();
date() 函数带一个参数,其缺省值是today()函数的返回值。today()函数返回今天日期的字符串表示。
当声明date()函数时,today()函数还没有执行评估直到我们调用date()函数。我们可以使用这个特性使得参数为必传参数。如果没有传入参数则抛错误:
function requiredArg() { throw new Error('The argument is required'); } function add(x = requiredArg(), y = requiredArg()){ return x + y; } add(10); // error add(10,20); // OK
- 缺省值中使用其他形式参数
参数缺省值可以引用其他缺省参数:
function bar(x = 1, y = x, z = x + y) { return x + y + z; } console.log(bar()); // 4
bar函数解释:
- y缺省值为x参数.
- z缺省值为x和y参数之和.
- bar() 函数返回 x, y, 和 z之和.
参数列表有其自己范围。如果引用参数没有初始化会报错:
function bar( x = y, y = 1 ) { return x - y; } bar(10);
错误信息:
Uncaught ReferenceError: Cannot access 'y' before initialization
- 使用函数作为缺省值
我们可以使用函数返回值作为参数缺省值:
let taxRate = () => 0.1; let getPrice = function( price, tax = price * taxRate() ) { return price + tax; } let fullPrice = getPrice(100); console.log(fullPrice); // 110
在getPrice()函数中通过调用 taxRate() 函数获得税率作为tax参数值并计算价格。
- 实参(arguments)对象
arguments对象值在函数内部是实际传入参数的数量:
function foo(x, y = 1, z = 2) { console.log( arguments.length ); return x + y + z; } foo(10); // 1 foo(10, 20); // 2 foo(10, 20, 30); // 3
4. 总结
本文首先介绍形参与实参,然后介绍Es6函数缺省值用法并通过几个示例说明缺省值的深入用法。希望这些知识能让你开发更加灵活的函数。
更多相关内容 -
函数参数的默认值
2018-01-21 21:59:47函数参数的默认值 基本用法 ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。 function log(x, y) { y = y || 'World'; console.log(x, y);}log('Hello') // Hello Worldlog('Hello', 'China...函数参数的默认值
基本用法
ES6 之前,不能直接为函数的参数指定默认值,只能采用变通的方法。
function log(x, y) {
y = y || 'World';
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello World
上面代码检查函数
log
的参数y
有没有赋值,如果没有,则指定默认值为World
。这种写法的缺点在于,如果参数y
赋值了,但是对应的布尔值为false
,则该赋值不起作用。就像上面代码的最后一行,参数y
等于空字符,结果被改为默认值。为了避免这个问题,通常需要先判断一下参数
y
是否被赋值,如果没有,再等于默认值。if (typeof y === 'undefined') {
y = 'World';
}
ES6 允许为函数的参数设置默认值,即直接写在参数定义的后面。
function log(x, y = 'World') {
console.log(x, y);
}
log('Hello') // Hello World
log('Hello', 'China') // Hello China
log('Hello', '') // Hello
可以看到,ES6 的写法比 ES5 简洁许多,而且非常自然。下面是另一个例子。
function Point(x = 0, y = 0) {
this.x = x;
this.y = y;
}
const p = new Point();
p // { x: 0, y: 0 }
除了简洁,ES6 的写法还有两个好处:首先,阅读代码的人,可以立刻意识到哪些参数是可以省略的,不用查看函数体或文档;其次,有利于将来的代码优化,即使未来的版本在对外接口中,彻底拿掉这个参数,也不会导致以前的代码无法运行。
参数变量是默认声明的,所以不能用
let
或const
再次声明。function foo(x = 5) {
let x = 1; // error
const x = 2; // error
}
上面代码中,参数变量
x
是默认声明的,在函数体中,不能用let
或const
再次声明,否则会报错。使用参数默认值时,函数不能有同名参数。
// 不报错
function foo(x, x, y) {
// ...
}
// 报错
function foo(x, x, y = 1) {
// ...
}
// SyntaxError: Duplicate parameter name not allowed in this context
另外,一个容易忽略的地方是,参数默认值不是传值的,而是每次都重新计算默认值表达式的值。也就是说,参数默认值是惰性求值的。
let x = 99;
function foo(p = x + 1) {
console.log(p);
}
foo() // 100
x = 100;
foo() // 101
上面代码中,参数
p
的默认值是x + 1
。这时,每次调用函数foo
,都会重新计算x + 1
,而不是默认p
等于 100。与解构赋值默认值结合使用
参数默认值可以与解构赋值的默认值,结合起来使用。
function foo({x, y = 5}) {
console.log(x, y);
}
foo({}) // undefined 5
foo({x: 1}) // 1 5
foo({x: 1, y: 2}) // 1 2
foo() // TypeError: Cannot read property 'x' of undefined
上面代码只使用了对象的解构赋值默认值,没有使用函数参数的默认值。只有当函数
foo
的参数是一个对象时,变量x
和y
才会通过解构赋值生成。如果函数foo
调用时没提供参数,变量x
和y
就不会生成,从而报错。通过提供函数参数的默认值,就可以避免这种情况。function foo({x, y = 5} = {}) {
console.log(x, y);
}
foo() // undefined 5
上面代码指定,如果没有提供参数,函数
foo
的参数默认为一个空对象。下面是另一个解构赋值默认值的例子。
function fetch(url, { body = '', method = 'GET', headers = {} }) {
console.log(method);
}
fetch('http://example.com', {})
// "GET"
fetch('http://example.com')
// 报错
上面代码中,如果函数
fetch
的第二个参数是一个对象,就可以为它的三个属性设置默认值。这种写法不能省略第二个参数,如果结合函数参数的默认值,就可以省略第二个参数。这时,就出现了双重默认值。function fetch(url, { body = '', method = 'GET', headers = {} } = {}) {
console.log(method);
}
fetch('http://example.com')
// "GET"
上面代码中,函数
fetch
没有第二个参数时,函数参数的默认值就会生效,然后才是解构赋值的默认值生效,变量method
才会取到默认值GET
。作为练习,请问下面两种写法有什么差别?
// 写法一
function m1({x = 0, y = 0} = {}) {
return [x, y];
}
// 写法二
function m2({x, y} = { x: 0, y: 0 }) {
return [x, y];
}
上面两种写法都对函数的参数设定了默认值,区别是写法一函数参数的默认值是空对象,但是设置了对象解构赋值的默认值;写法二函数参数的默认值是一个有具体属性的对象,但是没有设置对象解构赋值的默认值。
// 函数没有参数的情况
m1() // [0, 0]
m2() // [0, 0]
// x 和 y 都有值的情况
m1({x: 3, y: 8}) // [3, 8]
m2({x: 3, y: 8}) // [3, 8]
// x 有值,y 无值的情况
m1({x: 3}) // [3, 0]
m2({x: 3}) // [3, undefined]
// x 和 y 都无值的情况
m1({}) // [0, 0];
m2({}) // [undefined, undefined]
m1({z: 3}) // [0, 0]
m2({z: 3}) // [undefined, undefined]
参数默认值的位置
通常情况下,定义了默认值的参数,应该是函数的尾参数。因为这样比较容易看出来,到底省略了哪些参数。如果非尾部的参数设置默认值,实际上这个参数是没法省略的。
// 例一
function f(x = 1, y) {
return [x, y];
}
f() // [1, undefined]
f(2) // [2, undefined])
f(, 1) // 报错
f(undefined, 1) // [1, 1]
// 例二
function f(x, y = 5, z) {
return [x, y, z];
}
f() // [undefined, 5, undefined]
f(1) // [1, 5, undefined]
f(1, ,2) // 报错
f(1, undefined, 2) // [1, 5, 2]
上面代码中,有默认值的参数都不是尾参数。这时,无法只省略该参数,而不省略它后面的参数,除非显式输入
undefined
。如果传入
undefined
,将触发该参数等于默认值,null
则没有这个效果。function foo(x = 5, y = 6) {
console.log(x, y);
}
foo(undefined, null)
// 5 null
上面代码中,
x
参数对应undefined
,结果触发了默认值,y
参数等于null
,就没有触发默认值。函数的 length 属性
指定了默认值以后,函数的
length
属性,将返回没有指定默认值的参数个数。也就是说,指定了默认值后,length
属性将失真。(function (a) {}).length // 1
(function (a = 5) {}).length // 0
(function (a, b, c = 5) {}).length // 2
上面代码中,
length
属性的返回值,等于函数的参数个数减去指定了默认值的参数个数。比如,上面最后一个函数,定义了 3 个参数,其中有一个参数c
指定了默认值,因此length
属性等于3
减去1
,最后得到2
。这是因为
length
属性的含义是,该函数预期传入的参数个数。某个参数指定默认值以后,预期传入的参数个数就不包括这个参数了。同理,后文的 rest 参数也不会计入length
属性。(function(...args) {}).length // 0
如果设置了默认值的参数不是尾参数,那么
length
属性也不再计入后面的参数了。(function (a = 0, b, c) {}).length // 0
(function (a, b = 1, c) {}).length // 1
作用域
一旦设置了参数的默认值,函数进行声明初始化时,参数会形成一个单独的作用域(context)。等到初始化结束,这个作用域就会消失。这种语法行为,在不设置参数默认值时,是不会出现的。
var x = 1;
function f(x, y = x) {
console.log(y);
}
f(2) // 2
上面代码中,参数
y
的默认值等于变量x
。调用函数f
时,参数形成一个单独的作用域。在这个作用域里面,默认值变量x
指向第一个参数x
,而不是全局变量x
,所以输出是2
。再看下面的例子。
let x = 1;
function f(y = x) {
let x = 2;
console.log(y);
}
f() // 1
上面代码中,函数
f
调用时,参数y = x
形成一个单独的作用域。这个作用域里面,变量x
本身没有定义,所以指向外层的全局变量x
。函数调用时,函数体内部的局部变量x
影响不到默认值变量x
。如果此时,全局变量
x
不存在,就会报错。function f(y = x) {
let x = 2;
console.log(y);
}
f() // ReferenceError: x is not defined
下面这样写,也会报错。
var x = 1;
function foo(x = x) {
// ...
}
foo() // ReferenceError: x is not defined
上面代码中,参数
x = x
形成一个单独作用域。实际执行的是let x = x
,由于暂时性死区的原因,这行代码会报错”x 未定义“。如果参数的默认值是一个函数,该函数的作用域也遵守这个规则。请看下面的例子。
let foo = 'outer';
function bar(func = () => foo) {
let foo = 'inner';
console.log(func());
}
bar(); // outer
上面代码中,函数
bar
的参数func
的默认值是一个匿名函数,返回值为变量foo
。函数参数形成的单独作用域里面,并没有定义变量foo
,所以foo
指向外层的全局变量foo
,因此输出outer
。如果写成下面这样,就会报错。
function bar(func = () => foo) {
let foo = 'inner';
console.log(func());
}
bar() // ReferenceError: foo is not defined
上面代码中,匿名函数里面的
foo
指向函数外层,但是函数外层并没有声明变量foo
,所以就报错了。下面是一个更复杂的例子。
var x = 1;
function foo(x, y = function() { x = 2; }) {
var x = 3;
y();
console.log(x);
}
foo() // 3
x // 1
上面代码中,函数
foo
的参数形成一个单独作用域。这个作用域里面,首先声明了变量x
,然后声明了变量y
,y
的默认值是一个匿名函数。这个匿名函数内部的变量x
,指向同一个作用域的第一个参数x
。函数foo
内部又声明了一个内部变量x
,该变量与第一个参数x
由于不是同一个作用域,所以不是同一个变量,因此执行y
后,内部变量x
和外部全局变量x
的值都没变。如果将
var x = 3
的var
去除,函数foo
的内部变量x
就指向第一个参数x
,与匿名函数内部的x
是一致的,所以最后输出的就是2
,而外层的全局变量x
依然不受影响。var x = 1;
function foo(x, y = function() { x = 2; }) {
x = 3;
y();
console.log(x);
}
foo() // 2
x // 1
应用
利用参数默认值,可以指定某一个参数不得省略,如果省略就抛出一个错误。
function throwIfMissing() {
throw new Error('Missing parameter');
}
function foo(mustBeProvided = throwIfMissing()) {
return mustBeProvided;
}
foo()
// Error: Missing parameter
上面代码的
foo
函数,如果调用的时候没有参数,就会调用默认值throwIfMissing
函数,从而抛出一个错误。从上面代码还可以看到,参数
mustBeProvided
的默认值等于throwIfMissing
函数的运行结果(注意函数名throwIfMissing
之后有一对圆括号),这表明参数的默认值不是在定义时执行,而是在运行时执行。如果参数已经赋值,默认值中的函数就不会运行。另外,可以将参数默认值设为
undefined
,表明这个参数是可以省略的。function foo(optional = undefined) { ··· }
-
函数指针作为函数参数及函数作为函数参数
2018-12-02 10:47:07使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数。而那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会...转载于:http://blog.csdn.net/vlily/article/details/7244682
转载于:http://blog.csdn.net/shengnan_wu/article/details/8116935
转载于:http://blog.csdn.net/callmeback/article/details/4242260/
转载于:https://blog.csdn.net/initiallysunny/article/details/53708466
C++中一个函数作为作为另一个函数的参数:
把函数名作为参数就是函数的地址了.
要将函数名作为参数,需要使用函数指针。函数指针的定义格式为
ret_type (*var_name)(arg_list);
表示返回值为ret_type,参数列表为arg_list的函数指针var_name.
如
int (*p)(int,int);
表示返回值为int,参数为两个int型的函数指针p。
以函数指针作为形参,即可实现函数名作为参数,由另一个函数调用。
一、 定义某一函数的指针类型:
就像自定义数据类型一样,我们也可以先定义一个函数指针类型,然后再用这个类型来申明函数指针变量。
我先给你一个自定义数据类型的例子。
typedef int* PINT; //为int* 类型定义了一个PINT的别名
int main()
{
int x;
PINT px=&x; //与int * px=&x;是等价的。PINT类型其实就是int * 类型
*px=10; //px就是int*类型的变量
return 0;
}
根据注释,应该不难看懂吧!(虽然你可能很少这样定义使用,但以后学习Win32编程时会经常见到的。)
下面我们来看一下函数指针类型的定义及使用:(请与上对照!)
//自行包含头文件
void MyFun(int x); //此处的申明也可写成:void MyFun( int );
typedef void (*FunType)(int ); //这样只是定义一个函数指针类型
FunType FunP; //然后用FunType类型来申明全局FunP变量
int main(int argc, char* argv[])
{
//FunType FunP; //函数指针变量当然也是可以是局部的 ,那就请在这里申明了。
MyFun(10);
FunP=&MyFun;
(*FunP)(20);
return 0;
}
void MyFun(int x)
{
printf(“%d\n”,x);
}
看黑体部分:
首先,在void (*FunType)(int ); 前加了一个typedef 。这样只是定义一个名为FunType函数指针类型,而不是一个FunType变量。
然后,FunType FunP; 这句就如PINT px;一样地申明一个FunP变量。
其它相同。整个程序完成了相同的事。
这样做法的好处是:
有了FunType类型后,我们就可以同样地、很方便地用FunType类型来申明多个同类型的函数指针变量了。如下:
FunType FunP2;
FunType FunP3;
//……
二、 函数指针作为某个函数的参数
既然函数指针变量是一个变量,当然也可以作为某个函数的参数来使用的。所以,你还应知道函数指针是如何作为某个函数的参数来传递使用的。
给你一个实例:
要求:我要设计一个CallMyFun函数,这个函数可以通过参数中的函数指针值不同来分别调用MyFun1、MyFun2、MyFun3这三个函数(注:这三个函数的定义格式应相同)。
实现:代码如下:
//自行包含头文件
void MyFun1(int x);
void MyFun2(int x);
void MyFun3(int x);
typedef void (*FunType)(int ); //②. 定义一个函数指针类型FunType,与①函数类型一至
void CallMyFun(FunType fp,int x);
int main(int argc, char* argv[])
{
CallMyFun(MyFun1,10); //⑤. 通过CallMyFun函数分别调用三个不同的函数
CallMyFun(MyFun2,20);
CallMyFun(MyFun3,30);
}
void CallMyFun(FunType fp,int x) //③. 参数fp的类型是FunType。
{
fp(x);//④. 通过fp的指针执行传递进来的函数,注意fp所指的函数是有一个参数的
}
void MyFun1(int x) // ①. 这是个有一个参数的函数,以下两个函数也相同
{
printf(“函数MyFun1中输出:%d\n”,x);
}
void MyFun2(int x)
{
printf(“函数MyFun2中输出:%d\n”,x);
}
void MyFun3(int x)
{
printf(“函数MyFun3中输出:%d\n”,x);
}
输出结果:略
-
函数指针是指向函数的指针变量。c在编译时,每一个函数都有一个入口地址,该入口地址就是函数指针指向的地址处。有了指向函数的指针变量后。可以通过该指针变量调用函数,函数指针有两个用途:调用函数、做函数的参数:
-
调用函数,如下所示:
-
输出结果如下:
由以上可以说明成功调用。
b.无参函数指针做参数的实现,如下(标准写法)所示:
输出结果如下:
还有以下写法也能成功,因为c语言标准规定可以这样用:
也能成功输出
c.带参有返回值的函数指针做参数的
输出结果如下:
而不能写成如下所示:
也可写成以下形式,其中涉及到函数指针类型的转换:
2.函数指针数组的实用之处:当我们需要判断大量条件的时候,并且在每一个条件都有相应的处理函数,这时实用switch...case..的代码量会很大,并且效率会比较低,这个时候就可以使用函数指针数组来解决这个问题了,可以使用每个条件为数组下表:如下所示:
结果如下
回调函数
1、基础知识
所谓回调,就是模块A要通过模块B的某个函数b()完成一定的功能,但是函数b()自己无法实现全部功能,需要反过头来调用模块A中的某个函数a()来完成,这个a()就是回调函数。如下图
①约定接口规范。在模块B必须约定接口规范,也就是定义回调函数a()的函数原型
一开始是不好理解,用下面这个例子可能会有帮助:
诸葛亮(A)给赵子龙(B)一个锦囊(a()),吩咐他在干活时(b())若遇到危急时打开按锦囊(a())指示办, 锦囊里的命令就是回调函数,危急时刻就是回调的时机。
不同的锦囊里可以有不同的命令。在看LWIP时,见到用回调函数,再看某老外公司OPC源代码时,见到用回调函数。看我国内某些代码(我公司软件等)时没用到。于是,我对回调函数产生了很大的好奇。以前,我写VC程序时用到过回调函数,但是没有用C语言来使用。最近,看到国外大量的经典代码中广泛使用了回调函数(LWIP、某两个公司的OPC程序等),都是C语言来实现的,而不是VC windows程序中别人实现自己使用的那种。
为了弄明白这种函数的奥妙,首先提出三个问题:
1. 回调函数是什么东西?
2. 回调函数怎么开发,怎么使用?
3. 回调函数的作用,应该在什么情况下使用?
带着问题来学习,有目的!呵呵,个人经验。
打开baidu.com、google.cn搜索了好多资料,如下:
顺便提一下,某君的一个签名很让我佩服:1好好活着,因为我们会死很久。2五千年的文明 两百年的无奈
第一个问题:
*******************************************************************************
其实回调就是一种利用函数指针进行函数调用的过程.
为什么要用回调呢?比如我要写一个子模块给你用, 来接收远程socket发来的命令.当我接收到命令后, 需要调用你的主模块的函数, 来进行相应的处理.但是我不知道你要用哪个函数来处理这个命令, 我也不知道你的主模块是什么.cpp或者.h, 或者说, 我根本不用关心你在主模块里怎么处理它, 也不应该关心用什么函数处理它...... 怎么办?
使用回调!
—— lone wolf
使用回调函数实际上就是在调用某个函数(通常是API函数)时,将自己的一个函数(这个函数为回调函数)的地址作为参数传递给那个函数。而那个函数在需要的时候,利用传递的地址调用回调函数,这时你可以利用这个机会在回调函数中处理消息或完成一定的操作。
—— 某专家
回调函数,就是由你自己写的。你需要调用另外一个函数,而这个函数的其中一个参数,就是你的这个回调函数名。这样,系统在必要的时候,就会调用你写的回调函数,这样你就可以在回调函数里完成你要做的事。
—— 绿叶
http://hi.baidu.com/zhuyipeng/blog/item/863fefdb7c736c63d1164eec.html 是一篇比较好的文章。
什么是回调函数?
回调函数是应用程序提供给Windows系统DLL或其它DLL调用的函数,一般用于截获消息、获取系统信息或处理异步事件。应用程序把回调函数的地址指针告诉DLL,而DLL在适当的时候会调用该函数。回调函数必须遵守事先规定好的参数格式和传递方式,否则DLL一调用它就会引起程序或系统的崩溃。通常情况下,回调函数采用标准WindowsAPI的调用方式,即__stdcall,当然,DLL编制者可以自己定义调用方式,但客户程序也必须遵守相同的规定。在__stdcall方式下,函数的参数按从右到左的顺序压入堆栈,除了明确指明是指针或引用外,参数都按值传递,函数返回之前自己负责把参数从堆栈中弹出。
理解回调函数!—— jufengfeng
Function Pointers provide the concept of callback functions.
—— newty.de
*******************************************************************************
看了这么多的资料,我只将每位的定义总结一下就一句话:回调函数就是函数指针的一种用法。
在部分资料上,大量讨论了回调函数怎么被调用,到底被谁调用,还有好多的图形,我认为都没有看到问题的本质。
第二个问题:
*********************************************************************
我实现了一个很简单的回调函数。
#include <stdio.h>
void printWelcome(int len)
{
printf("欢迎欢迎 -- %d/n", len);
}
void printGoodbye(int len)
{
printf("送客送客 -- %d/n", len);
}
void callback(int times, void (* print)(int))
{
int i;
for (i = 0; i < times; ++i)
{
print(i);
}
printf("/n我不知道你是迎客还是送客!/n/n");
}
void main(void)
{
callback(10, printWelcome);
callback(10, printGoodbye);
printWelcome(5);
}
*******************************************************************************
上面的代码没有被任何系统函数调用,说明那些东西只是撒撒土迷迷路人眼而已。还有面相对象编程时,用class给封装起来也是掩人耳目,不要被外表所迷惑。
第三个问题:
*********************************************************************
用过STL的人都知道,在STL中众多算法和程序都用到回调函数,这实现了一种策略。只要任何符合我的标准的函数和计算都可以用我这个公式。你可以实现各种各样的回调函数,只要符合我的格式就能用。
就上面的程序来说,你只要函数格式符合cllback第二个参数的格式不论你给别人做饭、铺床叠被都可以正常工作。这就是回调的作用,把回调实现留给别人。
这是一个用法。
有一位朋友用分层的概念来解释了回调机制:callback函数为B层,main函数和print*函数为A层,A层调用了B层的回调函数callmeback,而B层的回调函数调用了A层的实现函数print*。说白了B层就是一个接口。
这是我的理解。Over!
-
-
C语言函数作为函数参数
2019-09-13 11:35:15c,c++中是可以向Python那样将函数作为函数参数的,具体实现方法如下C语言函数作为参数
为了满足DRY思想,减少代码的重复性,考虑将函数作为参数传入另一函数中。C语言是可以将函数作为函数参数的。
用到的方法是函数指针。
首先需要明确的是, 在C语言中,函数名就是函数的首地址,所以将函数作为函数参数的思路是将函数地址传入,形参是一个指针类型的变量,形参的基类型为函数原型。
参数原型为:
ElemType(*FunctionName)(ElemType, ElemType, ......)
其中:ElemType
为声明(如int, float, double,struct等)
以冒泡排序为例借助判断大小函数进行数组排序的具体实现方法如下:#include <stdio.h> #define YES 1 #define NO 0 ///*判断函数,进行元素大小判断,increase判断大小比较*/ int compare(int a, int b, int increase) { if (increase > 0) { if (a > b) return YES; else return NO; } else { if (a < b) return YES; else return NO; } } /*冒泡排序进行数组排序*/ void OrderArr(int arry[], int(*compare)(int, int, int), int length, int increase = 1) { for (int i = 0; i < length - 1; i++) { for (int j = 0; j < length - i - 1; j++) { if (compare(*(arry + j), *(arry + j + 1), increase)) { int temp = *(arry + j + 1); *(arry + j + 1) = *(arry + j); *(arry + j) = temp; } } } } /*输出函数*/ void Print(int a[], int length) { for (int i = 0; i < length; i++) { printf("%d ", *(a + i)); } printf("\n"); } int main() { int a[5] = { 1, 4, 2, 6, 3 }; //增序排列数组 OrderArr(a, compare, 5); Print(a, 5); //降序排列数组 OrderArr(a, compare, 5, -1); Print(a, 5); }
-
c语言中函数参数处理顺序以及默认参数原则
2019-07-04 11:20:59c语言中函数参数处理顺序 下面我们来看2个案例,分析下c语言中函数参数处理顺序。 第一个: #include "stdio.h" void fn(int a,int b,int c) { printf("%d,%d,%d", a, b, c); } void main() { int a = 3; .... -
Python 函数参数为空 空函数参数 None 怎么定义空函数参数 -YDOOK
2020-04-08 11:02:39将函数参数设置为 None,在python中,可变函数参数不输入为空的时候,应该用None代替不输入的真空。 示例代码: def Kong(None): pass -
以数组作为函数参数的函数调用
2018-11-28 15:04:44调用有参函数时需要提供实参,实参可以是常量变量或者表达式,数组元素就相当于变量,因此数组元素可以用作函数实参,但是不能用作函数形参(因为在函数被调用时临时分配存储单元,不可能为一个数组单独分配存储单元... -
结构体、结构指针作为函数参数
2019-05-20 07:19:25结构体、结构指针作为函数参数 结构体、结构体指针作为函数的参数现在应用的非常广泛,但一些细微之处还需要引起注意。本文将讨论其作为形参和实参的区别。 结构体作为参数 将结构体作为函数的参数,目的是通过... -
python函数参数的默认参数、元组参数和字典参数例解
2019-01-30 10:54:33python函数除了不带参数,带固定参数之外,还有另外三类函数参数有重要使用价值,是提升python水平需要强化的内容。它们是默认参数、元组参数和字典参数。下面举例子说明。 1.默认参数 即在函数定义时赋值的参数。... -
C/C++中函数参数传递详解
2018-07-26 17:06:24C/C++中函数参数传递详解 昨天看了内存管理的有关内容,有一点了解,但不是很深入,发现之前写代码时有很多细节问题没有注意到,只知道这样做可以实现功能,却不知道为什么可以这样,对于采用自己的方法造成的隐患... -
python的 字典作为函数参数
2019-08-08 17:57:51字典是可变对象作为参数传递时,函数内对字典进行修改,原来的字典也会进行改变。 extra = {'city': 'Beijing', 'job': 'Engineer'} def person(kw): kw['city']='qingdao' person(extra) print(extra) 结果: {'... -
c++函数参数默认值设置
2020-09-08 14:03:46c++函数参数默认值设置 1,函数参数默认值在函数声明的参数列表上定义,函数定义不变; #include <iostream> using namespace std; int add(int a = 0, int b = 0, int c = 0); int main() { std::cout <... -
C语言 数组做函数参数
2018-04-25 00:20:51这里如果真的把数组作为函数参数的话,需要分配临时的栈区执行拷贝等操作,所以编译器对代码进行了优化,把数组退化成了指针,因为指针做函数参数本身就是C语言的精华所在。这里的参数写成num[5],但是编译器在优化的... -
C++结构体作为函数参数传参
2020-06-25 10:45:56#include<iostream> using namespace std; #include<... *结构体作为函数参数传参 */ //值传递 void printStufdent1(struct Student st3) { cout << "子函数" << endl; st3.age . -
数组作为函数参数使用方法总结
2019-10-24 22:07:43C/C++数组作为函数参数使用方法总结 一维数组作为函数参数的三种方法: 方法1:形参与实参都用数组; 方法2:形参与实参都用对应数组的指针; 方法3:实参用数组名,形参用引用; 二维数组作为函数参数的四种方法... -
C++ 使用lambda表达式作为函数参数
2018-08-21 16:30:16作为函数参数传递 由于时间有限,我直接贴代码了 模板参数 #include <iostream> #include <string> template <typename F> void print(F cons... -
C/C++编程语言中指针变量作为函数参数的用法示例
2019-08-21 17:15:30本文主要介绍在 C/C++ 编程语言中,指针变量作为函数参数的用法示例。 1 示例1 示例代码如下: #include <stdio.h> #include <stdlib.h> int main() { /* 函数声明 */ int swap(int *p1, int *... -
c++类的对象作为方法参数(即函数参数)
2019-09-12 17:13:03c++类的对象作为参数传递时,有三种传递方式 #include <iostream> using namespace std; //求圆的面积 class Mycircle{ public: double r; double s; public: double getR(){ return r; } void setR... -
python 函数参数(必选参数、默认参数、可选参数、关键字参数)
2018-07-11 13:25:06http://www.liaoxuefeng.com/wiki/001374738125095c955c1e6d8bb493182103fac9270762a000/001374738449338c8a122a7f2e047899fc162f4a7205ea3000########################################################函数参数:... -
ES6函数参数解构
2018-10-10 20:23:44ES6函数参数解构 常规的JavaScript若要给函数传递的参数是一个对象,需要像下面这样来实现: function sayName(person) { if(Object.prototype.toString.call(person) == '[object Object]') { console.log( `${... -
C语言指针作为函数参数传递学习(一)
2018-12-01 00:18:341. 数组或者一维指针做函数形参 1.1传入的指针为NULL 比如下面的例子,很多人都会理解错: #include &lt;stdio.h&gt; void test(char *string) { string = "hello world"; } ... -
C语言中函数参数传递的三种方式
2017-07-22 21:09:40C语言中函数参数传递的三种方式(1)传值,就是把你的变量的值传递给函数的形式参数,实际就是用变量的值来新生成一个形式参数,因而在函数里对形参的改变不会影响到函数外的变量的值。(2)传址,就是传变量的地址... -
C语言数组作为函数参数
2019-02-24 20:24:59一:数组元素作为函数的实参 数组元素就是变量,与普通变量没有区别,将数组元素传送给形参,实现单向的值传递。 #include <stdio.h> float max(float x,float y){ if(x > y) return x; ... -
C++函数参数中&和*的意义
2020-02-03 20:52:56写这篇博文的动机是,在看Linux网络编程时,看到 //bind 函数定义 int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); //函数调用 bind(lfd, (struct ...觉得在函数参数中一会用 *,一会... -
golang用数组作为函数参数
2020-10-11 20:24:55作为参数传进函数时,传递的是数组的原始值拷贝,此时在函数内部是无法更新该数组的 package main import "fmt" func main() { x := [3]int{5,2,9} func(arr [3]int) { arr[0] = 8 fmt.Println(arr) }... -
C++中函数参数类型为指针的指针
2018-08-08 08:48:021 参数类型是非指针情况 例如,有如下函数 void func(char a) { a = 'a'; return; } 使用如下方式调用func()函数 char b = 'b'; func(b); 此时,变量a是func()函数的形参,变量b是func()函数的实参... -
函数参数中使用const
2018-12-28 11:59:57在函数的参数中使用const,可以让编译器知道在函数调用过程中,对于某个参数不会修改数据,从而可以提供给编译器更多的优化机会。 比如标准函数 char *strcpy(char *dst, const char *src); 这里,第二个输入参数... -
excel下拉让函数参数部分不变
2020-07-15 15:28:49原理 使用相对引用就会变,bai使用绝对引用du就不变 A1是相对引用,上下拉公式的时候会zhi...如要固定字符串拼接函数的第一个参数,后一个参数下拉变化 在编辑栏,选中第一个参数(J19),按下F4,变成了: ... -
用数组名作函数参数
2019-05-31 18:46:10关于用数组名作函数参数有两点要说明: (1) 如果函数实参是数组名,形参也应为数组名(或指针变量),形参不能声明为普通变量(如int array;)。实参数组与形参数组类型应一致(现都为int型),如不一致,结果将出错。 (2) 需要... -
结构体作为函数参数传递
2019-08-23 10:46:48结构变量是一个标量,它可以用于其他标量可以使用的任何场合,但把结构体作为参数传递给一个函数要注重效率 例如下面这样一个结构体: #define PRODUCT_SIZE 20 typedef struct{ char product[PRODUCT_SIZE]; ...