How to use the variable parameter function 1. Mark plus offset address method

     Basic knowledge: In fact, when parameters are passed into a function, what is actually passed in is the first address of a continuous memory area in the heap (the function is in the stack), and the parameters passed by the user are placed in the incoming order.

     The C language has a feature called "variable parameter function", which is convenient for users to pass in an indefinite number of parameters to facilitate the user to write some functions such as "string splicing" functions.
     Among them, pay attention to the problem of memory alignment. The currentParams in the example is a pointer to the address of the parameter memory area, so you should pay attention to the pointer type conversion when reading, so as to achieve the correct value after memory alignment. First convert the void* pointer into a pointer of a certain type, such as doublue*, and then add * in front to get the corresponding content, such as *((double*)currentParams).

      In some places, the integer taken out is actually an address. For example, when a string is passed in, it is actually the first address of the continuous memory area of ​​the character array, so after obtaining this address, for example, the integer *(int*)currentParams is obtained, It is known that this integer is the first address of the contiguous memory area of ​​a character array passed in. I want to output it through printf, so I need to convert the address represented by this integer to a char* pointer——(char*)(*(int* )currentParams) can be successfully recognized as a string by printf. In fact, pointers store integers such as memory addresses, and pointers of different types are like this. Only with type declarations, it is convenient for the program to align the memory when reading the content, so as to prevent 4 bytes of int data from being read by one byte. It also prevents the int array of 4 bytes per unit from being misread in groups of 8 bytes (such as double type, struct{int a, int b} type).
 
     The following is an example of reading variadic function variables. This code can be slightly modified and some assembly code added to implement custom printf functions such as outputting content to serial devices. It can also be said that the printf function is one of the most commonly used variadic functions:
     
#include "stdio.h"
#include "stdlib.h"
#include "string.h"

void testMultiParams(char *paramsFormat, ...){
	int paramsLength = 0, i, j = 0;
	/*Can't take paramsFormat plus offset address to take parameters, the address stored in paramsFormat is the first address of the continuous memory area of ​​the bunch of characters, not the first address of the parameter
	  So take the address of the data storage area that points to the first address of this contiguous memory area (addressing the "root" - find the address that points to this address) */
	void* firstParamsAddress = (void*) (¶msFormat + sizeof(char));
	void* currentParams = firstParamsAddress;
	for(i = 0; i < strlen(paramsFormat); i++){
		if(paramsFormat[i] == '%')
			paramsLength++;
	}
	printf("Parameter length%d\n", paramsLength);
	for(i = 1; i <= strlen(paramsFormat); i++){
		if(paramsFormat[i - 1] == '%') {
			switch(paramsFormat[i]){
				case 'd':
					printf("The content of the %d parameter is: %d\n", ++j, *((int*)currentParams));
					currentParams+=sizeof(int);
					break;
				case 'f':
					printf("The content of the %d parameter is: %f\n", ++j, *((double*)currentParams));
					currentParams+=sizeof(double);
					break;
				case 'c':
					printf("The content of the %d parameter is: %c\n", ++j, ((char*)currentParams)[0]);
					currentParams+=sizeof(char*);
					break;
				case 's':
					/*The string parameter is the address that holds the integer pointing to the "first address of the character array", get the address,
					  And get the content saved in the address - the address integer of "the first address of the character array", and then convert it to a string address
					 (addressing "leaf" - address of address) */
					printf("The content of the %d parameter is: %s\n", ++j, (char*)(*(int*)currentParams));
					currentParams+=sizeof(char*);
					break;
				cell 'l':
					if(paramsFormat[i+1] == 'd'){
						printf("The content of the %d parameter is: %ld\n", ++j, *(long*)currentParams);
						currentParams+=sizeof(long);
						i++;
					}
					break;
				default:
					printf("The type of the %d parameter is wrong, skipping...\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, "Variable parameter function test", 678, 678999999L, "Imitate Java polymorphism");
	testMultiParams("%d%s%d%yyy%s", 3, "Error Case Test", 678, 678999999L, "Test");
	printf("%.2f\n", 3.1415f);
	testMultiParams("%d%s%c%f%ld%s", 3, "test", 'b', 3.1415f, 678999999L, "test");
	getchar();
	return 0;
}



 



Advanced usage, string concatenation example :
#include "stdio.h"
#include "stdlib.h"
#include "string.h"

/**Array length judgment macro**/
#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;
}




Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325519275&siteId=291194637