運算子:
- 1、算術運算子
- 除法運算子 —— " / "
- 取余運算子 —— %
- 2、移位運算子
- 整型在記憶體中的儲存:
- 左移運算子和右移運算子:
- 左移運算子
- 右移運算子
- 警告:
- 3、位運算子
- 按位與 —— &
- 按位或 —— |
- 按位異或 —— ^
- 一道變態的面試題:
- 4、賦值運算子
- 復合賦值符
- 5、單目運算子
- 邏輯反操作 —— !
- 正負值號 —— + -
- 取地址和解參考運算子 —— & 和 *
- sizeof 運算子
- 取反運算子 —— ~
- ++ 和 - - 運算子
- (型別) —— 強制型別轉換
- 6、關系運算子
- 7、邏輯運算子
- 一道360的筆試題
- 8、 條件運算子
- 9、逗號運算式
- 10. 下標參考、函式呼叫和結構成員
- 10.1、[ ] 下標參考運算子
- 10.2、( ) 函式呼叫運算子
- 10.3、訪問結構成員的運算子
1、算術運算子
+(加) -(減) *(乘) /(除) %(取余)
除法運算子 —— " / "
#include<stdio.h>
int main()
{
int a = 5 / 2; //對于/運算子,如果兩個運算元都為整數,執行整數除法,
float b = 5 / 2;
float c = 5 / 2.0; //對于/運算子,如果有浮點數,執行浮點數除法,
printf("%d\n", a);
printf("%f\n", b);
printf("%f\n", c);
return 0;
}

取余運算子 —— %
#include<stdio.h>
int main()
{
int a = 5 % 2; //% 運算子的兩個運算元必須為整數,回傳的是整除之后的余數,
//int b = 5.0 % 2.0; 這樣寫是錯誤的,編譯器會報錯的
printf("%d\n", a);
return 0;
}
除了 % 運算子之外,其他的幾個運算子可以作用于整數和浮點數,
2、移位運算子
整型在記憶體中的儲存:
整型有 —— 原碼、反碼、補碼 —— 三種型別 —— 都是用二進制寫出來的
正整數 :—— 原碼、反碼、補碼相同,
負數:—— 原碼、反碼、補碼不相同,
原碼 —— 直接用二進制寫出來的數字,
反碼 —— 符號位不變,其它位按位取反,
補碼 —— 反碼+1,
在記憶體中儲存的是補碼
#include<stdio.h>
int main()
{
int a = 5;
//00000000000000000000000000000101 —— 原碼 —— 反碼 —— 補碼
int b = -1;
//10000000000000000000000000000101 —— 原碼
//11111111111111111111111111111110 —— 反碼 —— 符號位不變,其它位按位取反
//11111111111111111111111111111111 —— 補碼 —— 反碼加一
return 0;
}
左移運算子和右移運算子:
—— 左移運算子和右移運算子都是對二進制進行移位
<<(左移運算子) >>(右移運算子)
左移運算子
—— 移位規則 —— 左邊拋棄、右邊補0
#include<stdio.h>
int main()
{
int a = 5;
int b = a << 1; //10
//00000000000000000000000000000101 —— 原碼 —— 反碼 —— 補碼
//00000000000000000000000000001010 —— 左邊拋棄,右邊補零
int c = -1;
int d = c << 1; //-2
//10000000000000000000000000000101 —— 原碼
//11111111111111111111111111111110 —— 反碼 —— 符號位不變,其它位按位取反
//11111111111111111111111111111111 —— 補碼 —— 反碼加一
//11111111111111111111111111111110 —— 補碼執行左移運算子 —— 補碼
//11111111111111111111111111111101 —— 反碼 —— 補碼-1
//10000000000000000000000000000010 —— 原碼 —— 反碼符號位不變,其它位按位取反
printf("%d\n", b);
printf("%d\n", d);
return 0;
}

右移運算子
—— 移位規則 ——
- 邏輯移位 —— 左邊用0填充,右邊丟棄
- 算術移位 —— 左邊用原該值的符號位填充,右邊丟棄
大多數的的編譯器都是算術右移 —— VS2019也是用算術右移的
#include<stdio.h>
int main()
{
int a = 5;
int b = a >> 1; //2
//00000000000000000000000000000101 —— 原碼 —— 反碼 —— 補碼
//00000000000000000000000000000010 —— 左邊補原來數字的符號0,右邊拋棄
int c = -1;
int d = c >> 1; //-1
//10000000000000000000000000000101 —— 原碼
//11111111111111111111111111111110 —— 反碼 —— 符號位不變,其它位按位取反
//11111111111111111111111111111111 —— 補碼 —— 反碼加一
//
//11111111111111111111111111111111 —— 補碼執行右移運算子 —— 補碼
//11111111111111111111111111111110 —— 反碼 —— 補碼-1
//10000000000000000000000000000001 —— 原碼 —— 反碼符號位不變,其它位按位取反
printf("%d\n", b);
printf("%d\n", d);
return 0;
}

