1.类型与类型强转

强制转换就是把变量从一种类型转化成另外一种类型的数据。

简单的例子

方法:(想要转换的类型)变量

1
2
3
4
5
6
7
8
#include <stdio.h>
int main()
{
int sum = 17, count = 5;
double mean;
mean = (double) sum / count;
printf("Value of mean : %f\n", mean );
}

sum的值首先被转化未double性,然后除以count得到一个double型的值

这样的转换是显性的。转换也可以是隐型的

整数提升

也就是把小于int 或者 unsigned int 的整数转化成 跟高的过程

编译器首先会整数提升,如果操作数类型不同,则把他们转化为层次最高的类型

  • int->unsigned int->long->unsigned long->unsigned long long ->float->double->long double
1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main()
{
int i = 17;
char c = 'c'; /* ascii 值是 99 */
int sum;
sum = i + c;
printf("Value of sum : %d\n", sum );
}

运行得到的结果是:Value of sum : 116

1
2
3
4
5
6
7
8
9
#include <stdio.h>
int main()
{
int i = 17;
char c = 'c'; /* ascii 值是 99 */
float sum;
sum = i + c;
printf("Value of sum : %f\n", sum );
}

运行得到的结果是:Value of sum : 116.000000

在这里 c 首先被转化成整数 也就是99 但是最后的值是float型的,所以应用常用算数转化,编译器会把i 和 c 转化未浮点型,然后相加得到一个浮点数

2.字符与字符串

字符串和数组

C中要声明一个数组,需要指定元素的类型和元素的数量

1
2
3
4
5
type arrayName [ arraySize ];
比如:
int balance[10];
//初始化数组
double balance[5] = {1000.0, 2.0, 3.4, 7.0, 50.0}

字符串常量

以NULL字节结尾的0个或者多个字符组成的序列。

比如:”Hello!”、”\aWarning!\a”、”123abc\n”、””

最后自动补充\0 来表示 NULL 也就是结束

*在C语言中通常声明一个char类型的指针并将其初始化为一个字符串常量的方式来访问一个字符串

1
2
3
4
5
6
7
8
9
10
int main()
{
  char *message = "Hello World!";
  printf("%s\n",message);
  while(*message != '\0'){
    printf("%c",*message++);
  }
  printf("\n");
  return 0;
}

结果:hello world
hello world

字符数组

用于存放字符的数组就是字符数组

1
2
3
char charArray[] = {'H','e','l','l','o'}; // 声明并初始化⼀个字符数组
//傻逼才用上面的
char charArray[] = "Hello World!"; // 声明并初始化⼀个字符数组

1.字符串常量是一个字符数组,但是内容和长度在初始化的时候就已经固定了,不能变。可以通过一致指向字符串常量第一个元素的指针来访问该字符串常量

2.字符数组是一个用于存放字符的数组,字符数组的值是可以改变的。

常用字符串函数

#include <string.h>

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
void *memchr(const void *str, int c, size_t n) //在参数 str 所指向的字符串的
前 n 个字节中搜索第⼀次出现字符 c(⼀个⽆符号字符)的位置

int memcmp(const void *str1, const void *str2, size_t n) //把 str1 和
str2 的前 n 个字节进⾏⽐较

void *memcpy(void *dest, const void *src, size_t n) //从 src 复制 n 个字符
到 dest

void *memset(void *str, int c, size_t n) //复制字符 c(⼀个⽆符号字符)到参数
str 所指向的字符串的前 n 个字符

char *strcat(char *dest, const char *src) //把 src 所指向的字符串追加到 dest
所指向的字符串的结尾

char *strncat(char *dest, const char *src, size_t n) //把 src 所指向的字符
串追加到 dest 所指向的字符串的结尾,直到 n 字符⻓度为⽌

char *strchr(const char *str, int c)//在参数 str 所指向的字符串中搜索第⼀次出
现字符 c(⼀个⽆符号字符)的位置

int strcmp(const char *str1, const char *str2) //把 str1 所指向的字符串和
str2 所指向的字符串进⾏⽐较

int strncmp(const char *str1, const char *str2, size_t n) //把 str1 和
str2 进⾏⽐较,最多⽐较前 n 个字节

size_t strlen(const char *str) //计算字符串 str 的⻓度,直到空结束字符,但不包
括空结束字符

3.C语言表达式

加法

对应的汇编指令是ADD

