c语言 字符相加_C语言中自加自减的编译原理
自增自減規(guī)則
i++ 與 ++i 的主要區(qū)別有兩個(gè):
1、 i++ 返回原來的值,++i 返回加1后的值。 2、 i++ 不能作為左值,而++i 可以。
毫無疑問大家都知道第一點(diǎn)(不清楚的看下下面的實(shí)現(xiàn)代碼就了然了),我們重點(diǎn)說下第二點(diǎn)。 首先解釋下什么是左值(以下兩段引用自中文維基百科『右值引用』詞條)。
左值是對應(yīng)內(nèi)存中有確定存儲(chǔ)地址的對象的表達(dá)式的值,而右值是所有不是左值的表達(dá)式的值。一般來說,左值是可以放到賦值符號(hào)左邊的變量。
但能否被賦值不是區(qū)分左值與右值的依據(jù)。比如,C++的const左值是不可賦值的;而作為臨時(shí)對象的右值可能允許被賦值。左值與右值的根本區(qū)別在于是否允許取地址&運(yùn)算符獲得對應(yīng)的內(nèi)存地址。比如,
int i = 0; int *p1 = &(++i); //正確 int *p2 = &(i++); //錯(cuò)誤 ++i = 1; //正確 i++ = 5; //錯(cuò)誤那么為什么『i++ 不能作為左值,而++i 可以』? 看它們各自的實(shí)現(xiàn)就一目了然了:
// 前綴形式: int& int::operator++() //這里返回的是一個(gè)引用形式,就是說函數(shù)返回值也可以作為一個(gè)左值使用 {//函數(shù)本身無參,意味著是在自身空間內(nèi)增加1的 *this += 1; // 增加 return *this; // 取回值 } //后綴形式: const int int::operator++(int) //函數(shù)返回值是一個(gè)非左值型的,與前綴形式的差別所在。 {//函數(shù)帶參,說明有另外的空間開辟 int oldValue = *this; // 取回值 ++(*this); // 增加 return oldValue; // 返回被取回的值}如上所示,i++ 最后返回的是一個(gè)臨時(shí)變量,而臨時(shí)變量是右值。
運(yùn)算符讀取規(guī)則
C語言對于解決這個(gè)問題的解決方案可以歸納為一個(gè)很簡單的規(guī)則:每一個(gè)符號(hào)應(yīng)該包含盡可能多的字符。也就是說,編譯器將程序分解成符號(hào)的方法是:從左到右一個(gè)一個(gè)字符的讀入,字符一個(gè)字符地讀入,如果該字符可能組成一個(gè)符號(hào),那么再讀入下一個(gè)字符,判斷已經(jīng)讀入的兩個(gè)字符組成的字符串是否可能是一個(gè)符號(hào)的組成部分;如果可能,繼續(xù)讀入下一個(gè)字符,重復(fù)上述判斷,直到讀入的字符組成的字符串已不再可能組成一個(gè)有意義的符號(hào)。這個(gè)處理策略有時(shí)被稱為“貪心法”,或者,更口語化一點(diǎn),稱為“大嘴法”,Kernighan與Ritchie對這個(gè)方法的表述如下,“如果(編譯器的)輸入流截止至某個(gè)字符之前都已經(jīng)被分解為一個(gè)個(gè)符號(hào),那么下一個(gè)號(hào)將包括從該字符之后可能組成一個(gè)符號(hào)的最長字符串?!?/p>
? ---《C陷阱和缺陷》 第八頁
編譯器編譯情況
自增(后綴):
Turbo C中,先統(tǒng)一取值后依次自增;
VS中,從左向右依次取值自增;
int a,i=5; a=i+++i+++i++; //按照貪心算法,結(jié)果為(i++)+(i++)+(i++) //Turbo C中:a=5+5+5=15,i=8 //VS中:a=5+6+7=18,i=8以下編譯出錯(cuò)
int a,i=5; a=++i+++i+++i; //編譯錯(cuò)誤 //編譯器編譯后,((++i)++)+(i++)+i++i返回的是一個(gè)引用形式,無法在對(++i)在進(jìn)行自增。
匯編代碼
gcc編譯(++i)+(++i)+(++i)特別奇怪。。。
#include <stdio.h>int main() {int i=3;printf("%d",(++i)+(++i)+(++i));return 0; }匯編代碼(主要看main函數(shù))
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長字節(jié)放在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堆棧,此時(shí)i=5/661: 8b 45 fc mov -0x4(%rbp),%eax /把rbp短字節(jié)(i=5)堆棧值,移動(dòng)到寄存器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長字節(jié)放在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堆棧,此時(shí)i=5/661: 8b 45 fc mov -0x4(%rbp),%eax /把rbp短字節(jié)(i=5)堆棧值,移動(dòng)到寄存器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 /把結(jié)果i=6移動(dòng)到寄存器ax/66e: 01 d0 add %edx,%eax /把寄存器dx與寄存器ax相加,也就是10+6/670: 89 c6 mov %eax,%esi /把16移動(dòng)到寄存器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> /調(diào)用fun函數(shù)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是不是不符合規(guī)范?他并不像書上說的,自增比加號(hào)的優(yōu)先級(jí)高。
總結(jié)
以上是生活随笔為你收集整理的c语言 字符相加_C语言中自加自减的编译原理的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: jdbc mysql 字符集_JDBC对
- 下一篇: 为什么开不了4g网络_为什么4G网络越来