一篇文章帶你了解C語(yǔ)言:入門基礎(chǔ)(2)
本節(jié)將結(jié)束對(duì)初識(shí)C語(yǔ)言的概述,只追求大概,不求精細(xì)。
本節(jié)包括的內(nèi)容有操作符,常見關(guān)鍵字,#define定義常量和宏,指針以及結(jié)構(gòu)體。
操作符
首先第一部分操作符
分類如上,具體不再用文字闡述。
算術(shù)操作符
首先算術(shù)操作符,有除號(hào)值得一講,若想得浮點(diǎn)數(shù),兩端操作數(shù)至少有一個(gè)為浮點(diǎn)數(shù),否則就算變量用float定義也不行。
int main() { //除號(hào)任意兩端有浮點(diǎn)數(shù)則,進(jìn)行浮點(diǎn)數(shù)除法 //全為整數(shù)則進(jìn)行整數(shù)除法 float a = 5 / 2; printf("%f\n", a);//2.0000 float b = 5.0 / 2; printf("%f\n", b);//2.5000 float c = 5 / 2.0; printf("%f\n", c);//2.5000 float d = 5.0 / 2.0; printf("%f\n", d);//2.5000 return 0; }
移位操作符
接下來(lái)是移位操作符,分為左移和右移,右移較復(fù)雜先不講,先講左移。按二進(jìn)制位向左移動(dòng)一位,即把32個(gè)bit的二進(jìn)制序列寫出來(lái),整體向左移動(dòng)一位,左側(cè)移出去就刪去,右側(cè)補(bǔ)零。
當(dāng)然如果你寫的多的話就可以看出來(lái)左移一位即十進(jìn)制數(shù)字乘以二。
具體看代碼
int main() { //00000000 00000000 00000000 00001100 - 12 //00000000 00000000 00000000 00011000 - 24 int a = 12; int b = a << 1; printf("%d\n", b); //00000000 00000000 00000000 00000110 - 6 //00000000 00000000 00000000 00001100 - 12 int c = 6; int d = c << 1; printf("%d\n", d); //00000000 00000000 00000000 00000010 - 2 //00000000 00000000 00000000 00000100 - 4 int e = 2; int f = e << 1; printf("%d\n", f); //由2^0左移一位為2^1; //由2^1左移一位為2^2; //由2^2+2^3左移一位為2^3+2^4; //二進(jìn)制左移一位即十進(jìn)制乘以二 return 0; }
位操作符
接下來(lái)是位操作符,有按位與,按位異或和按位或,按位與和按位或是反義的。
具體方法是將兩個(gè)操作數(shù)的二進(jìn)制位寫出來(lái),相應(yīng)位對(duì)比。
按位與:有0則0,全1則1。
按位異或:相同為0,相異為1。(異或嘛~)
按位或:有1則1,全0才0。
具體看代碼
int main() { int a = 3; int b = 5; //00000000000000000000000000000011 - a //00000000000000000000000000000101 - b //00000000000000000000000000000001 - a & b //對(duì)應(yīng)的二進(jìn)制位有0就為0,全是1則為1 int c = a & b; printf("%d\n", c);//1 //00000000000000000000000000000011 - a //00000000000000000000000000000101 - b //00000000000000000000000000000110 - a ^ b //對(duì)應(yīng)的二進(jìn)制位相同為0,相異為1 int d = a ^ b; printf("%d\n", d);//6 //00000000000000000000000000000011 - a //00000000000000000000000000000101 - b //00000000000000000000000000000111 - a | b //對(duì)應(yīng)二進(jìn)制位有1就是1,全為0才得0 int e = a | b; printf("%d\n", e);//7 return 0; }
賦值操作符沒(méi)什么好講的,也就把a(bǔ)=a+1簡(jiǎn)寫成了a+=1這樣差不多的操作符。
單目操作符
接下來(lái)是單目操作符
邏輯反操作!
首當(dāng)其沖是邏輯反操作!,這就涉及到了真假的問(wèn)題。C語(yǔ)言中規(guī)定非0就是真,只有0是假。所以!上一個(gè)任意不為零的數(shù)都是0,但!0呢,規(guī)定!0就為1。
sizeof是個(gè)操作符,這也是很多人會(huì)忽略的一點(diǎn)。
按位取反~,經(jīng)過(guò)上面移位和位操作符的講解,應(yīng)該不難得出按位取反的含義,就是把二進(jìn)制位列出來(lái)然后1變0,0變1。
當(dāng)然不止上面這么簡(jiǎn)單,正數(shù)是這樣,那負(fù)數(shù)呢?
首先負(fù)整數(shù)時(shí)有符號(hào)的整數(shù),二進(jìn)制位最高位如果是0,則該數(shù)為正數(shù),如果時(shí)1,則為負(fù)數(shù)。
其實(shí)計(jì)算機(jī)在內(nèi)存中存儲(chǔ)整數(shù)的時(shí)侯呀,存儲(chǔ)的是二進(jìn)制,這大家都知道。然而一個(gè)整數(shù)的二進(jìn)制的表示形式有三種分別是 原碼,反碼,補(bǔ)碼
如果是正數(shù),那么原碼反碼補(bǔ)碼相同,那如果是負(fù)數(shù)呢,它的原反補(bǔ)是需要計(jì)算的。
其次我們應(yīng)該知道原反補(bǔ)是如何計(jì)算的。反碼,原碼符號(hào)位不變,其他位按位取反。補(bǔ)碼,反碼+1。
但是最重要也是最容易讓初學(xué)者混淆的一點(diǎn)是,計(jì)算機(jī)存儲(chǔ)整數(shù)時(shí),往內(nèi)存里存的是補(bǔ)碼,而不是大多數(shù)人想象的原碼,這就需要我們反過(guò)來(lái)計(jì)算原反補(bǔ)了。
可以以0為例,0的二進(jìn)制位全為0,所以可以看成是個(gè)正數(shù),所以其原反補(bǔ)相同。
如果我們想知道~0(對(duì)0按位取反)是個(gè)什么結(jié)果的話,
1.先對(duì)0的補(bǔ)碼按位取反的~0的補(bǔ)碼,
2.再反過(guò)來(lái)計(jì)算,補(bǔ)碼-1得反碼,
3.然后再符號(hào)位不變,其他按位取反得其原碼,
4.這樣就得到了~0的原碼,就可計(jì)算其十進(jìn)制數(shù)了
由此可得的重要結(jié)論,對(duì)一個(gè)數(shù)按位取反,反的是二進(jìn)制位的補(bǔ)碼!
int main() { //整數(shù)在內(nèi)存中存儲(chǔ)的時(shí)候,存儲(chǔ)的是二進(jìn)制 //一個(gè)整數(shù)的二進(jìn)制表示有3種形式:原碼,反碼,補(bǔ)碼 //正整數(shù):原碼,反碼,補(bǔ)碼相同; //負(fù)整數(shù):原碼,反碼,補(bǔ)碼需計(jì)算; //有符號(hào)的整數(shù),最高位是0,表示為正, // 最高位是1,表示為負(fù); //eg: // int a = 1; //00000000000000000000000000000001 - 原碼 //00000000000000000000000000000001 - 反碼 //00000000000000000000000000000001 - 補(bǔ)碼 // int a = -1; //10000000000000000000000000000001 - 原碼 //11111111111111111111111111111110 - 反碼 - 符號(hào)位不變,其他位按位取反 //11111111111111111111111111111111 - 補(bǔ)碼 - 反碼+1 //內(nèi)存存儲(chǔ)整數(shù)時(shí),存儲(chǔ)的是二進(jìn)制的補(bǔ)碼 //計(jì)算時(shí)也是從補(bǔ)碼開始計(jì)算 //~按(二進(jìn)制)位取反 int b = 0; printf("%d\n", ~b);//-1 //00000000000000000000000000000000 - 0的補(bǔ)碼 //11111111111111111111111111111111 - ~0的補(bǔ)碼 //11111111111111111111111111111110 - ~0的反碼 //10000000000000000000000000000001 - ~0的原碼 //故由0的補(bǔ)碼得~0的補(bǔ)碼,補(bǔ)碼-1得反碼,再(符號(hào)位不變)按位取反得原碼; //最后原碼代表的就是結(jié)果的二進(jìn)制序列; return 0; }
操作符++,--
操作符++,--,值得一提。很多學(xué)校喜歡考各種各樣的奇葩++--題,不同的編譯器得到的結(jié)果可能不同,所以那就是道錯(cuò)題。
在真正編程時(shí),使用++,--就老老實(shí)實(shí)使用。別搞別人看不懂的那一套,沒(méi)意思的,實(shí)力不是靠那個(gè)體現(xiàn)出來(lái)的。就分為前置和后置,也就是先++,在使用,還是先使用,再++的區(qū)別。
int main() { int a = 2; //a = a + 1; //a += 1; //printf("%d\n", a); //前置++;后置++; int c = ++a;//前置++;先++,后使用 printf("++a=%d\n", c);//3 printf(" a=%d\n", a);//3 a = 2; int d = a++;//后置++;先使用,后++ printf("a++=%d\n", d);//2 printf(" a=%d\n", a);//3 a = 2; int e = --a;//前置--;先--,后使用 printf("--a=%d\n", e);//1 printf(" a=%d\n", a);//1 a = 2; int f = a--;//后置--;先使用,后-- printf("a--=%d\n", f);//2 printf(" a=%d\n", a);//1 return 0; }
取地址操作符和解引用操作符是一對(duì),放在指針部分再講。
關(guān)系操作符沒(méi)什么好講的,和數(shù)學(xué)上一個(gè)含義。
邏輯操作符
邏輯操作符嘛,就如同數(shù)學(xué)里的邏輯真假一樣,邏輯與和邏輯或,分別是并且和或者的關(guān)系,他們兩邊分別是兩個(gè)條件,如果兩個(gè)都為真,邏輯與表達(dá)式就為真。如果兩個(gè)有一個(gè)真的,邏輯或表達(dá)式就是真。
int main() { //邏輯與 - &&(并且) //邏輯或 - ||(或者) int a = 1; int b = 4; if ((a = 1) && (b = 4)) { printf("&&\n"); } if ((a = 3) || (b = 4)) { printf("||\n"); } return 0; }
條件操作符
條件操作符,x?y1:2,?的兩邊分別是兩個(gè)條件,前面的成立則整個(gè)表達(dá)式的值為1,反正則2。
逗號(hào)表達(dá)式
逗號(hào)表達(dá)式,( , , ,) ,如這樣的一個(gè)例子,每一個(gè)表達(dá)式都是一個(gè)算式,從左向右依次計(jì)算,最后一個(gè)表達(dá)式的結(jié)果作為整個(gè)逗號(hào)表達(dá)式的值。
//條件操作符(三目操作符) int main() { int a = 10; int b = 0; //if (a == 5) //{ // b = -6; //} //else //{ // b = 6; //} b = (a = 5) ? (6) : (-6); printf("%d\n", b); return 0; } //逗號(hào)表達(dá)式 //( , , ... , ); //表達(dá)式從左向右計(jì)算,整個(gè)表達(dá)式的結(jié)果為最后一個(gè)表達(dá)式的值 int main() { int a = 0; int b = 3; int c = -1; int d = (a = b - 5, b = a + c, c = a + b, c -= 5); printf("%d\n", d); return 0; }
OK,關(guān)于操作符的內(nèi)容就先介紹到這兒。
下面是關(guān)鍵字的內(nèi)容。
常見關(guān)鍵字
上圖為常見關(guān)鍵字的思維導(dǎo)圖,接下來(lái)請(qǐng)隨我一同探討。
這些是C語(yǔ)言里常見的各種關(guān)鍵詞,下面將對(duì)其部分稍作討論。
typedef
首先是typedef,翻譯來(lái)就是類型重命名。顧名思義,就是將定義變量的類型的名字如int,char等,重新取個(gè)名字代替。當(dāng)然一般用于非常長(zhǎng)的類型名如unsigned int這樣,或者是結(jié)構(gòu)體類型。 如此之后就可以把unsigned int 改寫成 unint了。
//typedef 變量類型重命名 typedef unsigned int unint;
extern
然后是extern,翻譯過(guò)來(lái)就是外部的意思,故用于聲明引用外部(其他.c文件)的文件或者是函數(shù)等,但由于函數(shù)自帶外部鏈接屬性,所以一般不用于聲明外部函數(shù)。用法如extern int g_val;(g_val是外部的變量)
static
緊接著是static,它分別有修飾局部變量,修飾全局變量和修飾函數(shù)三種不同的用法,但個(gè)人認(rèn)為修飾全局變量和函數(shù)的意義相同。
修飾局部變量
修飾局部變量時(shí),使其出作用域不會(huì)被銷毀,準(zhǔn)確的來(lái)說(shuō)就是延長(zhǎng)了他的生命周期,但不影響作用域。
修飾全局變量和函數(shù)
修飾全局變量和函數(shù)時(shí),會(huì)使其外部鏈接屬性失效,也是就使其不可以再其他源文件中被使用。
void test() { //修飾局部變量 //改變其生命周期,不影響作用域 static int a = 1; a++; printf("%d ", a); } int main() { int i = 0; while (i < 10) { test(); i++; } //static修飾全局變量 printf("%d\n", g_val); int a = 10; int b = 20; //static修飾的函數(shù) int c = Add(a, b); printf("%d\n", c); return 0; }
//static修飾全局變量 //使其不可跨文件使用(外部鏈接屬性失效) static int g_val = 2021; //static修飾的函數(shù) //函數(shù)被static修飾,使其外部鏈接屬性變內(nèi)部鏈接屬性 static int Add(int x, int y) { return x + y; }
其它
其它如,auto,goto,register,union,稍微了解一下。
#define定義常量和宏
定義常量
定義常量時(shí),也是非常簡(jiǎn)單,例如:#define N 10; 就定義了一個(gè)不可被修改的常量其值為10。
定義宏
#define MAX(x,y) (x>y?x:y)
類似于函數(shù),但又有別于函數(shù),MAX(x,y)是宏,(x>y?x:y) 是宏體。MAX(x,y),像是函數(shù)名和傳參放在一塊,()就像是函數(shù)內(nèi)容。
//定義常量 #define NUM 100 //定義宏 #define MAX(X,Y) (X>Y?X:Y) int main() { printf("%d\n", NUM); int a = 0; int b = 10; int c = MAX(a, b); //實(shí)際操作,替換宏體 //int c = (a > b ? a : b); printf("%d\n", c); return 0; }
指針
指針一直是我之前自學(xué)的時(shí)候最害怕的內(nèi)容,但這次初識(shí)C語(yǔ)言讓我消除了對(duì)指針的恐懼,一步步的了解指針。
指針嘛,指向變量的內(nèi)存地址,故講指針之前必須把內(nèi)存搞清楚。
內(nèi)存單元
為了可以有效使用內(nèi)存,我們把內(nèi)存劃分了一個(gè)個(gè)小的內(nèi)存單元,每個(gè)內(nèi)存單元的大小為1byte。
我們需對(duì)內(nèi)存進(jìn)行編號(hào),當(dāng)然需要二進(jìn)制位序列表示(默認(rèn)我們是32位機(jī)器)。
每個(gè)二進(jìn)制序列有32個(gè)bit,從數(shù)學(xué)全排列角度看,一共有2的32次方種排列可能(32個(gè)全0到32個(gè)全1)。
所以若想對(duì)其進(jìn)行編號(hào),不如一人一個(gè)碼(一個(gè)內(nèi)存單元用一個(gè)二進(jìn)制序列表示)。并且我們把這些編號(hào)成為地址。
當(dāng)然,二進(jìn)制也可以轉(zhuǎn)化為十進(jìn)制或者十六進(jìn)制,所以我們?cè)僬{(diào)試時(shí)調(diào)用內(nèi)存會(huì)看到自動(dòng)顯示為十進(jìn)制數(shù)字。
我們了解了內(nèi)存,現(xiàn)在我們看看如何取出內(nèi)存的地址
int main() { int num = 1;//先創(chuàng)建一個(gè)變量 #//然后取出它的地址 printf("%p",&num);//最后以%p的形式打印地址 return 0; }
這樣我們就得到了num的地址,以十六進(jìn)制數(shù)字展示。
指針變量
我們講清楚了內(nèi)存,現(xiàn)在再來(lái)看看指針。
我們既然已經(jīng)得到了地址,那我們?nèi)绾稳?chǔ)存這些東西呢,這是程序員們就想到了一種東西叫指針變量,它用于存放地址。
關(guān)于該(指針)變量如何定義看下列代碼。
#include <stdio.h> int main() { int num = 10; int *p = # *p = 20; return 0; }
上述代碼中我們可以看到,指針變量的類型時(shí) int * 。而有了指針變量后,在其前面加上*有個(gè)可以改變?cè)兞康闹怠.?dāng)然之所以是int*而不是char*,是因?yàn)樵兞渴莍nt型的。
&取地址操作符,*解引用操作符
這里我們介紹一下,兩個(gè)操作符分別是&取地址操作符和*解引用操作符。
&+變量名 可以取出變量的地址。
*+指針變量名 就可以把它當(dāng)作原變量使用,通過(guò)這樣就可以進(jìn)行改變?cè)兞康倪@樣一系列的操作
可以說(shuō) pa = &a , *pa = a。
類型所占空間
那么我們既然知道了有種變量叫指針變量,那么他們的類型大小是多少呢?
答案是每種指針變量類型大小都為4個(gè)字節(jié)(32位機(jī)器),因?yàn)橹羔樧兞看娣诺刂?,地址為二進(jìn)制序列,32個(gè)bit,正好占4個(gè)byte。當(dāng)然64位機(jī)器就是8個(gè)字節(jié)。
結(jié)構(gòu)體
結(jié)構(gòu)體的出現(xiàn)使得C語(yǔ)言具有了描述復(fù)雜類型的能力。
C語(yǔ)言的類型int,char,float等可以描述很多東西,但是這畢竟太單一,使用結(jié)構(gòu)體可以描述更復(fù)雜的對(duì)象。
比如最經(jīng)典的例子,如學(xué)生,書籍等。
定義結(jié)構(gòu)體
描述學(xué)生的信息有名字,性別,年齡,學(xué)號(hào)等,下面且與我一同欣賞如何定義學(xué)生結(jié)構(gòu)體。(記得大括號(hào)后面有個(gè)分號(hào),vs2019自動(dòng)帶上)
struct stu { char name[20];//姓名 int age;//年齡 char sex[5];//性別 char id[20];//學(xué)號(hào) };
或者是針對(duì)書籍的描述,有書名,價(jià)格,作者名等
struct book { char name[20]; int price; char author[20]; };
注意,struct stu 這一整個(gè)相當(dāng)于 int float double 。
這樣我們就完成了結(jié)構(gòu)體變量類型的定義。
下面我們定義一個(gè)個(gè)的學(xué)生(結(jié)構(gòu)體)變量。
//創(chuàng)建結(jié)構(gòu)體變量 struct stu s1 = {"蕪湖大司馬",40,"男","2020313222"}; struct stu s2 = {"lisa",22,"女","2020313232"}; struct book b1 = {"C語(yǔ)言詳解",55,"譚浩強(qiáng)"};
使用結(jié)構(gòu)體變量
創(chuàng)建好了我們?nèi)绾稳ナ褂媚??最?jiǎn)單的輸出方式,使用操作符 .
形式上是 結(jié)構(gòu)體變量.成員名。
//輸出1 printf("name: %s,age: %d,sex: %s,id: %s\n", s1.name, s1.age, s1.sex, s1.id); printf("書名: %s,價(jià)格: %d,作者: %s\n", b1.name, b1.price, b1.author);
既然這樣可以的話,我們還可以定義指針變量代替變量名,用(*pb)代替b1。
struct book * pb = &b1; //先定義一下指針變量 printf("%s %d %s\n", (*pb).name, (*pb).price, (*pb).author); //指針變量解引用,就可以當(dāng)作原變量使用
當(dāng)然有更方便的操作符 ->,這樣我們可以直接使用指針啦,如結(jié)構(gòu)體指針->成員名。
struct book * pb = &b1; //別忘了定義指針 printf("%s %d %s\n", pb->name, pb->price, pb->author); //指針變量名直接加 —> 再加成員名
直到這里我們初識(shí)C語(yǔ)言的內(nèi)容就講完了,非常感謝您的觀看,創(chuàng)作著實(shí)不易。
總結(jié)
本篇文章就到這里了,希望能給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
C++ 實(shí)現(xiàn)線程安全的頻率限制器(推薦)
這篇文章主要介紹了在 C++ 中實(shí)現(xiàn)一個(gè)線程安全的頻率限制器,本文通過(guò)實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2020-05-05Qt結(jié)合libqrencode生成二維碼的實(shí)現(xiàn)示例
本文主要介紹了Qt結(jié)合libqrencode生成二維碼的實(shí)現(xiàn)示例,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-01-01實(shí)例講解C++設(shè)計(jì)模式編程中State狀態(tài)模式的運(yùn)用場(chǎng)景
這篇文章主要介紹了實(shí)例講解C++設(shè)計(jì)模式編程中State狀態(tài)模式的運(yùn)用場(chǎng)景,文章最后的適用性部分則介紹了一些State模式善于處理的情況,需要的朋友可以參考下2016-03-03C++中std::conditional的使用說(shuō)明
這篇文章主要介紹了C++中std::conditional的使用說(shuō)明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-07-07C語(yǔ)言內(nèi)存的動(dòng)態(tài)分配比較malloc和realloc的區(qū)別
這篇文章主要介紹了C語(yǔ)言內(nèi)存的動(dòng)態(tài)分配比較malloc和realloc的區(qū)別,本篇文章通過(guò)簡(jiǎn)要的案例,講解了該項(xiàng)技術(shù)的了解與使用,以下就是本文的詳細(xì)內(nèi)容,需要的朋友可以參考下2021-07-07