c语言 字符相加_C语言中自加自减的编译原理
自增自減規則
i++ 與 ++i 的主要區別有兩個:
1、 i++ 返回原來的值,++i 返回加1后的值。 2、 i++ 不能作為左值,而++i 可以。
毫無疑問大家都知道第一點(不清楚的看下下面的實現代碼就了然了),我們重點說下第二點。 首先解釋下什么是左值(以下兩段引用自中文維基百科『右值引用』詞條)。
左值是對應內存中有確定存儲地址的對象的表達式的值,而右值是所有不是左值的表達式的值。一般來說,左值是可以放到賦值符號左邊的變量。
但能否被賦值不是區分左值與右值的依據。比如,C++的const左值是不可賦值的;而作為臨時對象的右值可能允許被賦值。左值與右值的根本區別在于是否允許取地址&運算符獲得對應的內存地址。比如,
int i = 0; int *p1 = &(++i); //正確 int *p2 = &(i++); //錯誤 ++i = 1; //正確 i++ = 5; //錯誤那么為什么『i++ 不能作為左值,而++i 可以』? 看它們各自的實現就一目了然了:
// 前綴形式: int& int::operator++() //這里返回的是一個引用形式,就是說函數返回值也可以作為一個左值使用 {//函數本身無參,意味著是在自身空間內增加1的 *this += 1; // 增加 return *this; // 取回值 } //后綴形式: const int int::operator++(int) //函數返回值是一個非左值型的,與前綴形式的差別所在。 {//函數帶參,說明有另外的空間開辟 int oldValue = *this; // 取回值 ++(*this); // 增加 return oldValue; // 返回被取回的值}如上所示,i++ 最后返回的是一個臨時變量,而臨時變量是右值。
運算符讀取規則
C語言對于解決這個問題的解決方案可以歸納為一個很簡單的規則:每一個符號應該包含盡可能多的字符。也就是說,編譯器將程序分解成符號的方法是:從左到右一個一個字符的讀入,字符一個字符地讀入,如果該字符可能組成一個符號,那么再讀入下一個字符,判斷已經讀入的兩個字符組成的字符串是否可能是一個符號的組成部分;如果可能,繼續讀入下一個字符,重復上述判斷,直到讀入的字符組成的字符串已不再可能組成一個有意義的符號。這個處理策略有時被稱為“貪心法”,或者,更口語化一點,稱為“大嘴法”,Kernighan與Ritchie對這個方法的表述如下,“如果(編譯器的)輸入流截止至某個字符之前都已經被分解為一個個符號,那么下一個號將包括從該字符之后可能組成一個符號的最長字符串。”
? ---《C陷阱和缺陷》 第八頁
編譯器編譯情況
自增(后綴):
Turbo C中,先統一取值后依次自增;
VS中,從左向右依次取值自增;
int a,i=5; a=i+++i+++i++; //按照貪心算法,結果為(i++)+(i++)+(i++) //Turbo C中:a=5+5+5=15,i=8 //VS中:a=5+6+7=18,i=8以下編譯出錯
int a,i=5; a=++i+++i+++i; //編譯錯誤 //編譯器編譯后,((++i)++)+(i++)+i++i返回的是一個引用形式,無法在對(++i)在進行自增。
匯編代碼
gcc編譯(++i)+(++i)+(++i)特別奇怪。。。
#include <stdio.h>int main() {int i=3;printf("%d",(++i)+(++i)+(++i));return 0; }匯編代碼(主要看main函數)
000000000000064a <main>:64a: 55 push %rbp /賦值?/64b: 48 89 e5 mov %rsp,%rbp /rsp,rbp為堆棧地址?/64e: 48 83 ec 10 sub $0x10,%rsp /值為16/652: c7 45 fc 03 00 00 00 movl $0x3,-0x4(%rbp) /把i=3長字節放在rbp堆棧/659: 83 45 fc 01 addl $0x1,-0x4(%rbp) /i=i+1放入rbp堆棧/65d: 83 45 fc 01 addl $0x1,-0x4(%rbp) /i=i+1放入rbp堆棧,此時i=5/661: 8b 45 fc mov -0x4(%rbp),%eax /把rbp短字節(i=5)堆棧值,移動到寄存器ax/64a: 55 push %rbp /賦值?/64b: 48 89 e5 mov %rsp,%rbp /rsp,rbp為堆棧地址?/64e: 48 83 ec 10 sub $0x10,%rsp /值為16/652: c7 45 fc 03 00 00 00 movl $0x3,-0x4(%rbp) /把i=3長字節放在rbp堆棧/659: 83 45 fc 01 addl $0x1,-0x4(%rbp) /i=i+1放入rbp堆棧/65d: 83 45 fc 01 addl $0x1,-0x4(%rbp) /i=i+1放入rbp堆棧,此時i=5/661: 8b 45 fc mov -0x4(%rbp),%eax /把rbp短字節(i=5)堆棧值,移動到寄存器ax/664: 8d 14 00 lea (%rax,%rax,1),%edx /寄存器ax與寄存器ax相加放到寄存器dx/ /dx=10/667: 83 45 fc 01 addl $0x1,-0x4(%rbp) /i=i+1=6放入rbp堆棧/66b: 8b 45 fc mov -0x4(%rbp),%eax /把結果i=6移動到寄存器ax/66e: 01 d0 add %edx,%eax /把寄存器dx與寄存器ax相加,也就是10+6/670: 89 c6 mov %eax,%esi /把16移動到寄存器si/672: 48 8d 3d 9b 00 00 00 lea 0x9b(%rip),%rdi # 714 <_IO_stdin_used+0x4> /我也沒看懂/ 679: b8 00 00 00 00 mov $0x0,%eax /寄存器ax賦值/67e: e8 9d fe ff ff callq 520 <printf@plt> /調用fun函數printf/683: b8 00 00 00 00 mov $0x0,%eax /寄存器ax賦值/688: c9 leaveq 689: c3 retq 68a: 66 0f 1f 44 00 00 nopw 0x0(%rax,%rax,1) //可以看出,先自增兩次,相加,再自增,再相加,后賦值。 gcc是不是不符合規范?他并不像書上說的,自增比加號的優先級高。
總結
以上是生活随笔為你收集整理的c语言 字符相加_C语言中自加自减的编译原理的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jdbc mysql 字符集_JDBC对
- 下一篇: mysql的limit有什么缺陷_mys