第五章-C語言操作符
一、 操作符分類
(1)算術操作符
+(加)-(減) *(乘) /(除) %(取模)
#include<stdio.h> int main() { // / 操作符 int a = 5 / 2;//如果兩個運算元都為整數,執行整數除法 //商2餘1 double a2 = 5 / 2.0;//有浮點數執行的就是浮點數除法 // %操作符 int b = 5 % 2;//兩個運算元必須為整數 printf("a=%d\n", a);//a=2 printf("a2=%lf\n", a2);//a=2.500000 printf("b=%d\n", b);//b=1return 0; }
(2) 移位操作符
<< 左移操作符
#include<stdio.h> int main() { int a = 16;//0000000010000,即2^4 //>>--右移操作符 //移動的是二進位制 //算術右移--右丟棄,左補原位符號(通常使用) //邏輯右移--右丟棄,左補0 int b=a >> 1;//8--2^3 printf("%d\n", b); return 0; }
#include<stdio.h> int main() { int a = -1; //整數二進位制表述:原碼、反碼、補碼 //儲存在記憶體的是補碼 //原碼100000000000000000000000001 //反碼111111111111111111111111110 //補碼111111111111111111111111111 int b = a >> 1; printf("%d\n", b);//-1 return 0; }
>> 右移操作符
#include<stdio.h> int main() { int a =5;//101 int b = a << 1;//1010=2^3+2=10 //<<--左邊丟棄,右邊補零printf("%d\n", b);//10 return 0; }
注:
移位操作符的運算元只能是整數。
對於移位運算子,不要移動負數位,這個是標準未定義的
(3)位操作符
& 按位與
#include<stdio.h> int main() { //& 按二進位制位與 //兩個為1,則為1 //一個為0,則為0 int a = 3;//011 int b = 5;//101 int c = a & b;//001 printf("%d\n", c); return 0; }
| 按位或
#include<stdio.h> int main() { //| 按二進位制位或 //兩個為0,則為0 //一個為1,則為1 int a = 3;//011 int b = 5;//101 int c = a |b;//111 printf("%d\n", c);//7 return 0; }
^ 按位異或
#include<stdio.h> int main() { //^ 按位異或 //相異為1 //相同為0 int a = 3;//011 int b = 5;//101 int c = a^b;//110 printf("%d\n", c);//6 return 0; }
注:位操作符的運算元只能是整數。
//不能建立臨時變數(第三個變數) //實現兩個數的交換。 #include <stdio.h> int main() { int a =3;//011 int b =5;//101 //建立臨時變數 //int tmp = 0; //printf("before:a=%d\n b=%d\n", a, b); //tmp = a; //a = b; //a = tmp; //printf("after:a=%d\n b=%d\n", a, b); //加減法 //缺陷:過於大,超出表達範圍,會丟失,可能會溢位 //a = a + b; //b = a - b; //a = a - b; //printf("a = %d b = %d\n", a, b); //異或 //不會溢位 a = a ^ b;//110 b = a ^ b;//011 a = a ^ b;//101 printf("a = %d b = %d\n", a, b); return 0; }
//編寫程式碼實現 //求一個整數儲存在記憶體中的二進位制中1的個數 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int main() { int num = 0; scanf("%d", &num); //統計num的補碼中有幾個1 int count = 0; while (num) { if (num % 2 == 1)//餘數 count++; num = num / 2;//去個位數 } printf("二進位制中1的個數 = %d\n", count); return 0; }
//優化,可以計算負數 #define _CRT_SECURE_NO_WARNINGS #include <stdio.h> int main() { int num = 0; scanf("%d", &num); int i = 0; int count = 0;//計數 for (i = 0; i < 32; i++) { //if (num & (1 <<i)) if (1==((num >> i)&1)) //32bit //num & 1==1即第一位為1 count++; } printf("二進位制中1的個數 = %d\n", count); return 0; }
(4) 賦值操作符
初始化:變數建立給一個值
賦值:變數有一個值,再給一個值
複合賦值符
+=( a = a + 2 / a + = 2 )
-= *= /= %=
>>= ( a >> = 1 / a = a >> 1)
<<= &= |= ^=
(5)單目操作符
只有一個運算元
! 邏輯反操作(假變成真,真變成假)
#include <stdio.h> int main() { int a = 0; if (a) { printf("xi\n"); } if (!a) { printf("hehe\n"); } return 0; }
- 負值( a = - 5 / a = - a )
+ 正值
& 取地址(與指標搭配)
* 間接訪問操作符(解引用操作符)
#include <stdio.h> int main() { int a = 10; int* p = &a; *p = 20; return 0; }
sizeof 運算元的型別長度(以位元組為單位)
為型別,不能去掉括號
#include <stdio.h> int main() { int a = 10; char c = 'r'; char* p = &c; int arr[10] = { 0 }; //記憶體所佔空間的大小,單位是位元組 printf("%d\n", sizeof(a));//4(a為整型,佔4位元組 printf("%d\n", sizeof(int));//4 printf("%d\n", sizeof(c));//1(c為一個字元) printf("%d\n", sizeof(char)); printf("%d\n", sizeof(p));//4(p為指標,4或8) printf("%d\n", sizeof(char*)); printf("%d\n", sizeof(arr));//40(陣列為10個元素,每個元素四個位元組) printf("%d\n", sizeof(int [10])); return 0; }
#include<stdio.h> int main() { short s = 0; int a = 10; printf("%d\n", sizeof(s = a + 5));//2 s是短整型 printf("%d\n", s);//0 上面表示式不直接參與運算 return 0; }
~ 對一個數的二進位制按位取反
#include<stdio.h> int main() { int a = 11;//1011 a = a | (1 << 2); printf("%d\n", a);//15 a = a & (~(1 << 2)); printf("%d\n", a);//11 //int a = 0; //~按(二進位制)位取反 //00000000000000000000000000000000 //11111111111111111111111111111111--補碼 //11111111111111111111111111111110--反碼 //10000000000000000000000000000001--原碼 //printf("%d\n", ~a); return 0; }
-- 前置--(先--,後使用)、後置--(先使用,後--)
++ 前置++(先++,後使用)、後置++(先使用,後++)
(型別) 強制型別轉換 // int a = ( int ) 3.14 ;
(6)sizeof 和 陣列
#include <stdio.h> void test1(int arr[])//指標接收 { printf("%d\n", sizeof(arr));//4 } void test2(char ch[]) { printf("%d\n", sizeof(ch));//4 } int main() { int arr[10] = { 0 }; char ch[10] = { 0 }; printf("%d\n", sizeof(arr));//40--1個整型4個位元組,10*4=40 printf("%d\n", sizeof(ch));//10 test1(arr);//傳遞是首元素地址 test2(ch); return 0; }
(7)關係操作符
>大於
>=大於等於
<小於
<=小於等於
!= 用於測試“不相等”
== 用於測試“相等”
(8)邏輯操作符
&& 邏輯與(都為真,則為1;一個假,則為0)
|| 邏輯或(都為假,則為0;一個真,則為1)
#include <stdio.h> int main() { int a = 0; int b = 5; int c = a && b; int d = a || b; printf("%d\n", c);//0 printf("%d\n", d);//1 return 0; }
#include <stdio.h> int main() { //int i = 0, a = 0, b = 2, c = 3, d = 4; //i = a++ && ++b && d++;//1 2 3 4 //a先使用,後++ //由於左邊為假,右邊不再計算 int i = 0, a = 1, b = 2, c = 3, d = 4; i = a++||++b||d++;//2 2 3 4 //a=1,即為真,後面不用計算 printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d); return 0; }
(9) 條件操作符
exp1 ? exp2 : exp3
表示式1為真,即表示式2為結果
表示式1為假,即表示式3為結果
#include <stdio.h> int main() { int a = 0; int b = 0; /*if (a > 5) b = 3; else b = -3; b = (a > 5 ? 3 : -3);*/ int max = 0; if (a > b) max = a; else max = b; max = (a > b ? a : b); return 0; }
(10)逗號表示式
逗號表示式,就是用逗號隔開的多個表示式。
逗號表示式,從左向右依次執行。整個表示式的結果是最後一個表示式的結果。
int a = 1; int b = 2; int c = (a > b, a = b + 10, a, b = a + 1); //無結果,12=2+10;12;13=12+1 printf("%d\n", c);//13 if (a = b + 1, c = a / 2, d > 0) //判斷d是否大於0,大於0,前面為真,否則為假 a = get_val();//函式放於a中 count_val(a); while (a > 0) { //業務處理 a = get_val(); count_val(a); } //優化 while (a = get_val(), count_val(a), a > 0) { //業務處理 }
(11) 下標引用、函式呼叫和結構成員
1. [ ] 下標引用操作符
運算元:一個數組名 + 一個索引值
#include<stdio.h> int main() { int arr[10] = { 0 };//建立陣列 arr[4] = 10;//實用下標引用操作符 return 0; }
2.( ) 函式呼叫操作符
#include <stdio.h> int get_max(int x, int y) //這個括號不是函式呼叫操作符 { return x > y ? x : y; } int main() { int a = 10; int b = 20; int max = get_max(a, b); //呼叫函式的時候的()就是函式呼叫操作符 //運算元為a,b,get_max,有三個 printf("max=%d\n", max); return 0; }
3. 訪問一個結構的成員
. 結構體.成員名
-> 結構體指標->成員名
#include <stdio.h> //型別:結構體型別 struct Stu { //成員變數 char name[10]; int age; char id[20]; double score; }; int main() { //使用strut Stu的型別建立一個學生物件s1,並初始化 struct Stu s1 = {"曾",20,"201851520"}; struct Stu* ps = &s1; printf("%s\n",ps->name); printf("%d\n", ps->age); /*printf("%d\n", (*ps).name); printf("%s\n", s1.name); printf("%d\n", s1.age); printf("%s\n", s1.id);*/ return 0; }
二、表示式求值
表示式求值的順序一部分是由操作符的優先順序和結合性決定。
同樣,有些表示式的運算元在求值的過程中可能需要轉換為其他型別
(1)隱式型別轉換
整型提升:C的整型算術運算總是至少以預設整型型別的精度來進行的。為了獲得這個精度,表示式中的字元和短整型運算元在使用之前被轉換為普通整型。
如何進行整體提升呢? 整形提升是按照變數的資料型別的符號位來提升的
#include <stdio.h> int main() { char a = 3; //a中存放00000011 //提升加0 char b = 127; //b中存放01111111 char c = a + b; //c=a+b=10000010 //補碼111111...11110000010 //反碼111111...11110000001(減一) //原碼100000...00001111110(取反) printf("%d\n", c);//-126 //%d,即列印整型 return 0; }
#include<stdio.h> int main() { //定義 char a = 0xb6;//char 1位元組 //b6,b=10,兩個十六進位制的數,二進位制為10110110 short b = 0xb600;//short 2位元組 int c = 0xb6000000;//int 4位元組 if (a == 0xb6)//會補1 printf("a"); if (b == 0xb600) printf("b"); if (c == 0xb6000000) printf("c");//c return 0; }
#include<stdio.h> int main() { char c = 1;//char是1位元組 printf("%u\n", sizeof(c));//1 printf("%u\n", sizeof(+c));//4 printf("%u\n", sizeof(-c));//4 //整型提升,即計算的整型大小 printf("%u\n", sizeof(!c));//1 return 0; }
(2)算術轉換
個操作符的各個運算元屬於不同的型別,將其中一個運算元的轉換為另一個運算元的型別
long double 8位元組
double 8位元組
float 4位元組
unsigned longint 4位元組
longint 4位元組
unsigned int 4位元組
int 4位元組
上面這個列表中排名較低,那麼首先要轉換為另外一個運算元的型別後執行運 算
(3)操作符的屬性
複雜表示式的求值有三個影響的因素
1. 操作符的優先順序
2. 操作符的結合性
3. 是否控制求值順序(是否繼續運算)
#include<stdio.h> int main() { int a = 10; int b = 20; int c = b + a * 3; //操作符的優先順序 //+和=優先順序相同,比*優先順序低 int d = a + b + c; //優先順序一致 //操作符的結合性 return 0; }
問題表示式
a*b + c*d + e*f;
計算順序,不知道最先開始哪個,沒有唯一計算路徑
c + --c ;
--優先順序比+高,+操作符的左運算元c的值,可能是--c後,也可能是--c前獲取,是有歧義的。
ret = (++i) + (++i) + (++i) ;
answer = fun() - fun() * fun() ;