3 面试题3:形参与实参的区别
形参:函数定义时的参数,可以看作是一个占位符。形参只有在被调用的时候才分配内存单元,只在函数内部有效,调用结束后立即释放。
实参:调用函数时使用的参数,实参可以是常量、变量、表达式、函数等。如果实参和形参的类型不匹配,编译器会尝试进行类型转换。如果无法进行类型转换,编译器将会报错。
具体区别如下:
(1)内存分配:形参只有在调用的时候才会分配内存,调用结束后就会释放内存。而实参的情况就比较复杂,比如一个字符串常量的实参,其内存分配在文字常量区。
(2)类型匹配:实参和形参在数量上,类型上,顺序上应严格一致, 如果实参和形参的类型不匹配,编译器会尝试进行类型转换。如果无法进行类型转换,编译器将会报"类型不匹配"的错误。
(3)数据传送:函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。
(4)传递方式:将实参的值传送给形参的方式分为值传递、指针传递、引用传递。值传递时,形参的值发生改变,实参中的值不会变化。指针传递与引用传递时,对形参的改变会影响到实参。
重点注意:
指针传递时,如果对其做创建新对象的操作( new ),则实参中的值不会变化(即不会替换为这个新创建的对象),如下为样例代码:
#include <iostream>
using namespace std;class Student
{
public:Student(const string& name){m_name = name;}~Student() {}public:string getName(){return m_name;}private:string m_name;
};void changeName(Student* st, string name)
{st = new Student(name);
}int main()
{Student* st = new Student("zhangsan");changeName(st, "lisi");printf("name = %s\n", st->getName().c_str());delete st;st = nullptr;return 0;
}
上面代码的输出为:
name = zhangsan
这是由于形参 Student* st 与实参 Student* st 实际上还是两个变量,只不过这两个变量指向同一个地址,所以对形参做操作,其操作的对象还是他们共同指向的地址,即为函数外面的 st 对象。但是当对形参做 new 操作以后,形参便指向了新的地址,该函数执行结束后,打印的字符串依然是原来 st 对象的成员变量。
4 面试题4:值传递、指针传递、引用传递的区别
4.1 按值传递
按值传递是将实际参数的值复制到形式参数。使用这种方式,调用函数本身不对实参进行操作。例如:
#include <iostream>//函数的定义
void swap(int a, int b)
{int c=a;a=b;b=c;
}int main()
{int a=1,b=2;swap(a,b); //a,b 交换值失败printf("a=%d, b=%d\n",a,b);return 0;
}
上面代码的输出为:
a=1, b=2
4.2 按引用传递
按引用传递是将实际参数的地址传递给形式参数,此时的形参相当于实参的别名。使用这种方式,对形参的改变会影响到实参。例如:
#include <iostream>//函数的定义
void swap(int &a, int &b)
{int c=a;a=b;b=c;
}int main()
{int a=1,b=2;swap(a,b); //a,b 交换值成功printf("a=%d, b=%d\n",a,b);return 0;
}
上面代码的输出为:
a=2, b=1
4.3 按指针传递
按指针传递是指将实参的地址传递给形参,函数内部可以通过指针来操作实参的值。但需要注意指针为空的情况。样例代码如下:
#include <iostream>//函数的定义
void swap(int* a, int* b)
{int c=*a;*a=*b;*b=c;
}int main()
{int a=1,b=2;swap(&a,&b); //a,b 交换值成功printf("a=%d, b=%d\n",a,b);return 0;
}
上面代码的输出为:
a=2, b=1
需要注意的是:从效率上来说,按引用传递与按指针传递基本一样(按值传递有拷贝过程,性能很差),不过从安全角度出发,引用传递在参数传递过程中执行强类型检查,而指针传递的类型检查较弱,特别的,如果参数被声明为 void ,那么就不会做类型检查。所以推荐只用引用传递,最好不用指针传递。