青海省建設(shè)廳官方網(wǎng)站建設(shè)云蘭州seo實(shí)戰(zhàn)優(yōu)化
文章目錄
- 1.算術(shù)操作符
- 2.移位操作符
- 2.1 左移操作符
- 2.2 右移操作符
- 3.位操作符
- 按位與
- 按位或
- 按位異或
- 4.賦值操作符
- 復(fù)合賦值符
- 5.單目操作符
- 5.1單目操作符介紹
- 6.關(guān)系操作符
- 7.邏輯操作符
- 8.條件操作符
- 9.逗號(hào)表達(dá)式
- 10.下標(biāo)引用、函數(shù)調(diào)用和結(jié)構(gòu)成員
- 11表達(dá)式求值
- 11.1 隱式類型轉(zhuǎn)換
- 11.2算術(shù)轉(zhuǎn)換
- 11.3操作符的屬性
操作符分類:
- 算術(shù)操作符
- 移位操作符
- 位操作符
- 賦值操作符
- 單目操作符
- 關(guān)系操作符
- 邏輯操作符
- 條件操作符
- 逗號(hào)表達(dá)式
- 下標(biāo)引用、函數(shù)調(diào)用和結(jié)構(gòu)成員
1.算術(shù)操作符
+ - * / %
- 除了 % 操作符之外,其他的幾個(gè)操作符可以作用于整數(shù)和浮點(diǎn)數(shù)。
- 對(duì)于 / 操作符如果兩個(gè)操作數(shù)都為整數(shù),執(zhí)行整數(shù)除法。而只要有浮點(diǎn)數(shù)執(zhí)行的就是浮點(diǎn)數(shù)除法。
- % 操作符的兩個(gè)操作數(shù)必須為整數(shù)。返回的是整除之后的余數(shù)。
- / 得到的是商,% 得到的是余數(shù)
2.移位操作符
<< 左移操作符>>右移操作符注:移位操作符的操作數(shù)只能是整數(shù),移位操作符移動(dòng)的是二進(jìn)制的位
我們?nèi)粘V械臄?shù)字都是十進(jìn)制,十進(jìn)制向二進(jìn)制的轉(zhuǎn)化如下所示:
二進(jìn)制每一位都有他自己的權(quán)重,從右到左依次是2的0次方,2的1次方,2的3次方····
整數(shù)的二進(jìn)制表現(xiàn)形式是什么樣的呢?
二進(jìn)制的表現(xiàn)形式有三種:原碼,反碼,補(bǔ)碼
原碼:把一個(gè)數(shù)按照正負(fù)直接翻譯成二進(jìn)制就是原碼
反碼:反碼的符號(hào)位不變,其他位按位取反就是反碼
補(bǔ)碼:反碼+1
注:
正整數(shù)的原碼、反碼、補(bǔ)碼都是相同的
負(fù)整數(shù)的原碼、反碼、補(bǔ)碼是需要計(jì)算的
以-5為例:
最高的一位表示符號(hào)位,0是正數(shù),1是負(fù)數(shù)
整數(shù)在內(nèi)存中存儲(chǔ)的是:補(bǔ)碼的二進(jìn)制序列
2.1 左移操作符
移位規(guī)則:
左邊拋棄、右邊補(bǔ)0
看代碼:
#include <stdio.h>
int main()
{int a = 3;int b = a << 1;printf("%d\n", b);printf("%d\n", a);return 0;
}
3的二進(jìn)制位為
00000000000000000000000000000011
十進(jìn)制轉(zhuǎn)二進(jìn)制方法:十進(jìn)制的數(shù)除以要轉(zhuǎn)制的基數(shù)(二進(jìn)制就是2),取其余數(shù),由下向上寫結(jié)果,前邊剩余位置補(bǔ)0
如圖所示:
左移之后產(chǎn)生的結(jié)果為
00000000000000000000000000000110 打印結(jié)果為6
若a=-3,怎么求移動(dòng)之后的數(shù)呢?
a=-3時(shí)a的二進(jìn)制位為原碼為:
10000000000000000000000000000011
補(bǔ)碼為:(因?yàn)槎M(jìn)制中儲(chǔ)存移動(dòng)的時(shí)補(bǔ)碼)
11111111111111111111111111111101
移動(dòng)之后為 11111111111111111111111111111010
但是求移動(dòng)后的數(shù)是由原碼得到的,所以移動(dòng)后原碼為:
10000000000000000000000000000110
運(yùn)行結(jié)果為-6
原碼與補(bǔ)碼的轉(zhuǎn)換
2.2 右移操作符
移位規(guī)則:
首先右移運(yùn)算分兩種:
- 邏輯移位
左邊用0填充,右邊丟棄- 算術(shù)移位
左邊用原該值的符號(hào)位填充,右邊丟棄
右移的時(shí)候采用哪種方法是取決于編譯器的!
看代碼:
#include <stdio.h>
int main()
{int a = -1;int b = a >> 1;printf("b = %d\n", b);printf("a = %d\n", a);return 0;
}
-1在內(nèi)存中存儲(chǔ)的補(bǔ)碼為
11111111111111111111111111111111
運(yùn)行結(jié)果:
我們發(fā)現(xiàn)在vs2022運(yùn)行下結(jié)果為-1,所以編譯器采用的是算數(shù)右移(絕大多數(shù)編譯器都是算數(shù)右移)
警告?: 對(duì)于移位運(yùn)算符,不要移動(dòng)負(fù)數(shù)位,這個(gè)是標(biāo)準(zhǔn)未定義的。
例如:
int num = 10;
num>>-1;//error
3.位操作符
& //按位與| //按位或^ //按位異或注:他們的操作數(shù)必須是整數(shù)。
按位與
看代碼:
#include <stdio.h>
int main()
{int a = 3;int b = -5;int c = a & b;printf("%d\n", c);return 0;}
00000000000000000000000000000011 - 3的補(bǔ)碼
11111111111111111111111111111011 -5的補(bǔ)碼
按位與是對(duì)應(yīng)的二進(jìn)制位進(jìn)行按位與,1和0取0,同0取0,同1取1
按位與之后結(jié)果:
00000000000000000000000000000011
所以按位與結(jié)果為3
按位或
看代碼:
#include <stdio.h>
int main()
{int a = 3;int b = -5;int c = a | b;printf("%d\n", c);return 0;}
按位或也是對(duì)應(yīng)的二進(jìn)制位進(jìn)行按位或,1或0之間取1,同0為0,同1為1
按位或之后結(jié)果
11111111111111111111111111111011
所以結(jié)果為-5
按位異或
看代碼:
#include <stdio.h>
int main()
{int a = 3;int b = -5;int c = a ^ b;printf("%d\n", c);return 0;}
按位異或 -:對(duì)應(yīng)的二進(jìn)制位,相同為0,相異為1
按位異或結(jié)果為:
11111111111111111111111111111000
取原碼之后打印結(jié)果為-8
例題:
不能創(chuàng)建臨時(shí)變量(第三個(gè)變量),實(shí)現(xiàn)兩個(gè)數(shù)的交換。
#include <stdio.h>
int main()
{int a = 10;int b = 20;a = a ^ b;b = a ^ b;a = a ^ b;printf("a = %d b = %d\n", a, b);return 0;
}
a^a = 0
0^a = a
10^ 10 ^20= 20
10^ 20 ^10= 20
異或是支持交換律的
4.賦值操作符
他可以讓你改變一個(gè)你之前不滿意的值。也就是你可以給自己重新賦值
例如:
int weight = 120;//體重
weight = 89;//不滿意就賦值
double salary = 10000.0;
salary = 20000.0;//使用賦值操作符賦值。
賦值操作符可以連續(xù)使用,比如:
int a = 10;
int x = 0;
int y = 20;
a = x = y+1;//連續(xù)賦值
這樣的代碼感覺怎么樣?
那同樣的語(yǔ)義,你看看:
x = y+1;
a = x;
這樣的寫法是不是更加清晰爽朗而且易于調(diào)試。
復(fù)合賦值符
+=-=*=/=%=>>=<<=&=|=^=
這些運(yùn)算符都可以寫成復(fù)合的效果。
比如:
int x = 10;
x = x+10;
x += 10;//復(fù)合賦值
//其他運(yùn)算符一樣的道理。這樣寫更加簡(jiǎn)潔。
5.單目操作符
5.1單目操作符介紹
! 邏輯反操作- 負(fù)值+ 正值& 取地址sizeof 操作數(shù)的類型長(zhǎng)度(以字節(jié)為單位)~ 對(duì)一個(gè)數(shù)的二進(jìn)制按位取反-- 前置、后置--++ 前置、后置++* 間接訪問操作符(解引用操作符)(類型) 強(qiáng)制類型轉(zhuǎn)換
代碼演示:
#include <stdio.h>
int main()
{int a = 10;int* p = &a;//取地址操作符*p = 20;//解引用操作符(間接訪問操作符)return 0;
}
解引用和取地址一般是連在一起使用的
#include <stdio.h>
int main()
{int a = 10;printf("%d\n", sizeof a);printf("%d\n", sizeof(a));printf("%d\n", sizeof(int));//printf("%d\n", sizeof int);不能省略括號(hào)return 0;}
運(yùn)行結(jié)果
數(shù)組的使用方法也一樣
如圖所示:
#include <stdio.h>
int main()
{int arr[10] = { 0 };printf("%d\n", sizeof(arr));printf("%d\n", sizeof(arr[0]));int sz = sizeof(arr) / sizeof(arr[0]);//計(jì)算數(shù)組大小printf("%d\n", sz);return 0;
}
運(yùn)行結(jié)果
int a = 10;int b = a++;//后置++,先使用,再++//int b = a,a=a+1;
運(yùn)行結(jié)果為11,10
int a = 10;int b = ++a;//前置++,先++,再使用//a=a+1,int b = a;
運(yùn)行結(jié)果為11,11
- - 操作符和++的使用方法一樣
6.關(guān)系操作符
關(guān)系操作符種類:
>>=<<=!= 用于測(cè)試“不相等”== 用于測(cè)試“相等”
注意:
在編程的過程中== 和=不小心寫錯(cuò),導(dǎo)致的錯(cuò)誤。
7.邏輯操作符
邏輯操作符有哪些:
&& 邏輯與(并且)|| 邏輯或(或者)
區(qū)分邏輯與邏輯或與按位與按位或:邏輯與邏輯或只關(guān)注真假(結(jié)果為1/0),按位與按位或是二進(jìn)制的計(jì)算。
看代碼:
邏輯與
#include <stdio.h>
int main()
{int a = 3 && 0;//1 0printf("%d\n", a);return 0;
運(yùn)行結(jié)果:
邏輯或
#include <stdio.h>
int main()
{int a = 3 ||0;//1 0printf("%d\n", a);return 0;
}
運(yùn)行結(jié)果為1
練習(xí):
#include <stdio.h>
int main()
{int i = 0, a = 0, b = 2, c = 3, d = 4;i = a++ && ++b && d++;//i = a++||++b||d++;printf(" a = %d\n b = %d\n c = %d\n d = %d\n", a, b, c, d);return 0;
}
邏輯與結(jié)果
邏輯或結(jié)果
總結(jié):
邏輯與第一個(gè)表達(dá)式為假不進(jìn)行后邊的計(jì)算
邏輯或第一個(gè)表達(dá)式為真不進(jìn)行后邊的計(jì)算
8.條件操作符
exp1 ? exp2 : exp3
如果表達(dá)式1為真,表達(dá)式2的結(jié)果是整個(gè)表達(dá)式的結(jié)果
如果表達(dá)式1為假,表達(dá)式3的結(jié)果為整個(gè)表達(dá)式的結(jié)果
看代碼:
#include <stdio.h>int main()
{int a = 0;int b = 0;scanf("%d", &a);b = ((a > 5) ? 3 : -3);printf("%d\n", b);return 0;
}
9.逗號(hào)表達(dá)式
exp1, exp2, exp3, …expN
逗號(hào)表達(dá)式,就是用逗號(hào)隔開的多個(gè)表達(dá)式。
逗號(hào)表達(dá)式,從左向右依次執(zhí)行。整個(gè)表達(dá)式的結(jié)果是最后一個(gè)表達(dá)式的結(jié)果。
代碼演示:
#include <stdio.h>
int main()
{int a = 1;int b = 2;int c = (a > b, a = b + 10, a, b = a + 1);//逗號(hào)表達(dá)式printf("%d\n", c);return 0;
}
運(yùn)行結(jié)果為13
10.下標(biāo)引用、函數(shù)調(diào)用和結(jié)構(gòu)成員
- [ ] 下標(biāo)引用操作符
操作數(shù):一個(gè)數(shù)組名 + 一個(gè)索引值
int arr[10];//創(chuàng)建數(shù)組
arr[9] = 10;//實(shí)用下標(biāo)引用操作符。
[ ]的兩個(gè)操作數(shù)是arr和9。
- ( ) 函數(shù)調(diào)用操作符
接受一個(gè)或者多個(gè)操作數(shù):第一個(gè)操作數(shù)是函數(shù)名,剩余的操作數(shù)就是傳遞給函數(shù)的參數(shù)。
#include <stdio.h>
void test1()
{printf("hehe\n");
}
void test2(const char* str)
{printf("%s\n", str);
}
int main()
{test1(); //實(shí)用()作為函數(shù)調(diào)用操作符。test2("hello bit.");//實(shí)用()作為函數(shù)調(diào)用操作符。return 0;
}
- 訪問一個(gè)結(jié)構(gòu)的成員
. 結(jié)構(gòu)體.成員名
-> 結(jié)構(gòu)體指針->成員名
代碼演示:
#include <stdio.h>
struct S
{int num;char c;
};void test(struct S* ps)
{//-> 結(jié)構(gòu)成員訪問操作符//結(jié)構(gòu)體指針->結(jié)構(gòu)體成員printf("%d\n", ps->num);printf("%c\n", ps->c);
}
int main()
{struct S s = {100, 'b'};//結(jié)構(gòu)體的初始化使用{}//打印結(jié)構(gòu)中的成員數(shù)據(jù)//printf("%d\n", s.num);//printf("%c\n", s.c);//. 操作符 結(jié)構(gòu)體變量.結(jié)構(gòu)體成員名test(&s);return 0;
}
11表達(dá)式求值
表達(dá)式求值的順序一部分是由操作符的優(yōu)先級(jí)和結(jié)合性決定。
同樣,有些表達(dá)式的操作數(shù)在求值的過程中可能需要轉(zhuǎn)換為其他類型。
11.1 隱式類型轉(zhuǎn)換
C的整型算術(shù)運(yùn)算總是至少以缺省整型類型(int)的精度來進(jìn)行的。
為了獲得這個(gè)精度,表達(dá)式中的字符和短整型操作數(shù)在使用之前被轉(zhuǎn)換為普通整型,這種轉(zhuǎn)換稱為整型
提升.
整型提升的意義:
表達(dá)式的整型運(yùn)算要在CPU的相應(yīng)運(yùn)算器件內(nèi)執(zhí)行,CPU內(nèi)整型運(yùn)算器(ALU)的操作數(shù)的字節(jié)長(zhǎng)度一般就是int的字節(jié)長(zhǎng)度,同時(shí)也是CPU的通用寄存器的長(zhǎng)度。
因此,即使兩個(gè)char類型的相加,在CPU執(zhí)行時(shí)實(shí)際上也要先轉(zhuǎn)換為CPU內(nèi)整型操作數(shù)的標(biāo)準(zhǔn)長(zhǎng)度。
通用CPU(general-purpose CPU)是難以直接實(shí)現(xiàn)兩個(gè)8比特字節(jié)直接相加運(yùn)算(雖然機(jī)器指令中可能有這種字節(jié)相加指令)。所以,表達(dá)式中各種長(zhǎng)度可能小于int長(zhǎng)度的整型值,都必須先轉(zhuǎn)換為int或unsigned int,然后才能送入CPU去執(zhí)行運(yùn)算
//實(shí)例1
char a,b,c;
...
a = b + c;
b和c的值被提升為普通整型,然后再執(zhí)行加法運(yùn)算。
加法運(yùn)算完成之后,結(jié)果將被截?cái)?#xff0c;然后再存儲(chǔ)于a中。
如何進(jìn)行整體提升呢?
整形提升是按照變量的數(shù)據(jù)類型的符號(hào)位來提升的
//負(fù)數(shù)的整形提升
char c1 = -1;
變量c1的二進(jìn)制位(補(bǔ)碼)中只有8個(gè)比特位:
1111111
因?yàn)?char 為有符號(hào)的 char
所以整形提升的時(shí)候,高位補(bǔ)充符號(hào)位,即為1
提升之后的結(jié)果是:
11111111111111111111111111111111
//正數(shù)的整形提升
char c2 = 1;
變量c2的二進(jìn)制位(補(bǔ)碼)中只有8個(gè)比特位:
00000001
因?yàn)?char 為有符號(hào)的 char
所以整形提升的時(shí)候,高位補(bǔ)充符號(hào)位,即為0
提升之后的結(jié)果是:
00000000000000000000000000000001
//無(wú)符號(hào)整形提升,高位補(bǔ)0
代碼演示:
#include <stdio.h>
int main()
{char a = 3;//00000000000000000000000000000011//00000011-截?cái)?/span>char b = 127;//00000000000000000000000001111111//01111111-截?cái)?/span>char c = a + b;//00000000000000000000000000000011//00000000000000000000000001111111//00000000000000000000000010000010//10000010 - c//整型提升printf("%d\n", c);//11111111111111111111111110000010//11111111111111111111111110000001//10000000000000000000000001111110//-126return 0;
}
整形提升的例子:
//實(shí)例1
#include <stdio.h>
int main()
{//char -128~127char a = 0xb6;short b = 0xb600;int c = 0xb6000000;if (a == 0xb6)printf("a");if (b == 0xb600)printf("b");if (c == 0xb6000000)printf("c");return 0;
}
實(shí)例1中的a,b要進(jìn)行整形提升,但是c不需要整形提升
a,b整形提升之后,變成了負(fù)數(shù),所以表達(dá)式 a0xb6 , b0xb600 的結(jié)果是假,但是c不發(fā)生整形提升,則表達(dá)式 c==0xb6000000 的結(jié)果是真。
所程序輸出的結(jié)果是:
c
//實(shí)例2
#include <stdio.h>
int main()
{char c = 1;printf("%u\n", sizeof(c));printf("%u\n", sizeof(+c));printf("%u\n", sizeof(-c));return 0;
}
實(shí)例2中的,c只要參與表達(dá)式運(yùn)算,就會(huì)發(fā)生整形提升,表達(dá)式 +c ,就會(huì)發(fā)生提升,所以 sizeof(+c) 是4個(gè)字節(jié).表達(dá)式 -c 也會(huì)發(fā)生整形提升,所以 sizeof(-c) 是4個(gè)字節(jié),但是 sizeof? ,就是1個(gè)字節(jié).
11.2算術(shù)轉(zhuǎn)換
如果某個(gè)操作符的各個(gè)操作數(shù)屬于不同的類型,那么除非其中一個(gè)操作數(shù)的轉(zhuǎn)換為另一個(gè)操作數(shù)的類型,否則操作就無(wú)法進(jìn)行。下面的層次體系稱為 尋常算術(shù)轉(zhuǎn)換。
long double
double
float
unsigned long int
long int
unsigned int
int
如果某個(gè)操作數(shù)的類型在上面這個(gè)列表中排名較低,那么首先要轉(zhuǎn)換為另外一個(gè)操作數(shù)的類型后執(zhí)行運(yùn)算。
但是算術(shù)轉(zhuǎn)換要合理,要不然會(huì)有一些潛在的問題。
float f = 3.14;
int num = f;//隱式轉(zhuǎn)換,會(huì)有精度丟失
11.3操作符的屬性
復(fù)雜表達(dá)式的求值有三個(gè)影響的因素。
- 操作符的優(yōu)先級(jí)
- 操作符的結(jié)合性
- 是否控制求值順序。
兩個(gè)相鄰的操作符先執(zhí)行哪個(gè)?取決于他們的優(yōu)先級(jí)。如果兩者的優(yōu)先級(jí)相同,取決于他們的結(jié)合性.
部分操作符優(yōu)先級(jí):
一些問題表達(dá)式
//表達(dá)式的求值部分由操作符的優(yōu)先級(jí)決定。
//表達(dá)式1
a*b + c*d + e*f
注釋:代碼1在計(jì)算的時(shí)候,由于*比+的優(yōu)先級(jí)高,只能保證,的計(jì)算是比+早,但是優(yōu)先級(jí)并不能決定第三個(gè)比第一個(gè)+早執(zhí)行。
所以表達(dá)式的計(jì)算機(jī)順序就可能是:
a*b
c*d
a*b + c*d
e*f
a*b + c*d + e*f或者:
a*b
c*d
e*f
a*b + c*d
a*b + c*d + e*f
//表達(dá)式2
c + --c;
注釋:同上,操作符的優(yōu)先級(jí)只能決定自減–的運(yùn)算在+的運(yùn)算的前面,但是我們并沒有辦法得
知,+操作符的左操作數(shù)的獲取在右操作數(shù)之前還是之后求值,所以結(jié)果是不可預(yù)測(cè)的,是有歧義
的。
//代碼3
#include <stdio.h>
int fun()
{static int count = 1;return ++count;
}
int main()
{int answer;answer = fun() - fun() * fun();printf("%d\n", answer);//輸出多少?return 0;
}
這個(gè)代碼雖然在大多數(shù)的編譯器上求得結(jié)果都是相同的。
但是上述代碼 answer = fun() - fun() * fun(); 中我們只能通過操作符的優(yōu)先級(jí)得知:先算乘法,再算減法。
函數(shù)的調(diào)用先后順序無(wú)法通過操作符的優(yōu)先級(jí)確定。
//代碼4#include <stdio.h>
int main()
{int i = 1;int ret = (++i) + (++i) + (++i);printf("%d\n", ret);printf("%d\n", i);return 0;
}
//嘗試在linux 環(huán)境gcc編譯器,VS2013環(huán)境下都執(zhí)行,看結(jié)果
vs2013運(yùn)行結(jié)果
看看同樣的代碼產(chǎn)生了不同的結(jié)果,這是為什么?
簡(jiǎn)單看一下匯編代碼.就可以分析清楚.
這段代碼中的第一個(gè) + 在執(zhí)行的時(shí)候,第三個(gè)++是否執(zhí)行,這個(gè)是不確定的,因?yàn)橐揽坎僮鞣膬?yōu)先級(jí)和結(jié)合性是無(wú)法決定第一個(gè) + 和第三個(gè)前置 ++ 的先后順序。
總結(jié): 我們寫出的表達(dá)式如果不能通過操作符的屬性確定唯一的計(jì)算路徑,那這個(gè)表達(dá)式就是存在問題的。