C

一、创建一个项目

File -> New -> Projects -> win32 Console Application -> Ok -> A "Hello, World!" application -> Finish -> Ok -> FileView -> test.cpp

1、快捷键

F5 运行
F7 编译
F9 断点
shitg + F5 结束程序

2、关闭程序

File -> Close Workspace

3、打开一个文件

File -> Open Workspace

4、寄存器窗口

在程序执行时,界面上面右键点击Registers

5、内存窗口

在程序执行时,界面上面右键点击Memory

6、进入反汇编

在程序执行时,右键Go To Disassembly

7、再反汇编窗口去掉C语言

再反汇编窗口右键点Source Annotation

8、再反汇编窗口显示硬编码

再反汇编窗口右键点 Code Bytes

9、

反汇编中进入某个程序按F11(相当于汇编中的F7)
F10(相当于汇编中的F8)

二、函数

1、无参数,无返回值的函数格式

# void 代表这个函数什么都不返回,返回什么类型void就改成什么类型
void 函数名()
{

}

2、有参数,有返回值

int Plus1(int x,int y)
{
	return x + y;
}

int main(int argc, char* argv[])
{
	Plus1(1,2);
	return 0;
}

3、函数调用函数

#include "stdafx.h"

int Plus1(int x,int y)
{
	return x + y;
}

int Plus2(int x,int y,int z)
{
	int t;
	int r;
	t = Plus1(x,y);
	r = Plus1(t,z);
	return r;
}

int main(int argc, char* argv[])
{
	Plus2(1,2,3);
	return 0;
}

4、裸函数

正常函数,编译器会自动帮我们生成一些东西,裸函数则是编译器啥都不生成

void __declspec(naked) Plus()
{
}


int main(int argc, char* argv[])
{
	Plus();
	return 0;
}

编译可以正常编译,但是执行会报错,原因是函数执行之后没有返回地址,需要手动添加

#include "stdafx.h"

void __declspec(naked) Plus()
{
        # 在编译器中写汇编代码如下
	__asm
	{
		ret
	}
}


int main(int argc, char* argv[])
{
	Plus();
	return 0;
}


5、裸函数实现1+2

正常函数编译器会自动帮我们生成,以下代码,裸函数需要我们手动添加以下代码

#include "stdafx.h"

int __declspec(naked) Plus(int x,int y)
{
  
	__asm
	{
                //保留调用函数前的栈底
		push ebp
                //提升堆栈
		mov ebp,esp
		sub esp,0x40
                //保存现场
		push ebx
		push esi
		push edi
                //填充缓冲区
		mov eax,0xcccccccc
		mov ecx,0x10
		Lea edi,dword ptr ds:[ebp-0x40]
		rep stosd
                //函数的核心功能,两个参数相加
		mov eax,dword ptr ds:[ebp+0x8]
		add eax,dword ptr ds:[ebp+0xc]
                //恢复现场
		pop edi
		pop esi
		pop ebx
                //降低堆栈
		mov esp,ebp
		pop ebp
                
		ret
	}
}


int main(int argc, char* argv[])
{
	Plus(1,2);
	return 0;
}

三、调用约定

调用约定 参数压栈顺序 平衡堆栈
__cdecl 从右至左入栈 调用者清理栈
__stdcall 从右至左入栈 自身清理堆栈
__fastcall ECX/EDX传送前两个,剩下:从右至左入栈 自身清理堆栈

1、__cdecl(编译器默认调用,外平栈)

#include "stdafx.h"

int __cdecl Plus1(int a, int b)					
{					
		return a+b;				
}	

int main(int argc, char* argv[])
{
	Plus1(1,2);
	return 0;
}

2、__stdcall(内平栈)

#include "stdafx.h"

int __stdcall Plus2(int a, int b)					
{					
	return a+b;				
}


int main(int argc, char* argv[])
{
	Plus2(1,2);
	return 0;
}

3、__fastcall

当传递的参数为2个时,用寄存器传递,速度较快,且不需要平栈
当参数多余两个时,与其它两种调用方式没啥区别了,在函数里面进行平栈

