C Advanced Programming - memory partition

C Advanced Programming - memory partition

C Advanced Programming

data type

Concept of data type

Data type tells the compiler how much memory space is allocated to data to store data. Data type is the abstraction of data. For example, int represents integer, double represents double precision floating-point and char represents character.
The same data type has the same manifestation, storage format and related operations. All data in the program must have a clear data type. The data type can be understood as the mold for creating variables, that is, the alias of fixed size memory. For example, int number=10;, When the compiler sees this line of code, it will open up 4 bytes of space for the number variable, and the contents of this space will store the integer 4. The alias of this space is number.

Classification of C language data types used so far

Alias of data type

Use of typedef in structure

First, define a structure product, which contains product name, product price and inventory information

/*

	Define a product structure
*/
struct product {

	/*
		Product name
	*/
	char name[128];

	/*
		product price
	*/
	double price;

	/*
		Product inventory
	*/
	int inventory;

};

Declare and initialize the structure product when typedef is not used

struct product product1 = {"Alien 17 R5",39999.99,20};

In order to simplify the use of struct product, you can use the typedef keyword outside the function to declare a struct product, that is, typedef struct product alienware;, Then, in the main function, you can use the alias Alienware (equivalent to struct product) to initialize the product structure

	alienware product2 = { "Alien 15 R5",9999.99,20 };

typedef defines an alias for a structure

#define _CRT_SECURE_NO_WARNINGS //Resolve C4996 error
#include <stdio.h> //Standard I / O function declaration header file
#include <stdlib.h>// Standard library function declaration header file
#include <string.h> //String handler declaration header file



/*

	Define a product structure
*/
struct product {

	/*
		Product name
	*/
	char name[128];

	/*
		product price
	*/
	double price;

	/*
		Product inventory
	*/
	int inventory;

};


typedef struct product alienware;



/*

	typedef Use of 1 
	Omit the struct keyword when declaring variables of type struct
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{

	struct product product1 = {"Alien 17 R5",39999.99,20};

	alienware product2 = { "Alien 15 R5",9999.99,20 };

	printf("Product name%s\t Product price%.2lf\t Inventory of products%d\n",product1.name,product2.price, product1.inventory);
	printf("Product name%s\t Product price%.2lf\t Inventory of products%d\n", product2.name, product2.price, product2.inventory);


	//Pause the terminal, enter any key to exit the program
	system("pause");
	return 0; //Returns the value of normal exit
}


Program running results

In addition, there is a more common way for structures to define aliases, that is, when defining structures, they use typedef to define aliases.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


typedef struct product {
	/*
		Product name
	*/
	char name[128];

	/*
		product price
	*/
	double price;

	/*
		Product inventory
	*/
	int inventory;
} alienware;

/*
	typedef Another way to define a structure alias
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{

	//Use the alias of the product structure to initialize the structure
	alienware product1=  { "Alien 15 R5",9999.99,20 };

	system("pause");
	return 0;
}

typedef distinguishes data types

Declare two pointer variables of char * type char *p1, *p2;, If typedef char* PCHAR is used; After that, you can use the alias PCHAR to declare the pointer variable of char * type, PCHAR p1,p2;

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*

	typedef Distinguish data types
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/

//Alias char * PCHAR
typedef char* PCHAR;
int main(int argc, char* argv[])
{
	//Declare two pointer variables P1 and P2 of char *
	//char *p1, *p2;

	//Use typedef to alias char * PCHAR and declare pointer variables to distinguish data types
	PCHAR p1, p2;
	system("pause");
	return 0;
}

typedef improves code portability

The long long data type of C99 Standard cannot be used in C89 standard. Here, I can use typedef to define an alias for long long, typedef long long MY_INT;, Then use the alias my in the program_ Int as long long type.
Suppose you migrate to the C89 platform one day, you only need to add typedef long long my_ Change int to typedef int MY_INT is enough. However, most compilers currently support the C99 standard.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


typedef long long MY_INT;
/*
	typedef Improve code portability
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{

	MY_INT data = 100;
	MY_INT number = 100;

	system("pause");
	return 0;
}

void data type

Void literally means "no type". The universal pointer void * indicates a typeless pointer, which can point to any type of data.
The void definition variable is not arbitrary, when void num is declared; The compiler will make an error when the variable is.

The void keyword is mainly used in two aspects

  • The return value of the function uses void to indicate that there is no return value
  • The parameter void of the function indicates that there are no parameters
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>



int func() {
	return 10;
}

/*
	void Purpose 1: limit the return value of the function
	void Purpose 2: limit the parameters of the function
*/
void print_int_data(void) {

	printf("func() = %d\n",func());
}



