2020-08-13汇编学习00
请注意,本文编写于 838 天前,最后修改于 204 天前,其中某些信息可能已经过时。

目录


数组在汇编中的特征一般是,连续并且等宽的。

一、数组可以越界使用吗?

可以越界,但是要对堆栈图足够熟悉

1、越界演示

2、代码还是数据?没有区别

3、基于缓冲区溢出的HelloWorld

void HelloWord()
{
	printf("Hello World");

	getchar();
}
void Fun()
{
	int arr[5] = {1,2,3,4,5};

	arr[6] = (int)HelloWord;

}

程序运行效果

解读:
arr[6]中保存的实际上就是 函数HelloWord的地址, arr[6]在堆栈图中的位置是EBP+4,也就是eip下一跳的位置
所以,这个程序一旦执行,首先会执行int arr[5] = {1,2,3,4,5}然后,在堆栈图ebp+4的位置,执行,arr[6] = (int)HelloWord
这个时候ebp+4的位置保存的将不是,原来调用函数时保存的下一跳的地址,而是保存的函数HelloWord()的地址,所以程序运行,虽然没有直接调用函数HelloWord()却依然会打印printf("Hello World");

堆栈图

汇编

二、二维数组的初始化

1、二维数组的初始化

int arr[3][4] = {
	{1,2,3,4},
	{5,6,7,8},
	{9,7,6,5}
}

编译器如何分配空间

int arr[3*4] = {1,2,3,4,5,6,7,8,9,7,6,5};

所以实际上这个int arr[3*4] = {1,2,3,4,5,6,7,8,9,7,6,5};二维数组和一维数组int arr[12] = {1,2,3,4,5,6,7,8,9,7,6,5};没区别

2、给定部分值

int arr[3][4] = {

	{1,2},
	{5},
	{9}
}

int arr[3][4] = {

	{1,2,3,4,5},    //不能编译,编译器不允许
	{5},            //可以少写,它会自动补零
	{9}
}

缺少的地方补零 0

3、省略里面的{ }

int arr[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};    //可以编译,先计算空间,发现没有越界,所以可以编译,如同一维数组

int arr[3][4] = {1,2,3,4,5,6,7,8,9,10};          //可以编译,自动补0,如同一维数组

4、省略长度

int arr[][4] = {1,2,3,4,5,6,7,8,9,10,11,12};     //可以编译,前边的数字可以省略,自动判断,4个一组

int arr[][4] = {1,2,3,4,5,6,7,8,9,10};           //可以编译,前边的数字可以省略,自动判断,4个一组,缺少自动补0

int arr[][4] = {1,2,3,4,5,6,7,8};                //可以编译,前边的数字可以省略,自动判断,4个一组,所以变成的arr[2][4]

5、 为什么要使用二维数组:更加直观

比如:一年有12个月,每个月都有一个平均气温,存储5年的数据

int arr[5][12] = {

	{1,2,1,4,5,6,7,8,9,1,2,3}			//下标0

	{1,2,1,4,5,6,7,8,9,1,2,3}			//下标1

	{1,2,1,4,5,6,7,8,9,1,2,3}			//下标2

	{1,2,1,4,5,6,7,8,9,1,2,3}			//下标3

	{1,2,1,4,5,6,7,8,9,1,2,3}			//下标4

}
arr[1][5] == 6;

总结:

1、一维数组与多维数组在反汇编上没有区别.

2、数量如果不够,其他的值会补零.

3、不允许超过维度的最大长度.

4、可以省略里面的{ }

5、可以省略第一维的值

三、二维数组的读写

读写

代码

# include "stdafx.h"

void Fun()
{
	int n =5;
	int m = 12;
	int arr[5][12] = {

		{1,2,1,4,5,6,7,8,9,1,2,3},

		{11,12,11,14,15,16,17,18,19,11,12,13},

		{21,22,21,24,25,26,27,28,29,21,22,23},

		{31,32,31,34,35,36,37,38,39,31,32,33},

		{41,42,41,44,45,46,47,48,49,41,42,43}
	};

	for(int i = 0; i<n;i++)
	{
		for(int j = 0;j<m;j++)
		{
			printf("%d ",arr[i][j]);
		}
		printf("\n");
	}


}

int main(int argc, char* argv[])
{
	Fun();

	return 0;
}

4、三维数组的使用

三维数组的初始化(特点与二维数组相同):

