[C Data Structures and Algorithms] Use the system stack, assembly code, and registers to understand C formal parameters and actual parameters

      I am a newbie, I just learned C language roughly, I am currently learning C data structures and algorithms, and I want to write about my learning experience and gains in my blog. This is the first one. Talk about formal parameters and actual parameters in C language.

      According to my study, the number of formal parameters and actual parameters is equal;

                                            2. Position relationship correspondence;

                                            3. Consistent type;

                                            4. The effect is equal to   passing by value (the value of the actual parameter expression, copy a copy, and pass it to the formal parameter) ;

      

      Here's an example: Implementing a swap that swaps two integer variables

#include <stdio.h>

void exchange (int,int);

void exchange (int *one,int *another)

{

int tmp;

tmp = *one;

*one = *another;

*another = tmp;

}


int main()

{

int num1;

int num2;

scanf ("%d",&num1);

scanf ("%d",&num2);

exchange (&num1,&num2);

printf ("The result is: %d,%d\n",num1,num2);

return 0;

}

From a memory perspective:

  1. num1 is pushed into the stack, and the top pointer of the stack is moved up by 4 bytes;
  2. num2 is pushed into the stack, and the top pointer of the stack is moved up by 4 bytes;
  3. Save the on-site information of the main function, and move the stack top pointer up by 8 bytes;
  4. Push the address information of num2 into the stack, and move the top pointer of the stack up by 4 bytes;
  5. Push the address information of num1 into the stack, and move the top pointer of the stack up by 4 bytes; 
  6. Pass the address information (first address) of num1 to one;  
  7. Pass the address information (first address) of num2 to another;   ! ! ! The space occupied by the value calculated by the actual parameter expression in the stack is the space of the formal parameter variable. The formal parameter and the actual parameter are the relationship of value transfer . The space occupied by the formal parameter is the space that was previously allocated to the actual parameter by the system. !
  8. tmp is pushed into the stack, and the top pointer of the stack is moved up by 4 bytes;
  9. Assign the value of the space pointed to by one (the value of num1) to tmp;
  10. Assign the value of the space pointed to by another (the value of num2) to the space pointed to by one;
  11. Assign tmp to the space pointed to by another;
  12. The stack top pointer is moved down by 12 bytes, that is, it points to the main address field information  ;
  13. Continue to execute the main function. . . output. .                                         

      One point to mention here: formal parameters are one-way passing, and any modification to the row parameter will not change the original value of the actual parameter expression!

     

      If you continue to explore the essence, you have to mention the content of assembly. The command line compiles the program (cl /FAs exchange.c), generates a file in asm format, and opens the file:


	TITLE	exchange.c
	.386P
include listing.inc
if @Version gt 510
.model FLAT
else
_TEXT	SEGMENT PARA USE32 PUBLIC 'CODE'
_TEXT	ENDS
_DATA	SEGMENT DWORD USE32 PUBLIC 'DATA'
_DATA	ENDS
CONST	SEGMENT DWORD USE32 PUBLIC 'CONST'
CONST	ENDS
_BSS	SEGMENT DWORD USE32 PUBLIC 'BSS'
_BSS	ENDS
_TLS	SEGMENT DWORD USE32 PUBLIC 'TLS'
_TLS	ENDS
FLAT	GROUP _DATA, CONST, _BSS
	ASSUME	CS: FLAT, DS: FLAT, SS: FLAT
endif
PUBLIC	_exchange
_TEXT	SEGMENT
_one$ = 8
_another$ = 12
_tmp$ = -4
_exchange PROC NEAR

; 6    : {

	push	ebp                 
	mov	ebp, esp              
	push	ecx
	 
	//ebp value onto the stack (protect main() ebp)
	//Form an "empty stack" again, which is the empty stack of exchange()

; 7    : 	int tmp;
; 8    : 	tmp = *one;

	mov	eax, DWORD PTR _one$[ebp]        //mov eax, 4B ebp[4]
	mov	ecx, DWORD PTR [eax]             
	mov	DWORD PTR _tmp$[ebp], ecx

; 9    : 	*one = *another;

	mov	edx, DWORD PTR _one$[ebp]
	mov	eax, DWORD PTR _another$[ebp]
	mov	ecx, DWORD PTR [eax]
	mov	DWORD PTR [edx], ecx

; 10   : 	*another = tmp;

	mov	edx, DWORD PTR _another$[ebp]
	mov	eax, DWORD PTR _tmp$[ebp]
	mov	DWORD PTR [edx], eax

; 11   : 	}

	mov	esp, ebp
	pop ebp     //Assign the current value at the top of the stack (main() ebp) to ebp, and esp--4 
	//Return ebp to the ebp state of the main function
	ret 0
	//mov eip, top of stack 
	//Restore the CPU running order and return to the state when the main function was interrupted
 

