当前位置:网站首页>Deep exploration of functions with indefinite parameters in C language
Deep exploration of functions with indefinite parameters in C language
2022-06-11 01:54:00 【Suzhou program Dabai】
C Language Delve into functions with indefinite parameters
Blogger introduction
Personal home page : Suzhou program white Personal community :CSDN All over the country 🤟 The authors introduce : China DBA union (ACDU) member ,CSDN All over the country ( Yuan ) Gathering place Administrator . Currently engaged in industrial automation software development . Good at C#、Java、 Machine vision 、 Underlying algorithms and other languages .2019 Qiyue software studio was established in ,2021 Registered in Suzhou Kaijie Intelligent Technology Co., Ltd If you have any questions, please send a private letter , I will reply in time when I see it WeChat ID :stbsl6, WeChat official account : Suzhou program white If it helps you , Welcome to your attention 、 give the thumbs-up 、 Collection ( One key, three links ) Those who want to join the technical exchange group can add my friends , The group will share learning materials
Preface
C The language does not support generic programming ( At least C98 That's true ), however C Language supports functions with indefinite parameters , Here I'll delve into the principle , And learn how to use it , Simply implement a simple printf function . notes : the IDE by vs2022
As for how to implement functions with indefinite parameters ? Here you can see how the standard library is defined :
_Check_return_opt_
_CRT_STDIO_INLINE int __CRTDECL printf(
_In_z_ _Printf_format_string_ char const* const _Format,
...)
here char const* const _Format , Obviously, we passed in the format string , Back There is …, This type has never been seen , It should be the key to the implementation of variable parameters . stay C In language … Three points represent indefinite parameters , Here we have a new problem , How to take out an indefinite parameter after it is passed in ? Use a few macros to do this , Yes, it's macro .
C Language stdarg.h
In understanding ta Before , Or learn how to use ta.
| macro | describe |
|---|---|
| void va_start(va_list ap, last_arg) | This macro is initialized ap Variable , send ap Point to the starting parameter (last_arg) |
| type va_arg(va_list ap, type) | Get the next type as type Parameters of |
| void va_end(va_list ap) | Release ap |
| va_copy(destination, source) | Copy ap The content of |
Tips :va_start va_arg va_end Be sure to use... In this order
Example
It's hard to understand just looking at this form Let's practice one .
#include<stdio.h>
#include<stdarg.h>
// Realization args_nums individual int Add the numbers of the type
int sum(int args_num, ...)
{
va_list ap;
int sum = 0;
va_start(ap, args_num);//1. initialization ap
for (; args_num > 0; --args_num)
sum += va_arg(ap, int);//2. obtain next int Parameters of type
va_end(ap);// End use
return sum;
}
int main()
{
printf("sum: %d", sum(5, 1, 2, 3, 4, 5));
return 0;
}
Read the program carefully , We can get a general idea of ta Basic use of
1、va_start(ap, args_num) initialization ap.
2、va_arg(ap, int) Get the next one int Type parameter .
3、va_end(ap) End use .
4、 Add :stdarg.h It does not provide a method to help us judge that there are multiple indefinite parameters , Here I use Pass in a args_num To mark that there are multiple indefinite parameters , Don't think we have to pass in a int To mark , We can take other measures ( Add later ).
5、 Here we must pass in a certain parameter as the first parameter , because va_start A definite parameter initialization is required .
Running results :

ta Principle
The essence of function passing parameter
C Language is the closest language to assembly , What is the essence of function arguments , In a word —— Stack the parameters , How do you have the experience of compiling , You know that if you want to pass in parameters to a process, you need to push the passed parameters into the stack in advance ,C That's what language does , Of course, controlling stack pressing is such a troublesome operation that the compiler will help you complete the compilation process . Of course, this should come up with a knowledge point in the compilation , The basic unit of each stack pressing and stacking is not bytes , It's the present CPU Of the word length of , such as 32 Bit so every time you press the stack, you just 4 Of the basic unit of bytes .
Now let's look at , Stack order of multiple parameters , From left to right or vice versa ?
#define VNAME(val) (#val) // obtain val The name of the variable
typedef struct test {
char c[6]; } test; // Defining structure test
void foo(test a, char b,int c )
{
printf("%s addr: %p\n",VNAME(a),&a);
printf("%s addr: %p\n", VNAME(b), &b);
printf("%s addr: %p\n", VNAME(c), &c);
}
int main()
{
test t = {
"123456" };
foo(t, 2, 3);
return 0;
}
Add :C The bottom of the program stack is high address , The top of the stack is the low address .
stay X86 Under the environment of , We are the first 8 Line break point , Use the memory view tool to view memory .

We found that the size is only 1 Bytes of b All of them 4 The size of bytes , The size is 6 Of a Account for the 8 byte , This point is absolutely suitable for Previously mentioned 32 Bit so every time you press the stack, you just 4 Of the basic unit of bytes , If it is 64 For , that char It must take up 8 byte .
Output :
We found that from c To a The address is getting smaller and smaller , explain c First in stack , Only later b and a, Come to the conclusion C The stack order of language function parameters is from right to left .
If we get the address of the first parameter , Then we can determine the address of the next parameter according to the space occupied by the parameter , So we just get the value of the next parameter ?C So does language . for example : know a The address for 010FFAA4 ,A The space occupied is 8, that b Your address must be &a+8.
Let's simply verify : More a Get the address of b and c Value .
char* p = &a;
char bb = *(char*)(p += 8);
int cc = *(int*)(p += 4);
printf("b : %d,c : %d\n", bb, c);
What is emphasized here is , Let's use p by char*, The reason is simple , If it is a pointer of other types, such as int , that p+8 But it has shifted 48=32 Byte position , instead of 8 byte . Then I have reason to believe va_list Namely char
Our only drawback is that we only solve the special case of this function , Can't customize , If there is a function that can help us find Just offset .
Here we will unlock the true face of Lushan Mountain , See how the standard library defines them .( There have been many jumps here ).
typedef char* va_list; // Just as I thought
#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1)) // seek n type Space occupied in the stack
#define __crt_va_start_a(ap, v) ((void)(ap = (va_list)_ADDRESSOF(v) + _INTSIZEOF(v))) // initialization ap
#define __crt_va_arg(ap, t) (*(t*)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)))// Get the next variable
#define __crt_va_end(ap) ((void)(ap = (va_list)0))// End use ap
#define va_copy(destination, source) ((destination) = (source))// Copy
here Let's explain each macro principle separately .
_INTSIZEOF(n)
#define _INTSIZEOF(n) ((sizeof(n) + sizeof(int) - 1) & ~(sizeof(int) - 1))
_INTSIZEOF(n) The whole thing to do is to n The length of is reduced to int An integral multiple of the length , such as n by 5, Binary is 101b,int The length is 4, Binary for 100b, that n Into int The integral multiple of the length should be 8. In other words, it helps us find the offset between variables .
~ It means "bit inversion" .~(sizeof(int) – 1) ) It should be for ~(4-1)=~(00000011b)=11111100b, So any number & ~(sizeof(int) – 1) ) The last two must be 0, It must be 4 It's an integral multiple of .(sizeof(n) + sizeof(int) – 1) Will be greater than 4m But less than or equal to 4(m+1) Increase the number of to greater than or equal to 4(m+1) But less than 4(m+2)(m by 0,1,2,…), So again & ~(sizeof(int) – 1) ) After that, the original length will be supplemented to 4 A multiple of .
MSVC This is how it works , We can see GNUC How is it realized :
#define __va_rounded_size(TYPE) \ // The name is different, but the function is the same , After all, this is a different manufacturer
(((sizeof (TYPE) + sizeof (int) - 1) / sizeof (int)) * sizeof (int))
Obviously this is easier to understand ,(sizeof (TYPE) + sizeof (int) - 1)/ sizeof (int) Get type The space occupied is at least int How many integral multiples of type , Multiplied by 4 It just happened Make up the length to 4 Multiple . such as __va_rounded_size(char)= ((1+3)/4)4= 4.
Personal feeling MSVC Is a little more efficient , After all, Logical operations , Of course GNUC Method is easier to understand , I spent a lot of time studying the implementation of Microsoft .
Other macros
There are three macros left , Very easy to understand , I'm not going to explain , You can compare experiments and definitions , Soon you will understand the essence of it .
practice Realization printf
Here we just practice passing multiple parameters Instead of going deep into the bottom printf This function , You can go and have a look if you like vprintf The implementation of the , There are many mysteries worth exploring .
Here release my code , Very Pediatrics .
#include<stdio.h>
#include<stdarg.h>
int myPrintf(const char* string, ...);
int main()
{
//printf("sum: %d", sum(5, 1, 2, 3, 4, 5));
myPrintf("helloworld%d%s%c\na", 15, "122", 'z');
return 0;
}
// General train of thought take string Medium %d %c \n etc. Replace with Corresponding characters Storage and then buffer in
// It's only realized here %d %c %s \n
int myPrintf(const char* string, ...)// Returns the actual number of output characters
{
char buffer[BUFSIZ];
int temp = 0;
va_list arg;
char* p_string = NULL;
char* p_buffer = buffer;
char* p_temp = NULL;
int counter = 0;// Output character length
int number = 0;
int foo = 0;
va_start(arg, string);
for (counter = 0, p_string = string; *(p_string) != '\0';)
{
switch (*p_string)
{
// Handle %
case '%':
{
switch (*++p_string)
{
case 'd':
temp = va_arg(arg, int);
foo = temp;
// obtain digit
while (foo)
{
number++;
counter++;
foo /= 10;
}
foo = temp;
// Integer to string
while (number)
{
*(p_buffer + number - 1) = (foo % 10)+'0';
foo /= 10;
number--;
}
p_buffer += number;
break;
case 'c':
temp = va_arg(arg, int);
*(p_buffer++) = temp;
break;
case 's':
p_temp = va_arg(arg, char*);
while (*p_temp !='\0')
{
*(p_buffer++) = *(p_temp++);
counter++;
}
break;
default:
break;
}
++p_string;
break;
}
// Handle Transfer character
case '\\':
{
p_string++;
switch (*p_string)
{
case 'n':
*(p_string++) = '\n';
break;
default:
break;
}
break;
}
default:
*(p_buffer++) = *(p_string++);
counter++;
}
}
*(p_buffer++) = '\0';
va_end(arg);
p_buffer = NULL;
puts(buffer);
return counter;
}
Click to get the information directly