/*
	void Purpose 3: Universal pointer
*/
void void_pointer() {

	//Pointer variables are 4 bytes in size
	void* p_void = NULL;
	printf("p_void The memory space occupied is%d Bytes\n",sizeof(p_void));

	int* p_int = NULL;
	char* p_char = NULL;
	//Type mismatch, cannot assign value warning C4133: "=": incompatible types from "int *" to "char *"
	//p_char = p_int;
	//The C4133 warning will not appear through cast
	p_char = (char*)p_int;
	//Due to p_void is a universal pointer. It can be converted into any type of pointer without forced type conversion
	p_char = p_void;
}


/*
	void Use of keywords
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{
	// A compilation error prevented the creation of untyped variables because the compiler did not know how much memory space was allocated
	//void num = 10;

	//print_int_data();

	void_pointer();
	system("pause");
	return 0;
}

sizeof operator

1. Essence is an operator
2. The return value is an unsigned int
3. Count the size of the array. When the array name is used as a formal parameter, it will degenerate into a pointer, which points to the address of the first element of the array

Sizeof is essentially an operator rather than a function, which is used to measure the number of bytes occupied by variables, constants or data types. However, sizeof operator can not add () when measuring variables, and must add () when measuring data types

printf("sizeof int =%d \n", sizeof(int));
double dbl = 3.14;
//The byte size of the measurement variable can be omitted ()
printf("sizeof dbl =%d \n", sizeof dbl);

The return value of sizeof operator is unsigned int, that is, unsigned integer. The results of unsigned int and signed int are unsigned

unsigned int number = 10;
	//The result of unsigned int and signed int operation is unsigned int, i.e. greater than 0
	if (number - 30 > 0) {

		printf("Greater than 0\n");
	}
	else {
		printf("Less than 0\n");
	}
	
	if (sizeof(int) - 5 > 0) {

		printf("Greater than 0\n");

		int result = sizeof(int) - 5;
		//Use unsigned printout to output the value of result 4294967295
		printf("result = %u\n", result);
	}

In addition, sizeof can also be used to calculate the length of the array. The length of the array can be obtained by dividing the total element size of the array by the size of a single element of the array

int array[10] = {1,2,3,4,5,6,7,8,9,10};
	//sizeof operator measures the size of an array
	printf("array Array occupation%d Bytes\n",sizeof array);
	calculate_array(array);
	int length = sizeof(array) / sizeof(array[0]);
	printf("array The length of the array is%d\n", length);

However, when an array is used as a formal parameter of a function, it will degenerate into a pointer. For example, int array [] will degenerate into int *array. By default, the pointer points to the address of the first element of the array. If sizeof(array) is used at this time, the size obtained is the size of the pointer variable, that is, 4 bytes.

/*
	Count the size of the array
*/
void calculate_array(int array[]) {

	//If the array name is passed into the formal parameter of the function, it will be degenerated into an int * pointer, which points to the address of the first element of the array. Therefore, int[] array is equivalent to int*
	printf("Formal parameter array Array occupation%d Bytes\n", sizeof array);

}

Use of sizeof operator

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>