#include "stdafx.h"

int __fastcall Plu3(int a, int b)					
{					
	return a+b;				
}

int main(int argc, char* argv[])
{
	Plus3(1,2);
	return 0;
}

四、程序入口

1、程序入口

main是我们代码的入口的方法不是真正程序入口

int main(int argc, char* argv[])
{
	return 0;
}

真正的程序入口

五、数据类型

1、C语言数据类型

2、数据类型的三个要素

  • 存储数据的宽度
  • 存储数据的格式
  • 作用范围(作用域)

3、整数类型

char 8BIT 1字节

short 16BIT 2字节

int 32BIT 4字节

long 32BIT 4字节

4、无符号和有符号

#include "stdafx.h"

void Plus()
{
	char i = 0xFF;
	unsigned char k = 0xFF;
}


int main(int argc, char* argv[])
{
	Plus();
	return 0;
}

5、浮点类型


1、先将这个实数的绝对值化为二进制格式													

2、将这个二进制格式实数的小数点左移或右移n位,直到小数点移动到第一个有效数字的右边。													

3、从小数点右边第一位开始数出二十三位数字放入第22到第0位。 													

4、如果实数是正的,则在第31位放入“0”,否则放入“1”。													

5、如果n 是左移得到的,说明指数是正的,第30位放入“1”。如果n是右移得到的或n=0,则第30位放入“0”。													

6、如果n是左移得到的,则将n减去1后化为二进制,并在左边加“0”补足七位,放入第29到第23位。如果n是右移得到的或n=0,则将n化为二进制后在左边加“0”补足七位,再各位求反,再放入第29到第23位。													

6、ASCII

标准ASCII码只有7位,最高位永远是0(表示128可能的字符,表示常用的字符a-z,A-Z,0-9,逗号等)
扩展ASCII码,最高位永远是1(扩展ASCI码允许将每个字符的第8位用于确定附加的128 个特殊符号字符、外来语字母和图形符号)

#include "stdafx.h"

void Plus()
{
	char i = 'A';
}


int main(int argc, char* argv[])
{
	Plus();
	return 0;
}

7、GB2312

GB2312需要用两个字节来表示,一个小于127的字符的意义与原来相同,但两个大于127的字符连在一起时,就表示一个汉字,前面的一个字节(他称之为高字节)从0xA1用到0xF7,,后面一个字节(低字节)从0xA1,到0xFE,这样我们就可以组合出大约7000多个简体汉字了

8、内存图

9、变量

全局变量

  • 全局变量在程序编译完成后地址就已经确定下来了,只要程序启动,全局变量就已经存在了,启动后里面是否有值取决于声明时是否给定了初始值,如果没有,默认为0

  • 全局变量的值可以被所有函数所修改,里面存储的是最后一次修改的值.

  • 全局变量所占内存会一直存在,知道整个进程结束.

  • 全局变量的反汇编识别:
    MOV 寄存器,byte/word/dword ptr ds:[0x12345678]

局部变量

  • 局部变量在程序编译完成后并没有分配固定的地址.

  • 在所属的方法没有被调用时,局部变量并不会分配内存地址,只有当所属的程序被调用了,才会在堆栈中分配内存.

  • 当局部变量所属的方法执行完毕后,局部变量所占用的内存将变成垃圾数据.局部变量消失.

  • 局部变量只能在方法内部使用,函数A无法使用函数B的局部变量.

  • 局部变量的反汇编识别:
    [ebp-4]

10、分析参数

六、if语句

1、if语句

#include "stdafx.h"

int a;

void test(int x,int y)
{
	if(x>y)
	{
		a = x;	
	}
	
}

int main(int argc, char* argv[])
{
	test(3,5);
	return 0;
}


2、if else语句

// test.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"

int g;

void test(int x,int y)
{
	if(x>y)
	{
		g = x + g;	
	}else
	{
		g = y + g;
	}
	
}






int main(int argc, char* argv[])
{
	test(3,5);
	return 0;
}

