The principle of Variable Parameter Function execution in C language is applied

Source: Internet
Author: User

A Variable Parameter Function is also called a va function, such as printf, scanf, and exec.

1. Example:

// Fun: print the value of the parameter after N

Void fun (int n ,...);

Int main ()

{

Int Part1 = 128;

Int Part 2 = 256;

Int Part3 = 512;

Fun (Part1, Part2, Part3 );

Return 0;

}

Void fun (INT Part1 ,...)

{

Int * P = & Part1; // get the address of Part1

Printf ("% d \ n", * ++ P); // print the value of Part2

Printf ("% d \ n", * ++ P); // print the value of Part3

}

C's default function call specification is _ cdecl, that is, all parameters are pushed to the stack from right to left. The strict fun statement should be:

Void _ cdecl fun (int n ,...);

In Main, before calling the fun function, add the parameters to the stack. The order of the parameters in the stack is as follows:

Push Part3

Push Part2

Push Part1

Then call fun to execute the function body code.

The parameters of the stack are placed at the high address. Because the stack is grown from the high address to the low address, the order of Part1, Part2, and Part3 in the memory will be:

0xfe6c Part3

0xfe70 Part2

0xfe74 Part1

In this way, the following parameters can be accessed by obtaining the address & Part1 and ++ of the first parameter, but this must be the _ cdecl function call specification, for example, the library functions of the printf series (sprintf, fprintf) are all functions that can accept variable parameters. Assume that the following statement is provided:

Printf ("% d \ n", m, n, k );

We can see that we can get the number (number of format operators) and type (for example, % d) of parameters through the first parameter ), this is why printf ("% d \ n", m, n, k) can be successfully executed, while printf ("% d \ n", m, n) the cause of the failure. If the number of passed parameters is greater than the number of format characters, you can ignore them (the parameters can be executed normally in Linux). If the number is smaller than the number of format characters, access is out of bounds (a warning is reported in Linux, but the parameters are still executed normally, out-of-bounds parameters are random values ). (When executing your own program, remember to add./, for example,./A. Out)

2. Use the varargs macro to compile functions that support variable parameter lists. In the asci c standard, these macros are included in stdarg. h.

For example, the following code:

# Include <stdio. h>

# Include <stdarg. h>

Void _ cdecl fun (int n,...); // do not add _ cdecl

Int main (INT argc, char * argv [])

{

Int Part1 = 128;

Int Part 2 = 256;

Int Part3 = 512;

Fun (Part1, Part2, Part3 );

Return 0;

}

Void fun (int n ,...)

{

Va_list AP;

Va_start (AP, N );

Printf ("% d \ n", va_arg (AP, INT ));

Printf ("% d \ n", va_arg (AP, INT ));

Va_end (AP );

}

In the implementation provided by Microsoft for VC, we can see the following definition:

# DEFINE _ addressof (V) (& (v ))

# DEFINE _ intsizeof (N) (sizeof (n) + sizeof (INT)-1 )&~ (Sizeof (INT)-1 ))

Typedef char * va_list;

# Define va_start (AP, V) (AP = (va_list) _ addressof (v) + _ intsizeof (V) // AP points to the address of the next parameter in V

# Define va_arg (AP, t) (* (T *) (AP + = _ intsizeof (t)-_ intsizeof (t ))) // AP points to the address of the next parameter, but returns the value of this parameter. //

# Define va_end (AP) (AP = (va_list) 0)

Va_list a char pointer, each with a single byte addressing.

Va_start calculates the parameter type size through _ intsizeof and allows the AP to obtain the address of the parameter object after v.

Va_arg AP points to the next parameter object of the AP in the parameter list, and returns the T-type parameter object pointed to before the AP.

_ Intsizeof (n) converts the length of N to an integer multiple of the int length. If n is of the char type, sizeof (n) = 1 is converted to an integer multiple of the int length. Assume that sizeof (N) = 4 m + K (M> = 0, K =,), sizeof (n) + 4-1 = 4 m + (K + 3 ),~ (Sizeof (INT)-1) = ~ (4-1) = ~ (100000011b) = 11111100b, so that any number &~ (Sizeof (INT)-1), the last two digits must be 0, which is a multiple of 4, (4 m + (K + 3 ))&~ (Sizeof (INT)-1) can store 4 m + K.

Rewrite these macros to the fun function:

Void fun (INT Part1 ,...)

{

Int Part2, Part3;

Char * AP; // va_list AP;

AP = (char *) & Part1 + 4; // va_start (AP, Part1), AP points to Part2

Part2 = * (int *) (AP + = 4-4); // return Part2, AP points to Part3, Part2 = va_arg (AP, INT)

Part3 = * (int *) (AP + = 4-4); // return Part2, AP points to Part3, Part3 = va_arg (AP, INT)

AP = (char *) 0; // AP pointing to null, var_end (AP)

}

If Part2 is Char, Shor is automatically converted to int type, and float is automatically converted to double type.

# Include <stdio. h>
# Include <stdarg. h>
Void fun (int n ,...);

Int main (INT argc, char * argv [])
{
Int Part1 = 128;
Float part2= 256.0; // float
Float Part3 = 512.0;
Fun (Part1, Part2, Part3 );
Return 0;
}
Void fun (int n ,...)
{
Va_list AP;
Va_start (AP, N );
Printf ("% F \ n", va_arg (AP, double); // double is correct, float is incorrect, and is automatically stored as double
Printf ("% F \ n", va_arg (AP, double ));
Va_end (AP );
}

# Include <stdio. h>
# Include <stdarg. h>
Void fun (int n ,...);

Int main (INT argc, char * argv [])
{
Int Part1 = 128;
Char Part2 = 'C'; // char
Short Part3 = 8; // short
Fun (Part1, Part2, Part3 );
Return 0;
}
Void fun (int n ,...)
{
Va_list AP;
Va_start (AP, N );
Printf ("% C \ n", va_arg (AP, INT); // char is automatically stored as int and cannot use Char. Even so, % C output characters are still available.
Printf ("% d \ n", va_arg (AP, INT); // The short is automatically stored as int and cannot be used.
Va_end (AP );
}

These problems are caused by memory alignment.

3. vprintf, vfprintf, vsprintf, vsnprintf, and vasprintf format output. There is a va_list parameter.

# Include <stdio. h>

# Include <stdarg. h>

Int vprintf (const char * format, va_list AP); // format the output to the standard output, corresponding to printf

Int vfprintf (File * stream, const char * format, va_list AP); // format the output to the file stream, corresponding to fprintf

Int vsprintf (char * s, const char * format, va_list AP); // format the output string, corresponding to sprintf

Int vsnprintf (char * s, size_t N, const char * format, va_list AP); // format the output to a string with a fixed length, corresponding to snprintf

Int vasprintf (char ** ret, const char * format, va_list AP); // corresponds to asprintf

For example, use vprintf to Implement error:

# Include <stdio. h>

# Include <stdarg. h>

Void error (char * function_name, char * format ,...)

{

Va_list AP;

Va_start (AP, format );

/* Print out name of function causing Error */

(Void) fprintf (stderr, "err in % s:", function_name );

/* Print out remainder of Message */

(Void) vfprintf (stderr, format, AP );

Va_end (AP );

(Void) Abort ();

}

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.