/*
	sizeof()Type of return value
*/
void sizeof_return_type() {



	unsigned int number = 10;
	//The result of unsigned int and signed int operation is unsigned int, i.e. greater than 0
	if (number - 30 > 0) {

		printf("Greater than 0\n");
	}
	else {
		printf("Less than 0\n");
	}


	if (sizeof(int) - 5 > 0) {

		printf("Greater than 0\n");

		int result = sizeof(int) - 5;
		//Use unsigned printout to output the value of result 4294967295
		printf("result = %u\n", result);
	}

}
/*
	Count the size of the array
*/
void calculate_array(int array[]) {

	//If the array name is passed into the formal parameter of the function, it will be degenerated into an int * pointer, which points to the address of the first element of the array. Therefore, int[] array is equivalent to int*
	printf("Formal parameter array Array occupation%d Bytes\n", sizeof array);

}

/*
	sizeof()Other uses of operators
	1.Count the length of the array
*/
void sizeof_operator() {

	int array[10] = {1,2,3,4,5,6,7,8,9,10};
	//sizeof operator measures the size of an array
	printf("array Array occupation%d Bytes\n",sizeof array);
	calculate_array(array);
	int length = sizeof(array) / sizeof(array[0]);
	printf("array The length of the array is%d\n", length);

}


/*

	sizeof()Operator usage

	sizeof You can measure the byte size occupied by variables or constants
	However, sizeof does not need to add parentheses () when measuring variables, and () when measuring statistical types
	sizeof The essence of is an operator, not a function

	sizeof The return value of the operator is an unsigned integer, that is, unsigned int
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{

	printf("sizeof int =%d \n", sizeof(int));
	double dbl = 3.14;
	//The byte size of the measurement variable can be omitted ()
	printf("sizeof dbl =%d \n", sizeof dbl);

	sizeof_return_type();
	sizeof_operator();

	system("pause");
	return 0;
}

Program running results

variable

The nature of variables

Variables represent memory objects that can be read and written. Objects that cannot be modified once initialized are called constants. Variables must be defined before use. The syntax format of defining variables is data type, variable name = variable value;

The nature of variable names

  • The variable name represents the alias of a continuous space
  • The program applies for and names the memory space through variable names, such as int number=10; It means to apply for 4 bytes of memory space to store integer 10. The alias of this space is 4
  • Access memory space by variable name
  • When reading and writing data, it is not for the variable name, but to read and write data to the memory space represented by the variable name

Two ways to modify variables

At present, there are two ways to modify the value of ordinary variables, for example, int number =10;,number=20; Direct modification. The other is to modify the value of the variable through the pointer, int * P_ number=&number;,* p=30;

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
	Two ways to modify variables
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{

	//directly modify
	int number = 10;
	number = 20;

	printf("directly modify number = %d\n",number);

	int* p_number = &number;
	//Modifying the value of a variable through a pointer
	*p_number = 30;
	printf("Modify by pointer number = %d\n", number);

	system("pause");
	return 0;
}

Program running results

The value of structure members can also be modified by direct modification and indirect modification

First, define a structure product, alias is alienware

/*

	Define a product structure
*/
typedef struct product {

	/*
		Product name
	*/
	char name[128]; //128 bytes

	/*
		product price
	*/
	double price; //Eight bytes 

	/*
		Product inventory
	*/
	int inventory; //4 bytes

}alienware;

Then initialize the structure in the main function

	alienware product1 = { "Alien 17 R5",39999.99,20 };
	printf("Initialize product data:Product name%s\t Product price%.2lf\t Inventory of products%d\n", product1.name, product1.price, product1.inventory);

Struct modifies the value of a struct member through a struct variable

//Modify the value of the attribute directly through the structure variable
	//10% off the price
	product1.price *= 0.9;
	printf("[Modify attribute value directly through structure variable] product name%s\t Product price%.2lf\t Inventory of products%d\n", product1.name, product1.price, product1.inventory);

Modify the value of structure member through structure pointer

	//Modify the value of the attribute through the structure pointer variable
	struct product* p_product = &product1;
	//20% off the price
	p_product->price *= 0.8;
	printf("[Pointer variable through structure p_prodcut Modify attribute value] product name%s\t Product price%.2lf\t Inventory of products%d\n", product1.name, product1.price, product1.inventory);