假设一共有5个班,每个班4个组,每组3个人

int arr[5][4][3] = {

	{{1,2,3},{4,5,6},{7,8,9},{11,12,13}},                //0

	{{11,12,13},{14,15,16},{17,18,19},{111,112,113}},    //1

	{{21,22,23},{24,25,26},{27,28,29},{211,212,213}},    //2

	{{31,32,33},{34,35,36},{37,38,39},{311,312,313}},    //3

	{{41,42,43},{44,45,46},{47,48,49},{411,412,413}}     //4

};

第2个班,第3组的,第二个人

arr[1][2][1] = 18
//要找的人在第二个班,所以找下标为1的
arr[143 + 23 +1] 一个班有43个人,再继续,一个组三个人,要找的人在第三组也就是下标为2
//接着要找的人是第二个人,也就是下标为1, arr[143+2*3+1] = 下标19

第4个班,第4组的,第二个人
arr[3][3][1] = 312

arr[343 + 33 +1] = 36+9+1 = 下标46 = 312
//要找的人在第4个班,所以找下标3, arr[3],一个班有4
3个人 ,所以arr[343]
//一组三个人,要找第4组,也就是下标3,arr[][3], 所以arr[343 + 33]
//要找的人是第2个人,所以也就是下标1,arr[][][1],所以arr[3
43 + 33 + 1]

公式

int arr[n][m][k][w][r]

arr[2][3][4][2][2]

arr[2mkwr+3kwr+4wr+2r+2]

作业

1、假设现在有5个班,每个班10个人,设计一个二维数组存储这些人的年龄.


# include "stdafx.h"

void Fun()
{
	int arr[5][10] = {

		{19,22,18,24,21,23,19,17,25,23},

		{18,20,19,14,15,16,17,18,22,10},

		{21,22,21,24,25,26,27,28,29,21},

		{16,17,18,24,21,23,21,24,25,26},

		{19,18,21,22,23,17,25,21,22,27}
	};



}

int main(int argc, char* argv[])
{
	Fun();

	return 0;
}

2、如果想知道第二个班的第6个人的年龄,应该如何获取?编译器该如何获取?


# include "stdafx.h"

void Fun()
{
	int arr[5][10] = {

		{19,22,18,24,21,23,19,17,25,23},		//0

		{18,20,19,14,15,16,17,18,22,10},		//1

		{21,22,21,24,25,26,27,28,29,21},		//2

		{16,17,18,24,21,23,21,24,25,26},		//3

		{19,18,21,22,23,17,25,21,22,27}			//4
	};

	//如果想知道第二个班的第6个人的年龄,应该如何获取?编译器该如何获取?
	printf("%d\n",arr[1][5]);

	/*
		编译器是 第二班,也就是下标 1 的数组 乘以 每个班10个人 , 加第6个人也就是下标为 5 。
		1 *  10 + 5 = 下标 15 ,从头开始数第15个数也就是 16 了
	*/
}

int main(int argc, char* argv[])
{
	Fun();

	return 0;
}

3、打印所有班级,所有学生的年龄(每个班级打印一行).

# include "stdafx.h"

//打印所有班级,所有学生的年龄(每个班级打印一行).

# define Cls 5	//常量, 班级数
# define Stu 10	//常量, 学生数

void Fun()
{
	int arr[Cls][Stu] = {

		{19,22,18,24,21,23,19,17,25,23},		//0

		{18,20,19,14,15,16,17,18,22,10},		//1

		{21,22,21,24,25,26,27,28,29,21},		//2

		{16,17,18,24,21,23,21,24,25,26},		//3

		{19,18,21,22,23,17,25,21,22,27}			//4
	};

	for(int i = 0; i < Cls;i++)				//外循环负责打印班级
	{
		for(int j = 0;j < Stu;j++)			//内循环负责打印每个学生年龄
		{
			printf("%d ",arr[i][j]);
		}
		printf("\n");						//每打印完一个班级 换行
	}


}

int main(int argc, char* argv[])
{
	Fun();

	return 0;
}

4、将第二个班级的超过20岁的学生的年龄修改为21岁.

# include "stdafx.h"

//将第二个班级的超过20岁的学生的年龄修改为21岁.

# define Cls 5	//常量, 班级数
# define Stu 10	//常量, 学生数

