柚子快報(bào)激活碼778899分享:1. C語言之初識C語言
柚子快報(bào)激活碼778899分享:1. C語言之初識C語言
文章目錄
前言一、什么是C語言二、第一個(gè)C語言程序三、數(shù)據(jù)類型四、變量,常量1、變量1.1 變量的命名1.2 變量的分類1.3 變量的使用1.4 變量的作用域和生命周期2、變量
五、字符串1. 概念2. 求解字符串的長度【strlen】3. 轉(zhuǎn)義字符【含筆試題】
六、注釋七、選擇語句八、循環(huán)語句九、函數(shù)十、數(shù)組1、數(shù)組的定義2、數(shù)組的下標(biāo)3、數(shù)組的使用
十一、操作符1、算數(shù)運(yùn)算符2、移位操作符3、位操作符4、賦值操作符5、單目操作符6、關(guān)系操作符7、邏輯操作符8、條件操作符9、逗號表達(dá)式10、其他
十二、常見關(guān)鍵字1、前言2、有關(guān)數(shù)據(jù)存儲的底層原理3、typedef關(guān)鍵字4、static關(guān)鍵字4.1、static關(guān)鍵字修飾局部變量4.2、static關(guān)鍵字修飾全局變量4.3、static關(guān)鍵字修飾函數(shù)
十三、 #define 定義常量和宏十四、 指針1、引言2、內(nèi)存的概念和介紹3、指針變量的大小
十五、結(jié)構(gòu)體
前言
想必剛接觸C語言的同學(xué)們不知道C語言是什么?,有什么用,那么你來對了,本系列就會帶你入門C語言,從入門到“入土”,開玩笑的,正如標(biāo)題所說,本教程首先對C語言有一個(gè)初步的認(rèn)識,能夠看懂別人寫的是什么,有一個(gè)大概的框架,那么,我要開始講解了。 這一章主要是初始C語言的一個(gè)大綱。
一、什么是C語言
C語言是什么?
語言:漢語,日語,英語等。語言是一個(gè)自然語言,是人與人交流的語言。 計(jì)算機(jī)語言:類比過來,是人與計(jì)算機(jī)之間的交流。 C語言是一門通用計(jì)算機(jī)編程語言,廣泛應(yīng)用于底層開發(fā)。C語言的設(shè)計(jì)目標(biāo)是提供一種能以簡易 的方式編譯、處理低級存儲器、產(chǎn)生少量的機(jī)器碼以及不需要任何運(yùn)行環(huán)境支持便能運(yùn)行的編程語 言。 盡管C語言提供了許多低級處理的功能,但仍然保持著良好跨平臺的特性
二、第一個(gè)C語言程序
我們在了解完C語言是什么后,我們需要了解如何使用集成開發(fā)環(huán)境去完成第一個(gè)C語言程序。
什么是集成開發(fā)環(huán)境呢? 集成開發(fā)環(huán)境(IDE,Integrated Development Environment )是用于提供程序開發(fā)環(huán)境的應(yīng)用程序,一般包括代碼編輯器、編譯器、調(diào)試器和圖形用戶界面等工具。 像vscode就是一種編輯器,它不是集成開發(fā)環(huán)境,但是它可以添加許多的插件來編輯代各種形式的代碼。 像vs2022就是一種集成開發(fā)環(huán)境,在平時(shí)使用很方便,但是存儲空間比較大,畢竟有好有缺點(diǎn)嘛,不能兩者兼容。 但是我本人不太推薦使用那種上古編譯器比如VC++,DEV++…
接下來我們就開始正式寫第一個(gè)C語言程序
進(jìn)入官網(wǎng)下載VS2022,不要去別的地方下載那些盜版的?。?!點(diǎn)擊進(jìn)入官網(wǎng)
點(diǎn)擊個(gè)人免費(fèi)下載,學(xué)習(xí)下載這個(gè)完全夠用了。
打開下載
打開軟件,創(chuàng)建一個(gè)新項(xiàng)目
在C語言的學(xué)習(xí)中,大多數(shù)人的第一個(gè)程序就是hello word!,那么我們怎么打印出hello word,開始我們學(xué)習(xí)編程之旅,下面就跟著我來一起學(xué)習(xí)。
我們知道,C語言所有的字符,符號,都是英文的。C語言中代碼是從main函數(shù)的第一行開始的。main函數(shù)是程序的入口,一個(gè)工程中main函數(shù)有且只有一個(gè)。其中代碼中的printf是編譯器的頭文件引入的,可以直接使用。
#include
int main()
{
printf("hello world!\n");
return 0;
}
有人可能看到我這里寫的是int main(){ ... return 0},通常就是這樣寫的,也是最普遍的寫法,還有一種就是void main(){ },這種寫法比較古老,現(xiàn)在不會使用這種寫法。
三、數(shù)據(jù)類型
寫出了我們的第一個(gè)C語言程序,接下來我們來說一說C語言中的數(shù)據(jù)類型
在了解數(shù)據(jù)類型前,我們先探究為什么要寫程序?
是為了用程序解決生活中的一些問題。在現(xiàn)實(shí)生活中,我們的各種數(shù)據(jù)有很多不同的類型,為了更加豐富的表達(dá)生活中的各種值,所以在C語言程序中設(shè)置這么多數(shù)據(jù)類型。
char //字符數(shù)據(jù)類型
short //短整型
int //整形
long //長整型
long long //更長的整形
float //單精度浮點(diǎn)數(shù)
double //雙精度浮點(diǎn)數(shù)
每種類型的字節(jié)大小是多少? 下圖所顯示的就是:
計(jì)算機(jī)中常見的單位:bit(比特),byte(字節(jié)),KB,MB,GB,TB,PB。
他們的單位換算為:
1 byte = 8 bit 1 KB = 1024 byte 1 MB = 1024 KB 1 GB = 1024 MB 1 TB = 1024 GB 1 PB = 1024 TB
四、變量,常量
在生活中有些值是不變的(比如:圓周率,性別,身份證號碼,血型等等),有些值是可變的(比如:年齡,體重,薪資等等),引入了變量和常量的概念。
首先,先講述一下變量:
1、變量
語法形式為:type + name。代碼如下:
int age = 150;
float weight = 45.5f; //浮點(diǎn)數(shù)默認(rèn)為雙精度浮點(diǎn)數(shù),在后面加個(gè)f,說明是單精度浮點(diǎn)數(shù)類型
char ch = 'w';
1.1 變量的命名
只能由字母(包括大小寫),數(shù)字,下劃線組成。不能以數(shù)字開頭。長度不能超過63個(gè)字符。C語言是區(qū)分大小寫的。變量名字不能以關(guān)鍵字命名字。變量的名字盡量有意義。
1.2 變量的分類
變量分為局部變量和全局變量
#include
int global = 2019;//全局變量
int main()
{
int local = 2018;//局部變量
//下面定義的global會不會有問題?
int global = 2020;//局部變量
printf("global = %d\n", global);
return 0;
}
總結(jié):
上面的局部變量global變量的定義其實(shí)沒有什么問題的! 當(dāng)局部變量和全局變量同名的時(shí)候,局部變量優(yōu)先使用。
1.3 變量的使用
了解了什么是變量,現(xiàn)在我們就要具體地去使用這個(gè)變量了,讓我們一起來看看
#include
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("輸入兩個(gè)操作數(shù):>");
scanf("%d %d", &num1, &num2);
sum = num1 + num2;
printf("sum = %d\n", sum);
return 0;
}
在vs編譯時(shí)會發(fā)現(xiàn),我們在這里會出現(xiàn)4996警告。
為什么會出現(xiàn)這個(gè)警告呢?
是因?yàn)樵趘s中輸入函數(shù),微軟有自己的定義scanf_s,而用scanf函數(shù)不安全。
如何消除這個(gè)警告呢?
第一種方法就是把scanf()改成scanf_s(),scanf()也是vs提供的,這種方式很簡單,但是代碼的跨平臺,可移植性就變差了,不推薦這種方法。第二種方法就是在.c文件中添加#pragma warning(disable:4996)這樣就不會提示這個(gè)告警了。跟著下圖一步一步的操作
首先下載everthing軟件,下載好點(diǎn)擊打開搜索newc++file.cpp,打開文件
把這行粘貼進(jìn)去#define _CRT_SECURE_NO_WARNINGS 1,后面還要加上個(gè)【1】不要忘了
這種方法一勞永逸,可以很好的解決我們的問題~~
再次創(chuàng)建一個(gè).c文件,可以看到自動就有那一行了
1.4 變量的作用域和生命周期
清楚了如果使用變量去編寫程序,接下來我們來看一下變量的作用域和生命周期
作用域:
作用域(scope)是程序設(shè)計(jì)概念,通常來說,一段程序代碼中所用到的名字并不總是有效/可用的通常來說,一段代碼中所用到的變量名并不總是有效的,而限定這個(gè)名字的可用性的代碼范圍就是這個(gè)變量名的作用域。
了解了其基本概念,我們就到代碼中來演示一下
可以看到程序報(bào)錯(cuò)了,說【未定義標(biāo)識符】,可以看出這個(gè)作用域具有限定范圍的
int main()
{
{
int a = 10;
printf("a = %d", a);
}
printf("a = %d", a);
return 0;
}
以上的話就是局部變量的作用域從下面代碼可以看出,完全沒有報(bào)出錯(cuò)誤,因?yàn)閷τ谌肿兞康淖饔糜?,是從定義變量開始到整個(gè)程序運(yùn)行結(jié)束,整個(gè)變量才會被銷毀,而這個(gè)變量a,則是在這個(gè)代碼塊結(jié)束之后便會銷毀,所以我們在后面才訪問不到這個(gè)變量
對于全局變量的作用域,還可以這樣使用,把這個(gè)b變量定義在add.c這個(gè)文件下,然后我們在test.c這個(gè)文件定義一下這個(gè)變量b,用這個(gè)extern聲明一下即可
生命周期:
變量的生命周期指的是變量的創(chuàng)建到變量的銷毀之間的一個(gè)時(shí)間段
局部變量的生命周期是:進(jìn)入作用域生命周期開始,出作用域生命周期結(jié)束。
全局變量的生命周期是:整個(gè)程序的生命周期
講完了變量,接下去我們來講講常量
2、變量
C語言中常量分為一下幾種:
字面常量const修飾的常變量#define 定義的標(biāo)識符常量枚舉常量
#include
//舉例
enum Sex
{
MALE,
FEMALE,
SECRET
};
//括號中的MALE,FEMALE,SECRET是枚舉常量
//#define的標(biāo)識符常量 演示
#define MAX 100
int main()
{
//字面常量演示
3.14;//字面常量
1000;//字面常量
//const 修飾的常變量
const float pai = 3.14f; //這里的pai是const修飾的常變量
pai = 5.14;//是不能直接修改的!
printf("max = %d\n", MAX);
//枚舉常量演示
printf("%d\n", MALE);//0
printf("%d\n", FEMALE);//1
printf("%d\n", SECRET);//2
//注:枚舉常量的默認(rèn)是從0開始,依次向下遞增1的
return 0;
}
常量就是不變的量,所以叫常量
五、字符串
1. 概念
在講字符串之前,我們先來看看下面這個(gè),因?yàn)樵谟?jì)算機(jī)中存儲、表示的都是二進(jìn)制,所以我們打印出來的也會是一個(gè)二進(jìn)制首先定義一個(gè)char類型的字符變量ch,然后分別以%c、%d、%s去打印出來,可以看到,顯示的結(jié)果各不相同%c打印的就是一個(gè)字符,%d打印的則是這個(gè)字符在ASCLL碼表中的值,最后的%s,打印的就是ch這個(gè)字符串
然后開始講一講字符串
概念:雙引號引起的一串字符叫做字符串
2. 求解字符串的長度【strlen】
說完了字符串了基本概念,了解了什么是字符串,接下去我們來看看如何入求解字符串的長度
對于字符串,大家要知道,結(jié)束標(biāo)志是一個(gè) \0 的轉(zhuǎn)義字符,轉(zhuǎn)義字符我們下個(gè)模塊就講到,這個(gè)\0表示這個(gè)字符串已經(jīng)結(jié)束了。但是在計(jì)算字符串長度的時(shí)候,這個(gè)\0是不計(jì)算在內(nèi)的也是說,求字符串的長度統(tǒng)計(jì)的是字符串中\(zhòng)0之前出現(xiàn)多少個(gè)字
那我們?nèi)绾稳デ蠼庖粋€(gè)字符串的長度呢?
這里我們需要使用到一個(gè)【string.h】頭文件里的一個(gè)函數(shù),叫做strlen(),可以計(jì)算一個(gè)字符串的長度,也就是\0之前的長度,這個(gè)函數(shù)我們后面會細(xì)講
printf("len = %d\n", strlen("abc"));
前面我們說到過char類型可以定義一個(gè)字符變量,不僅如此,在這里,我們還可以定義一個(gè)字符數(shù)組,什么是字符數(shù)組呢?就是這個(gè)數(shù)組中存放的都是字符,我們來看一個(gè)具體的定義
char arr[] = "abcdef";
以上就是字符數(shù)組的基本定義接著我們再來看一種,下面這一種也是字符數(shù)組的定義方式
char arr2[] = { 'a','b','c','d','e','f' };
可以看到,這兩個(gè)字符數(shù)組中的內(nèi)容是一樣,那他們打印出來,以及計(jì)算出來的長度是否是一樣的呢,我們一起來看一下
很明顯,從以上結(jié)果來看,是不一樣的,這是為什么呢?我們通過一張圖示來了解一下
從以上這張圖我們應(yīng)該可以很明顯地看出來,為什么第二個(gè)arr2數(shù)組在打印的時(shí)候會出現(xiàn)【燙燙燙】這種東西呢,如果你自己去編譯器里試試的話應(yīng)該也是這樣因?yàn)榈诙€(gè)數(shù)組的內(nèi)容值給到了f,并不是一個(gè)完整的字符串,但是第一個(gè)數(shù)組給到的卻是一個(gè)完整的字符串,所以自動擁有一個(gè)’\0’,第二個(gè)數(shù)組就沒了,所以后面打印出來的只會是一個(gè)隨機(jī)值,然后直至碰到一個(gè)\0為止,遍歷才會結(jié)束所以我們可以看到這個(gè)長度也是受到了影響,arr1數(shù)組的長度就是6,但是arr2數(shù)組的長度卻是加上了一些隨機(jī)字符后的長度,這就顯得不準(zhǔn)確了
3. 轉(zhuǎn)義字符【含筆試題】
那什么叫做轉(zhuǎn)義字符呢?我們通過代碼來看看
int main()
{
printf("abcndef");
printf("abc\ndef");
}
通過運(yùn)行結(jié)果我們可以看到,這個(gè)n,若是在其前面加上了一個(gè)\,則它的意思就發(fā)生了變化,在轉(zhuǎn)義字符里面就叫做【\n換行】,所以可以看到打印到【abc】就發(fā)生了換行,在后一行打印了def
然后我們再來看到一個(gè)打印一個(gè)test.c的路徑從運(yùn)行結(jié)果來看,并沒有真正地打印出來一個(gè)路徑,而是出現(xiàn)了很多空格細(xì)心的小伙伴應(yīng)該可以發(fā)現(xiàn)這個(gè)test的第一個(gè)字符t和路徑的\是同一個(gè)顏色,都淡化了,在轉(zhuǎn)義字符里,這個(gè)叫做【\t水平制表符】,也及時(shí)們鍵盤上的【Tab鍵】,敲一下就能空出4個(gè)空格
所以我們在printf輸出一個(gè)東西的時(shí)候,不要去寫一些轉(zhuǎn)義字符,那C語言中有哪些轉(zhuǎn)義字符呢,我們一起來看一下
用一個(gè)表格給大家呈現(xiàn):
轉(zhuǎn)義字符釋義?在書寫連續(xù)多個(gè)問號時(shí)使用,防止他們被解析成三字母詞\ ’用于表示字符常量\“用于表示一個(gè)字符串內(nèi)部的雙引號\ \用于表示一個(gè)反斜杠,防止它被解釋為一個(gè)轉(zhuǎn)義序列符\a警告字符,蜂鳴\b退格符\f進(jìn)紙符\n換行\(zhòng)r回車\t水平制表符\v垂直制表符\dddddd表示1~3個(gè)八進(jìn)制的數(shù)字。如:\130X\xdddd表示2個(gè)十六進(jìn)制數(shù)字。 如:\x30 0
我們挑重點(diǎn)的說一下
首先是二三兩個(gè),看第一行代碼,若你printf的是一個(gè)【‘’‘】,那么編譯器就會報(bào)錯(cuò),你本是想打印一個(gè)【’】,這個(gè)時(shí)候該怎么辦呢,你只需要在前面加上一個(gè)\,這就變成了一個(gè)轉(zhuǎn)義字符,然后就可以像運(yùn)行結(jié)果一樣輸出了然后第三個(gè)也是同理,這里便不做過多詳解
然后就是剛才我們所說的打印路徑,要怎樣才能打印出這個(gè)路徑呢?沒錯(cuò),只需要再添加一個(gè)【\】就可以了,這就表示一個(gè)反斜杠,我們到代碼里看一下可以看到,這個(gè)路徑已經(jīng)被完全打印出來了
最后,我們再來講一下最后兩個(gè)有關(guān)八進(jìn)制和十六進(jìn)制的一樣,先給出運(yùn)行結(jié)果
有關(guān)【\ddd】,表示的就是八進(jìn)制,也就是通過八進(jìn)制去計(jì)算,如果用%d打印出來的就是計(jì)算的結(jié)果,如果使用%c打印出來的就是這個(gè)數(shù)字所對應(yīng)的ASCLL碼符號 對于十六進(jìn)制也是同理,/xdd值就是十進(jìn)制,然后看上面的打印也是同理 對于【\x3a】的話就是用a*160 然后我們來說一道有關(guān)轉(zhuǎn)義字符的筆試題
printf("%d\n", strlen("c:\test\628\test.c"));
這個(gè)結(jié)果是什么呢?答案不盡相同而真正的答案是【14】,為什么呢?我們來看一下
分析
首先第一步,我們剛學(xué)了轉(zhuǎn)義字符,【\t】表示的一定是一個(gè)字符,所以18排除然后第二步,相信大家最疑惑的就是這個(gè)\628,這其實(shí)就是我們上面所說的\ddd,但是這都能算嗎?這個(gè)時(shí)候你就要去想,八進(jìn)制能包含8嗎,八進(jìn)制就是0~7的數(shù)字,所以是不會有8,因此這個(gè)【\62】算一個(gè)轉(zhuǎn)義字符最后我們就可以得出答案為【14】
六、注釋
首先我們要先了解一下注釋是什么,它可以用來干嘛
注釋可以將你不需要或者還不想刪除地代碼暫時(shí)屏蔽起來,在程序執(zhí)行的時(shí)候會直接跳過注釋的代碼,不會運(yùn)行如果在一些程序中有一些晦澀難懂的代碼,可能你寫出來的代碼需要需要一些批注后面在閱讀你代碼的人才能知道這段代碼是什么意思,這個(gè)時(shí)候你就可以在這段代碼的上方或是下方寫一些注釋,這樣后人就可以看懂你寫的代碼
int main()
{
//下面是創(chuàng)建一個(gè)整型變量并賦值10
int a = 10;
int b = 100;
//C++ 注釋風(fēng)格 - C99
/* C語言的注釋風(fēng)格
int c = 10;
int d = 20;
printf("ff");
*/ // - 嵌套的話此處結(jié)束注釋
//不支持嵌套注釋
return 0;
}
我們來分析一下,對于雙斜杠//,這個(gè)是C99中對于C++的注釋風(fēng)格,那C語言的注釋風(fēng)格是怎樣的呢,就是/**/但是后面我還補(bǔ)充了一句話,就是對于C語言的這種注釋方法,不可以產(chǎn)生嵌套的,例如說這里若是在外層加上一個(gè)/**/,這樣return 0;不會被注釋了,為什么呢?因?yàn)樯厦娴?*和return 0;上面的那個(gè)先匹配了,所以就不會到下面的
七、選擇語句
在C語言或是其他編程語言中,都有著三種結(jié)構(gòu)方式:【順序】、【選擇】和【循環(huán)】那這個(gè)選擇語句是什么呢?就是你有時(shí)候會面臨兩種或多種選擇,不同的選擇對應(yīng)的就是不同的結(jié)果
具體我們通過代碼來看一下
int main()
{
int input = 0;
printf("你要好好學(xué)習(xí)嗎(1/0)\n");
scanf("%d", &input);
if (input == 1)
{
printf("拿一個(gè)好offer\n");
}
else
{
printf("回家種田\n");
}
return 0;
}
上面這一段代碼,就是一個(gè)選擇語句,你可以在終端輸入你的選擇,問你要不要好好學(xué)習(xí)。若你選擇的是1好好學(xué)習(xí),那么你就可以拿一個(gè)好offer;但若是你選擇0擺爛,那么就只能回家種田
八、循環(huán)語句
然后我們再來說說另一種形式,也就是循環(huán)語句
什么是循環(huán)呢?循環(huán)就是你一直不斷重復(fù)地做一件事,直到某個(gè)條件滿足時(shí)才會退出
然后我們通過一段代碼再來看一下
int main()
{
int line = 0;
printf("好好學(xué)習(xí)\n");
while (line < 20000)
{
printf("寫代碼:%d\n", line);
line++;
}
if (line == 20000)
printf("好offer\n");
return 0;
}
然后對于循環(huán)的話,不僅僅是有while,還有do…while(),for這些,我們在后續(xù)都會講到
九、函數(shù)
函數(shù)其實(shí)就是將一個(gè)功能單獨(dú)封裝成為一個(gè)模塊,然后這個(gè)模塊可以被多次調(diào)用,以便來實(shí)現(xiàn)代碼的簡化
我們先來看這么一段代碼這是一段兩數(shù)求和的代碼,輸入兩個(gè)數(shù)據(jù),然后輸出它們的和但是設(shè)想,若是我們要再求另外兩個(gè)數(shù)的和,那么就要再次輸入,然后求和的代碼就要再寫一遍,這就徒增了代碼量,顯得整個(gè)程序很冗余,那要怎么簡化呢?對就是使用函數(shù)。
int main()
{
int num1 = 0;
int num2 = 0;
int sum = 0;
printf("請輸入兩個(gè)操作數(shù)字:>");
scanf("%d %d", &num1, &num2);
sum = num1 + num2;
printf("sum = %d\n", sum);
return 0;
}
我們來看一下使用函數(shù)簡化完之后是什么樣子這段代碼使用了三次求和,得到了三個(gè)求和結(jié)果
int Add(int m, int n)
{
return (m + n);
}
int main()
{
int sum1 = 0;
int sum2 = 0;
int sum3 = 0;
sum1 = Add(1, 2);
sum2 = Add(3, 4);
sum3 = Add(5, 6);
printf("sum1 = %d\n", sum1);
printf("sum2 = %d\n", sum2);
printf("sum3 = %d\n", sum3);
return 0;
}
十、數(shù)組
1、數(shù)組的定義
首先我們知道數(shù)字這個(gè)概念,但是當(dāng)我們需要一堆的數(shù)字,那么這個(gè)數(shù)字存到哪里去呢?沒錯(cuò),也就是用一個(gè)叫【數(shù)組】的東西存放起來那要怎么存放呢,我們來看一下
int a[10] = { 1,2,3,4,5,6,7,8,9,10 };
首先需要先聲明這個(gè)數(shù)組是什么類型的,是整型、字符型還是浮點(diǎn)型等,數(shù)組的話這些數(shù)據(jù)都是可以存儲的,然后在一個(gè)變量后面加上一個(gè)[],括號里可以寫上你準(zhǔn)備為這個(gè)數(shù)組開辟多大的空間,比如說上面寫的是10,那么這個(gè)數(shù)組中最多能存下的數(shù)據(jù)也就只有10個(gè),但是若你不寫的話,就可以存多個(gè),后面會教大家一個(gè)方法去計(jì)算沒有給出數(shù)據(jù)具體大小如何去求這個(gè)數(shù)組的大小這里先給出,大家可以先看看,sizeof()是一種單目操作符,是用來計(jì)算你所使用的操作數(shù)所占的空間字節(jié)大小
int sz = sizeof(a)/sizeof(a[0]);
剛才說過,數(shù)組除了可以存放整數(shù)數(shù)據(jù)外,還可以存放字符型、浮點(diǎn)型的數(shù)據(jù)
char b[] = { 'a','b','c'};
double c[] = {2.5,6.9,7.7,1.5 }
2、數(shù)組的下標(biāo)
知道了數(shù)組怎么聲明,那我們聲明的這些數(shù)組怎么獲取到呢?
C語言規(guī)定:數(shù)組的每個(gè)元素都有一個(gè)下標(biāo),下標(biāo)是從0開始的訪問。下面就是我們通過下標(biāo)去訪問的a數(shù)組中下標(biāo)為8的元素,打印出來的就會是9
//下標(biāo)訪問數(shù)組元素
printf("%d\n", a[8]);
具體大家看圖示就能一目了然了 可以看到,這里arr數(shù)組的大小是10,那我們?nèi)ピL問18這下標(biāo)會怎么樣呢,去編譯器里看看 從圖中可以看出,總共下標(biāo)也就只有9,你訪問到了,那就會產(chǎn)生越界訪問,那么你訪問到的就會是一個(gè)隨機(jī)值
3、數(shù)組的使用
看一下代碼。我們首先定義了一個(gè)大小而10的整型數(shù)組,然后將其內(nèi)容初始化為0,然后我們通過一個(gè)在while循環(huán)中通過scanf去輸入一些數(shù)據(jù),將其一一地通過下標(biāo)放入數(shù)組中。然后呢,還是一樣,通過循環(huán)去遍歷這個(gè)數(shù)組,調(diào)用printf去打印出里面的數(shù)據(jù)這就是數(shù)組的一種使用方法,其余的我們放到后面數(shù)組章節(jié)細(xì)講
int main()
{
int arr[10] = { 0 };
//0 ~ 9 - 輸入10個(gè)值給數(shù)組
int i = 0;
while (i < 10)
{
scanf("%d", &arr[i]);
i++;
}
i = 0;
while (i < 10)
{
printf("%d ", arr[i]);
i++;
}
return 0;
}
十一、操作符
C語言中操作符不少,這里我們做簡要介紹,后續(xù)會詳細(xì)講解
1、算數(shù)運(yùn)算符
這里我們重點(diǎn)來講講【除】和【取余】首先來看這段代碼,結(jié)果會是多少?
int a = 7 / 2;
printf("%d\n", a);
運(yùn)行結(jié)果是3,因?yàn)檫\(yùn)算符的左右兩邊都是整數(shù),所以執(zhí)行的是整數(shù)除法,包括下面這段也是一樣,運(yùn)行結(jié)果都是3,只是因?yàn)閒loat浮點(diǎn)數(shù)的原因,小數(shù)點(diǎn)后多出6個(gè)0
float f = 7 / 2;
printf("%f\n", f);
那怎么將其變?yōu)楦↑c(diǎn)數(shù)除法,也就是得到3.5這個(gè)答案,你只需要將7或2任意一個(gè)數(shù)字改為浮點(diǎn)數(shù)即可,例如說7.0/2.0,至少有一個(gè)操作數(shù)是浮點(diǎn)數(shù)執(zhí)行的才是浮點(diǎn)數(shù)除法
我們來看看結(jié)果
然后我們再來看看【取余】操作符
int main()
{
// % 取余操作符,關(guān)注的是除法后的余數(shù)
int a = 7 % 2; //商3余1
printf("%d\n", a);
return 0;
}
取到就是一個(gè)整數(shù)對另一個(gè)整數(shù)做除法后的余數(shù)
2、移位操作符
這一塊大家先了解一下,先不做細(xì)講,會在操作符章節(jié)細(xì)講
左移就是擴(kuò)大 右移就是縮小
3、位操作符
上面叫移位,這里叫位,區(qū)別大嗎?區(qū)別可大了,完全是兩個(gè)概念 這里也會在后面章節(jié)詳解
4、賦值操作符
有關(guān)賦值運(yùn)算符,第一個(gè)大家應(yīng)該不陌生,就是我們常見的賦值運(yùn)算,后面呢則是一些【加減乘除取余移位】這些復(fù)合而成的,你可以到編譯器里自己試試看
5、單目操作符
重點(diǎn)來說一下單目運(yùn)算法
首先是這個(gè)取反操作,在C語言中呢,表示真假只有兩種,用0表示假,用非0表示真所以看下面的代碼,這個(gè)a就是【真】,所以會執(zhí)行“haha”語句,但若是你把a(bǔ)換成0,那么!a就是【真】,就會打印“hehe”語句
//C語言是如何表示真假的呢?
//0表示假,非0表示真
//-1 表示的就是真
int main()
{
//把假變成真,把真變成假
int a = 10;
if (a)
printf("haha\n");
if(!a) //這個(gè)不執(zhí)行
printf("hehe\n");
return 0;
}
然后【取正】【取負(fù)】很簡單,看到下面代碼,會打印a的相反數(shù)
int a = -10;
printf("%d\n", +a);
printf("%d\n", -a);
然后對于取地址和星號運(yùn)算符,我們在下面指針的部分介紹然后來看看sizeof()這個(gè)運(yùn)算符,sizeof()是一個(gè)函數(shù)嗎?不是的,sizeof是一個(gè)操作符
int a = 10;
char c = 'w';
int arr[10] = { 0 };
printf("%d\n", sizeof(a)); //4
printf("%d\n", sizeof(c)); //1
printf("%d\n", sizeof(arr)); //40
對于sizeof()這個(gè)運(yùn)算符呢,它是用來計(jì)算所占內(nèi)存空間的大小,單位是字節(jié),所以上面代碼的輸出分別為整型、字符型和一個(gè)數(shù)組所占內(nèi)存空間的大小因?yàn)檫@些變量都是用int、char這些變量類型定義出來的,所以可以直接用sizeof()傳入這些變量的類型,出來的結(jié)果也是一樣的
printf("%d\n", sizeof(a)); //4
printf("%d\n", sizeof(int)); //4
printf("%d\n", sizeof(c)); //1
printf("%d\n", sizeof(char)); //1
既然這是一個(gè)操作符,那干嘛要這個(gè)括號呢?直接去掉不就好了,不然引起歧義,就像下面這樣
printf("%d\n", sizeof int);
我們在編譯器上跑一下可以看到,是不能運(yùn)行的~~這其實(shí)就更好地可以說明sizeof是一個(gè)操作符,不是函數(shù),括號可以省略然后我們就可以得出結(jié)論:變量可去括號,類型不可取去括號
上面我們有提到過,當(dāng)我們沒有對一個(gè)數(shù)組設(shè)定初始化元素個(gè)數(shù)時(shí),可以使用sizeof()去計(jì)算這個(gè)數(shù)組中有多少元素
就是下面這樣,sizeof(arr)就是整個(gè)數(shù)組的大小,sizeof(arr[0])便是一個(gè)元素的大小,當(dāng)然你也可以寫成【sizeof(int)】,因?yàn)檎蛿?shù)組中一個(gè)元素的大小一定是4個(gè)字節(jié),也就是int所占的字節(jié)大小
printf("%d\n", sizeof(arr));;
int sz = sizeof(arr) / sizeof(arr[0]);
printf("%d\n", sz);
然后我們來說一下 【前置、后置–】與【 前置、后置++】
這兩個(gè)其實(shí)是一樣的,我們先來看一下【 前置、后置++】對于前置++的話就是先++在賦值,所以下面的a會把++完之后的值給到b,然后自己也會++,因此最后輸出的便是11 11
int a = 10;
int b = ++a; //先++后賦值
// a = a + 1
// b = a
printf("%d %d\n", a, b); //11 11
對于后置++就不一樣了,剛好相反,就是c會先把自己的值給到d,然后自己再++,所以d得到的就是10,c后面++完后之后就是11
int c = 10;
int d = c++; //先賦值后++
// d = c;
// c = c + 1
printf("%d %d\n", c, d); //11 10
然后我們來說【前置、后置–】,同理
int a = 10;
int b = --a; ///先--在賦值
printf("%d %d\n", a, b); //9 9
int c = 10;
int d = c--; ///先賦值后--
printf("%d %d\n", c, d); //9 10
然后這一段也是一樣,首先打印a–的一定是之前的值,然后打印的就是–之后的值,也就是9
int a = 10;
printf("%d\n", a--); //先使用,后--
printf("%d\n", a);
對于前置、后置的±,老是喜歡出一些面試題,把你搞暈,其實(shí)根本沒什么意義,我們來看看
int a = 1;
int b = (++a) + (++a) + (++a);
printf("%d\n", b);
從VS來看,這段代碼的運(yùn)行結(jié)果是12,但是在Linux的gcc編譯器中,運(yùn)行結(jié)果竟然是10 一段代碼在不同編譯器運(yùn)行結(jié)果不同,說明這段代碼其實(shí)有問題的,準(zhǔn)確的說是存在歧義的 我們來講單目操作符的最后一個(gè),強(qiáng)制類型轉(zhuǎn)換 首先來看這兩句代碼,你認(rèn)為這會輸出什么
int a = 3.14;
printf("%d\n", a);
因?yàn)?.14給到了一個(gè)整型變量a,所以只會保留整數(shù),這個(gè)時(shí)候大家看下面我用紅筆畫起來的一段Warning,說這個(gè)【從“double”轉(zhuǎn)換到“int”】,可能會丟失數(shù)據(jù)這個(gè)時(shí)候應(yīng)該怎么辦呢?你可以在3.14前面加上一個(gè)(int),將這個(gè)數(shù)強(qiáng)制轉(zhuǎn)換成整型,也就是我上面注釋掉的一行代碼,這個(gè)時(shí)候你再去運(yùn)行試試,就不會報(bào)出Warning了
6、關(guān)系操作符
對于前面的四個(gè),直接用就可以了,和直觀地進(jìn)行一個(gè)比較。我們來講一下后面的兩個(gè)也就是比較兩個(gè)數(shù)是否相等與不等,這個(gè)要與【賦值運(yùn)算符】中的【=】做區(qū)別,一個(gè)是比較兩個(gè)數(shù)或是變量的,另一個(gè)則是進(jìn)行賦值運(yùn)算的,不混淆了
int a = 10;
int b = 20;
if (a == b)
printf("haha\n");
if(a != b)
printf("hehe\n");
那這個(gè)時(shí)候就有同學(xué)問了,除了這個(gè)數(shù)字的比較,可以比較字符串嗎,我們來看看
char arr1[] = "abcdef";
char arr2[] = "abcdef";
if (arr1 == arr2)
printf("==\n");
else
printf("!=\n");
可以看到,兩個(gè)字符數(shù)組明明是一樣的,但是卻走了第二個(gè)分支,打印了【!=】,這里其實(shí)就出問題了。為什么呢?這個(gè)我們后面會說到,數(shù)組名是整個(gè)數(shù)組的首元素地址,其實(shí)【==】比較的是它們的地址對于字符串的比較,在C語言中有專門的函數(shù),叫做strcmp(),它和strlen()一樣都是屬于【string.h】頭文件里的,若比較的兩者相等的話,則會返回0,前者大于后者,返回 > 0的數(shù),后者大于前置,返回 < 0的數(shù),所以只需要去判斷一下去和0的大小即可
//兩個(gè)字符串不可以用“==”來判斷是否相等,使用strcmp(庫函數(shù))
char arr1[] = "abcdef";
char arr2[] = "abcdef";
if (strcmp(arr1,arr2) == 0)
printf("==\n");
else
printf("!=\n");
這樣的話結(jié)果就正確了
7、邏輯操作符
邏輯與【&&】是并且的意思,邏輯或【| |】是或者的意思。為真則為1,為假則為0對于邏輯與,只有兩個(gè)數(shù)均為1是才為1,只要有一個(gè)為0,結(jié)果即為0
int a = 5;
int b = 4;
int c = a && b;
printf("c = %d\n", c);
if (a && b)
printf("hehe\n");
從上述代碼和運(yùn)行結(jié)果可以看出,因?yàn)閍,b都不是0,因此它們的結(jié)果為1,才可以進(jìn)入下面那個(gè)if判斷,若是把a(bǔ),b其中任意一個(gè)改為0,則結(jié)果便為0,然后不會進(jìn)入下面的這個(gè)判斷對于邏輯或的話,只要其中有一個(gè)為1,則為1,只有兩個(gè)數(shù)均為0是,才為0所以下面的顯示結(jié)果是0,并且沒有進(jìn)入這個(gè)if條件的判斷
int a = 0;
int b = 0;
int c = a && b;
printf("c = %d\n", c);
if (a || b)
printf("hehe\n");
8、條件操作符
下面是一段簡單的if分支判斷,然后給b賦值
int a = 10;
int b = 0;
if (a > 5)
b = 3;
else
b = -3;
printf("b = %d\n", b);
但是對于三目運(yùn)算符來說,不用這么麻煩,只需要這么一句就好了具體意思就是,判斷a是否大于5,若是,則將b賦值為3,若不是,則將b賦值為-3
a > 5 ? b = 3 : b = -3;
但是其還有更簡便的寫法,也是一樣去判斷,最后把得出來的值給到左邊的b即可
b = (a > 5 ? 3 : -3);
9、逗號表達(dá)式
我們先來說一下其運(yùn)算規(guī)則:從左向右依次計(jì)算,整個(gè)表達(dá)式的結(jié)果是最后一個(gè)表示式的結(jié)果
列舉了一個(gè)逗號表達(dá)式,大家可以去自己試著計(jì)算一下,每過一個(gè)表達(dá)式參與運(yùn)算的變量都會改變,最后看打印出的a,b,c的值,也是發(fā)生了變化
int a = 3;
int b = 5;
int c = 0;
int d = (a += 2, b = b - c + a, c = a + b);
//a = 5 b = 10 c = 5 + 10 = 15
printf("d = %d\n", d);
printf("%d %d %d\n", a, b, c);
10、其他
【下標(biāo)引用操作符】
什么是下標(biāo)引用操作符呢?也就是這個(gè)[ ],我們在定義數(shù)組的時(shí)候指定的數(shù)組大小
int arr[10] = { 0 };
arr[4] = 5;
printf("%d\n", arr[4]);
對于上面這段代碼,我們稱arr4 是 [ ]的兩個(gè)操作符我們回憶一下【+】運(yùn)算符,這是一個(gè)雙目運(yùn)算符,比如說2 + 3,那么就可以稱2 3 是 +的兩個(gè)操作符,對于我們來說2 + 3可以寫成3 + 2,既然雙目運(yùn)算符可以這么交換著來做,那【下標(biāo)引用操作符】可以嗎,答案是可以的?。。∩厦孢@段代碼,我們還可以寫成這樣
int arr[10] = { 0 };
4[arr] = 5;
printf("%d\n", 4[arr]);
接下來的話是這個(gè)叫【函數(shù)調(diào)用】的操作符
int Add(int x, int y)
{
return (x + y);
}
int main()
{
int c = Add(2, 3); //()是函數(shù)調(diào)用操作符,操作數(shù)是:Add 2 3
printf("c = %d\n", c);
return 0;
}
很明確,就是函數(shù)外面的兩個(gè)小括號,這個(gè)我們在上面說到sizeof()操作符時(shí)也提到過,對于sizeof(),雖然其有(),但是不可以把它認(rèn)為是一個(gè)函數(shù),它也會是一個(gè)操作符然后對于函數(shù)調(diào)用操作符的話,看到上面的Add函數(shù),()是操作符,那么操作數(shù)就是Add 2 3
十二、常見關(guān)鍵字
1、前言
了解了C語言中的常見運(yùn)算符,接下來我們來看看C語言中的關(guān)鍵字
首先對于關(guān)鍵字,我們要注意的兩點(diǎn)是 1、關(guān)鍵字是直接使用的,我們得了解 2、變量名不能是關(guān)鍵字 從下面這些可以看出,在C語言中,關(guān)鍵字還是蠻多的
首先單獨(dú)說一下【auto】,它比較特殊,因?yàn)樵诰幾g器中,當(dāng)你定義一個(gè)變量的時(shí)候默認(rèn)就是存在的auto,翻譯過來就是自動的,在C語言里指得是自動變量,所有你定義的局部變量都是自動創(chuàng)建、自動銷毀的,所以局部變量都是auto修飾的就像下面這個(gè)整型變量a,你在int的前面加上或是不加auto 都是不會報(bào)錯(cuò)的, 原因就是所有局部變量都是auto
//auto int a = 10;
int a = 10; //auto可以省略
2、有關(guān)數(shù)據(jù)存儲的底層原理
有關(guān)寄存器這個(gè)東西,涉及到了數(shù)據(jù)存儲,我們來詳細(xì)說一下,讓大家先了解一下這個(gè)底層原理
首先你要知道在計(jì)算機(jī)中的數(shù)據(jù)是存放在哪里的
①內(nèi)存 ②硬盤 ③高速緩存 ④寄存器
然后我們再通過這張圖來了解一下
對于計(jì)算機(jī)中的寄存器,其實(shí)它所空間是很小的,單位只有字節(jié),但是它的讀寫速度非??欤梢灾苯优cCPU【中央處理器】進(jìn)行交互 然后越往下這個(gè)空間越大,內(nèi)存的話現(xiàn)在普遍都是8G,16G這樣,大一點(diǎn)有32G,不會像很早之前的只有2MB這樣;對于硬盤的話,我們?nèi)ナ袌錾腺I也是500G,1個(gè)T這樣的大小 我們的寄存器,因?yàn)樗淖x取速度很快,因此CPU直接到寄存器里面拿數(shù)據(jù),但這個(gè)時(shí)候寄存器內(nèi)存不夠大了怎么辦呢?裝不過這么多,這個(gè)時(shí)候我們所知道的高速緩存,也就是Cache,會給寄存器提供內(nèi)存,那高速緩存里又不夠了,這個(gè)時(shí)候就繼續(xù)讓內(nèi)存給它提供。這樣的話整體地提高了計(jì)算機(jī)的運(yùn)行效率 下面就是【register】這個(gè)關(guān)鍵字的用法,在定義這個(gè)變量b的時(shí)候加上了這個(gè)關(guān)鍵字,就是【建議】編譯器把這個(gè)變量放到寄存器中,這里要注意,只是建議,而不是直接放入 具體再如何使用大家可以去查閱一些資料,這里不做過多詳解
//建議把b放到寄存器中
register int b = 10;
3、typedef關(guān)鍵字
首先就是這個(gè)【typedef】,這個(gè)關(guān)鍵字的話是用來重命名的,用在結(jié)構(gòu)體上會比較多
typedef struct Linknode{
int data[MaxSize];
struct Linknode* next;
}LNode;
當(dāng)然它不止應(yīng)用在結(jié)構(gòu)體上,對于一些數(shù)據(jù)類型也是可以重命名的,例如下面這個(gè)【unsigned int】,后面你在使用【uint】定義變量的時(shí)候就和【unsigned int】一樣
typedef unsigned int uint;
int main()
{
unsigned int num1 = 0;
uint num2 = 0; //與num1一樣
return 0;
}
4、static關(guān)鍵字
最后再來說一下這個(gè)static關(guān)鍵字
這里是來說說C語言中的static關(guān)鍵字,首先你要了解static關(guān)鍵字可以用來修飾什么,它主要是可以用來修飾下面三個(gè)
修飾局部變量修飾全局變量修飾函數(shù)
4.1、static關(guān)鍵字修飾局部變量
首先我們來看看static對于局部變量的修飾
void test()
{
int a = 3;
a++;
printf("%d ", a);
}
int main()
{
int i = 0;
while (i < 10)
{
test();
i++;
}
return 0;
}
你認(rèn)為上面這段程序會輸出什么??吹街鞒绦?,使用while循環(huán)來控制i變量,當(dāng)i = 10的時(shí)候邊不會進(jìn)入循環(huán),所以是會循環(huán)10次,然后看內(nèi)部的test()函數(shù),每次循環(huán)調(diào)用這個(gè)函數(shù)的時(shí)候都會定義一個(gè)變量a,然后++之后變?yōu)?,然后輸出所以這段程序的運(yùn)行結(jié)果是會輸出10個(gè)4
然后我們修改一下這個(gè)定義的變量a,將其設(shè)置為靜態(tài)變量,也就是在int前面加上一個(gè)static修飾那這個(gè)時(shí)候你認(rèn)為上面那段程序會打印出什么呢?
static int a = 3;
首先你要了解靜態(tài)變量的特性以及其余普通變量之前的區(qū)別
①普通的局部變量是放在內(nèi)存的棧區(qū)上的,進(jìn)入局部范圍,變量創(chuàng)建,出了局部范圍變量銷毀 ②當(dāng)static修飾局部變量時(shí),局部變量是在靜態(tài)區(qū)開辟空間的,這時(shí)的局部變量,出了作用域變量不銷毀,下次進(jìn)去作用域,使用的是上一次遺留的數(shù)據(jù)(改變了存儲位置,由棧區(qū)–>靜態(tài)區(qū),使得變量的生命周期發(fā)生了變化)
知道了這些,你應(yīng)該清楚這個(gè)打印結(jié)果是多少了,4~13,每一次進(jìn)入這個(gè)test()函數(shù)時(shí),這個(gè)變量a將不再被執(zhí)行,也就是只會在第一次進(jìn)入這個(gè)函數(shù)的時(shí)候定義,之后就會一直存在,知道這個(gè)程序結(jié)束時(shí)它才會被銷毀,所以這個(gè)變量a每次保留的便是上一次++后的結(jié)果
看到了上面的結(jié)果,對于靜態(tài)變量修飾成員相信你也有了一個(gè)初步的了解,對于普通的局部變量,是存儲在棧區(qū)上的。但是對于靜態(tài)變量,你知道它是存儲在什么地方的嗎?沒錯(cuò),就靜態(tài)區(qū),我們通過下面這張圖再來了解一下在計(jì)算機(jī)內(nèi)存中變量到底是如何存儲的
4.2、static關(guān)鍵字修飾全局變量
add.c:
int g_val = 2022;
test.c:
extern int g_val; //聲明外部符號
int main()
{
printf("%d\n", g_val);
return 0;
}
以上的這種全局變量聲明,以及【extern】關(guān)鍵字調(diào)用,是聲明一個(gè)變量,但是若這個(gè)g_val變量被定義成了靜態(tài)變量,會怎么樣呢?我們來看看
可以看到,這個(gè)外部命令無法被解析 全局變量是具有外部鏈接屬性的,如果全局變量被static修飾,外部鏈接屬性就變成了內(nèi)部鏈接屬性,其他源文件就沒法再通過鏈接找到這個(gè)符號 所以可以得出結(jié)論,static修飾后的局部變量只能在自己所在的.c文件內(nèi)部使用~
4.3、static關(guān)鍵字修飾函數(shù)
其實(shí)這個(gè)理念也是一樣的,若你不使用static修飾,那你可以用extern關(guān)鍵字做一個(gè)引入,那它就是一個(gè)外部鏈接,但若是你使用static修飾,那么這個(gè)函數(shù)就只能本源文件使用,不可以給到外部的文件使用,這就是一個(gè)內(nèi)部鏈接了
可以看到,也是同理,這是一個(gè)內(nèi)部鏈接,外部是訪問不到的,即使是有這個(gè)extern關(guān)鍵字
十三、 #define 定義常量和宏
首先來說說這個(gè)#define去定義常量
#define MAX 100
int main()
{
printf("%d\n", MAX);
int a = MAX;
int arr[MAX] = { 0 };
printf("%d\n", a);
return 0;
}
看到如上代碼,我使用#define定義了一個(gè)MAX常量,并且其值為100,在main函數(shù)中,你就可以直接使用這個(gè)常量,對它進(jìn)行打印、賦值 當(dāng)然除了定義整型數(shù)據(jù)常量,其他類型也是可以的,例如字符串也可以
#define STR "abcdef"
printf("%s\n", STR);
講完使用#define去定義常量,我們再來說說宏定義,它也是利用#define去聲明的
下面有一個(gè)求和的函數(shù)以及宏定義求和的寫法
//函數(shù)
int Add(int x, int y)
{
return x + y;
}
//宏
#define ADD(x,y) ((x) + (y)) //為了保持整體性
除了求和的功能外,其實(shí)你還可以定義其他功能,例如說比較兩個(gè)數(shù)的較大值,就可以像下面這么去寫,使用一個(gè)三目運(yùn)算符即可
#define MAX(x,y) ((x) > (y) ? (x) : (y))
十四、 指針
1、引言
對于指針這一塊,很多同學(xué)在剛開始學(xué)習(xí)C語言的時(shí)候就聽別人說起過,說指針很難很難,訪問內(nèi)存、取地址這些操作,既危險(xiǎn)又難搞
2、內(nèi)存的概念和介紹
對于指針這一塊的話,是直接和計(jì)算機(jī)中的內(nèi)存發(fā)生關(guān)系的,所以我們先來講講內(nèi)存相關(guān)的知識,帶大家先行了解一下底層的知識 對于內(nèi)存,大家在日常生活中應(yīng)該也有聽到過,例如我們?yōu)殡娔X、筆記本買的內(nèi)存條,以及我們手機(jī)的內(nèi)存,對于這個(gè)內(nèi)存來說,一般都是8G或是16個(gè)G,與內(nèi)存相對應(yīng)的,那就是硬盤,一般的話都是500G或是1個(gè)T這樣 在計(jì)算機(jī)中,內(nèi)存是一塊連續(xù)的存儲空間,但是這樣就無法分配到每一個(gè)部分進(jìn)行使用,這個(gè)時(shí)候呢就將內(nèi)存分成了一塊塊小的內(nèi)存單元,那為了能夠有效的訪問到內(nèi)存的每個(gè)單元,就給內(nèi)存單元進(jìn)行了編號,這些編號就被稱為該內(nèi)存單元的地址
從上面這張圖示,就可以很直觀地看出內(nèi)存在計(jì)算機(jī)中到底是如何存儲的,每一個(gè)內(nèi)存單元的大小都是一個(gè)字節(jié),然后為它們都進(jìn)行了編號,這樣在外界需要訪問時(shí),就可以根據(jù)這個(gè)內(nèi)存編號去指定進(jìn)行一個(gè)訪問,這個(gè)一個(gè)個(gè)編號其實(shí)就被稱為是地址
知曉了地址的基本概念后,我們再來到編譯器中去看看這個(gè)地址究竟是怎樣分部的
首先我們來看最簡單的一句代碼,就是定義一個(gè)變量a,我們都知道int整型的變量在內(nèi)存中是占4個(gè)字節(jié)的,我們在這句代碼上打個(gè)斷點(diǎn)進(jìn)入內(nèi)存窗口一看究竟
int main()
{
int a = 4;
}
輸入這個(gè)【&a】,就可以看到內(nèi)存中為變量a開辟的4個(gè)內(nèi)存單元,首地址就是從【0x00CFF814】開始,整型變量占4個(gè)字節(jié),看我框起來的這4個(gè)就是,對于每一個(gè)字節(jié)它都有自己的一個(gè)編號, &a呢就是取到第一個(gè)字節(jié)的地址
那我們要怎么使用代碼去獲取這個(gè)地址呢
&a // 拿到的值第一個(gè)字節(jié)的地址
我們通過調(diào)試窗口再來看一下。很明顯,得到了我們想要的結(jié)果
或者你不想到調(diào)試窗口中去看的話,也是可以的我們直接printf打印出來即可,可以看到,這里使用的是%p,這是專門對于地址訪問的,如果你是%d,出來的就是格式化后的數(shù)字了
講完了內(nèi)存,講完了地址,接下去才是真正的指針,但其實(shí)對于地址來說,它就是指針,指針是地址的一個(gè)別名,下面就來看一下指針是如何去定義的
int a = 4;
int* pa = &a;
可以看到,在int類型后我加上了一個(gè)【*】星號,這就說明這是一個(gè)指針類型的變量,這個(gè)pa就是【指針變量】,因?yàn)樯厦嬲f過指針是地址的別名,所以這個(gè)等式是成立的,pa這個(gè)指針變量可以去接收a的地址,也存放了a的地址
既然這個(gè)指針變量存放了變量的地址,那么可不可以通過這個(gè)指針變量去訪問到這個(gè)地址并且把它打印出來呢?答案是可以的,這就是涉及到我們的下一個(gè)知識點(diǎn),就是指針的解引用 利用【*】這個(gè)操作符
*pa
上面定義了,pa就是一個(gè)指針變量,*pa其實(shí)就是通過pa中存放的地址,找到這個(gè)地址所存放的空間,這個(gè)時(shí)候取到的其實(shí)就是變量a,因?yàn)槿〉搅诉@個(gè)地址,這塊空間上所存放的起始就是a變量的內(nèi)容,我們通過運(yùn)行來看一下
小結(jié)
①指針其實(shí)就是地址,地址就是內(nèi)存單元的編號 ②把地址進(jìn)行存儲的時(shí)候,就可以放到一個(gè)變量中,這個(gè)變量就是【指針變量】 ③我們說的指針,實(shí)際上在專業(yè)術(shù)語中叫做【指針變量】 ④【*】星號解引用可以通過存放變量的地址快速訪問到那個(gè)變量在內(nèi)存中所存放的位置,繼而獲取內(nèi)容
3、指針變量的大小
求一個(gè)變量的大小就是用sizeof()這個(gè)關(guān)鍵字
int a = 10;
printf("%d\n",sizeof(a));
printf("%d\n",sizeof(int));
上面這段代碼的打印就是4 4,那請問下面這段呢?
int a = 10;
int* pa = &a;
printf("%d\n",sizeof(pa));
printf("%d\n",sizeof(int*));
很明顯,也是4 4
在編譯器的偏左上角,有一個(gè)x64和x86,這個(gè)東西叫做【運(yùn)行環(huán)境】,x64代表你在64位OS的環(huán)境下運(yùn)行代碼,x86代表的就是32位,剛才我選擇的是32位,現(xiàn)在我把它改成64位,
很明顯,一樣的代碼,但是在不同的運(yùn)行環(huán)境下所產(chǎn)生的值卻不同,
指針變量存放的就是地址,所以指針變量的大小取決于存儲一個(gè)地址需要多大的空間
要看到不是地址,其實(shí)是存儲的地址線,沒錯(cuò),就是硬件上的地址線
我們所用的電腦,其實(shí)就是硬件,是硬件的話就需要通電,那在我們的電腦上其實(shí)就存在著這么一種【地址線】,我們上面所說的32位與64位,也可以對應(yīng)到這個(gè)地址線中,因?yàn)樵?2位的環(huán)境下,是32根地址線;64位環(huán)境下就是64根地址線當(dāng)我們是32根地址線時(shí),在通電之后就會有一個(gè)電信號,這個(gè)電信號就是0/1,那這些電信號具體是怎樣的呢,我們來看一下
就是0101這樣的存儲方式,然后根據(jù)二進(jìn)制的逢二進(jìn)一去羅列出這32根地址線可以存儲下多少地址,這里告訴你,一共是有232個(gè)地址可以存儲那這其實(shí)就可以得出結(jié)論了,32個(gè)0或者1組成得的地址序列,需要32個(gè)bit,也就是4個(gè)byte去存放,而這4個(gè)字節(jié)也就對應(yīng)著我們指針變量的大小,因此就可以得出為什么指針變量的大小是4個(gè)字節(jié)了然后來解釋一下為什么在64位環(huán)境下這個(gè)指針變量就變成了8個(gè)字節(jié),這其實(shí)你自己也可以去推導(dǎo),32個(gè)0或1組成的地址序列需要32個(gè)比特位,那么這里便需要64個(gè)比特位,根據(jù)1B = 8bit,所以就需要8個(gè)byte去存放,這也可以得出在64位環(huán)境下指針變量的大小是8個(gè)字節(jié)
了解了上面這些,知道了指針變量的大小取決于地址的大小,下面我們來看看這些指針變量的大小是多少,我是在32位環(huán)境下運(yùn)行的
printf("%d\n", sizeof(short*));
printf("%d\n", sizeof(long*));
printf("%d\n", sizeof(long long*));
printf("%d\n", sizeof(float*));
printf("%d\n", sizeof(double*));
是1 4 8 4 8 嗎,如果是這個(gè)答案的話請你再回去仔細(xì)看一下上面的推導(dǎo)過程我們來看一下運(yùn)行結(jié)果
可以看到,均為4,它們都是指針變量,指針變量求它的大小看的是什么,看到就是地址的大小,上面說了,我是在32位環(huán)境下運(yùn)行的,因此就是32根地址線,需要32個(gè)bit,也就是4個(gè)byte去存放 可以看到,報(bào)了很多Warning,這是為什么呢,明明這個(gè)代碼就是可以運(yùn)行的,而且還可以出結(jié)果這里報(bào)了一個(gè)【符號不匹配】的問題,為什么呢?這里明確說一下,sizeof()計(jì)算數(shù)據(jù)字節(jié)大小的時(shí)候默認(rèn)返回的類型是unsigned int,也就是無符號整型,但%d是用來打印整型變量的,所以這里才會出現(xiàn)【符號不匹配】的問題
應(yīng)該將其修改問%zu去打印才對,你只要記住它是專門用來打印sizeof()的返回值的就行了,不行深入了解也沒關(guān)系修改如下,可以看到,已經(jīng)一個(gè)Warning都沒有了
十五、結(jié)構(gòu)體
對于結(jié)構(gòu)體,也是C語言中比較重要的一個(gè)部分,因?yàn)镃語言是一門面向過程的語言,它不像C++、Java那樣面向?qū)ο?,具有類,可以在類?nèi)定義成員變量和成員方法,所以C中就有了結(jié)構(gòu)體這一個(gè)東西,可以用來描述復(fù)雜對象
我們都去書店買過書,知道書它有書名、出版社、作者,這些都可以定義為字符類型,但是還有書的價(jià)格,怎么定義呢,難道也定義成字符類型嗎,當(dāng)然不是,依舊是定義成浮點(diǎn)類型,除了這些,其實(shí)還有很多種類需要去定義但是對于這么多的類型,都要分開嗎,這肯定不行,這樣這本書就不是一個(gè)整體了,如果你有面向?qū)ο蟮乃季S就知道,它的屬性和行為都是定義在這一個(gè)類中,都是封裝好的,這就是類的封裝在C語言中,我們也可以實(shí)現(xiàn)封裝,那就是用結(jié)構(gòu)體
我們以學(xué)生類型來做一個(gè)封裝
可以看到,我們使用到了struct這個(gè)關(guān)鍵字,這個(gè)就是在關(guān)鍵字那一模塊所屬要留在這里講解的關(guān)鍵字,stu就是student的簡寫。可以看到,里面有著三種類型,分別是姓名、年齡和成績,因?yàn)橐粋€(gè)學(xué)生都具備這三種屬性,這是他們共同的屬性,所以可以將他們封裝在一起
struct stu {
char name[20]; //姓名
int age; //年齡
float score; //成績
};
那對于結(jié)構(gòu)體這種復(fù)合類型怎么去定義變量進(jìn)行初始化呢,我們來看看其實(shí)和普通變量也是一樣的,你要把【struct stu】看做是一個(gè)類型,用這個(gè)類型定義出來的變量就叫做【結(jié)構(gòu)體變量】,對于每個(gè)結(jié)構(gòu)體變量的初始化,都需要使用一對大括號{},里面去一一按照順序去初始化你在結(jié)構(gòu)體中定義的變量溫馨提示:這里的float成績類型后面加上f與double類型作為區(qū)分
struct stu s1 = { "zhangsan",20,80.5f };
struct stu s2 = { "lisi",25,90.5f };
那初始化了這些變量后如何將他們打印出來呢,也就訪問到每一個(gè)同學(xué)的信息
這里就要使用到【.】點(diǎn)操作符,這個(gè)也是我們前面留下的,這個(gè)操作符可以去通過結(jié)構(gòu)體聲明出來的變量訪問當(dāng)前這個(gè)變量所具有的信息,代碼如下:
//格式:結(jié)構(gòu)體變量.結(jié)構(gòu)體成員
printf("%s %d %f\n", s1.name, s1.age, s1.score);
printf("%s %d %f\n", s2.name, s2.age, s2.score);
好,初識C語言就到這里結(jié)束了!
柚子快報(bào)激活碼778899分享:1. C語言之初識C語言
參考閱讀
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場。
轉(zhuǎn)載請注明,如有侵權(quán),聯(lián)系刪除。