Except through structure pointer p_ In addition to product to modify the attribute value, you can also define a pointer of char * type to modify the price. Before modifying the price, you need to know the arrangement of the elements of the structure in memory. You can calculate the size of the structure through the sizeof operator. When the structure variable is increased by 1, the whole structure is exaggerated.

	printf("structural morphology product1 The size of is%u\n",sizeof product1);
	//Get structure pointer variable p_ Address of prodcut
	printf("Structure pointer variable p_product Your address is%u\n", p_product);
	//144 bytes across a structure
	printf("Structure pointer variable p_product+1 Your address is%u\n", p_product + 1);

If you modify the price through a pointer variable of type char *, you need to know the location of the price in memory

	printf("product1.name Your starting address is%u\n", &product1.name);
	printf("product1.price Start address of%u\n", &product1.price);
	printf("product1.inventory Your starting address is%u\n", &product1.inventory);

After knowing the location of the price in memory, you can modify the price through the arithmetic operation of the pointer and forced type conversion, where p_ The value of price is the value of the structure variable product1, p_price + 128 is the memory address of the price attribute
And p_price is of char * type, which means that the width of the pointer is 1, while the type of price is double, so it needs to be converted to double *, and the width of double * is 8 bytes.

	//Through pointer variable p of char * type_ Price to modify the price
	char* p_price = &product1;
	printf("Pointer variable p_price Your address is%u\n", p_price);
	double current_price = (*(double*)(p_price + 128));
	printf("What is the price of the current product%.2f\n",current_price);
	(*(double*)(p_price + 128))*=0.7;
	printf("[adopt char*Pointer variable p_price Modify price attribute value] product name%s\t Product price%.2lf\t Inventory of products%d\n", product1.name, product1.price, product1.inventory);

In the same way, you can modify the inventory of products

	//Through pointer variable p of char * type_ Inventory to modify inventory
	char* p_inventory = &product1;
	int current_inventory = (*(int*)(p_inventory + 128+8));
	printf("Current product inventory is%d\n", current_inventory);
	//Double inventory
	(*(int*)(p_inventory + 128 + 8)) += 20;

	printf("[adopt char*Pointer variable p_inventory Modify value of inventory attribute] product name%s\t Product price%.2lf\t Inventory of products%d\n", product1.name, product1.price, product1.inventory);

Program running results

Static variable

Static variables are variables decorated with static. When static variables are defined outside the function, they can be used in the current source file. When they are defined inside the function, they can only be used within the scope inside the function. Static variables are initialized only once. Static variables allocate memory before the program runs
, its life cycle dies after the program runs. Static variables belong to internal link attribute by default and can only be used in the current file, not across multiple source files.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

//Define a global static variable
//Static variables allocate memory before the program runs
//The life cycle dies after the program runs
//Static variables are internal link attributes by default and can only be used in the current file, not across multiple source files.
static int number = 10;



