C語言表示式在記憶體中的執行方式

2021-10-12 02:51:51 字數 4942 閱讀 8653

c語言程式中,所有變數的值都以補碼的形式儲存在記憶體之中。計算機中資料的表示方法有三種形式:原碼、反碼和補碼。

其中:正數的補碼=原碼,負數的補碼按照下面的規則進行計算:

原碼:直接將資料轉化為二進位制序列。(如果是有符號數:最高位為符號位,符號位為0代表正數,符號位為1代表負數。 如果是無符號數,則沒有符號位)。

補碼:原碼的符號位不變,其餘二進位制位 按位取反即可得到反碼。

補碼:反碼+1

其中:補碼轉化為原碼時,不需要按照上訴步驟進行反操作。可以直接將操作順序中的補碼和原碼互換(即:原碼=補碼按位取反(符號位不變),再+1。補碼=原碼按位取反(符號位不變),再+1)。

原碼和補碼都是二進位制,為什麼非要將原碼換成補碼再放到記憶體中呢?答案是:如果記憶體中直接放原碼可能會導致一些問題。

假設記憶體中存放的是原碼,無符號數或正數的操作完全沒問題。

比如:char a=1;   char b=2;   char c=a+b;

轉換為二進位制原碼:a= 0000 0001;      b=0000 0010;   c=a+b=0000 0011=3. 

但對於有符號負數來說,就會存在乙個問題:符號位怎麼操作? 

比如: char a= -1;   char b=-2;   char c=a+b;

轉換為二進位制原碼:a= 1000 0001;      b=1000 0010;   c=a+b=1 0000 0011=0000 0011=3;(二個符號位都是1,相加進製,但char只能儲存8位資料,所以,最高位被丟失,本來不是符號位的0變成了符號位,致結果錯誤)。

如果將其轉化為補碼:a=1111 1111; b=1111 1110;  c=a+b=11111 1101;    去掉最高位後c=1111 1101,此時c表示的是補碼,再將其轉化為原碼:c=1000 0011=-3

所以,將資料轉化為補碼後再放入記憶體中,就不需要將符號位單獨拿出來進行處理。這也是為什麼記憶體中存放的資料都是補碼的原因。

表示式和我們寫的數學公式是類似的,必須明確優先順序才能得出我們需要的值。不同的是,對於數學問題來說,編譯器是人腦,對於c語言程式來說,編譯器是電腦軟體。這將導致一些我們人腦認為沒有問題的表示式,電腦卻看不懂。

比如:a=b*c + c*d +d*e;       對於人腦來說,因為+的優先順序比*低,所以我們缺省會先計算a*b    c*d    d*e,再將三個值相加。但是對於編譯器來說卻不一定是這樣,第乙個 * 比第乙個 + 的優先順序高,但第三個 * 的優先順序一定比第乙個 + 的優先順序高嗎?  答案:這取決於編譯器,不同的編譯器會給出不同的解釋,最後也會導致不同的結果。

所以:表示式求值的順序只有一部分是由操作符的優先順序和結合性決定的。

對於下面這個**:

int main()

不同的編譯器可能會給出不同的結果:

值編譯器

-128

tandy 6000 xenix 3.2

30dec alpha osf1 2.0

4visual studio 2019

上訴**就是邏輯混亂的**,又叫非法**。(所以我們寫**時,只有在保證邏輯正確的基礎上,才能簡化**。所以**不是越長越好,但也不是越短越好)。

對於char short 和int型的變數,如果其是整數,在進行整數運算時,運算環境都是在cpu中的內整形運算器(alu)內完成的,而alu的運算元的位元組長度是int型,這也是目前cpu的通用暫存器的長度。 這意味著:哪怕只是2個char型的整數相加運算,運算前cpu也會將變數轉化為int型變數,再計算。 這裡不討論最後輸出的資料型別,因為c語言支援 強制型別轉換,你可以將結果輸出為你需要的資料型別。

舉例: char a=8;  char b= -1;   char c=a+b;     首先:a的二進位制補碼為:a=0000 1000 ; b的二進位制補碼為: b=1111 1111;   

a b都是整數,運算時,將a b進行整型提公升(相當於將a b強制型別轉換為 int型),轉換時:如果是有符號數,補符號位。如果是無符號數,補0;  (注意:a b是不是有符號是只取決於定義a b時,a b的資料型別是不是有符號的)

這裡a b都是有符號數,所以高位補 符號位。(假設系統為32位)即a= 00000000 00000000 00000000 0000 1000;  b=11111111 11111111 11111111 11111111;   c=a+b=1 00000000 00000000 00000000 00000111。  

因為定義c時,將其資料型別定義為char型,所以此時記憶體中c=00000111;(只保留後8位)。   此時c對應的10進製數為:c=00000111 =7;(注意:正數的補碼=原碼,只有負數才需要通過取反+1計算補碼)