特点分析

  • 如果不跳转,那么会执行到jmp处,jmp直接跳转到END处
  • 如果跳转,则会直接跳过jmp END处的代码,直接执行后面的代码
  • 第一个jxx跳转的地址前面有一个jmp ,可以判断是if...else...语句

七、基础知识

1、声明和赋值

  • 声明变量就是告诉计算机,我要用一块内存,你给我留着,宽度和存储格式有数据类型决定

  • 计算机什么时候把这块内存给你,取决于变量的作用范围,如果是全局变量,在程序编译完就已经分配了空间,如果是局部变量,只有所在的程序被调用的时候,才会分配空间

  • 全局变量如果不赋初始值,默认是0,但局部变量在使用前一定要赋初值,不然就是CCCCCCCC

2、类型转换

MOVSX 先符号扩展,再传送(结果是FFFF,符号位是什么扩展的部分就是什么,MOVSX用于有符号)

MOV AL,0FF
MOVSX CX,AL
MOV AL,80
MOVSX CX,AL


MOVZX 先零扩展,再传送(结果是00FF,扩展的部分全填0,MOVZX用于无符号)
MOV AL,0FF
MOVZX CX,AL
MOV AL,80

3、表达式

  • 表达式无论多么复杂,都只有一个结果

  • 只有表达式,可以编译通过,但并不生成代码,需要与赋值或者其他流程控制语句一起组合的时候才有意义.

  • 当表达式中存在不同宽度的变量时,结果将转换为宽度最大的那个.

  • 当表达式中同时存在有符号和无符号数的时候,表达式的结构将转换为无符号数

4、语句

影响内存或者cpu的就叫语句

5、运算符

  • 关系运算符(“==”、“!=”、“>=”、“<=”、“>”、“<”)
  • 逻辑运算符(&& || ! )
  • 单目运算符(++i)
  • 三目运算符( x>y?x:y;)

八、循环语句

1、交换两个数

#include "stdafx.h"

void test()
{
	int x = 2;
	int y = 1;
	x = x + y;
	y = x - y;
	x = x - y;
	printf("%d:%d",x,y);
	
}

int main(int argc, char* argv[])
{
	test();
	getchar();
	return 0;
}

2、将一个数组倒序输出

#include "stdafx.h"



void test()
{
	int arr[5] = {2,4,1,5,3};
	int i = 4;
	while(i>=0)
	{
		printf("%d\n",arr[i]);
		i--;
	
	}
	
}

int main(int argc, char* argv[])
{
	test();
	getchar();
	return 0;
}

3、找出数组中最大的值

#include "stdafx.h"

void test()
{
	int arr[5] = {2,4,1,5,3};
	int r = arr[0];
	int i = 1;
	while(i<=4)
	{
		if(arr[i]>r)
		{
			r = arr[i];
		}
		i++;
	}
	printf("%d",r);	
}

int main(int argc, char* argv[])
{
	test();
	getchar();
	return 0;
}

4、写一个函数int prime(int x),如果x是素数返回值为1,否则返回0。

#include "stdafx.h"

int test(int x)
{
	int i = 2;

	while(i<x)
	{
		if(x%i == 0)
		{
			return 0;
		}
		i++;
	}
	return 1;

}

int main(int argc, char* argv[])
{
	int a = test(9);
	printf("%d",a);
	getchar();
	return 0;
}

5、俩俩比较数组的值,将最大的一个存储到数组的最后一个位置

#include "stdafx.h"

void test()
{
	int arr[5] = {2,5,1,4,3};
	int t = 0;
	int i = 0;
	while(i<4)
	{

		if(arr[i] > arr[i+1])
		{
			t = arr[i];
			arr[i] = arr[i+1];
			arr[i+1] = t;
		}
		i++;
	}

	int k = 0;
	while(k<5)
	{
		printf("%d\n",arr[k]);
		k++;
	
	}

}


int main(int argc, char* argv[])
{
	test();
	getchar();
	return 0;
}

6、判断数组是否是对称的,如果是返回1,不是返回0.

#include "stdafx.h"