/*
	Static variable
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{
	//data can only be used within the scope of the main function
	static int data = 10;
	system("pause");
	return 0;
}

global variable

The extern keyword is added in front of the global variable of C language by default. The global variable belongs to the external link attribute, that is, the global variable can be used across multiple source files. The global variable is usually defined outside the function. When it is used across files, the extern declaration is used to tell the compiler that there is the global variable in other files. When it is linked, it can be found in other files. If it is not found, an error will be reported in the program link.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
	Use of global variables
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{
	extern int datax;

	printf("datax =%d \n",datax);
	system("pause");
	return 0;
}


Use of global variables

constant

Variables modified by const in C language are constants. Constants can be divided into global constants and local constants according to different scopes. Global constants are stored in the constant area and cannot be modified directly or through the pointer. When the pointer is modified, the syntax passes, but there is an error in operation.
Local constants are stored in the stack area and belong to pseudo constants, that is, they are not real constants, and their constant values cannot be used for array initialization. You can modify the value of a local constant through a pointer, but you cannot directly modify the value of a local constant

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

// The const modified global constant is placed in the constant area and protected by the constant area
const int _datax = 10;
/*		
	const Constants cannot be modified directly
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{
	//The global const constant cannot be modified directly. Compilation fails directly
	//data = 20;

	//However, it can be modified by pointer
	//Although the syntax passed, but the operation failed
	int* p_data = &_datax;


	//The pointer to the local variable can be modified on the stack, that is, the local variable can be put on the stack
	const int number = 10;
	printf("Local constant number = %d \n", number);

	//number = 20;
	int* p_number = &number;
	*p_number = 20;

	printf("Modifying the value of a local constant through a pointer*p_number = %d \n",*p_number);

	//Pseudo constants cannot be used as array initializers
	//int array[number];


	system("pause");
	return 0;
}

string constant

String constants are the contents contained in "" and are stored in the constant area. Whether they can be modified depends on the compiler. MSVC and GCC compilers cannot be modified. Multiple identical string constants can be shared to save memory. For example, MSVC compiler will treat multiple identical strings as the same. For example, when multiple pointer variables point to the same string constant at the same time, the address is the same.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*
	string constant 
	When using different pointers to the same string constant, the address of the pointer is the same.
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{
	//Initialize three pointer variables of char * type to point to the same string constant hello world
	char* p1 = "hello world";
	char* p2 = "hello world";
	char* p3 = "hello world";

	//p1 p2 p3 is the address of the same constant area
	// 	When using different pointers to the same string constant, the address of the pointer is the same.
	printf("p1 = %d\tp2 = %d\tp3 = %d\n",p1,p2,p3);
	printf("hello world Your address is%d\n",&"hello world");

	// If the compilation passes, an error will occur at runtime, because hello world is located in the constant area and cannot be modified through the pointer
	//p3[0] = 'y';

	system("pause");
	return 0;
}


Program running results

Memory partition of program

Memory partition before running

Before the program runs, it needs to go through four steps: preprocessing, compiling, assembling and linking

  • Preprocessing: macro replacement, header file inclusion, conditional compilation, no syntax check
  • Compilation: check syntax errors and generate the preprocessed file into an assembly file
  • Assembly: generate assembly file into object file (binary file, binary file of Windows is. o)
  • Link: convert the target file to an executable file (the executable file suffix of Windows is. exe)

When the source file is preprocessed, compiled, assembled and linked, use size HelloWorld on Windows Exe to view the memory distribution of the executable file. The unit size is in bytes

  • text indicates the code area, that is, the number of memory bytes occupied by the code
    The code area stores the machine instructions executed by the CPU. The code area can be shared (that is, other programs can call it). The purpose of making it shareable is that for frequently executed programs, only one code needs to be in memory. The code area is usually read-only, that is, the program is read-only when running. The reason is to prevent the modification of instructions outside the program. The code area also stores the information of local variables.

  • Data represents the data area
    The data area is used to store global variables (external definitions of functions), static variables (static modified variables), constants (string constants, const modified variables), and the data in the data area is initialized.

  • bss represents an uninitialized data area
    Stored are uninitialized global variables (externally defined by the function), static variables (static modified variables), constants (string constants, const modified variables). The data in the uninitialized data area is initialized to 0 or NULL by the kernel before the program starts execution. For example, the initialization value of statc int num is 0, while the initialization value of static double dbl is 0.0, and the data of structure type is initialized to NULL (essentially 0).

  • dec indicates the size of text+data+bss displayed in decimal

  • hex indicates the size of text+data+bss displayed in hexadecimal

  • filename indicates the file name

Generally speaking, before the program runs, the memory is divided into code area and data area. The code area is shared, read-only, and stores machine instructions. The data area stores static variables, global variables and constants. The data area is subdivided into uninitialized and initialized parts.

Memory partition after running

After the program runs, it opens up two memory spaces: stack and heap

  • Stack is a first in and last out data structure (similar to bullet clip). Stack memory is automatically allocated and released by the compiler. The stack area is used to store function parameters, return values, local variables and so on. The data in the stack area is loaded and released in real time during the operation of the program, so the life cycle of the local variable is before the end of its function, that is, the local variable is released by the system at the end of the function. The memory capacity of the stack is small. If you don't pay attention to the program, it is easy to cause stack overflow.

  • Heap is a large container. Its capacity is much larger than that of stack, but the heap area is not in the order of first in and last out like stack. It is used for dynamic memory allocation. Heap is located between uninitialized data area (BSS) and stack area in memory. Heap area is generally allocated and released by programmers. For example, call library function malloc() to apply for heap memory space of specified size, and then use free() function to release heap memory space, If the programmer does not release the heap memory space, the operating system will release the requested heap memory space at the end of the program (such as closing the terminal window of Visual Studio 2019).
    A good development habit is that malloc() and free() appear in pairs and are released in time after the application is completed.


Memory structure

Precautions for stack area and heap area

Precautions for stack area

The variables declared inside the function body are called local variables, which are stored in stack memory. When the local variables cannot be used as the return value of the function, because the system will release the memory space corresponding to the local variables after the end of the function, and the declaration cycle of the local variables is before the end of the function. Another operation is illegal and the result is unknown.

Define the pointer of int type returned by the method, and then define the variable in a method to receive the return value of the function multiple times. The result is an uncertain result

/*
	Defines the pointer to the local variable returned by the method
*/
int* get_number_address() {


	int number = 10;
	return &number;
}