边栏推荐
- Flutter status management
- 1.4px4 program download
- Win11系统使用DISM命令备份驱动程序的方法
- Video compression data set TVD
- Leetcode 665 non decreasing array (greedy)
- [leetcode] a group of K flipped linked lists
- Threejs: pit encountered in drawing Bessel curve with two-point coordinates
- 1个月不到暴增900万播放量,B站3个流量增长密码!
- A brief history of neural network
- 2021-2-14 gephi learning notes
猜你喜欢

【交通标志识别】基于matlab GUI YCbCr特征提取+BP神经网络交通标志识别【含Matlab源码 1869期】

On permutation and Combination in Probabilistic Statistics

逻辑漏洞 / 业务漏洞

1.3 introduction to ROS UAV

A tutorial on building a website from scratch with complete steps (7000 words and 102 screenshots for everyone to understand, with source code attached)

【音乐】基于matlab演奏《青花瓷》【含Matlab源码 1873期】

Xpath Injection

threejs:流光效果封装

threejs:如何获得几何体的boundingBox?

threejs:两点坐标绘制贝赛尔曲线遇到的坑
随机推荐
2021-3-1matlas MNIST database training for CNN writing
并发编程基础底层原理学习(四)
(已解决)Latex--取消正文中参考文献引用的上标显示(gbt7714-2015会导致默认上角标引用)(上角标&平齐标混合使用教程)
LeetCode 1749 Maximum Absolute Sum of Any Subarray (dp)
QGC ground station tutorial
Linux Installation MySQL database details
關於概率統計中的排列組合
数据库概述
[Haas hands on] creative case of new video program launch we started the first phase together E01: Internet of things engineers started the remote control manipulator with you
The argument type ‘int?‘ can‘t be assigned to the parameter type ‘num‘
1.3 introduction to ROS UAV
ACM教程 - 堆排序
Leetcode 1574 shortest subarray to be removed to make array sorted
晚餐阿帮的手艺
Byte Beijing 23K and pinduoduo Shanghai 28K, how should I choose?
1.2. Ros+px4 preliminary basic knowledge
2021-2-26 compilation of programming language knowledge points
关于概率统计中的排列组合
Leetcode 1605 find valid matrix given row and Column Sums
1.5 Px4 vehicle selection