C語言操作符超詳細(xì)講解下篇
前言
本文接著學(xué)習(xí)操作符的內(nèi)容。
賦值操作符
賦值操作符就是能夠重新賦值
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ù)賦值,不規(guī)范 不友好 // x = y+1; a = x; //這樣的寫法更加清晰而且易于調(diào)試
//復(fù)合賦值符 += -= *= /= %= >>= <<= &= |= ^= int x = 10; x = x+10; x += 10;//復(fù)合賦值 //其他運(yùn)算符一樣的道理。這樣寫更加簡潔
單目操作符
單目操作符介紹
! 邏輯反操作
- 負(fù)值
+ 正值
& 取地址
sizeof 操作數(shù)的類型長度(以字節(jié)為單位)
~ 對(duì)一個(gè)數(shù)的二進(jìn)制按位取反
-- 前置、后置--
++ 前置、后置++
* 間接訪問操作符(解引用操作符)
(類型) 強(qiáng)制類型轉(zhuǎn)換
int main()
{
int a = -10;
int *p = NULL;
printf("%d\n", !2);
printf("%d\n", !0);
a = -a;
p = &a;
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof a);//可以,不建議
printf("%d\n", sizeof int);//寫法不行
return 0;
}
sizeof 和 數(shù)組
void test1(int arr[])//直接受到首元素的地址。接受的數(shù)組只要1個(gè)首元素
{
printf("%d\n", sizeof(arr));//對(duì)地址求長度,4個(gè)字節(jié)
}
void test2(char ch[])
{
printf("%d\n", sizeof(ch));對(duì)地址求長度,4個(gè)字節(jié)
}
int main()
{
int arr[10] = {0};
char ch[10] = {0};
printf("%d\n", sizeof(arr));//40
printf("%d\n", sizeof(ch));//10
test1(arr);//數(shù)組傳參,只傳首元素的地址
test2(ch);
return 0;
}
結(jié)果見下圖,函數(shù)中,數(shù)組傳參,只傳首元素的地址。函數(shù)接收到首元素的地址,此時(shí)數(shù)組只有1個(gè)首元素。
對(duì)地址求長度的結(jié)果為4。這是固定的,和數(shù)組類型無關(guān)。

int main()
{
int a = 0;
printf("%d\n", ~a);
//00000000 00000000 00000000 00000000 補(bǔ)碼
//11111111 11111111 11111111 11111111 取反后的補(bǔ)碼
//11111111 11111111 11111111 11111110 反碼
//10000000 00000000 00000000 00000001 -> 原碼 -1
int b = 11;
//00000000000000000000000000001111
//11111111111111111111111111111011
//00000000000000000000000000000100
//1<<2
b |= (1<<2);
printf("%d\n", b);//15
int c = 11;
c &= (~(1 << 2));
printf("%d\n", c);//11
return 0;
}
int main()
{
int a = 3;
int b = ++a;//前置++,先++,后使用//a=a+1,b=a
int b = a++;//后置++,先使用,后++。//b=a,a=a+1
int b = --a;//前置--,先--,后使用 //a=a-1,b=a
int b = a--;//后置--,先使用,再-- //b=a,a=a-1
printf("%d\n", b);
return 0;
}
關(guān)系操作符
>
>=
<
<=
!= 用于測(cè)試“不相等”
== 用于測(cè)試“相等”
int main()
{//判斷字符串相同,不能這樣判斷
if ("abcdef" == "abbq")//字符相同不能這樣判斷
{
}
return 0;
}
邏輯操作符
&& 邏輯與
|| 邏輯或要區(qū)分邏輯與和按位與
1&2----->0
1&&2---->1要區(qū)分邏輯或和按位或
1|2----->3
1||2---->1
int main()
{
int a = 0;
int b = 0;
/*int y = 0;
if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0))
{
}*/
if (a || b)
{
printf("hehe\n");
}
return 0;
}
int main()
{
int i = 0,a=0,b=2,c =3,d=4;
//從左邊開始算,如果為0,后面不會(huì)算的,直接結(jié)束
//如果為1,接著算后面的
i = a++ && ++b && d++;
//i = a++||++b||d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0;
}

