1.什麼是表達式, 表達式的屬性是什麼
寫得好的博客貼在這裏:https://blog.csdn.net/astrotycoon/article/details/50857326
定義:概括說來表達式是由一系列運算符(operators)和操作數(operands)組成的。這既是表達式的定義,同時也指明瞭表達式的組成成份。運算符指明瞭要進行何種運算和操作,而操作數則是運算符操作的對象
屬性:任何表達式都有值和類型兩個屬性
2.左值&右值(C和指針P79)
右值不細說, 左值概括出來就是:如果能確定一個表達式最終結果的存儲位置, 那麼這個表達式可以作爲左值,需要注意的是某些表達式也可作爲左值
這一部分的練習可以參考<C和指針>P99的指針表達式
3.強制類型轉換運算符(譚浩強第四版:P56)
覺得寫得好的博客貼在下面:https://blog.csdn.net/simple_the_best/article/details/48421279
4.示例
上面藍色星星說進行強制類型運算(int)x後得到一個int類型的中間數據, 賦值後就不再存在了,應當如何理解呢?正好最近在看的項目代碼中有大量的指針強轉,結合這一點,從AT&T彙編角度看一下編譯器怎麼處理的,因爲GCC用的是AT&T彙編,所以需要參考<AT&T彙編語言>這本手冊
爲了把每條語句看清楚, 我用printf將語句隔開,看註釋就好,同時需要注意彙編中movl和leal的區別,看註釋
root@ubuntu:/lianxi/lianxi_oj/cast_pointer# gcc cast_pointer.c -m32
root@ubuntu:/lianxi/lianxi_oj/cast_pointer# ./a.out
=====1=====
=====2=====
&a = 0xbfb8e8f4
=====3=====
=====4=====
p = 0xbfb8e8f4
=====5=====
&p = 0xbfb8e8f8
=====6=====
=====7=====
q = 0xbfb8e8f4
=====8=====
&q = 0xbfb8e8fc
=====9=====
*q = 0x78
=====10=====
#include <stdio.h>
//#define DEBUG(format,...) printf(format,##__VA_ARGS__)
//#define PRINT(format,args...) printf(format,##args)
int main(int argc, char* argv[])
{
printf("=====1=====\n");
int a = 0x12345678;//little endian mem:low-0x78 0x56 0x34 0x12-high
printf("=====2=====\n");
printf("&a = %p\n", &a);
printf("=====3=====\n");
int* p = &a;
printf("=====4=====\n");
printf("p = %p\n", p);
printf("=====5=====\n");
printf("&p = %p\n", &p);
printf("=====6=====\n");
char* q = (char*)p;
printf("=====7=====\n");
printf("q = %p\n", q);
printf("=====8=====\n");
printf("&q = %p\n", &q);
printf("=====9=====\n");
printf("*q = %#x\n", *q);
printf("=====10=====\n");
return 0;
}
root@ubuntu:/lianxi/lianxi_oj/cast_pointer# gcc -S cast_pointer.c
.file "cast_pointer.c"
.section .rodata
.LC0:
.string "=====1====="
.LC1:
.string "=====2====="
.LC2:
.string "&a = %p\n"
.LC3:
.string "=====3====="
.LC4:
.string "=====4====="
.LC5:
.string "p = %p\n"
.LC6:
.string "=====5====="
.LC7:
.string "&p = %p\n"
.LC8:
.string "=====6====="
.LC9:
.string "=====7====="
.LC10:
.string "q = %p\n"
.LC11:
.string "=====8====="
.LC12:
.string "&q = %p\n"
.LC13:
.string "=====9====="
.LC14:
.string "*q = %#x\n"
.LC15:
.string "=====10====="
.text
.globl main
.type main, @function
main:
.LFB0:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
andl $-16, %esp
subl $32, %esp
movl $.LC0, (%esp) printf("=====1=====\n");
call puts
movl $305419896, 20(%esp) int a = 0x12345678;
將立即數305419896放到(寄存器esp值+20)的值作爲地址對應的內存中
從打印可以看出&a==0xbfb8e8f4, 所以esp寄存器的初始值應該是0xbfb8e8e0
movl $.LC1, (%esp) printf("=====2=====\n");
call puts
movl $.LC2, %eax printf("&a = %p\n", &a);
leal 20(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
首先&a是一個表達式, 將(寄存器esp的值+20)的值放到寄存器edx中, edx的值爲:0xbfb8e8f4
然後再將edx的值放到(寄存器esp的值+4)的值作爲地址對應的內存中進行打印操作
在這裏我們看到&a是一個表達式, 它的值其實是放在寄存器edx中的, 所以不能作爲左值
movl $.LC3, (%esp) printf("=====3=====\n");
call puts
leal 20(%esp), %eax int* p = &a;
movl %eax, 24(%esp)
同樣, &a是一個不能作爲左值的表達式, 其值0xbfb8e8f4放在寄存器eax中
然後將eax中的值放到(寄存器esp的值+24)的值作爲地址對應的內存中
所以p的值是0xbfb8e8f4, p的地址是0xbfb8e8f8
在這裏我們看出p就可以作爲左值, 因爲它表示一個特定的內存位置
movl $.LC4, (%esp) printf("=====4=====\n");
call puts
movl 24(%esp), %edx printf("p = %p\n", p);
movl $.LC5, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
1.首先將(寄存器esp的值+24)的值作爲地址對應的內存中的值放到寄存器edx中
即將0xbfb8e8f8地址對應的內存內容0xbfb8e8f4放到寄存器edx中
2.進行printf打印工作
movl $.LC6, (%esp) printf("=====5=====\n");
call puts
movl $.LC7, %eax printf("&p = %p\n", &p);
leal 24(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
1.將(寄存器esp的值+24)的值放到寄存器edx中
即將0xbfb8e8f8放到寄存器edx中
2.進行printf打印工作
movl $.LC8, (%esp) printf("=====6=====\n");
call puts
movl 24(%esp), %eax char* q = (char*)p;強轉重點!!!
movl %eax, 28(%esp)
1.將(寄存器esp的值+24)的值作爲地址對應的內存中的值放到寄存器eax中
即將地址0xbfb8e8f8對應內存中的內容0xbfb8e8f4放到寄存器eax中
2.將寄存器eax的值放到(寄存器esp的值+28)的值作爲地址對應的內存中
所以q的地址爲0xbfb8e8fc, q的值爲0xbfb8e8f4
所以這個過程和<譚浩強>P56中的描述一致:
我們可以理解爲在強轉的過程中產生了一箇中間變量char* tmp(實際就是寄存器eax)
先將p的值0xbfb8e8f4拷貝入tmp, 再將tmp的值拷貝到p中即可
實際操作完成後tmp(eax)被系統回收, p的值和q相同, 只是類型不同
movl $.LC9, (%esp) printf("=====7=====\n");
call puts
movl 28(%esp), %edx printf("q = %p\n", q);
movl $.LC10, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $.LC11, (%esp) printf("=====8=====\n");
call puts
movl $.LC12, %eax printf("&q = %p\n", &q);
leal 28(%esp), %edx
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $.LC13, (%esp) printf("=====9=====\n");
call puts
movl 28(%esp), %eax printf("*q = %#x\n", *q);
movzbl (%eax), %eax
movsbl %al, %edx
movl $.LC14, %eax
movl %edx, 4(%esp)
movl %eax, (%esp)
call printf
movl $.LC15, (%esp) printf("=====10=====\n");
call puts
movl $0, %eax return 0...
leave
.cfi_restore 5
.cfi_def_cfa 4, 4
ret
.cfi_endproc
.LFE0:
.size main, .-main
.ident "GCC: (Ubuntu/Linaro 4.6.3-1ubuntu5) 4.6.3"
.section .note.GNU-stack,"",@progbits
5.補充
在本文的附件中上傳了int2short和short2int的源碼和彙編註釋
很好地解釋了強轉的規則,與文中地址博客中作者的觀點一致