int test()
{
	int arr[5] = {1,2,3,2,1};
	int t = 4;
	int i = 0;
	while(i<t)
	{

		if(arr[i] != arr[t])
		{
			return 0;
		}
		i++;
		t--;
	}
	return 1;

}

int main(int argc, char* argv[])
{
	int a =test();
	printf("%d",a);
	getchar();
	return 0;
}

7、冒泡排序

#include "stdafx.h"

void test()
{
	int arr[8] = {61,21,34,18,16,33,58,45};
	int lenght = 8;
	int i =0;
	int k = 0;
	while(i<lenght-1)
	{
		for(k=0;k<lenght-1-i;k++)
		{
			if(arr[k] > arr[k+1])
			{
				arr[k] = arr[k] + arr[k+1];
				arr[k+1] = arr[k] - arr[k+1];
				arr[k] = arr[k] - arr[k+1];
			}
		}
		i++;
	}


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


int main(int argc, char* argv[])
{
	test();
	getchar();
	return 0;
}

九、参数传递

1、本机尺寸

  • 如果本机是32位的,那么对32位的数据支持最好,如果是64位的,那么对64位的支持最好

  • 整数类型的参数,一律使用int类型,char类型或者short类型的参数不但没有节省空间,反而浪费了多余的操作.

  • 参数传递的本质:将上层函数的变量,或者表达式的值“复制一份”,传递给下层函数

2、局部变量

  • 小于32位的局部变量,空间在分配时,按32位分配.

  • 使用时按实际的宽度使用.

  • 不要定义char/short类型的局部变量.

  • 参数与局部变量没有本质区别,都是局部变量,都在栈中分配.

  • 完全可以把参数当初局部变量使用

#include "stdafx.h"

void test()
{

}


int main(int argc, char* argv[])
{
	test();
	getchar();
	return 0;
}


#include "stdafx.h"

void test()
{
	char a = 1;
	char b = 2;
}


int main(int argc, char* argv[])
{
	test();
	getchar();
	return 0;
}

3、64位宽度存取

#include "stdafx.h"
					
__int64 Function()				
{				
	__int64 x = 0x1234567890;			
	return x;			
}				

int main(int argc, char* argv[])
{
	__int64 x = Function();
	getchar();
	return 0;
}

4、比较char arr[3] 与 char arr[4] 哪个更省空间

char arr[4] :

void test()				
{				
	char arr[4] = {0};
}				
					
int main(int argc, char* argv[])
{
	test();

	return 0;
}

char arr[3]:

#include "stdafx.h"

					
					
void test()				
{				
	char arr[3] = {0};
}				
				
int main(int argc, char* argv[])
{
	test();

	return 0;
}

5、数组寻址

#include "stdafx.h"					
					
void test()					
{					
	int x = 1;				
	int y = 2;				
	int r;				
	int arr[10] = {1,2,3,4,5,6,7,8,9,10};				
					
	r = arr[1];				
	r = arr[x];				
	r = arr[x+y];				
	r = arr[x*2+y];				
}					
				
int main(int argc, char* argv[])
{
	test();

	return 0;
}

6、桶排序

原理:

0 0 0 0 0 0 0
0 1 2 3 4 5 6 

1、先定义一个需要排序的数组

int arr[6]  = {2,4,6,3,1,2};

2、再定义一个排序的数组(这个数组的长度由arr决定,长度是arr中最大的数是6)

int ret[7]  = {0};

3、arr中的值,对应着ret的下标

4、循环arr的值,并且将对应的ret下标的值+1

5、打印


#include "stdafx.h"
						
void test()					
{								
	int arr[6]	= {2,4,6,3,1,2};
	int ret[7]	= {0};

	for(int i = 0 ; i < 6 ; i++)
	{
                
		ret[arr[i]] = ret[arr[i]] + 1;
	
	}

	int m = 0;
        #k<7 是因为,arr最大的数是6,所以需要循环6次
	for(int k = 0 ; k < 7 ; k++)
	{
		if(ret[k] > 0)
		{
			m = ret[k];
			for(;m > 0;m--)
			{
				printf("%d\n",k);
			}
		}			
	}	
}					
				
int main(int argc, char* argv[])
{
	test();
	getchar();
	return 0;
}

十、多维数组

1、数组越界

#include "stdafx.h"
					
void test()			
{			
	int arr[5] = {1,2,3,4,5};		
			
	arr[6]	= 0x123456;
			
}			

int main(int argc, char* argv[])
{
	test();
	getchar();
	return 0;
}

2、缓冲区溢出

#include "stdafx.h"
			
void HelloWord()			
{			
	printf("Hello World");		
			
	getchar();		
}			
void Fun()			
{			
	int arr[5] = {1,2,3,4,5};
        //HelloWord函数是全局变量,编译器编译后是个固定的值					
	arr[6] = (int)HelloWord;		
			
}			


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

	
	Fun();
	getchar();
	return 0;
}

