The Road to C Language Learning (Advanced) - Variables and Memory Distribution (Part 1)

Note: This blog is written word by word by the blogger, which is not easy. Please respect the originality, thank you all!

type of data

1) Data type concept

What are data types? Why do we need data types?

The data type is for better memory management and allows the compiler to determine how much memory to allocate.

In our real life, dogs are dogs, birds are birds, etc. Each thing has its own type, so the data types used in programs also come from life.

When we allocate memory to a dog, it is equivalent to building a doghouse for the dog. When we allocate memory to a bird, it is equivalent to building a birdhouse for the bird. We can build a villa for each of them, but it will cause a waste of memory. , cannot make good use of memory space.

We are thinking that if we allocate memory to a bird, we only need a space the size of a bird's nest. If we allocate memory to a dog, we only need a memory the size of a dog's nest. Instead of allocating a villa to both birds and dogs, causing memory problems. of waste.

When we define a variable,a = 10, how does the compiler allocate memory? A computer is just a machine, how does it know how much memory can fit10?
Therefore, the data type is very important. It can tell the compiler how much memory to allocate to hold our data.

There are dogs in the doghouse and birds in the birdhouse. If there are no data types, how do you know there is an elephant in the refrigerator?

Basic concepts of data types:

  • A type is an abstraction of data;
  • Data of the same type has the same representation, storage format, and related operations;
  • All data in the program must be of a certain data type;
  • Data type can be understood as a mold for creating variables: An alias for fixed-size memory;

Insert image description here

2) Data type alias

Sample code:

#define _CRT_SECURE_NO_WARNINGS // VS不建议使用传统库函数,如果不用这个宏,会出现一个错,编号:C4996
#include <stdio.h> // std 标准 i input  输入   o  output 输出 
#include <stdlib.h> // strcpy   strcmp  strcat  strstr
#include <string.h> // // malloc  free

//程序入口
int main()
{
    
    

	system("pause"); // 按任意键暂停  阻塞功能
	return EXIT_SUCCESS; //返回 正常退出值  0
}

2.1 Simplify structure keywords

typedefUse, simplify structure keywordsstruct

struct Person
{
    
    
	char name[64];
	int age;
};
typedef struct Person  myPerson;

void test01()
{
    
    	
	// 未使用typedef,在初始化成员时需要加struct修饰
	struct Person p1 = {
    
     "张三", 19 };
	// 使用typedef,可以简化结构体关键字struct
	myPerson p2 = {
    
     "李四", 20 };
}

Simplify the above writing

//主要用途  给类型起别名
//语法  typedef  原名  别名
typedef struct Person
{
    
    
	char name[64];
	int age;
}myPerson;

void test01()
{
    
    	
	// 未使用typedef,在初始化成员时需要加struct修饰
	struct Person p1 = {
    
     "张三", 19 };
	// 使用typedef,可以简化结构体关键字struct
	myPerson p2 = {
    
     "李四", 20 };
}

2.2 Distinguish data types

typedefUse to distinguish data types

// 2、区分数据类型
void test02()
{
    
    
	char* p1, p2; // p1是char *  而 p2 是char
}

Verify the data of and through the method in c++ Typetypeidp1p2

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
using namespace std;

int main()
{
    
    
	char* p1, p2;
	printf("p1的数据类型为:%s\n", typeid(p1).name());
	printf("p2的数据类型为:%s\n", typeid(p2).name());

	system("pause");
	return EXIT_SUCCESS;
}

Insert image description here

transmissiontypedeftravelp1, p2citychar *numerical fixed type< /span>

// 2、区分数据类型
void test02()
{
    
    
	//char* p1, p2; // p1是char *  而 p2 是char
	// 通过typedef来区分数据类型
	typedef char* PCHAR;
	PCHAR p1, p2;
	char *p3, *p4; // p3 和 p4都是char *
}

Insert image description here

2.3 Improve code portability

For example, put the following code to run under C89. C89 does not support the long long type and cannot run the program. Then you need to change all long long types to int integers, which is very mechanical.

void test03()
{
    
    
	long a = 10;

	long b = 20;
}