1
2
3
4
5
6
7
#include <stdio.h>
int main()
{
int a=1,b=2,c;
c=a+b;
printf("%d",c);
}
1
2
3
4
5
6
7
8
9
10
11
12
int a = 1, b = 2, c;
00C61875 mov dword ptr [a],1
00C6187C mov dword ptr [b],2
c = a + b;
00C61883 mov eax,dword ptr [a]
00C61886 add eax,dword ptr [b]
00C61889 mov dword ptr [c],eax
printf("%d", c);
00C6188C mov eax,dword ptr [c]
00C6188F push eax
00C61890 push offset string "%d" (0C67B30h)
00C61895 call _printf (0C610D2h)

[a],[b],[c]是a,b,c的偏移地址

1
2
3
4
5
6
7
include<stdio.h>
int main()
{
int c;
c=1+2;
printf("%d",c);
}
1
2
3
4
5
6
7
8
int c;
c = 1 + 2;
00EA1875 mov dword ptr [c],3
printf("%d", c);
00EA187C mov eax,dword ptr [c]
00EA187F push eax
00EA1880 push offset string "%d" (0EA7B30h)
00EA1885 call _printf (0EA10D2h)

这里1+2,常量的运算在编译的过程中就已经处理完。

减法

减法运算的指令是sub , 计算机在运算减法的时候是将这个转化为补码变成加法来运算

2-1 => 2+(1的反码+1)【也就是补码】