3、二维数组

  • 一维数组与多维数组在反汇编上没有区别

  • 数量如果不够,其他的值会补零

  • 不允许超过维度的最大长度(不然不能编译)

int arr[3][4] = {					
	{1,2,3,4,5,6},	
	{5},	
	{9}	
}	
  • 可以省略里面的
arr的长度是12,现在只分了10,剩下两个编译器自动帮我们补0
int arr[3][4] = {1,2,3,4,5,6,7,8,9,10};	
  • 可以省略第一维的值
它会按4个一组分配,多余的补0
int arr[][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
  • 二维数组查找
arr[0][8] = arr[0*12 + 8]
arr[1][7] = arr[1*12 + 7]

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

4、三维数组

arr[1][2][1] = arr[1*4*3 + 2*3 +1] = 18
arr[3][3][1] = arr[3*4*3 + 3*3 +1] = 312
					
int arr[5][4][3] = {						
						
	{{1,2,3},{4,5,6},{7,8,9},{11,12,13}},					
						
	{{11,12,13},{14,15,16},{17,18,19},{111,112,113}},					
						
	{{21,22,23},{24,25,26},{27,28,29},{211,212,213}},					
						
	{{31,32,33},{34,35,36},{37,38,39},{311,312,313}},					
						
	{{41,42,43},{44,45,46},{47,48,49},{411,412,413}}											
};		

十一、结构体

1、定义结构体(不会分配空间)

struct  AA				
{				
		
}				


AA  int  char  float double  平级的

2、声明一个变量(声明时会分配空间)

struct  AA				
{				
		
}				

AA y;

3、结构体使用

#include "stdafx.h"
				
//struct是一个关键字 st是用户自己定义的一个名字			
struct st 
{	
	//可以定义多种类型
	int a;
	char b;
	short c;
};	
												
int main(int argc, char* argv[])
{

	//声明一个结构体变量
	st s;
	//给结构体成员赋值
	int a = s.a = 10;
	int b = s.b = 20;
	int c = s.c = 30;

	printf("%d %d %d",a,b,c);
	getchar();
	return 0;
}

4、结构体的参数传递

#include "stdafx.h"
			
struct st	
{	
	char a;
	short b;
	int c;
};	

void Function(st s)	
{	
	
}	

int main(int argc, char* argv[])	
{	
	st s;
	s.a = 1;
	s.b = 2;
	s.c = 3;
	
	Function(s);
		
	return 0;
}	

结构的数据多了传递方式

#include "stdafx.h"
			
struct st	
{	
	int a;
	int b;
	int c;
	int d;
	int e;
	int f;

};	

void Function(st s)	
{	
	
}	

int main(int argc, char* argv[])	
{	
	st s;
	s.a = 1;
	s.b = 2;
	s.c = 3;
	s.d = 4;
	s.e = 5;
	s.f = 6;
	
	Function(s);
		
	return 0;
}	

5、结构体的返回值

#include "stdafx.h"
			
		
struct st	
{	
	int a;
	int b;
	int c;
	int d;
	int e;
	
};	
st Function()	
{	
	st s;
	s.a = 1;
	s.b = 2;
	s.c = 3;
	s.d = 4;
	s.e = 5;
	
	return s;
};	
int main(int argc, char* argv[])	
{	
	st s = Function();
	
	
	return 0;
}	

十二、结构体对齐

1、sizeof

点击查看代码
#include "stdafx.h"
				
void test()
{				
	char a = 10;			
	short b = 20;			
	int c = 30;			
				
	char arr1[10] = {0};			
	short arr2[10] = {0};			
	int arr3[10] = {0};			
				
	printf("%d\n",sizeof(a));			
	printf("%d\n",sizeof(b));			
	printf("%d\n",sizeof(c));			
	printf("%d\n",sizeof(arr1));			
	printf("%d\n",sizeof(arr2));			
	printf("%d\n",sizeof(arr3));			
}				

int main(int argc, char* argv[])	
{	
	test();
	
	getchar();
	return 0;
}	

2、数据对齐

本质:效率还是空间,二选一的结果

3、字节对齐

对齐原则:

原则一:数据成员对齐规则:结构的数据成员,第一个数据成员放在offset为0的地方,以后每个数据成员存储的起始位置要从,该成员大小的整数倍开始(比如int在32位机为4字节,则要从4的整数倍地址开始存储).

原则二:结构体的总大小,也就是sizeof的结果,必须是其内部最大成员的整数倍,不足的要补齐。

原则三:如果一个结构里有某些结构体成员,则结构体成员要从其内部最大元素大小的整数倍地址开始存储。(struct a里存有struct b,b里有char,int,double等元素,那b应该从8的整数倍开始存储.)

原则四:对齐参数如果比结构体成员的sizeof值小,该成员的偏移量应该以此值为准.也就是说,结构体成员的偏移量应该取二者的最小值.

//对齐参数:n为字节对齐数,其取值为1、2、4、8,默认是8。
#pragma pack(8)	
struct Test				
{				
     int a ;				
     __int64 b ;				
     char c ;				
};	
#pragma pack()		
  • 1字节对齐

  • 2字节对齐

  • 4个字节对齐

  • 8个字节对齐

建议:按照数据类型由小到大的顺序进行书写

4、typedef关键字

点击查看代码
		
		
1、对已有类型定义别名		
		
typedef unsigned char BYTE;				
typedef unsigned short WORD;				
typedef unsigned int DWORD;				

2、一维数组类型的定义格式		
			
typedef int vector[10];							
int main(int argc, char* argv[])				
{				
	vector v;			
				
	v[0] = 1;			
	v[1] = 2;			
	v[2] = 3;			
	v[3] = 4;			
	v[4] = 5;			
				
				
	return 0;			
}	

3、 二维数组类型的定义格式		

typedef int name[5][5];				
typedef int nameTable[5][5][5];								
int main(int argc, char* argv[])				
{				
	name v;			
	nameTable n;			
				
	v[0][0] = 1;			
	v[0][1] = 2;			
	v[0][2] = 3;			
	v[0][3] = 4;			
	v[0][4] = 5;			
				
	n[0][0][0] = 1;			
				
	return 0;			
}				
				
4、结构体的定义格式					
					
typedef struct student					
{					
	int x;				
	int y;				
}stu;					
					

			

5、字符数组赋值

点击查看代码
#include "stdafx.h"
#include "string.h"
					
typedef struct Test				
{				
	char arr[10];
}T;				
			
			

int main(int argc, char* argv[])	
{	
	T s;
        //需要依赖"string.h"
	strcpy(s.arr,"china");
	printf("%s",s.arr);
	
	getchar();
	return 0;
}	

6、结构体数组

点击查看代码
#include "stdafx.h"
#include "string.h"
					
typedef struct Test				
{				
	int x;
	int y;
}T;				
			
T arr[10];

int main(int argc, char* argv[])	
{	
	
	arr[0].x = 10;
	arr[0].y = 20;

	int test = arr[0].x;
	printf("%d",test);
	
	getchar();
	return 0;
}	

7、习题

  • 习题一
点击查看代码
#include "stdafx.h"
									
struct S1		
{		
	char c;	
	double i;	
};		


int main(int argc, char* argv[])	
{		
	printf("%d",sizeof(S1));
	
	getchar();
	return 0;
}	

  • 习题二
点击查看代码
#include "stdafx.h"
						
struct S1		
{		
	char c;	
	double i;	
};		

struct S3	
{	
	char c1; 
	S1 s;    
	char c2; 
	char c3; 
};	
		
int main(int argc, char* argv[])	
{	
	

	printf("%d",sizeof(S3));
	
	getchar();
	return 0;
}	

  • 习题三
点击查看代码
#include "stdafx.h"
						
struct S1		
{		
	char c;	
	double i;	
};		
			
struct S3		
{		
	char c1; 	
	S1 s;    	
	char c2; 	
	double c3; 	
};		
			
int main(int argc, char* argv[])	
{	
	

	printf("%d",sizeof(S3));
	
	getchar();
	return 0;
}	

  • 习题四
点击查看代码
#include "stdafx.h"
									
struct S4		
{		
	int c1; 	
	char c2[10]; 	
};		
			
int main(int argc, char* argv[])	
{	
	

	printf("%d",sizeof(S4));
	
	getchar();
	return 0;
}	

十三、switch语句

1、要求

  • 后面必须是常量表达式

  • case后常量表达式的值不能一样

  • switch后面表达式必须为整数

2、当分支条件很少的时候,没必要用switch语句,跟if...else语句没区别

3、编译时switch语句会在内存中创建一张大表,将每一个分支语句的地址都存到这张大表中,根据表达式直接定位要跳转的地址

点击查看代码
#include "stdafx.h"
									
void test(int x)
{

	switch(x)
	{
		case 1:
			printf("1");
			break;
		case 2:
			printf("2");
			break;
		case 3:
			printf("3");
			break;
		case 4:
			printf("4");
			break;
		default:
			printf("5");
			break;	
	}

}
		
int main(int argc, char* argv[])	
{	
	
	test(2);
	getchar();
	return 0;
}	

4、将语句中的常量值的顺序打乱,不会影响生成大表

5、将连续的几项中抹去,1或者2项,大表中会会将空缺的几项地址填充为default的地址

点击查看代码
#include "stdafx.h"
									
void test(int x)
{

	switch(x)
	{
		case 1:
			printf("1");
			break;
		case 5:
			printf("5");
			break;
		case 6:
			printf("6");
			break;
		case 7:
			printf("7");
			break;
		case 8:
			printf("8");
			break;
		default:
			printf("error");
			break;	
	}

}
		
int main(int argc, char* argv[])	
{	
	
	test(2);
	getchar();
	return 0;
}	

6、在连续抹去一定的数目,会生成一张小表(节省内存)

点击查看代码
#include "stdafx.h"
									
void test(int x)
{

	switch(x)
	{
		case 1:
			printf("1");
			break;

		case 8:
			printf("8");
			break;
		case 9:
			printf("9");
			break;
		case 10:
			printf("10");
			break;

		default:
			printf("error");
			break;	
	}

}
		
int main(int argc, char* argv[])	
{	
	
	test(2);
	getchar();
	return 0;
}	

十四、do..while

十五、while

十六、for

十七、位运算

1、算术移位指令

指令格式:SAL/SAR Reg/Mem, CL/Imm

  • SAL(Shift Arithmetic Left): 算术左移

  • SAR(Shift Arithmetic Right): 算术右移

2、逻辑移位指令

指令格式:SHL/SHR Reg/Mem, CL/Imm

  • SHL(Shift Left): 逻辑左移

  • SHR(Shift Right): 逻辑右移

3、循环移位指令

指令格式:ROL r/m, i8 ROR r/m, CL

  • ROL(Rotate Left): 循环左移

  • ROR(Rotate Right): 循环右移

4、带进位的循环移位指令

指令格式:RCL r/m, i8 RCR r/m, CL

  • RCL(Rotate through Carry Left): 带进位循环左移

  • RCR(Rotate through Carry Right): 带进位循环右移

posted @ 2021-11-27 21:46  lnterpreter  阅读(31)  评论(0编辑  收藏  举报