So in order to avoid this kind of situation from happening (currently, compilers support standards above C99, just for example), after using typedef, you only need to replace is finetypedef long long MYINT;long long

//3、提高代码移植性
typedef int MYINT; //typedef long long MYINT; 只需要替换 long long 就可以了
void test03()
{
    
    
	MYINT a = 10;

	MYINT b = 10;
}

3) void data type

voidLiterally means" no type",< /span>void* Untyped pointer (universal pointer), untyped pointer can point to any type of data.

voidIt makes no sense to define variables with . When you define void a, the compiler will report an error.

voidIt is really used in the following two aspects:

  • Limitations on function returns;
  • Limitations on function parameters;

3.1 Variables cannot be created without type

// 1、无类型是不可以创建变量的
void test04()
{
    
    
	void a = 10; // error 编译器直接报错,因为不知道给a分配多少内存空间
}

3.2 Function return values ​​can be limited

void func01()
{
    
    
	return 10;
}

void test05()
{
    
    
	printf("%d\n", func01()); // error %d 是需要整型格式,但是func01方法具有viod类型,所以出错
}
//2、可以限定函数返回值
void func01()
{
    
    
	return 10;
}

void test05()
{
    
    	
	func01(); // 即使可以编译过去,但是会给出一个警告
	//printf("%d\n", func()); // error %d 是需要整型格式,但是func方法具有viod类型,所以出错
}

Insert image description here

3.3 You can limit the function parameter list

When calling a parameterless function, pass parameters

int func02()
{
    
    
	return 10;
}
void test06()
{
    
    	
	func02(10, 20);  // 编译成功,无报错和警告,这也是c语言中存在不严谨的地方之一
}

The function parameters arevoid

//3、限定函数参数列表
int func02(void)
{
    
    
	return 10;
}
void test06()
{
    
    	
	func02(10, 20); // 编译成功,无错误,但是有警告,会提示我们这里其实存在问题
}

Insert image description here

3.4 Can be used as a universal pointer type

Printvoid *The size of the universal pointer

//4、void *  万能指针
void test07()
{
    
    
	void * p = NULL;
	printf("size of void *   = %d\n", sizeof(p));  // 4个字节
}

Assignment between pointers of different data types

//4、void *  万能指针
void test07()
{
    
    
	void* p = NULL;
	printf("size of void *   = %d\n", sizeof(p));  // 4个字节

	int* pInt = NULL;
	char* pChar = NULL;

	pInt = pChar; // 编译无误,会警告 “char *”到“int *”的类型不兼容
}

Insert image description here

When assigning values ​​between pointers of different data types, casts are required to avoid warnings.

//4、void *  万能指针
void test07()
{
    
    
	void* p = NULL;
	printf("size of void *   = %d\n", sizeof(p));  // 4个字节

	int* pInt = NULL;
	char* pChar = NULL;

	//pInt = pChar; // 警告 “char *”到“int *”的类型不兼容
	pInt = (int *)pChar;
}

Universal pointers can be converted into pointers of any type without forced type conversion.

//4、void *  万能指针
void test07()
{
    
    
	void* p = NULL;
	printf("size of void *   = %d\n", sizeof(p));  // 4个字节

	int* pInt = NULL;
	char* pChar = NULL;

	//pInt = pChar; // 警告 “char *”到“int *”的类型不兼容
	pInt = (int *)pChar; // 不同数据类型的指针之间赋值,需要进行强制转换才不会出警告
	pInt = p; // 万能指针可以不通过强制类型转换,就可以转为任意类型的指针
}

4) sizeof operator

sizeof is an operator in the c language, similar to ++, --, etc. sizeofIt can tell us the size allocated by the compiler when allocating space in memory for a specific data or a certain type of data. The size is measured in bytes a> is the unit.

Basic syntax:

sizeof(variable); sizeof variable; sizeof(type);