1
2
3
4
5
6
7
8
int main()
{
int a = 1, b = 2, c;
c = b-a;
printf("%d", c);
getchar();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
int a = 1, b = 2, c;
00391875 mov dword ptr [a],1
0039187C mov dword ptr [b],2
c = b-a;
00391883 mov eax,dword ptr [b]
00391886 sub eax,dword ptr [a]
00391889 mov dword ptr [c],eax
printf("%d", c);
0039188C mov eax,dword ptr [c]
0039188F push eax
00391890 push offset string "%d" (0397B30h)
00391895 call _printf (03910D2h)

乘法

乘法的指令:imul 和 mul

imul 对应的是有符号的乘法,mul 对应的是无符号的乘法

1
2
3
4
5
6
7
8
9
10
int main()
{
int a = 1, b = 2, c,d,e;
c = b*a;
d = b * (-a);
e = b * 2;
printf("%d,%d,%d", c,d,e);
getchar();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int a = 1, b = 2, c,d,e;
00E21865 mov dword ptr [a],1
00E2186C mov dword ptr [b],2
c = b*a;
00E21873 mov eax,dword ptr [b]
00E21876 imul eax,dword ptr [a]
00E2187A mov dword ptr [c],eax
d = b * (-a);
00E2187D mov eax,dword ptr [a]
00E21880 neg eax
00E21882 imul eax,dword ptr [b]
00E21886 mov dword ptr [d],eax
e = b * 2;
00E21889 mov eax,dword ptr [b]
00E2188C shl eax,1
00E2188E mov dword ptr [e],eax

NEG:把操作数按位取反加一 (可以用来求一个数的相反数)

shl:左移指令,左移一位就是乘以2

除法

除法的指令:idiv 和 div

idiv 对应的是有符号的乘法,div 对应的是无符号的乘法

1
2
3
4
5
6
7
8
9
10
int main()
{
int a = 1, b = 2, c,d,e;
c = b/a;
d = b / (-a);
e = b /2;
printf("%d,%d,%d", c,d,e);
getchar();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
int a = 1, b = 2, c,d,e;
00304535 mov dword ptr [a],1
0030453C mov dword ptr [b],2
c = b/a;
00304543 mov eax,dword ptr [b]
00304546 cdq
00304547 idiv eax,dword ptr [a]
0030454A mov dword ptr [c],eax
d = b / (-a);
0030454D mov ecx,dword ptr [a]
00304550 neg ecx
00304552 mov eax,dword ptr [b]
00304555 cdq
00304556 idiv eax,ecx
00304558 mov dword ptr [d],eax
e = b /2;
0030455B mov eax,dword ptr [b]
0030455E cdq
0030455F sub eax,edx
00304561 sar eax,1
00304563 mov dword ptr [e],eax

原文链接:https://blog.csdn.net/oBuYiSeng/article/details/50349139

cdq: 是一个让很多人感到困惑的指令。 这个指令把 EAX 的第 31 bit 复制到 EDX 的每一个 bit 上。 它大多出现在除法运算之前。它实际的作用只是把EDX的所有位都设成EAX最高位的值。也就是说,当EAX <80000000, EDX 为00000000;当EAX >= 80000000, EDX 则为FFFFFFFF。

例如 :
假设 EAX 是 FFFFFFFB (-5) ,它的第 31 bit (最左边) 是 1,
执行 CDQ 后, CDQ 把第 31 bit 复制至 EDX 所有 bit
EDX 变成 FFFFFFFF
这时候, EDX:EAX 变成 FFFFFFFF FFFFFFFB ,它是一个 64 bit 的大型数字,数值依旧是 -5。

    EDX:EAX,这里表示EDX,EAX连用表示64位数

也就是说:cdq是扩展字节的指令

在除以2的时候,使用了sar右移的指令,方便得一批~

自增和自减

1
2
3
4
5
6
7
8
9
10
#define MAX 6
int main()
{
int i=0;
while (i < MAX)
{
printf("%d\n", i);
i++;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
int i=0;
00684535 mov dword ptr [i],0
while (i < MAX)
0068453C cmp dword ptr [i],6
00684540 jge __$EncStackInitStart+42h (068455Eh)
{
printf("%d\n", i);
00684542 mov eax,dword ptr [i]
00684545 push eax
00684546 push offset string "%d" (0687BD0h)
0068454B call _printf (06810D2h)
00684550 add esp,8
i++;
00684553 mov eax,dword ptr [i]
00684556 add eax,1
00684559 mov dword ptr [i],eax
}
0068455C jmp __$EncStackInitStart+20h (068453Ch)
}

jge:大于等于就跳,这里用来跳出循环

inc:自加一

运算逻辑关系

jmp 跳转有关的指令,很多。

比如:je jz js jnz jne … 遇到的时候不知道可以去百度

并且这种有关系的跳转指令,一般都要和cmp 和 test 一下。

test:俩操作数进行运算.

cmp:俩操作数进行减法运算.

大概知道就好了,后面遇到了在查。

逻辑运算

  • 与&&
  • 或||
  • 非!
1
2
3
4
int i=0
if(!i) //真
1==2||1==1 //真
1==2&&1==1 //假

条件表达式

三目运算符

exp1? exp2:exp3

1为真执行 2 ;1为假执行3

1
2
3
4
5
6
7
8
9
int main()
{
int i=1,a=2,c=3,d;
if (i !=MAX)
{
d = (a == c) ? i : a;
printf("%d\n", d);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
 int i=1,a=2,c=3,d;
007D4535 mov dword ptr [i],1
007D453C mov dword ptr [a],2
007D4543 mov dword ptr [c],3
if (i !=MAX)
007D454A cmp dword ptr [i],6
007D454E je __$EncStackInitStart+6Ah (07D4586h)
{
d = (a == c) ? i : a;
007D4550 mov eax,dword ptr [a]
007D4553 cmp eax,dword ptr [c]
007D4556 jne __$EncStackInitStart+47h (07D4563h)
007D4558 mov ecx,dword ptr [i]
007D455B mov dword ptr [ebp-0F4h],ecx
007D4561 jmp __$EncStackInitStart+50h (07D456Ch)
007D4563 mov edx,dword ptr [a]
007D4566 mov dword ptr [ebp-0F4h],edx
007D456C mov eax,dword ptr [ebp-0F4h]
007D4572 mov dword ptr [d],eax
printf("%d\n", d);
007D4575 mov eax,dword ptr [d]
007D4578 push eax
007D4579 push offset string "%d" (07D7BD0h)
007D457E call _printf (07D10D2h)
007D4583 add esp,8
}
}

执行的结果是:2

位运算

  • << 左移
  • >> 右移
  • | 位或 有一个1就是1
  • &位与 有一个0就是0
  • ^ 异或 相同则为0,不同则为1
  • ~ 取反 1变0 ,0 变1
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int main()
{
int i=1,a,b,c,d,e,f;
a = i << 1;
b = i >> 1;
c = i | 1;
d = i & 1;
e = i ^ 1;
f = ~i;
printf("%x\n",a);
printf("%x\n", b);
printf("%x\n", c);
printf("%x\n", d);
printf("%x\n", e);
printf("%x\n", f);

}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
int i=1,a,b,c,d,e,f;
00414535 mov dword ptr [i],1
a = i << 1;
0041453C mov eax,dword ptr [i]
0041453F shl eax,1
00414541 mov dword ptr [a],eax
b = i >> 1;
00414544 mov eax,dword ptr [i]
00414547 sar eax,1
00414549 mov dword ptr [b],eax
c = i | 1;
0041454C mov eax,dword ptr [i]
0041454F or eax,1
00414552 mov dword ptr [c],eax
d = i & 1;
00414555 mov eax,dword ptr [i]
00414558 and eax,1
0041455B mov dword ptr [d],eax
e = i ^ 1;
0041455E mov eax,dword ptr [i]
00414561 xor eax,1
00414564 mov dword ptr [e],eax
f = ~i;
00414567 mov eax,dword ptr [i]
0041456A not eax
0041456C mov dword ptr [f],eax

输出的结果是:

2
0
1
1
0
fffffffe

4.分支语句

也就是要判断

if else switch ..

IF

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#pragma warning(disable:4996)
int main()
{
int a;
printf("number: ");
scanf("%d",&a);
if(a==0)
{
printf("you are wrong!");
}
else
{
printf("yes");
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
 int a;
printf("number: ");
0012186F push offset string "%d" (0127BD0h)
00121874 call _printf (01210D2h)
00121879 add esp,4
scanf_s("%d", &a);
0012187C lea eax,[a]
0012187F push eax
00121880 push offset string "%d" (0127B30h)
00121885 call _scanf_s (01213C0h)
0012188A add esp,8
if (a == 0)
0012188D cmp dword ptr [a],0
00121891 jne _main+52h (01218A2h)
{
printf("you are wrong!");
00121893 push offset string "you are wrong!" (0127BDCh)
00121898 call _printf (01210D2h)
0012189D add esp,4
}
001218A0 jmp _main+5Fh (01218AFh)
else
{
printf("yes");
001218A2 push offset string "yes" (0127BECh)
001218A7 call _printf (01210D2h)
001218AC add esp,4
}
}

jne 不等则跳转

SWITCH

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <stdio.h>
int main()
{
int a;
printf("input integer number: ");
scanf("%d",&a);
switch(a)
{
case 1:printf("Monday\n");
break;
case 2:printf("Tuesday\n");
break;
case 3:printf("Wednesday\n");
break;
case 4:printf("Thursday\n");
break;
case 5:printf("Friday\n");
break;
case 6:printf("Saturday\n");
break;
case 7:printf("Sunday\n");
break;
default:printf("error\n");
}
}

汇编:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
int a;
printf("input integer number: ");
00D5524F push offset string "input integer number: " (0D57CD0h)
00D55254 call _printf (0D510D2h)
00D55259 add esp,4
scanf_s("%d", &a);
00D5525C lea eax,[a]
00D5525F push eax
00D55260 push offset string "%d" (0D57B30h)
00D55265 call _scanf_s (0D513C0h)
00D5526A add esp,8
switch (a)
00D5526D mov eax,dword ptr [a]
00D55270 mov dword ptr [ebp-0D4h],eax
00D55276 mov ecx,dword ptr [ebp-0D4h]
00D5527C sub ecx,1
00D5527F mov dword ptr [ebp-0D4h],ecx
00D55285 cmp dword ptr [ebp-0D4h],6
00D5528C ja $LN10+0Fh (0D55304h)
00D5528E mov edx,dword ptr [ebp-0D4h]
00D55294 jmp dword ptr [edx*4+0D5535Ch]
{
case 1:printf("Monday\n");
00D5529B push offset string "Monday\n" (0D57BDCh)
00D552A0 call _printf (0D510D2h)
00D552A5 add esp,4
break;
00D552A8 jmp $LN10+1Ch (0D55311h)
case 2:printf("Tuesday\n");
00D552AA push offset string "%d" (0D57BD0h)
00D552AF call _printf (0D510D2h)
00D552B4 add esp,4
break;
00D552B7 jmp $LN10+1Ch (0D55311h)
case 3:printf("Wednesday\n");
00D552B9 push offset string "Wednesday\n" (0D57BE4h)
00D552BE call _printf (0D510D2h)
00D552C3 add esp,4
break;
00D552C6 jmp $LN10+1Ch (0D55311h)
case 4:printf("Thursday\n");
00D552C8 push offset string "Thursday\n" (0D57CE8h)
00D552CD call _printf (0D510D2h)
00D552D2 add esp,4
break;
00D552D5 jmp $LN10+1Ch (0D55311h)
case 5:printf("Friday\n");
00D552D7 push offset string "Friday\n" (0D57CF4h)
00D552DC call _printf (0D510D2h)
00D552E1 add esp,4
break;
00D552E4 jmp $LN10+1Ch (0D55311h)
case 6:printf("Saturday\n");
00D552E6 push offset string "Saturday\n" (0D57E20h)
00D552EB call _printf (0D510D2h)
00D552F0 add esp,4
break;
00D552F3 jmp $LN10+1Ch (0D55311h)
case 7:printf("Sunday\n");
00D552F5 push offset string "Sunday\n" (0D57E2Ch)
00D552FA call _printf (0D510D2h)
00D552FF add esp,4
break;
00D55302 jmp $LN10+1Ch (0D55311h)
default:printf("error\n");
00D55304 push offset string "error\n" (0D57E34h)
00D55309 call _printf (0D510D2h)
00D5530E add esp,4
}
}

mov eax,[ebx+8]则是把内存地址为ebx+8处的数据赋给eax。

lea是“load effective address”的缩写,简单的说,lea指令可以用来将一个内存地址直接赋给目的操作数