当前位置:网站首页>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

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

Definition

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 :

 Insert picture description here

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 .

 Insert picture description here
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 :
 Insert picture description here
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

 Insert picture description here

️ Concerned about the official account of the Suzhou public ️



原网站

版权声明
本文为[Suzhou program Dabai]所创,转载请带上原文链接,感谢
https://yzsam.com/2022/162/202206110034352363.html