1, 操作数提示符:
三种:
立即数:常数值, 在ATT格式的汇编代码中, 书写格式是 $ + 整数, 如:$-123 $0X12
寄存器:如:32位的%eax 16位的%ax 8位的%al
存储器引用:
Imm(Eb) 表示地址为:Eb+Imm
Imm(Eb, Ei) 地址为:Eb(基址)+ Ei(变址) + Imm(立即数偏移),
Imm(Eb, Ei, s) 地址为: Eb + Ei * s + Imm
2, 字节传送指令:
mov 同等传送, 即俩者的大小一致 (如:movb byte, movew word, movel longword(DW))
movs/ movz 不同等传送, 即俩者的大小可以不一致, 如:movsbl 表示将源byte传送到目的longword, 用1 或0补充剩余位(取决于源byte的最高位是否为1), movzbl 与movsbl相似, 用0补充剩余位
push & pop
3, 特殊指令:
lea: 直接上例:
lea 4(%eax) %ebx //表示 %ebx 里面的值为%eax + 4 。
而 mov 4(%eax) %ebx //表示 %ebx里面的值为【eax+4】这个地址的值
其实, 复杂的来说, lea 表示获取源地址的偏移地址, 对于上例来说, 偏移地址当然是%eax + 4 咯。
int fun (unsigned x)
{
int val = 0;
while (x) {
val ^= x;
x >>= 1;
}
return val & 0X1;
}
这个函数是测试x二进制里面1的个数的奇偶, 奇返回1, 偶返回0;
理解, 第一着重于移位操作, 每次移位 都使前一位移动至后一位(即使得n位与n-1位之间的运算成为可能), 第二 着重于异或操作,对于1bit ^ 1bit来说, 恰好是查看这俩者的1的总个数是否为偶, 第三着重于第一位, 每次移位操作 都使得前一位都能跟当前第一位进行异或操作, 而这一位的结果是这一位上的1的个数是否为偶。loop, 测试前前一位。。。。。 again and again until it ends.
int fun (unsigned x) {
int val = 0;
int i;
for (i = 0; i< 32; i++) {
val = (val <<1) | (x & 0x1);
x >>= 1;
}
return val;
}
这是获取x的位反转制造出来的镜像
4, switch指令较条件跳转的优越性
条件跳转, 在asm中, 是以类似cmp + jne 等指令实现的。 对于N种情况来说, 就意味着要N对cmp-jne这些指令。
switch对于条件转换来说, 是利用了跳转表来进行的(其实就是地址偏移),例如情况为100-106, 即只需要计算当前值-100 的大小(k), 跳转到jt【k】所指的位置即可。
其实这个也说明了某种问题, 也就是对于疏散的情况集合, 对于跳转表的使用会不会添加空白无用的区域浪费呢?
to be continued.......
5, Union的作用:
类似struct可以存放数据, 不同在于, Union所有数据共用内存, 即Union的大小为数据中大小最大的, 而不是大小之和。
用处: 类型转换。。。注意小端法读取数据
6, 为什么要数据对齐:
条件:1,平台获取数据都是以固定字节数获取的。 2,有些数据不规则,如:struct{int a, char c;}A; 其标准大小是5Bytes
导致的情况: 比如固定字节数为8, 不对齐数据就意味着第一次读取, 获得 A1 以及A2的部分数据, 为了读取A2的值, 必须读取两次,以及需要进行高低位的修补。。。。
所以数据对齐, 是一种以空间换时间的策略。
这也是为什么 sizeof(A)!= 5 而是8(test in VS2013)。
测试:
struct A{
int a;
char c;
int b;
};
void testSizeOf()
{
printf("size of A is %d\n", sizeof(A));
A k = { 1, 'c', 2 };
printf("A:\na: %d\nc: %c\nb: %d\n\n", k.a, k.c, k.b);
printf("A:\na: %x\nc: %x\nb: %x\n\n", k, k, k); //其中%x表示十六进制整数。 科普:\x 表示其后面两位为十六进制数,常用于printf cout等。
}
结果为:
size of A is 12
A:
a: 1
c: c
b: 2
A:
a: 1
c: cccccc63 // 63是 'c'的十六进制ASCII字符表示形式
b: 2
表明:
1, 对于结构体k来说, 执行printf多次输出,其过程类似于文件流读取(读即前进)。
2, 结构体A中, c占用的字节为4, 其中多余3字节作为补充。
注:char *c 对象大小为4, 因为里面装的是地址。。。。。 void *v 大小亦是4,同理。
这也表明了数据位置排列的重要性。一般来说 应按照从大到小排列。 因为这样可以缩小对齐要求的字节数,如4->>2。(???若是从小到大, 会怎样? 会呵呵。。。)
7, 理解指针:
1) 指针指向函数:
int func(int a, int *b){...}
typedef int (*fp) (int a, int *b);
fp = func;
int result = fp(a, &b);
其实函数调用call也是移动到某一指令, 跟指针有差别吗?
2)指针跟数组无异:
int a[n], *(a+1) = a [1];
3) 改变类型后 寻址的区别:
char *cp;
(int*)cp + 7 => (cp, 4, 7)的位置
(int*)(cp+7) => (cp, 1, 7)的位置
8, 存储器的越界引用以及缓冲区溢出:
防范:
注: sizeof(“1234”)is 5; because of ‘\0’(0x00); 但是strlen返回的是4, 故在malloc的调用时, 应该malloc ( strlen(buf)+1 ). 记得要对返回值进行NULL判断。
9. AGE OF 64:
特殊数字: 0x101010101010101 72340172838076673