/*
	Multiple get_ number_ It is uncertain that the address () method gets the content pointed to by the pointer
*/
void test_get_number_address() {

	int* p = get_number_address();
	//Get the content pointed to by the pointer returned by getNumberAddress() multiple times, and number has long been released
	printf("*p = %d \n",*p);
	//It is illegal to operate the memory of number again, and the result is unknown
	printf("*p = %d \n",*p);

}

Define a pointer of char* type returned by a method, and then call the method in another method to get a garbled code

/*
	Return the first address of hello world
*/
char* get_string() {

	//str is located in the stack area
	// The helloworld string is located in the constant area, and the data in the constant area is read-only
	//helloworld will be copied to the stack area

	char str[] = "hello world";
	//Return to the end of str function. The helloworld in the stack area no longer exists and is released by the system
	return str;
}

void test_get_string() {

	//Stack area
	char* p = NULL;
	//
	p = get_string();
	//Cannot print hello world
	printf("p = %s \n",p);
}

Stack area precautions

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

/*
	Precautions for use of stack area
	The variables inside the function are local variables, and the life cycle ends after the function ends
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{
	test_get_number_address();
	test_get_string();
	system("pause");
	return 0;
}

Program running results

Precautions for stacking area

The memory in the heap area is applied through malloc(size) function, and then the space is released through free(p).
The difference between heap area and stack area is that local variables can be used as the return value of the function. Even if the function ends execution, the data in heap area can be operated through the pointer of local variables

Correct use of stacking area

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>



/*
	Heap memory opens up space to store integer arrays and initialize assignments
*/
int* get_space() {

	//Open up 40 bytes of heap memory space to store int type data
	int*p =(int*)malloc(sizeof(int)*10);

	//No assignment succeeded
	if (NULL==p) {
		perror("Failed to allocate heap memory\n");
		return NULL;
	}

	//Assign values to the elements of the array
	for (int i = 0; i< 10;i++) {
	
		p[i] = 100 + i;
	}

	return p;
}