注:
- a >> 1 等價于 a / 2 ———— a << 1 等價于 a * 2
- a >> 2 等價于 a / 2 / 2 ———— a << 2 等價于 a * 2 * 2 —— 依次類推
- 對于左移,右移運算子,運算元的左邊和右邊都必須是整數 —— 不適用浮點型
警告:
對于移位運算子,不要移動負數位,這個是標準未定義的,
例如:
int num = 10;
num >> -1; //error
3、位運算子
&(按位與) |(按位或) ^(按位異或)
注:他們的運算元必須是整數,
按位與 —— &
按位與是按照二進制位與 —— 兩數都為1則為1,兩數不都為1則為0,
#include<stdio.h>
int main()
{
int a = 3;
//00000000000000000000000000000011
int b = -2;
//10000000000000000000000000000010 —— 原碼
//11111111111111111111111111111101 —— 反碼
//11111111111111111111111111111110 —— 補碼
int c = a & b;
//00000000000000000000000000000011 —— 3的補碼
//11111111111111111111111111111110 —— -2的補碼
//00000000000000000000000000000010 —— c的補碼 —— 又因為c為正數,所以補碼就是原碼
printf("%d", c); //列印值為2
//%d —— 表示列印有符號的整數
//%u —— 表示列印無符號的整數
return 0;
}
按位或 —— |
按位或是按照二進制位或 —— 兩數都為0則為0,兩數不都為0則為1,
#include<stdio.h>
int main()
{
int a = 3;
//00000000000000000000000000000011
int b = -2;
//10000000000000000000000000000010 —— 原碼
//11111111111111111111111111111101 —— 反碼
//11111111111111111111111111111110 —— 補碼
int c = a | b;
//00000000000000000000000000000011 —— 3的補碼
//11111111111111111111111111111110 —— -2的補碼
//11111111111111111111111111111111 —— c的補碼 —— c為負數
//11111111111111111111111111111110 —— c的反碼
//10000000000000000000000000000001 —— c的原碼
printf("%d", c); //列印值為-1
return 0;
}
按位異或 —— ^
按異或是按照二進制位異或 —— 兩數相同為0,不相同為1
#include<stdio.h>
int main()
{
int a = 3;
//00000000000000000000000000000011
int b = -2;
//10000000000000000000000000000010 —— 原碼
//11111111111111111111111111111101 —— 反碼
//11111111111111111111111111111110 —— 補碼
int c = a ^ b;
//00000000000000000000000000000011 —— 3的補碼
//11111111111111111111111111111110 —— -2的補碼
//11111111111111111111111111111101 —— c的補碼 —— c為負數
//11111111111111111111111111111100 —— c的反碼
//10000000000000000000000000000011 —— c的原碼
printf("%d", c); //列印值為-3
return 0;
}
一道變態的面試題:
我們經常用的兩個數交換的函式:
#include<stdio.h>
int main()
{
int a = 3;
int b = 5;
int tem = 0;
printf("%d %d\n", a, b);
tem = a;
a = b;
b = tem;
printf("%d %d\n", a, b);
return 0;
}
不能創建臨時變數(第三個變數),實作兩個數的交換,
第一種方法:
#include<stdio.h>
int main()
{
int a = 3;
int b = 5;
printf("%d %d\n", a, b);
a = a + b;
b = a - b;
a = a - b;
printf("%d %d\n", a, b);
return 0;
}
第二種方法:
#include<stdio.h>
int main()
{
int a = 3;
int b = 5;
printf("%d %d\n", a, b);
a = a ^ b;
b = a ^ b;
a = a ^ b;
printf("%d %d\n", a, b);
return 0;
}
決議:
//011 —— a
//101 —— b
//110 —— a = a ^ b
//011 —— b = a ^ b
//101 —— a = a ^ b
其中:
a ^ b 可以看做是一個鑰匙
這個鑰匙和 a 異或就是 b —— a ^ b ^ a = b
這個鑰匙和 b 異或就是 a —— a ^ b ^ b = a
一些其他的 異或 公式:a ^ 0 = a b ^ b = 0,
4、賦值運算子
= (賦值運算子)
賦值運算子是一個很好的運算子,他可以讓你得到一個你之前不滿意的值,
也就是你可以給一個變數重新賦值,
int weight = 120; //體重
weight = 100; //不滿意就賦值
double salary = 10000.0;
salary = 20000.0; //使用賦值運算子賦值,
//賦值運算子可以連續使用,比如:
int a = 10;
int x = 0;
int y = 20;
a = x = y+1; //連續賦值 —— 這樣寫不容易讀懂 —— 可讀性差
//下面是a = x = y + 1 的分部寫法
x = y+1;
a = x;
//這樣的寫法更加清晰爽朗而且易于除錯,
復合賦值符
+=
-=
*=
/=
%=
>>=
<<=
&=
|=
^=
上面的這些復合賦值符是為了寫起來更加的方便和便捷,提升我們寫代碼的效率,
舉例:
#include<stdio.h>
int main()
{
int c1 = 0;
int c2 = 0;
int d1 = 10;
int d2 = 10;
c1 += 10;
d1 <<= 1;
printf("%d\n", c1); //列印值為10
printf("%d\n", d1); //列印值為20
c2 = c2 + 10;
d2 = d2 << 1;
printf("%d\n", c2); //列印值為10
printf("%d\n", d2); //列印值為20
return 0;
}
從上面代碼中可知:
c += 10 和 c = c + 10 是等價的
c >>= 10 和 c = c >> 10 是等價的
5、單目運算子
—— 只有一個運算元的運算子
!(邏輯反操作) -(負值) +(正值)
& (取地址) sizeof(運算元的型別長度) ~(對一個數的二進制按位取反)
--(前置、后置--) ++(前置、后置++) * (間接訪問運算子(解參考運算子))
(型別) —— 強制型別轉換
邏輯反操作 —— !
#include<stdio.h>
int main()
{
int a = 1;
a = !a;
printf("%d\n", a); //列印值為0
if (a) //表示:a為真的時候列印hehe
printf("hehe\n");
if (!a) //表示:a為假的時候列印haha
printf("haha\n");
return 0;
}
//注釋:
//當a為非零數的時候 —— !a的值為0
//當a為零的時候 —— !a的值為1
正負值號 —— + -
#include<studio.h>
int main()
{
int a = 10;
a = -a;
printf("%d\n", a); //列印值為-10
a = -a;
printf("%d\n", a); //列印值為 10
return 0;
}
取地址和解參考運算子 —— & 和 *
取地址和解參考運算子一般都是一起使用的
#include<stdio.h>
int main()
{
int a = 10;
int arr[10] = {0};
int* p = &a; // *表示 p 是指標變數,int 表示p地址所指向的內容是整型
// & —— 表示取出a的地址 p = &a —— 表示把a的地址放在P指標中
*p = 20; //*是解參考運算子, 能從地址找到地址中的內容
//int* p 和 *p = 20 中的*p是不一樣的
//其中的int* p中的 * 表示p是一個指標變數
//*p = 20 中的 * 表示解參考,利用p中存放的地址找到地址中的內容
&arr;//—— 表示取出整個陣列的地址 —— &arr + 1 跳過40個位元組(10個整型)
arr;//—— 表示取出首元素的地址 —— arr + 1 跳過4個位元組(一個整型)
&arr[0];// —— 表示取出首元素的地址 —— &arr[0] + 1 —— 跳過4個位元組(一個整型)
}
補充:
左值和右值 —— 放在等號左邊的值是左值,放在等號右邊的值為右值
左值表示地址所指向的空間,右值表示所指向空間的內容
例如:a = 10 b = a
a = 10 —— a 為地址空間,10為值
b = a —— b 為地址所指向的那塊空間, a為a所指向那塊空間的值10
sizeof 運算子
—— 強調sizeof是運算子,不是函式
—— 計算資料型別或者是變數在記憶體中所占的空間大小,單位是位元組,和變數中所存的值的大小無關
- 陣列名如果單獨放在 sizeof() 中,陣列名表示的是整個陣列 —— 計算整個陣列的大小
- &陣列名 —— 也表示的是整個陣列
- 除了上面兩種情況陣列名表示的是整個陣列,其他的陣列名都表示的是首元素地址,
#include<stdio.h>
int main()
{
char ch[10] = "abcd";
int a = 10;
printf("%d\n", sizeof(ch)); //列印值為10
printf("%d\n", strlen(ch)); //列印值為4 —— strlen是計算字串的大小,遇見\0停止
printf("%d\n", sizeof(int)); //列印值為4
printf("%d\n", sizeof(a)); //列印值為4
printf("%d\n", sizeof a); //列印值為4 —— 從這個就可以說明sizeof是運算子,不是函式
//如果是函式就必須有函式參考運算子 —— ()
printf("%d\n", sizeof int); //這種寫法是錯誤的
return 0;
}
例題:
- 第一題
#include<stdio.h>
int main()
{
int a = 5;
short b = 10;
printf("%d\n", sizeof(b = a + 2));
printf("%d\n", b);
}
答案:2 10
決議:因為 sizeof 內部的運算式不參與運算
- 第二題
#include <stdio.h>
void test1(int arr[])
{
printf("%d\n", sizeof(arr));//
}
void test2(char ch[])
{
printf("%d\n", sizeof(ch));//
}
int main()
{
int arr[10] = {0};
char ch[10] = {0};
printf("%d\n", sizeof(arr));//
printf("%d\n", sizeof(ch));//
test1(arr);
test2(ch);
return 0;
}
答案:40 10 4/8 4/8
決議:
sizeof(arr)—— arr單獨放在 sizeof 中表示的是 —— arr是整個陣列 —— 整個陣列的大小為40位元組
sizeof ( arr ) —— ch單獨放在 sizeof 中表示的是 —— ch表示是整個陣列 —— 大小為10
函式傳參傳的是陣列首元素的地址 —— 只要是地址就是4(32位平臺下)\8(64位平臺下)個位元組,
取反運算子 —— ~
—— 按二進制位取反 —— 原來為1的值取0,原來為0的值取1,
#include<stdio.h>
int main()
{
int a = 0;
a = ~a;
printf("%d\n", a); //列印值為-1
//決議:
//00000000000000000000000000000000 —— a的原碼,反碼,補碼
//11111111111111111111111111111111 —— ~a的補碼
//11111111111111111111111111111110 —— ~a的反碼
//00000000000000000000000000000001 —— ~a的原碼 —— 值為:-1
}
++ 和 - - 運算子
#include<stdio.h>
int main()
{
int num1 = 10;
int num2 = num1++; //后置++ —— 先賦值,后++ —— num2先等于num1(10),然后num1再++
int num = 11;
int num3 = --num; //前置-- —— 先++,后賦值 —— num先--,然后在對num3賦值
printf("%d\n", num1); //列印值為11
printf("%d\n", num2); //列印值為10
printf("%d\n", num); //列印值為10
printf("%d\n", num3); //列印值為10
}
(型別) —— 強制型別轉換
int main()
{
int a = 3.14 //直接賦給變數一個浮點數,默認的型別為double型別的浮點數,這樣直接賦給int型別
//的變數會出現警告, 為了不出現警告,用下面的寫法
int a = (int) 3.14 //其中(int)會把double型別的值轉變成int型別的值
return 0;
}
6、關系運算子
//關系運算子
>
>=
<
<=
!= // 用于測驗“不相等”
== // 用于測驗“相等”
這些關系運算子比較簡單,沒什么可講的,但是我們要注意一些運算子使用時候的陷阱,
警告:
在編程的程序中== 和=不小心寫錯,導致的錯誤,
7、邏輯運算子
&& (邏輯與) ||(邏輯或)
#include<stdio.h>
int main()
{
int a = 0;
int b = 1;
int c = a && b;
printf("%d\n", c); //列印值為0 —— a 和 b 同時為真才為真
c = a || b;
printf("%d\n", c); //列印值為1 —— a 和 b 同時為假才為假
return 0;
}
一道360的筆試題
#include <stdio.h>
int main()
{
int i = 0,a=0,b=2,c =3,d=4;
i = a++ && ++b && d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
//上面的列印值為:1 2 3 4
//分析:a++為后置++ —— 先使用a —— a等于0 —— 為假,當a為假的時候 —— 按位于后面的內容無論是什么
//整體都為假 —— 所以后面的內容(++b, d++)不執行,只執行了a++;
//a++執行 a為1,其他的不執行,為原值 b = 2,c = 3,d = 4;
a=0,b=2,c =3,d=4;
i = a++||++b||d++;
printf("a = %d\n b = %d\n c = %d\nd = %d\n", a, b, c, d);
//上面的列印值為:1 3 3 4
//分析:a++為后置++ —— 先使用a —— a等于0 —— 為假,當a為假的時候 —— 按位或后面的內容會影響整體
//的結果 —— 執行++b —— ++b為真 —— 按位或后面的內容無論是什么 —— 整體都為真 —— 后面的內容不執行
//a++執行 a = 1;++b執行 b = 3;其他的不執行 c = 3,d = 4,
return 0;
}
8、 條件運算子
—— 三目運算子
exp1? exp2 : exp3
//其中的exp1,exp2,exp3都是一個運算式
//翻譯:如果exp1式子成立,列印exp2式子的結果,否則列印exp3式子的結果
練習:使用條件運算式實作找出兩個數中的較大值
#include<stdio.h>
int main()
{
int a = 0;
int b = 4;
int c = (a) > (b) ? (a) : (b); //如果a大于b,列印a,否則列印b
printf("%d\n", c);
return 0;
}
9、逗號運算式
exp1, exp2, exp3, …expN
逗號運算式,就是用逗號隔開的多個運算式,
逗號運算式,從左向右依次執行,整個運算式的結果是最后一個運算式的結果,
//代碼1
int a = 1;
int b = 2;
int c = (a>b, a=b+10, a, b=a+1); //逗號運算式 —— c的值為13
//代碼2
if (a =b + 1, c=a / 2, d > 0) //從左到右一直執行,最終由最后一個運算式判斷(d>0),
10. 下標參考、函式呼叫和結構成員
10.1、[ ] 下標參考運算子
—— 運算元:一個陣列名 + 一個索引值
#include<stdio.h>
int mian()
{
int arr[] = { 1,2,3,4,5 };
int i = 0;
for (i = 0; i < 5; i++)
{
printf("%p --- %p\n", &arr[i], (arr + i)); //列印的兩個地址相同
}
//由上面的結果可知:arr[i] 和 *(arr + i) 等價
//arr[i] = *(arr + i) = *(i + arr) = i[arr]
for (i = 0; i < 5; i++)
{
printf("%d --- %d\n", arr[i], i[arr]);
}
//arr[i] 和 i[arr] 的列印值相同
return 0;
}