sizeof Note:

  • sizeofThe size of the space returned is the size allocated for this variable, not just the space it uses. It is similar to the concepts of building area and usable area of ​​today’s housing. Therefore, when using structures, in most cases you have to consider the issue of byte alignment;
  • sizeofThe data result type returned by isunsigned int;
  • Pay attention to the difference between array names and pointer variables. Under normal circumstances, we always feel that array names and pointer variables are similar, but there is a big difference when using sizeof. Using sizeof for an array name returns the entire array. Size, and when operating on a pointer variable, what is returned is the space occupied by the pointer variable itself, which is generally under the conditions of the 32 bit machine. And when the array name is used as a function parameter, inside the function, the formal parameter is also a pointer, so the size of the array is no longer returned;4

4.1 Basic usage of sizeof

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

// sizeof是不是一个函数?
// 1、本质不是一个函数, 是一个运算符,如 + - * /
void test08()
{
    
    
	// sizeof在统计类型的时候,是需要添加小括号的
	printf("sizeof int = %d\n", sizeof(int));

	// sizeof在统计变量的时候,是可以不加小括号的
	double d = 3.14;
	printf("sizeof d = %d\n", sizeof d);
}

int main()
{
    
    	
	test08();
	system("pause");
	return EXIT_SUCCESS;
}
输出结果
sizeof int = 4
sizeof d = 8

4.2 sizeof result type

First, before verifying that the sizeof return result is of type unsigned int, let’s take a look at the operation results of unsigned and signed types.

// 2、sizeof返回值是什么? unsigned int
void test09()
{
    
    
	unsigned int a = 10; // 如果一个unsigned int 和 int进行运算,那么会将结果统一转换为 unsigned int 类型
	if (a - 20 >0)
	{
    
    
		printf("结果大于0\n");
	}
	else
	{
    
    
		printf("结果小于0\n");
	}
}

int main()
{
    
    	
	//test08();
	test09();
	system("pause");
	return EXIT_SUCCESS;
}
运算结果
结果大于0

Through the above judgment results, we can verify whether the return type of sizeof is the unsigned int type

void test09()
{
    
    
	if (sizeof(int)-5 > 0)
	{
    
    
		printf("结果大于0\n");
		printf("%u\n", sizeof(int) - 5);
	}
}

Insert image description here

4.3 Other uses of sizeof

Statistical array occupies memory space

// 3、sizeof其他用法
// 统计数组占用内存空间大小
void test10()
{
    
    
	int arr[] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	printf("sizeof(arr) = %d\n", sizeof(arr));  // 40  int类型4*10个元素;
}

To explain, after the array name is passed into the function as a formal parameter, it will degenerate into a pointer, and the pointer points to the address of the first element of the array.

void calcArray(int arr[])  // 当数组名传入到函数中,会被退化成一个指针,指向数组中第一个元素的地址 int arr[] 就等价于 int *arr
{
    
    
	printf("calcArray->sizeof(arr) = %d\n", sizeof(arr)); // 4

}
// 3、sizeof其他用法
// 统计数组占用内存空间大小
void test10()
{
    
    
	int arr[] = {
    
     1,2,3,4,5,6,7,8,9,10 };
	printf("sizeof(arr) = %d\n", sizeof(arr));  // 40  int类型4*10个元素;
	calcArray(arr);
}

Insert image description here

5) Summary of data types

  • A data type is essentially an alias for a fixed memory size, a mold.CLanguage regulations:Define variables through data types;
  • Data type size calculation (sizeof);
  • You can alias existing data typestypedef;
  • Data type encapsulation (void universal type);

variable

1) The concept of variables

A memory object that can be both read and written is called a variable; Once initialized Objects that cannot be modified are called constants.

Variable definition form: type identifier, identifier, …, identifier

2) The nature of variable names

  • The essence of a variable name: an alias for a continuous memory space;
  • The program applies for and names memory space through variables int a = 0;
  • Access memory space through variable names;
  • Instead of reading and writing data to the variable name, it reads and writes data to the memory space represented by the variable;

3) Two ways to modify variables

3.1 Direct modification

void test11()
{
    
    
	// 1、直接修改
	int a = 10;
	a = 20;
	printf("a = %d\n", a); // 20
}

