变参函数的使用方法一、标记加偏移地址法

来源:互联网 发布:php 调用setcookie 编辑:程序博客网 时间:2024/05/19 20:21
     基础知识:其实参数传入函数的时候,其实实际传入的是一个堆中连续内存区的首地址(函数在栈中),里面以传入的顺序放入了用户传递的参数。

     而C语言有一种叫“变参函数”的特性方便用户传入不定数量的参数以方便用户进行某些如“字符串拼接”函数等功能的编写。
     其中要注意内存对齐的问题,例子中的currentParams是指向参数内存区地址的指针,所以读取的时候要注意做好指针类型转换,转换完之后以便实现内存对齐后的正确取值。先把void*指针转化为一个确定类型的指针如doublue*,再在前面加*即可取得对应内容,如*((double*)currentParams)。

      而有些地方取出的整数其实是一个地址,例如字符串传入的时候其实传入的是字符数组连续内存区的首地址,所以得到这个地址之后,例如得到了*(int*)currentParams这个整数,已知这个整数是传入的一个字符数组的连续内存区的首地址,我想通过printf输出,因此需要把这个整数代表的地址转换为一个char*指针——(char*)(*(int*)currentParams)才可以被printf顺利识别为一个字符串。其实指针保存的都是内存地址这种整数,不同类型的指针都是这样,只是有了类型声明,方便程序读内容时对齐内存,以免如4个字节的int类数据被读了一个字节出来,也避免了4个字节一个单元的int数组被以8个字节一组的方式(如double类型、struct{int a, int b}型)读错。
 
     以下是读取变参函数变量的一个例子,这套代码可以稍加修改加上一些汇编代码,实现如输出内容到串口设备等的自定义printf函数。也可以说printf函数就是一种最常用的变参函数之一:
     
#include "stdio.h"#include "stdlib.h"#include "string.h"void testMultiParams(char *paramsFormat, ...){int paramsLength = 0, i, j = 0;/*不能拿paramsFormat加偏移地址拿参数,paramsFormat存的地址是那堆字符的连续内存区的首地址,不是参数的首地址  所以要拿指向这个连续内存区的首地址的数据存储区的地址(向“根”寻址——找到指向这个地址的地址)*/ void* firstParamsAddress = (void*) (&paramsFormat + sizeof(char)); void* currentParams = firstParamsAddress;for(i = 0; i < strlen(paramsFormat); i++){if(paramsFormat[i] == '%') paramsLength++;}printf("参数长度%d\n", paramsLength); for(i = 1; i <= strlen(paramsFormat); i++){if(paramsFormat[i - 1] == '%') {switch(paramsFormat[i]){case 'd':printf("第%d参数内容是:%d\n", ++j, *((int*)currentParams));currentParams+=sizeof(int);break;case 'f':printf("第%d参数内容是:%f\n", ++j, *((double*)currentParams));currentParams+=sizeof(double);break;case 'c':printf("第%d参数内容是:%c\n", ++j, ((char*)currentParams)[0]);currentParams+=sizeof(char*);break;case 's':/*字符串形参数是保存着指向一个“字符数组首地址”这个整数的地址,取得该地址,  并取得地址中保存了的内容——“字符数组首地址”这个地址整数,然后转为字符串地址  (向“叶”寻址——地址的地址)*/ printf("第%d参数内容是:%s\n", ++j, (char*)(*(int*)currentParams)); currentParams+=sizeof(char*);break;case 'l':if(paramsFormat[i+1] == 'd'){printf("第%d参数内容是:%ld\n", ++j, *(long*)currentParams);currentParams+=sizeof(long);i++;}break;default:printf("第%d参数类型标记错误,正在跳过...\n", j+1);while(paramsFormat[++i] != '%');currentParams+=sizeof(void*);++j;break;}}}}int main(){testMultiParams("%d", 123);testMultiParams("%d%d", 123, 345);testMultiParams("%d%s%d", 123, "asdasdasd", 678);testMultiParams("%d%s%d%ld%s", 123, "变参函数测试", 678, 678999999L, "模仿Java多态");testMultiParams("%d%s%d%yyy%s", 3, "错误情况测试", 678, 678999999L, "测试");printf("%.2f\n", 3.1415f);testMultiParams("%d%s%c%f%ld%s", 3, "测试", 'b', 3.1415f, 678999999L, "测试");getchar(); return 0;}



 