10.2、( ) 函式呼叫運算子
—— 接受一個或者多個運算元:第一個運算元是函式名,剩余的運算元就是傳給函式的引數,
#include<stdio.h>
#include<string.h>
test1()
{
printf ("haha\n");
}
test2(const char* p)
{
printf("%s\n", p);
}
int main()
{
char arr[] = "abcdef";
size_t c = strlen(arr); //strlen為庫函式 ——需要參考頭檔案<string.h>
//strlen 的庫函式的回傳值為無符號整型 —— size_t表示無符號整型
printf("%u\n", c);
//%u —— 表示列印無符號整型
//%d —— 表示列印有符號整型
test1(); //實用()作為函式呼叫運算子
test2("hello bit"); //實用()作為函式呼叫運算子
return 0;
}

10.3、訪問結構成員的運算子
. 結構體.成員名
-> 結構體指標->成員名
10.3.1、結構體 . 成員名
#include<stdio.h>
#include<string.h>
//定義一個結構體型別
struct book
{
char name[20];
float price;
char id[10];
}; // ; 不可少
int main()
{
//對結構體進行初始賦值
struct book b = {"c語言", 40.0f, "OF010111"};
printf("%s\n", b.name); //要找到結構體中的成員 —— 需要用到 . 運算子
printf("%f\n", b.price);
printf("%s\n", b.id);
printf("-----------------\n");
//修改結構的值
b.price = 60.0f;
strcpy(b.name,"資料結構"); //其中b.name指向的是一個陣列名 —— 首元素地址 —— 不能直接改變
//需要用到strcpy庫函式(拷貝字串) —— 參考頭檔案<string.h>
printf("%s\n", b.name);
printf("%f\n", b.price);
printf("%s\n", b.id);
return 0;
}

10.3.2、結構體指標->成員名
#include<stdio.h>
//定義一個結構體型別
struct book
{
char name[20];
float price;
char id[10];
}; // ; 不可少
void Print1(struct book* b) //利用結構體指標接收地址
{
printf("%s\n", (*b).name);
printf("%f\n", (*b).price);
printf("%s\n", (*b).id);
printf("-----------------\n");
printf("%s\n", b -> name); //由結構體指標找到結構體中的成員 —— 需要用到 -> 運算子
printf("%f\n", b -> price);
printf("%s\n", b -> id);
}
//當接收到的是結構體指標時 —— 通常都使用 -> 符號來找結構體成員
//當接收到的是結構體變數時 —— 使用 . 運算子
int main()
{
struct book b = {"c語言", 40.0f, "OF010111"};
//定義一個列印函式
Print1(&b); //傳的是結構體的地址
return 0;
}

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/299723.html
標籤:其他
上一篇:排序演算法學習(1)(直接插入排序,希爾排序,選擇排序,堆排序,冒泡排序)
下一篇:編程之美-字串函式