void Fun()
{
	int arr[Cls][Stu] = {

		{19,22,18,24,21,23,19,17,25,23},		//0

		{22,20,19,23,15,16,17,18,22,10},		//1

		{21,22,21,24,25,26,27,28,29,21},		//2

		{16,17,18,24,21,23,21,24,25,26},		//3

		{19,18,21,22,23,17,25,21,22,27}			//4
	};

	for(int i = 0; i < Stu;i++)
	{
		if(arr[1][i] > 20)			//循环遍历arr[1][i]的值,判断如果大于20 将arr[1][i]的值修改为21
		{
			arr[1][i] = 21;
		}
	}

	for(int j = 0; j < Stu;j++)
	{
		printf("%d ",arr[1][j]);	//打印arr[1][j]
	}

}

int main(int argc, char* argv[])
{
	Fun();

	return 0;
}

5、打印出每个班级学生的年龄的和.

# include "stdafx.h"

//打印出每个班级学生的年龄的和.

# define Cls 5	//常量, 班级数
# define Stu 10	//常量, 学生数

void Fun()
{
	int Age = 0;		//定义一个变量,用来保存年龄总数
	int t = 1;			//计数器,用来 打印哪个班级
	int arr[Cls][Stu] = {

		{19,22,18,24,21,23,19,17,25,23},		//0

		{22,20,19,23,15,16,17,18,22,10},		//1

		{21,22,21,24,25,26,27,28,29,21},		//2

		{16,17,18,24,21,23,21,24,25,26},		//3

		{19,18,21,22,23,17,25,21,22,27}			//4
	};

	for(int i = 0; i < Cls;i++)
	{
		printf("第 %d 班:",t);		//打印第几个班
		for(int j = 0;j < Stu; j++)
		{
			Age += arr[i][j];		//计算总数
		}
		printf("%d ",Age);			//打印年龄
		Age = 0;					//重置 Age总数,为下一个班级年龄计算做准备
		t++;						//班级号加1
		printf("\n");
	}

}

int main(int argc, char* argv[])
{
	Fun();

	return 0;
}

6、将两个数组中所有数据进行从小到大的排序,存储到另一个数组中.

数组一:[3,5,7,9,12,25,34,55]
数组二:[4,7,9,11,13,16]

# include "stdafx.h"

//将两个数组中所有数据进行从小到大的排序,存储到另一个数组中.

int arr1[8] = {3,5,7,9,12,25,34,55};
int arr2[6] = {4,7,9,11,13,16};
int arr3[14] = {0};

void Fun1()
{
	int i = 0;
	int j = 0;
	int k = 0;

	// 采用归并排序

	/* 使用while循环判断 i是否小于8,同时j是否小于6
	如果成立,那么判断arr1[i]是否小于arr2[j],如果
	arr1[i]小于arr2[j]那么,将arr1[i]的值赋给arr3[k],
	并且i+1,k+1,  arr2[j]小于arr1[i]那么,将arr2[j]的值赋给arr3[k],
	并且j+1,k+1,重复上述过程,直到某一个数字内的值被排完。
	*/
	while(i < 8 && j < 6)
	{
		if(arr1[i] < arr2[j])
		{
			arr3[k] = arr1[i];
			i++;
		}
		else
		{
			arr3[k] = arr2[j];
			j++;
		}
		k++;
	}

	//将另一个数组内的剩余的值,放入arr3,


	//判断arr1内是否还有值,通过下标i是否大于8来判断
		while(i < 8)
		{
			arr3[k] = arr1[i];
			i++;
			k++;

		//存入arr3之后,重新进行一次排序
		}
	//判断arr2内是否还有值,通过下标j是否大于6来判断
		while(j < 6)
		{
			arr3[k] = arr2[j];
			j++;
			k++;
		}
		// 排序完成后,循环打印arr3的全部内容
		printf("已将两个数组内的值,全部排序完毕:\n");
		for(int t = 0; t < 14;t++)
		{
			printf("%d ",arr3[t]);
		}
}

int main(int argc, char* argv[])
{

	Fun1();

	return 0;
}

存在一些问题,如果剩下的数组里的值小于 其他值,那arr3排序就变成了 小-大-小,采用冒泡排序再对arr3进行一次排序吗?或者应该怎么办呢?
笔记内容来自滴水三期

本文作者:Na1r

本文链接:

版权声明:本博客所有文章除特别声明外,均采用 BY-NC-SA 许可协议。转载请注明出处!