进阶用法,字符串拼接例子
#include "stdio.h"#include "stdlib.h"#include "string.h"/**数组长度判断宏**/ #define ARR_LENGTH(arr)    sizeof(arr)/sizeof(arr[0])    char* stringAllPlus(int paramsCount, ...){void* firstParamsAddress = (void*) (¶msCount + 1); void* currentParams = firstParamsAddress;char* totalStr = NULL;char* cursorPointer = NULL;int i, j, totalStrLength = 0, offset = 0;/*统计传入的字符串总长度*/ for(i = 0; i < paramsCount; i++){totalStrLength += strlen((char*)(*(int*)currentParams));currentParams+=sizeof(char*);}/*恢复游标到连续地址空间的头地址*/ currentParams = firstParamsAddress;/*printf("Total String length:%d\n", totalStrLength);*//*拼接成一个字符串*/ if(totalStrLength <= 0) return NULL;totalStr = cursorPointer = (char*) malloc(sizeof(char) * totalStrLength + 1);memset((void*)cursorPointer, 0, totalStrLength);for(i = 0; i < paramsCount; i++){memcpy((void*)cursorPointer, (void*)(*(int*)currentParams), strlen((char*)(*(int*)currentParams)));cursorPointer+=strlen((char*)(*(int*)currentParams));currentParams+=sizeof(char*);}totalStr[totalStrLength] = '\0';return totalStr;}void stringAllPlus2(int paramsCount, ...){char* firstParamsAddress = (char*) (¶msCount + 1); printf("%s\n", ((int*)firstParamsAddress)[0]); printf("%s\n", ((int*)firstParamsAddress)[1]);} char* stringAllPlus3(int paramsCount, ...){int* paramsAddress = (int*) (¶msCount + 1);char* cursorPointer = NULL;char* totalStr = NULL;int i, totalStrLength = 0;/*统计传入的字符串总长度*/ for(i = 0; i < paramsCount; i++)totalStrLength += strlen((char*) paramsAddress[i]);/*拼接成一个字符串*/ if(totalStrLength <= 0) return NULL;totalStr = malloc(totalStrLength + 1); cursorPointer = totalStr; memset(cursorPointer, 0, totalStrLength);for(i = 0; i < paramsCount; i++){memcpy((void*) cursorPointer, (void*) paramsAddress[i], sizeof((char*) paramsAddress[i]));cursorPointer += strlen((char*) paramsAddress[i]);}totalStr[totalStrLength] = '\0';return totalStr;} void test(int a, ...){int* arr = &a;void* arr2 = (void*)&a;printf("%d,%d,%d\n", *(int*)(&a), *(int*)(&a + 1), *(int*)(&a + 2));printf("%d\n", ((int*)&a)[0]);printf("%d\n", ((int*)&a)[1]);printf("%d\n", ((int*)&a)[2]);printf("%d\n", arr[0]);printf("%d\n", arr[1]);printf("%d\n", arr[2]);printf("%d\n", ((int*)arr2)[0]);printf("%d\n", ((int*)arr2)[1]);printf("%c\n", ((char*)arr2)[2]); //错误用法,内存对齐错误 }int main(){char* temp = stringAllPlus3(3, "abc", "sdfsdfsdf\n", "fghhhhhhhhh");char* totalStr[] = {stringAllPlus3(3, "abc", "哈哈哈", "fgh"), stringAllPlus3(2, "you ", "suck")};char* temp2 = "";int i;printf("total str:%s\n", temp);printf("total str:%s\n", totalStr[0]);printf("total str:%s\n", totalStr[1]);for(i = 'a'; i <= 'z'; i++){temp2 = stringAllPlus(2, temp2, &i);}printf("%s\n", temp2);test(33, 55, 97);stringAllPlus2(2, "you ", "suck");return 0;}




原创粉丝点击