_exchange ENDP
_TEXT	ENDS
PUBLIC _main                       //Main function
EXTRN	_printf:NEAR
EXTRN	_scanf:NEAR
_DATA SEGMENT                       //Data area
$SG347	DB	'%d', 00H
	ORG $+1
$SG348	DB	'%d', 00H
	ORG $+1
$SG349	DB	0bdH, 0e1H, 0b9H, 0fbH, 0ceH, 0aaH, ':%d,%d', 0aH, 00H
_DATA	ENDS
_TEXT	SEGMENT
_num1$ = -4                  //
_num2$ = -8
_main	PROC NEAR

; 14   : {

	push ebp               //Push the ebp value into the stack, esp will be -4 
	mov ebp, esp            //Assign the value of esp to ebp; So, esp and ebp point to the same space at this time, forming an empty stack, each function Both have 
	sub esp, 8              //The value of esp is -8, which means that the stack top pointer is raised by 8B, which means that there is an 8B gap between the stack top pointer and the stack bottom pointer, and 8B should be two int spaces; is num1 and num2
	                       //Call the "function" of the main function (the code of the operating system), his ebp is protected (through push and mov), and the bottom space of the original stack is protected
; 15   : 	int num1;
; 16   : 	int num2;
; 17   : 	scanf ("%d",&num1);

	lea	eax, DWORD PTR _num1$[ebp]          //-4[ebp]    3[a] = a[3] 
	  
	//lea eax,DWORD PTR -4[ebp]  <=> lea eax,DWORD PTR  ebp[-4]
	//eax is stored in the int space starting with ebp-4
	//DWORD_PTR is the corresponding to int
	//The system stack grows to the lower end, which means that the push will be --esp, and the pop will be ++esp
	//esp: System stack top pointer. ebp: system stack bottom pointer
	
	//ebp above the stack bottom pointer, separated by 4B space, is the space of the local variable num1 of the main function
	
	//Look at the local variable num2 of the main function, (_num2$ = -8), for the same reason, he is farther away from the pointer at the bottom of the stack, separated by 8B space
	//! ! ! This means that there is a 4B space between the bottom of the stack and the first local variable (num1)! ! !           
	
	//5. This 4B space is the space of num1! ! ! Because the higher the address value in the system stack, the smaller the address value, the lower the address value is!
	
	push	eax
	push	OFFSET FLAT:$SG347
	call	_scanf
	add	esp, 8

; 18   : 	scanf ("%d",&num2);

	lea ecx, DWORD PTR _num2 $ [ebp]
	push	ecx
	push	OFFSET FLAT:$SG348
	call	_scanf
	add	esp, 8

; 19   : 	
; 20 : exchange (&num1,&num2);                // call the function

	lea	edx, DWORD PTR _num2$[ebp]
	push	edx
	lea	eax, DWORD PTR _num1$[ebp]
	push	eax
	//Push the first address of num2 and num1 into the stack;
	
	call	_exchange                
	               
	//The operation of push eip will be executed inside the call instruction! ! ! Protect the value of eip so that it can continue executing the next instruction of the calling function when it returns!
	//This instruction is the following add esp, 8; then call will execute mov eip, exchange; exchange is the first address constant of the sub-function exchange
	//CPU will take out the first instruction of the exchange function and start executing, that is, start executing the sub-function exchange
	//The call here is because push eip is executed; therefore, esp will be -4 again at this time
	
	add esp, 8    //esp drops 8 bytes, it just returns to the state before the call

; 21 : printf ("The result is: %d,%d\n",num1,num2);

	mov	ecx, DWORD PTR _num2$[ebp]
	push	ecx
	mov	edx, DWORD PTR _num1$[ebp]
	push	edx
	push OFFSET FLAT:$SG349             //The essence of the string constant is the first address constant of the string constant
	call	_printf
	add	esp, 12					; 0000000cH

; 22   : 	return 0;

	xor	eax, eax

; 23   : }

	mov	esp, ebp
	pop	ebp
	ret 0
_main ENDP
_TEXT	ENDS
END
It is recommended to draw and understand according to the assembly code and the understanding and comments I gave!

Guess you like

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