這裡需要注意的是:(我們定義:占用空間小的資料型別為 小資料型別,占用空間大的資料型別為 大資料型別。  這裡的小 大 只是相對而言,比如int 相對於char 就是大資料型別,char就是小資料型別)

(1)二個不同資料型別的整數進行運算,一般都是先將小資料型別的數提公升到大資料型別。比如說 int a  和 float b,   a b進行運算時,先將a的資料型別提公升到float,再運算。    

(2)二個不同資料型別的整數進行運算,如果大資料型別還沒int大,則將二個整數都提公升到int。比如 int a 和char b,a b進行運算時,先將b的資料型別提公升到int型,再運算。   char a  short b,  a b運算時,將a和b都提公升到int型,再運算。

(3)運算結果的資料型別與大資料型別保持一致(>=int)。比如:int a  和 float b的運算結果是float型。   char a  short b運算的結果是int型。     最後用來接收結果的變數c只會根據自己的資料型別進行資料的擷取,比如 int a=00000000 00000000 00000001 00000000, char b=a; 那麼b=00000000(a的後8位)。

(4)最重要的一點:只有參與運算才會發生整型提公升。(什麼是參與運算: char a;   a+1;  這裡a參與了運算。   char a;  !a;  這裡只是將a進行取反操作,a本身並沒有參與表示式運算)

有了上訴結論,如何運算時不注意資料型別,那麼就會導致出現一些我們不想看到的結果。這些問題的本質就是程式執行會按照既定的邏輯進行,這與我們的主觀想象並不全是一樣的,這也叫隱式轉換

這裡先給出c語言常用資料型別:

c語言常用資料型別

資料型別

所佔空間

char

1位元組(8bit)

short

2位元組(16bit)

int4位元組(32bit)

long

4位元組(32bit)

long long

8位元組(64bit)

float

4位元組(32bit)

double

8位元組(64bit)

整型提公升例項:

例項1:

#include int main()

輸出結果為: 1  4  1   (程式中u%是指無符號整型(unsigned int)。sizeof(a) 求的是a空間佔幾個位元組)

這裡需要注意的是:sizeof(a)的作用是求a空間佔幾個位元組,但sizeof是c語言的關鍵字,即中sizeof(a)過程中,a並沒有參與運算。

printf("%u\n", sizeof(c));  //求c的空間大小,c為char型,並且sizeof(a)不是運算操作,使用c並沒有發生整型提公升,即結果為1位元組

printf("%u\n", sizeof(+c));//   +c是運算,c被整型提公升,所以輸出結果為4位元組(這也是整型提公升最直接的證據)

printf("%u\n", sizeof(!c));  //   !c並沒有參與表示式運算,所以輸出結果為1位元組

例項2:

#include int main()

輸出結果為: c

if語句中都進行了賦值操作,即a b c都參與了表示式運算,而a b的資料型別比int小,所以要進行整型提公升,而c不需要進行整型提公升,所以只有c=0xb600000000成立。

如果我們想要輸出 a和b,我們通過整形提公升來反推a b的真實值。

char a=0xb6;   寫出二進位制:a=1011 0110 。   a為有符號數,整型提公升時,高位補符號位,則 a= 11111111 11111111 11111111 10110110=0xffffffb6

char b=0xb600;  其二進位制為b= 10110110 00000000。      b為有符號數,整型提公升時,高位補符號位,則b= 11111111 11111111 10110110 00000000=0xffffb600

將**改為:

#include int main()

輸出結果: a b c

(1)c語言中,支援資料型別強制轉換。強制轉換時,如果將小資料型別轉為大資料型別,相當於將某塊記憶體中的資料放到乙個更大的記憶體空間中(這樣做一般不會造成程式問題)。但如果反之,相當於把某塊記憶體中的資料放到乙個更小的記憶體空間中,這就可能導致資料擷取時發生資料丟失。

(2)c語言中,如果變數的資料型別比int小,則其參與表示式運算時,cpu會先將其資料型別強制轉換為int,再讓其進行運算。

C語言表示式

逗號表示式逗號表示式 就是用逗號隔開的多個表示式,從左向右依次執行 exp1,exp2,wxp3,wxpn注意 整個表示式的結果是最後乙個表示式的結果。void test 下表引用 函式呼叫和結構成員 1.下表引用符操作符 運算元 乙個陣列名 以後索引值 int arr 10 arr 9 10 有兩...

Go語言 表示式

go語言僅有25個保留的關鍵字,下面我們就列舉下這25個關鍵字 break default func inte ce select case defer gomap struct chan else goto package switch const fallthrough ifrange type...

c語言 表示式的計算

include stack.h 提供棧及其基本操作函式 include mymath.h 提供階乘函式 include include define buffsize 128 define charsize 10 define pi 3.141593 define e 2.718282 define...