/*
	Proper use of heap area
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{

	int* p = get_space();
	//Traverse array elements
	for (int i = 0; i < 10;i++) {
		printf("p[%d]=%d\n",i,p[i]);
	}

	//Free heap data
	free(p);
	//The pointer is set to null
	p = NULL;
	system("pause");
	return 0;
}

Program running results

Precautions for use of stacking area:

If the pointer in the calling function (for example, main here is the calling function) is empty (for example, p_stack is empty), the same level pointer (char* p_args) cannot be used to allocate memory in the called function (allocate_space() here is the called function),
Low level pointers (such as char* p_stack) should be decorated with high-level pointers (such as char** p_args_multi_level)

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>


void malloc_space(char* p_args) {

	//The heap area requests 100 bytes of space
	// temp is located in the stack memory and stores the first address of the requested heap memory
	char* temp = (char*)malloc(100);
	//Clear the requested space 0
	memset(temp, 0, sizeof(100));
	// helloworld is a constant area
	//strcpy() is responsible for copying helloworld to the heap memory of the request
	strcpy(temp, "helloworld");
	// Change p_args will not affect the incoming p_stack, because they all pass values
	p_args = temp;


}

/*
	The secondary pointer receives the address of the primary pointer
	At this point p_args_multi_level is not empty. It stores the address of the first level pointer

*/
void malloc_space2(char** p_args_multi_level) {

	//The heap area requests 100 bytes of space
	// temp is located in the stack memory and stores the first address of the requested heap memory
	char* temp = (char*)malloc(100);
	//Clear the requested space 0
	memset(temp, 0, sizeof(100));
	// helloworld is a constant area
	//strcpy() is responsible for copying the helloworld of the constant area to the requested heap memory
	strcpy(temp, "helloworld");
	//*p_args_multi_level is equivalent to * p_stack
	*p_args_multi_level = temp;

}