int main()
{
int i = 0,a=0,b=2,c =3,d=4;
//從左邊開始算,如果為1,后面不會(huì)算的,直接結(jié)束
//如果為0,接著算后面的
i = a++||++b||d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
return 0;
}

條件操作符
exp1 ? exp2 : exp3
int main()
{
int a = 3;
int b = 0;
int m = (a > b ? a : b);//效果等同于下面的選擇語句
//if (a > 5)
// b = 3;
//else
// b = -3;
//b = (a > 5 ? 3 : -3);
return 0;
}
逗號(hào)表達(dá)式
exp1, exp2, exp3, …expN
/代碼1
int a = 1;
int b = 2;
//從左向右此次執(zhí)行,
int c = (a>b, a=b+10, a, b=a+1);//逗號(hào)表達(dá)式
printf("a=%d b=%d\n", a, b);
printf("c=%d\n", c);
//代碼2
if (a =b + 1, c=a / 2, d > 0)
//代碼3
a = get_val();
count_val(a);
while (a > 0)
{
//業(yè)務(wù)處理
a = get_val();
count_val(a);
}
//如果使用逗號(hào)表達(dá)式,改寫:
while (a = get_val(), count_val(a), a>0)
{
//業(yè)務(wù)處理
}

下標(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ù)
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)體指針->成員名
struct Stu
{
char name[20];
int age;
float score;
};
void print1(struct Stu ss)
{//結(jié)構(gòu)體變量.成員名
printf("%s %d %f\n", ss.name, ss.age, ss.score);
}
void print2(struct Stu* ps)
{
//printf("%s %d %f\n", (*ps).name, (*ps).age, (*ps).score);
//結(jié)構(gòu)體指針->成員名,與上面的方式效果一樣
printf("%s %d %f\n", ps->name, ps->age, ps->score);
}
int main()
{
struct Stu s = {"張三", 20, 90.5f};
strcpy(s.name, "張三豐");
//scanf("%s", s.name);
// 這是錯(cuò)誤的方式 s.name是地址 字符串也是地址
//*(s.name) = "張三豐" //兩個(gè)不同的地址賦值有問題的
print1(s);
printf("\n");
print2(&s);
return 0;
}
表達(dá)式求值
表達(dá)式求值的順序一部分是由操作符的優(yōu)先級(jí)和結(jié)合性決定。
同樣,有些表達(dá)式的操作數(shù)在求值的過程中可能需要轉(zhuǎn)換為其他類型
隱式類型轉(zhuǎn)換-整形提升
C的整型算術(shù)運(yùn)算總是至少以缺省整型類型的精度來進(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é)長度一般就是int的字節(jié)長度,同時(shí)也是CPU的通用寄存器的長度
- 因此,即使兩個(gè)char類型的相加,在CPU執(zhí)行時(shí)實(shí)際上也要先轉(zhuǎn)換為CPU內(nèi)整型操作數(shù)的標(biāo)準(zhǔn)長度
- 通用CPU(general-purpose CPU)是難以直接實(shí)現(xiàn)兩個(gè)8比特字節(jié)直接相加運(yùn)算(雖然機(jī)器指令中可能有這種字節(jié)相加指令)。所以,表達(dá)式中各種長度可能小于int長度的整型值,都必須先轉(zhuǎn)換為int或unsigned int,然后才能送入CPU去執(zhí)行運(yùn)算
//負(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
//無符號(hào)整形提升,高位補(bǔ)0
//整形提升是按照變量的數(shù)據(jù)類型的符號(hào)位來提升的
int main()
{//字符只占一個(gè)字節(jié),先提升到4個(gè)字節(jié)
char c1 = 3;
//00000000000000000000000000000011 補(bǔ)碼
//00000011 - c1
char c2 = 127;
//00000000000000000000000001111111
//定義c3是一個(gè)字節(jié),先提升到4個(gè)字節(jié)
//01111111 - c2
char c3 = c1 + c2;
//00000000000000000000000000000011 補(bǔ)碼
//00000000000000000000000001111111 補(bǔ)碼
//00000000000000000000000010000010 補(bǔ)碼相加
//10000010 - c3 字符型結(jié)果只能裝下8位
//再提升到4個(gè)字節(jié)
//11111111111111111111111110000010 補(bǔ)碼
//11111111111111111111111110000001 反碼
//10000000000000000000000001111110 原碼
//-126 打印原碼
printf("%d\n", c3);//
return 0;
}
int main()
{
char a = 0xb6;//10110110 整型提升后都是前面都是1
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á)式 a == 0xb6,b == 0xb600 的結(jié)果是假,但是c不發(fā)生整形提升,則表達(dá)式 c == 0xb6000000 的結(jié)果是真。
所程序輸出的結(jié)果是:
c
//實(shí)例2
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é)。
算術(shù)轉(zhuǎn)換
如果某個(gè)操作符的各個(gè)操作數(shù)屬于不同的類型,那么除非其中一個(gè)操作數(shù)的轉(zhuǎn)換為另一個(gè)操作數(shù)的類型,否則操作就無法進(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ì)有精度丟失
操作符的屬性
復(fù)雜表達(dá)式的求值有三個(gè)影響的因素。
- 操作符的優(yōu)先級(jí)
- 操作符的結(jié)合性
- 是否控制求值順序
兩個(gè)相鄰的操作符先執(zhí)行哪個(gè)?取決于他們的優(yōu)先級(jí)。如果兩者的優(yōu)先級(jí)相同,取決于他們的結(jié)合性。
一些問題表達(dá)式:
//表達(dá)式的求值部分由操作符的優(yōu)先級(jí)決定。
//表達(dá)式1
a*b + c*d + e*f//不明確的
//表達(dá)式2
c + --c;//不明確的
//代碼3-非法表達(dá)式
int main()
{
int i = 10;
i = i-- - --i * ( i = -3 ) * i++ + ++i;//不明確的
printf("i = %d\n", i);
return 0;
}
//代碼4
int fun()
{
static int count = 1;
return ++count;
}
int main()
{
int answer;
answer = fun() - fun() * fun();//不明確的
printf( "%d\n", answer);//輸出多少?
return 0;
}
//代碼5
int main()
{
int i = 1;
int ret = (++i) + (++i) + (++i);//不明確的
printf("%d\n", ret);
printf("%d\n", i);
return 0;
}
寫出的表達(dá)式如果不能通過操作符的屬性確定唯一的計(jì)算路徑,那這個(gè)表達(dá)式就是存在問題的。有時(shí)候同樣的表達(dá)式在不編譯器的結(jié)果是不同的。
總結(jié)
邏輯操作符種類較多,以上都是較常用的。這部分內(nèi)容學(xué)習(xí)基本結(jié)束了。
下一篇開始學(xué)習(xí)指針相關(guān)內(nèi)容了。(直達(dá)鏈接)
到此這篇關(guān)于C語言操作符超詳細(xì)講解下篇的文章就介紹到這了,更多相關(guān)C語言 操作符內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Visual Studio2022+QT6創(chuàng)建桌面應(yīng)用實(shí)現(xiàn)
本文主要介紹了Visual Studio2022+QT6創(chuàng)建桌面應(yīng)用實(shí)現(xiàn),文中通過圖文介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-02-02
Opencv 視頻轉(zhuǎn)為圖像序列的實(shí)現(xiàn)
今天小編就為大家分享一篇Opencv 視頻轉(zhuǎn)為圖像序列的實(shí)現(xiàn),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2019-12-12
ubuntu系統(tǒng)vscodeC++編譯環(huán)境配置與使用方式
這篇文章主要介紹了ubuntu系統(tǒng)vscodeC++編譯環(huán)境配置與使用方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-12-12
C++優(yōu)先級(jí)隊(duì)列的使用指南與模擬實(shí)現(xiàn)
優(yōu)先級(jí)隊(duì)列是一種特殊的隊(duì)列,其中每個(gè)元素都有一個(gè)與之關(guān)聯(lián)的優(yōu)先級(jí),優(yōu)先級(jí)較高的元素會(huì)在隊(duì)列中較早地被處理,而優(yōu)先級(jí)較低的元素會(huì)在后續(xù)處理,本文給大家介紹C++優(yōu)先級(jí)隊(duì)列的使用指南與模擬實(shí)現(xiàn),需要的朋友可以參考下2023-09-09
C++實(shí)現(xiàn)LeetCode(14.最長共同前綴)
這篇文章主要介紹了C++實(shí)現(xiàn)LeetCode(14.最長共同前綴),本篇文章通過簡要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07

