2010/11/8
关键字:函数
函数零散知识点
通常函数不应该有vector和其他标准库容器类型的形参.调用含有普通的非引用vector形参的函数会复制vector中的每一个元素.
有两种方法避免,将形参改为引用类型或将需要处理的迭代器作为形参来传递容器.
数组做形参
1. 数组有两个特性,一是不能复制数组,二是使用数组名字时,数组名会自动转换为指向其第一个元素的指针.虽然不能直接传递数组,但是函数的形参可以写成数组的形式.下面三种定义是等价的:
void func1(char* array){}
void func2(char array[]){}
void func3(char array[10]){} //编译器会忽略数组大小
编译器会忽略任何数组形参指定的长度,根据数组的长度可以写出下面的函数:
void func3(char array[10]) //编译器会忽略数组大小
{
for (int i=0; i<10; ++i)
{
array[i] += 1;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
char array[2] = {0, 1};
func3(array); //编译通过,但是调用时会有问题
return 0;
}
函数假定所传递的数组至少含有10个元素,但是C++没有任何机制强制实现这个假设.
可靠的方法是用另一个参数传递数组的大小.
非引用数组形参,编译器只检测实参和形参是否具有相同的类型,而不会去检查实参实际上是否指向指定大小的数组.
2. 引用类型的数组做形参时,编译器会传递数组引用本身作为实参,会严格要求数组形参和实参的大小一致.
void func3(char (&array)[10])
{
for (int i=0; i<10; ++i)
{
array[i] += 1;
}
}
int _tmain(int argc, _TCHAR* argv[])
{
char array[2] = {0, 1};
func3(array); //编译失败
char array1[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
func3(array1); //编译ok,运行ok
return 0;
}
char (&array)[10]中的()是必须的,因为[]操作优先级比&高.
char &array[10],是引用的数组,char (&array)[10]是数组的引用.
3. 同理,多维数组做参数时下面三种也是相同的:
void func3(char (*array)[10]){}
void func4(char array[][10]){}
void func5(char array[10][10]){} //第一个会被忽略
void func3(char (*array)[10]){}中的()是必须的,因为*比[]优先级要低,
char *array[10]表示array是char*类型指针的数组,实际等效于char [10][],
char (*array)[10]是char[10]类型的指针,实际等效于char [][10].
同样,多维数组的引用和数组的引用声明、使用等方式相同.
char (&array)[10][10],表示char[10][10]类型的引用,
char &array[10][10],表示引用的数组的数组.
编译器会检查实参的数组大小是否和形参相同.
void func5(char (&array)[2][2]){}
int _tmain(int argc, _TCHAR* argv[])
{
char array[2][2] = {1, 2, 4, 5};
char array2[2][3] = {1, 2, 4, 5};
func5(array); //ok
func5(array2); //error
return 0;
}
含有可变形参的函数
C++中的省略符形参是为了编译使用了varargs的C语言程序.省略符暂停了类型检查机制.因此无法检测实参的个数和类型是否正确.需要程序员自己保证.否则可能会导致运行时错误.
用法形如:
int Func(char *pText, ...)
{
char buffer[1024]; // working buffer
va_list arglist; // variable argument list
// make sure both the error file and string are valid
if (!pText)
return FAIL;
// print out the string using the variable number of arguments on stack
va_start(arglist,pText);
vsprintf(buffer,pText,arglist);
va_end(arglist);
//操作buffer
}
VC8中相关定义
typedef char * va_list;
#define va_start _crt_va_start
#define _crt_va_start(ap,v) ( ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v) )
#define _ADDRESSOF(v) ( &reinterpret_cast<const char &>(v) )
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1) )
#define va_arg _crt_va_arg
#define va_end _crt_va_end
#define _crt_va_end(ap) ( ap = (va_list)0 )
函数入栈顺序,从内存自高位到低位:
倒数第一个参数 内存地址高位
倒数第二个
…
第一个参数 pText的地址
函数返回地址
函数代码 内存地址低位
其中va_start的作用是获取第二个参数(本例中pText之后的参数)的地址,然后根据参数的类型依次获取剩余的参数值.而参数类型可以在pText中找到.printf(“%s, %d”, ”124”, 2);而使用vsprintf(buffer,pText,arglist);则避免了自己读取每个参数的麻烦.最后va_end会将指针置为0