/*
	Precautions for use of stacking area
	If the pointer in the calling function (for example, main here is the calling function) is empty (for example, p_stack is empty), the same level pointer (char* p_args) cannot be used to allocate memory in the called function (allocate_space() here is the called function),
	Low level pointers (such as char* p_stack) should be decorated with high-level pointers (such as char** p_args_multi_level)
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{
	
	char* p_stack = NULL;
	malloc_space(p_stack);
	//p_stack is NULL
	printf("Pass first level pointer p_stack = %s\n", p_stack);

	//Pass the address of the first level pointer
	malloc_space2(&p_stack);
	//p_stack is helloworld
	printf("After passing the address of the first level pointer p_stack = %s\n", p_stack);

	system("pause");
	return 0;
}

Program running results

Function call model

Macro function

Before using the macro function, go back to the macro constant, that is, the macro constant defined with #define. For example, #define NUMBER 10 defines a macro with a value of 10. During precompiling, the NUMBER will be replaced with an integer of 10. It should be noted that macro definitions do not need to end with semicolons.
Macro functions are also defined with #define, such as #define MY_ADD(x,y) x+y means that a macro function my is defined_ Add () method is used to calculate the added value of two variables. In order to ensure the operation integrity of macro function, the expression x+y needs to be enclosed by (), because #define is only replaced during precompiling.

The usage scenario of macro functions is that short functions that are frequently called can be encapsulated as macro functions. Its advantage is to trade space for time. Because the parameters of ordinary functions need to consume space, while macro functions do not.

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define NUMBER 10

#define MY_ADD(x,y) x+y //Macro operations maintain the integrity of operations
#define MY_ADD_V2(x,y) ((x)+(y))


/*
	Macro function
	Usage scenario: frequently short functions encapsulate macro functions
	Advantages: space for time
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{
	//Using macros to define NUMBER
	printf("NUMBER = %d \n",NUMBER);

	int left = 10;
	int right = 20;
	
	int result = MY_ADD(left, right);
	printf("%d + %d = %d \n", left, right, result);
	result = MY_ADD(left, right) * 20;
	printf("%d + %d *20 = %d \n", left, right, result);

	printf("%d + %d = %d \n", left,right,MY_ADD_V2(left, right)) ;
	printf("%d + %d *20 = %d \n", left, right, MY_ADD_V2(left, right) * 20);
	system("pause");
	return 0;
}

Program running results

Function calling process

The information required for the process of a function call includes the following aspects

  • Return address of function
  • Parameters of function
  • Temporary variable
  • Saved context: includes registers that need to remain unchanged before and after function calls

First, write a function to complete the addition of two integers, and then call this function in the main function to realize the addition of 10 and 20

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>



int add_int(int left,int right) {

	int tmp_left = left;
	int tmp_right = right;
	//The register of CPU completes the operation of two variables
	return tmp_left + tmp_right;

}

/*
	
	Call procedure of function
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{

	int result = 0;
	result = add_int(10,20);

	system("pause");
	return 0;
}

Specific calling process of main function

The program starts with the first line of the main function
C/C + + function calls follow the cdecl calling convention. The calling convention refers to that the caller (also known as the calling function) and the callee (the called function) of the function have a clear regulation on how to call the function. Only when both parties abide by the same regulation can the function be called correctly. Such a convention is the calling convention.
The calling convention mainly specifies the stacker, the order of parameter transfer and name modification, while the function caller (i.e. the calling function) of cdecl calling convention is the stacker, the parameter transfer is from right to left, and the name modification adopts underline + function name.

  1. First, the local variable result is put on the stack with a value of 0
  2. When executing to result = add_int(10,20);, Stack record add_ The return address of the int() function, because a jump will occur.
  3. add_ The parameters left and right of int() function are stacked from right to left, and their variable values are assigned by the actual parameters, which are 10 and 20 respectively
  4. add_ Temporary variable TMP inside int() function_ Left and tmp_right is put on the stack and assigned by the formal parameter, whose values are 10 and 20 respectively
  5. return tmp_left + tmp_right is completed in the register inside the CPU. After that, the calculation result 30 is put on the stack and returned to result, that is, the value of result is 30 at this time
  6. When add_ After the int() function is executed, the formal parameters left,right and the temporary variable temp_left,temp_right will come out of the stack
  7. When the main function is executed to return 0, the program execution is completed and the local variable result is out of the stack.

Function variable transfer

  • Suppose that the main function calls sub function 1, sub function 1 calls sub function 2, and all sub functions can use the memory opened up by the main function in the stack area or heap area.
  • Suppose that main function calls subfunction 1 and subfunction 1 calls subfunction 2. The memory space opened up by subfunction 1 in the stack area can be used by subfunction 1 and subfunction 2.
  • Suppose that main function calls subfunction 1 and subfunction 1 calls subfunction 2. The memory space opened up by subfunction 1 in the heap area can be used by subfunction 1 and subfunction 2.
  • Suppose that main function calls subfunction 1, subfunction 1 calls subfunction 2, and subfunction 2 opens up memory in the global area. Both subfunction 1 and main function can be used.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>




void child(int* number, int* data) {

	printf(" child method -> number = %d \t data = %d ", *number, *data);

}


void parent(int* number) {


	//The data variable can be used in the parent and child functions
	int data = 10;

	child(number,&data);
}
/*

	Variable transfer of function
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{
	//The number variable can be used in the main(),parent(),child() function
	int number = 10;
	parent(&number);
	system("pause");
	return 0;
}

Growth direction of stack and memory storage mode

Stack is a first in and last out data structure. The growth direction of stack is that the top of stack is low address and the bottom of stack is high address.

##define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/*

	Growth direction of stack and storage mode of memory
	@author liuguanglei 18601767221@163.com
	@wechat 18601767221
	@website ittimeline.net
	@version 2020/11/30
*/
int main(int argc, char* argv[])
{

	//Stack is a first in and last out data structure

	//Therefore, number is at the bottom of the stack and result is at the top of the stack

	int number = 10;
	int data = 20;
	int value = 30;
	int result = 40;

	//Print out the addresses of the four variables
	printf("number Your address is%d\n",&number); //Stack bottom high address
	printf("data Your address is%d\n", &data);
	printf("value Your address is%d\n", &value);
	printf("result Your address is%d\n", &result);//Stack top low address

	system("pause");
	return 0;
}

Program running results
Running from the top of the stack to the bottom of the stack is high, so the result is that the program runs from the top of the stack to the bottom of the stack.

The storage of data in the stack is also low address storage low byte, high address storage high byte, also known as small end alignment.

int tmp = 0x10203040;
	char* p = &tmp;
	printf("Pointer variable p Your address is%p\n",&p);
	printf("*p = %#x \n",*p); //Low address stores low bytes

	printf("*(p+1) = %#x \n",*(p+1)); //High address stores high bytes

Posted by Cragsterboy on Wed, 04 May 2022 08:12:29 +0300