几乎所有的高级语言都实现了将不同类型的数据组合转换为字符串的函数。
VC中有等等。 VC里有等等,都是很常用、很方便的功能。
这些函数的使用与普通函数最大的区别在于其中一个参数的个数和类型是不确定的。 换句话说,参数是可变的。 这一特性使得此类功能异常强大。 当然,极其方便。 我们如何在自己的代码中实现这样的功能呢?
1.VC实现可变参数功能
这个特殊参数在VC中用“...”表示。 这是一段代码:
使用标准;
双字(字符*,...)
字符[1024]={0};
精氨酸;
(argp,); /* 将变长参数转换为 */
/* 将被传递给子函数*/
int iRLen = (, 512, , argp);
(argp);
//下面的可以忽略
int iLen = ();
sTmp(, iLen);
DWORD dwRet = (sTmp, false);
dwRet;
//s上面的函数其实并不高级,因为它实际上是借助help实现的,但是它的原理已经差不多清楚了。
首先声明一个可以处理的数组。
然后使用宏获取你传入函数的参数变量并将其存储在数组中;
这里的功能是告诉我们如何获取后续所有参数变量的地址。
参数是如何获得的?
通常,我们对函数参数的使用无非就是直接使用变量。 事实上,对于CPU和进程来说,具体的参数地址或者参数值都是在将参数压入栈后,通过栈顶指针得到的。 我们可以直接使用参数变量的原因是因为编译器已经帮你处理好了,你正在做编译器已经帮你处理好的工作。 如果函数声明时不使用“...”作为第二个参数,而是作为第三个参数,同时第二个参数是其他参数,例如:
DWORD(char*,DWORD dwVal,...),那我们该怎么处理呢?
这是传入 dwVal 而不是 dwVal 的宏。 通常情况下,由于32位CPU的地址对齐功能会使一个参数地址只占用4个字节,所以使用宏几乎可以满足所有情况,而如果C语言在其他16位机或嵌入式开发中,则需要根据具体情况来定,但原则是根据前一个参数“...”的地址获取整个堆栈的地址内容,存入数组中。
参数获取后,我这里就偷懒了,直接使用。 因为接受参数,所以内部会获取实际的参数。 如果遇到%d,则获取4字节内容。 如果遇到%s,则获取到0x0之前的连续字符。
这是原则。 你也可以通过处理其中存储的参数来实现自己的函数功能。 到时候你就不需要了。 相反,它是一个完整的你自己的可变参数函数。 最后处理后不要忘记使用数组。
2.实现可变参数功能
可变参数功能是通过const类型参数的Array来实现的,Array是一个数组。
结构体是实现类似于泛型变量功能的结构体,如下:
= ^;
= { 不要打包这个 ; 这是 - }
案例字节
: (: ; VType: 字节);
:(:);
: (VChar: 字符);
:(:);
:(:);
:(:);
: (: PChar);
:(:);
:(:);
:(:);
:(:);
:(:);
:(:);
:(:);
:(:);
:(:);
:(:);
结尾;
这个类型看起来有很多成员,但实际上,实际数据只有2个成员。
案例用于实现VC类型结构中并集的功能。
注意其中一行:
: (: ; VType: 字节);
其他成员使用与该成员相同的地址,并且大小不大于类型。
这行代码中的第二个成员是该结构的关键成员:VType 成员。 该成员的值由编译器的内部实现分配。
VType成员的值对应于Byte of情况下的Byte值,具体来说:
= 0;
= 1;
= 2;
= 3;
= 4;
= 5;
= 6;
= 7;
= 8;
= 9;
= 10;
= 11;
= 12;
= 13;
= 14;
= 15;
= 16;
上面几乎包含了所有的基本数据类型,也就是说,根据VType,我们可以直接判断数组成员的数据类型。
看到这个,在VC中应该能联系到了吧。 这个数组的作用其实和VC中的差不多。
VC中的成员数据类型需要通过函数来赋值,所以如果你传入的参数个数与字符串中%的个数不一致,就会出现内存错误。 如果你使用的参数是“Test%d,%d”,但是实际的变长参数只传入了一个1,那么根据“Test%d,%d”,会取到第二个“%d”堆栈。 参数,但实际上栈只有一个参数,直接导致栈指针超出。 此时栈顶指针已经错误,会导致后续所有代码执行出错,造成栈内存溢出。
编译器自动实现代码处理,不根据“Test%d,%d”带参数,所以安全很多。 具体可以查看CPU代码,这里就不贴了。 传入参数之前只需一步。 避免了通过栈获取参数的麻烦,因为传递的是const的Array,是一个动态数组。 对于这个函数来说,它实际上只是一个指针。 所以我们直接处理const的Array相对来说比VC更方便。 一些。
这是一段代码:
(参数:常量数组):;
变量
我: ;
开始
:= '';
如果(参数)=0 则退出;
对于 I:=0 到 High(param) 执行
[我愿意
案例 V 类型
: := + ();
: := + (, 真);
: := + VChar;
//以下其他类型不予处理。
//: (:);
//: (:);
//: (:);
//: (: PChar);
//: (:);
//: (:);
//: (:);
//: (:);
//: (:);
//: (:);
//: (:);
//: (:);
//: (:);
//: (:);
结尾;
结尾;
其实实现这种功能并不难,因为难点已经被人实现封装了,或者已经被编译器处理过了。 这些与其说是实现,不如说是使用,但知道如何使用它们也算是一门技术。