int main()
{
    
    
	test11();
	system("pause");
	return EXIT_SUCCESS;
}

3.2 Indirect modification

void test11()
{
    
    
	// 1、直接修改
	int a = 10;
	a = 20;
	printf("a = %d\n", a); // 20
	// 2、间接修改
	int* p = &a;
	// *p 表示解引用
	*p = 30;
	printf("a = %d\n", a); // 30
}

3.3 Modification of custom data classes

struct MyStruct
{
    
    
	char a;
	int b;
	char c;
	int d;
};

void test12()
{
    
    
	struct MyStruct m1 = {
    
     "a", 10, "b", 20 };
	// 直接修改d属性
	m1.d = 200;
	printf("m1.d = %d\n", m1.d); // m1.d = 200
	// 间接修改d属性
	struct MyStruct* p = &m1;
	p->d = 2000;
	printf("p->d = %d\n", p->d); // p->d = 2000
}

The above is the simplest way to indirectly modify thed attribute. We can also use the step size to find the location of thed attribute in the memory; first Let’s take a lookstruct MyStructThe size of the result body

void test12()
{
    
    
	struct MyStruct m1 = {
    
     "a", 10, "b", 20 };
	// 直接修改d属性的值
	m1.d = 200;
	printf("m1.d = %d\n", m1.d); // m1.d = 200
	struct MyStruct* p = &m1;
	p->d = 2000;
	printf("p->d = %d\n", p->d); // p->d = 2000
	printf("%d\n", sizeof(struct MyStruct)); // 16
}

Print outstruct MyStructThe size of the result body is16 bytes,How is the 16 bytes calculated?< /span>, and so on. bytes, so it is occupies Because it follows the memory alignment method, the next three are useless; , and all is given directly to accounts for , then is an integer multiple of The starting address must be of type where to look at the next onechar a;Start from the first address0int b;int bint4char a;0~3char a;int b;44~7

struct MyStruct
{
    
    
	char a; // 0~3
	int b; // 4~7
	char c; // 8~11
	int d; // 12~15
};

な么struct MyStruct0~15范围, 訳16个字节

Insert image description here

After knows the size of the structure, then p+1 spans the entire structure. Print to verify p and p+1Address difference

printf("p = %d\n", p);
printf("p+1 = %d\n", p+1);

Insert image description here

struct MyStructThe type pointer obviously cannot point to the d attribute through the step size, so you can define the char type pointer and jump one by one, because we currently know dThe attribute position starts from 12, then the char type pointer variable p+12 will be enough, But please note: because p+12 is of type char*, and the d attribute is of type int, you can only get one byte of data without forced conversion, so you need to dereference it after forced conversion*

void test12()
{
    
    
	struct MyStruct m1 = {
    
     "a", 10, "b", 20 };
	// 直接修改d属性的值
	m1.d = 200;
	printf("m1.d = %d\n", m1.d); // m1.d = 200
	//struct MyStruct* p = &m1;
	//p->d = 2000;
	//printf("p->d = %d\n", p->d); // p->d = 2000
	//printf("%d\n", sizeof(struct MyStruct)); // 16
	//printf("p = %d\n", p);
	//printf("p+1 = %d\n", p+1);
	char* p = &m1;
	printf("*(int *)(p + 12) = %d\n", *(int *)(p + 12));  // 因为p+12为char*类型,而d属性为int类型,如果不强转只能取一个字节的数据,所以需要强转后再解引用*
}

Insert image description here

Similarly, if the char type pointerp is forced to be converted to the int * type first, then, so the jump step size is different depending on the pointer type. You can control which attribute you want to access by yourself, provided you have enough knowledge of the memory address. attribute whose start address is p+3 step size can reach the 12d

char* p = &m1;
printf("*(int *)(p + 12) = %d\n", *(int *)(p + 12));  // 因为p+12为char*类型,而d属性为int类型,如果不强转只能取一个字节的数据,所以需要强转后再解引用*
printf("*((int *)p+3) = %d\n", *((int *)p+3));

Insert image description here

Guess you like

Origin blog.csdn.net/qq_41782425/article/details/128210029