C語(yǔ)言的概述
? ?TIOBE 編程社區(qū)指數(shù)(The TIOBE Programming Community index)是編程語(yǔ)言流行度的指標(biāo),該榜單每月更新一次,指數(shù)基于全球技術(shù)工程師、課程和第三方供應(yīng)商的數(shù)量。包括流行的搜索引擎喂分,如谷歌、必應(yīng)机蔗、維基百科蒲祈、亞馬遜 和百度都用于指數(shù)計(jì)算甘萧。
? ? TIOBE 指數(shù)并不代表語(yǔ)言的好壞,開發(fā)者可以使用該榜單檢查自身的編程技能是否需要更新梆掸,或者在開始構(gòu)建新軟件時(shí)對(duì)某一語(yǔ)言做出選擇的一個(gè)重要參考扬卷。
C語(yǔ)言被稱呼為:“C生萬物,編程之本”
與C語(yǔ)言相關(guān)的語(yǔ)言很多酸钦。
其中最早的一門語(yǔ)言叫 Algol 60怪得,是 1960 年產(chǎn)生的,它是真正的第一門面向問題的語(yǔ)言卑硫。
1963 年劍橋大學(xué)在 Algol 60 的基礎(chǔ)上研發(fā)出了 CPL徒恋。
1967 年劍橋大學(xué)的Martin Richards對(duì) CPL 進(jìn)行了簡(jiǎn)化,產(chǎn)生了 BCPL欢伏。
1970 年入挣,美國(guó) AT&T公司【美國(guó)電話電報(bào)公司(American Telephone and Telegraph Company)】所屬貝爾實(shí)驗(yàn)室(AT&T Bell Laboratory)的研究員Ken Thompson以 BCPL 為基礎(chǔ),設(shè)計(jì)出了很簡(jiǎn)單而且很接近硬件的B語(yǔ)言(取 BCPL 的首字母)硝拧。
1971 年径筏,貝爾實(shí)驗(yàn)室的Dennis Ritchie加入了Ken Thompson的開發(fā)項(xiàng)目,合作開發(fā) UNIX河爹。
UNIX 系統(tǒng)是世界上第一個(gè)真正的操作系統(tǒng)匠璧。
1972 年桐款,Dennis Ritchie在B語(yǔ)言的基礎(chǔ)上最終設(shè)計(jì)出了一種新的語(yǔ)言咸这,C語(yǔ)言。
1973 年年初魔眨,C語(yǔ)言的主體完成媳维。Ken Thompson和Dennis Ritchie開始用C語(yǔ)言完全重寫 UNIX,這就是 UNIX 第 5 版遏暴。
由于 UNIX 操作系統(tǒng)是用C語(yǔ)言編寫的侄刽,而這個(gè)系統(tǒng)很流行,于是C語(yǔ)言也跟著流行起來朋凉。
隨后又出現(xiàn)了 C++州丹。
?? C++ 是Bjarne Stroustrup編寫的,? 他也來自貝爾實(shí)驗(yàn)室杂彭,是C語(yǔ)言創(chuàng)始人Dennis Ritchie的下屬墓毒。
后來 Sun 公司又對(duì) C++ 進(jìn)行改寫,產(chǎn)生了 Java亲怠。
而微軟公司發(fā)現(xiàn) Java 很流行所计,就造出了一個(gè)類似的語(yǔ)言——C#
要學(xué)習(xí) C++、Java 或者 C# 的話团秽,那么C語(yǔ)言就最好要學(xué)一下主胧!
Win上搭建C語(yǔ)言的學(xué)習(xí)環(huán)境
MinGW-W64下載網(wǎng)頁(yè):https://www.mingw-w64.org/
下載的兩個(gè)選擇叭首,壓縮版和在線exe安裝版
下面對(duì)幾個(gè)選項(xiàng)給出說明
x86_64:表示MinGW-W64要在64為的操作系統(tǒng)上運(yùn)行
i686:表示MinGW-W64要在32位的操作系統(tǒng)上運(yùn)行
posix:表示MinGW-W64在非windows系統(tǒng)上運(yùn)行
win32:表示MinGW-W64在windows系統(tǒng)上運(yùn)行
sjlj:表示MinGW-W64編譯生成的程序可以在32位和64位系統(tǒng)上運(yùn)行
dwarf:表示MinGW-W64編譯生成的程序只能在32位系統(tǒng)上運(yùn)行
seh:表示MinGW-W64編譯生成的程序只能在64位系統(tǒng)上運(yùn)行
下載壓縮包的話,選擇合適位置解壓踪栋,將mingw64/bin加入環(huán)境變量即可
MinGW系列只提供了名字為 mingw32-make.exe 的執(zhí)行文件焙格,
事實(shí)上,該.exe 和make.exe 功能一樣夷都,為了make執(zhí)行時(shí)能找到該文件间螟,
建議復(fù)制 mingw32-make.exe 一份,并將復(fù)制文件命名為'make.exe'损肛,先做在這里厢破,以后用的時(shí)候用。
驗(yàn)證是否成功配置
記事本上寫第一個(gè)C語(yǔ)言的程序:hello.c
/*
多行
注解:給人看的治拿,計(jì)算機(jī)執(zhí)行程序的時(shí)候摩泪,看不見
*/
//單行注解,這里寫的程序是第一個(gè)C程序劫谅,主要目的见坑,感受一下C語(yǔ)言程序怎么寫
//通過這個(gè)程序,了解GCC編譯器內(nèi)部通過哪些細(xì)節(jié)步驟捏检,把C源程序編程操作系統(tǒng)可以執(zhí)行的二進(jìn)制程序的
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
printf("hello world!");
exit(0);
}
???? 用編譯器gcc荞驴,從一個(gè)C語(yǔ)言的源文件到執(zhí)行,要經(jīng)歷的過程:
1贯城、C源文件
2熊楼、預(yù)處理 gcc -E hello.c > hello.i , 凡是'#'開頭的內(nèi)容都是在預(yù)處理階段進(jìn)行處理,hello.i就是預(yù)處理后的結(jié)果文件能犯。
3鲫骗、編譯 gcc -S hello.i , 默認(rèn)會(huì)生成編譯的結(jié)果文件hello.s , 做的事情其實(shí)就是生成匯編語(yǔ)言。
4踩晶、匯編 gcc -c hello.s , 默認(rèn)會(huì)生成匯編語(yǔ)言的匯編成目標(biāo)文件hello.o , 這個(gè)時(shí)候hello.o就已經(jīng)是一個(gè)二進(jìn)制文件执泰。
5、鏈接 gcc hello.o -o hello ,就直接生成可執(zhí)行文件了渡蜻。
6术吝、可執(zhí)行文件 hello 就可以執(zhí)行當(dāng)前目標(biāo)下的hello了。
????上面這樣做很麻煩茸苇,只是為了讓大家了解gcc編程c語(yǔ)言內(nèi)部要經(jīng)歷的過程排苍!
直接gcc hello.c 或者 gcc hello.c -o hello 或者 gcc -g hello.c -o hello 就可以了,
gcc會(huì)自動(dòng)完成上面我們講的所有步驟税弃!
加上-g 選項(xiàng)纪岁,會(huì)保留代碼的文字信息,便于后面的調(diào)試则果。
編輯工具
下載安裝vscode:
https://visualstudio.microsoft.com/zh-hans/
設(shè)置中文:
??命令面板【Ctrl+Shift+P】--config 然后搜索出來的【Configure Display Language】
輸入Chinese幔翰,然后選擇【Chinese (Simplified)Language Pack for Visual Studio Code】漩氨,然后點(diǎn)擊右側(cè)的【Install】
安裝好中文語(yǔ)言包之后軟件會(huì)提示重啟VSCode,點(diǎn)擊【Yes】重啟VSCode軟件
安裝插件
- Code Runner:右鍵即可編譯運(yùn)行單文件遗增,很方便叫惊;但無法Dubug
-
C/C++:又名 cpptools,提供Debug和Format功能
載入寫好的hello.c文件
??打開vscode載入剛剛的c文件做修,網(wǎng)上網(wǎng)友說編譯c文件要打開文件所在的文件夾霍狰,我嘗試了下,確實(shí)是這樣的饰及。如下圖:
然后點(diǎn)運(yùn)行--啟動(dòng)調(diào)試蔗坯,他會(huì)跳出一個(gè)launch.json的一個(gè)配置文件:是要修改的
{
// 使用 IntelliSense 了解相關(guān)屬性。
// 懸停以查看現(xiàn)有屬性的描述燎含。
// 欲了解更多信息宾濒,請(qǐng)?jiān)L問: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "C/C++ Runner: Debug Session",
"type": "cppdbg",
"request": "launch",
"args": [],
"stopAtEntry": false,
"externalConsole": true,
"cwd": "c:/Users/Administrator/Desktop/筆記/AnLi",
"program": "c:/Users/Administrator/Desktop/筆記/AnLi/build/Debug/outDebug",
"MIMode": "gdb",
"miDebuggerPath": "gdb",
"setupCommands": [
{
"description": "Enable pretty-printing for gdb",
"text": "-enable-pretty-printing",
"ignoreFailures": true
}
]
}
]
}
//修改成,gdb調(diào)試
{
// 使用 IntelliSense 了解相關(guān)屬性屏箍。
// 懸停以查看現(xiàn)有屬性的描述绘梦。
// 欲了解更多信息,請(qǐng)?jiān)L問: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "(gdb) 啟動(dòng)",
"type": "cppdbg",
"request": "launch",
"program": "${workspaceRoot}/${fileBasenameNoExtension}.exe",
"args": [],
"stopAtEntry":false,
"cwd": "${workspaceRoot}",
"environment":[],
"externalConsole":false,
"MIMode":"gdb",
"miDebuggerPath":"c:\\mingw64\\bin\\gdb.exe",
"preLaunchTask":"gcc",
"setupCommands":[
{
"description":"為gdb啟用整齊打印",
"text":"-enable-pretty-printing",
"ignoreFailures":true
}
]
}
]
}
program赴魁,指明了需要運(yùn)行的文件卸奉,${}的格式是表明這是變量。
miDebuggerPath颖御,是我們安裝gdb的路徑榄棵,gdb工具是用來調(diào)試的二進(jìn)制文件(可執(zhí)行二進(jìn)制文件也叫命令)。
preLauchTask郎嫁,是一個(gè)重點(diǎn)秉继,我們這個(gè)配置的意思是用gdb去調(diào)試 program參數(shù)指定的二進(jìn)制文件,
??但如果沒有這個(gè)文件怎么辦泽铛,我們可以運(yùn)行g(shù)cc編譯工具生成二進(jìn)制*.exe文件,preLaunchTask就是干這個(gè)的辑鲤!
tasks.json,下面是一個(gè)任務(wù)時(shí)的配置盔腔,多個(gè)任務(wù)用[]括起來
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"command":"gcc",
"args": ["-g","${file}","-o","${fileBasenameNoExtension}.exe"]
//gcc -g hello.c -o hello.exe
}
用CMD
命令面板(Ctrl+Shift+P)中,輸入select選擇第一條:
之后選擇目標(biāo)Shell
Linux+Win10上搭建C語(yǔ)言的學(xué)習(xí)環(huán)境**
1)在VMware上安裝Ubuntu20.04操作系統(tǒng)
下載
????可以到百度搜索ubuntu 再進(jìn)入官網(wǎng)或https://mirrors.tuna.tsinghua.edu.cn/ubuntu-releases/
,找到一個(gè)合適的下載月褥,最新版本是22.x版弛随,安裝時(shí)選桌面安裝,這樣好調(diào)試c語(yǔ)言開發(fā)宁赤。
????安裝ubuntu20.04時(shí)舀透,最好讓虛擬機(jī)斷網(wǎng)安裝,這樣就可以節(jié)省下Language pack download的時(shí)間决左,本地網(wǎng)速不太理想的情況下愕够,download會(huì)是一個(gè)相當(dāng)漫長(zhǎng)的過程走贪。
設(shè)置root密碼,安裝vm-tools設(shè)置CDROM為自動(dòng)檢測(cè)惑芭,換源
sudo passwd root //設(shè)置root用戶密碼坠狡,先輸入當(dāng)前用戶密碼,再輸兩次新密碼---我一般設(shè)密碼為:xiong
su - root //進(jìn)入root用戶
下圖是安裝vm-tools遂跟,安裝它的好處是逃沿,可以復(fù)制貼貼到虛擬機(jī)以外的系統(tǒng)中
SecureCRT連接Ubuntu
??首先:查詢ip地址ifconfig,如果沒有此命令,則要安裝net-tools:sudo apt install net-tools
下面是換源幻锁,先備份本地的源配置
//下面是換源凯亮,先備份本地的源配置
cp -v /etc/apt/sources.list /etc/apt/sources.list.backup
chmod 777 /etc/apt/sources.list
vi /etc/apt/sources.list
//阿里云的源:按DD,刪除所有sources.list中的內(nèi)容,復(fù)制以下內(nèi)容放到sources.list中
deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
//選按esc,再按:wq,修改后:
apt update
apt upgrade //升級(jí)
安裝C語(yǔ)言編譯器,首先進(jìn)和root用戶中
sudo - root
apt-get install gcc
2)建立Windows和Ubuntu20.04的共享文件夾或者搭建samba服務(wù)
一哄尔,設(shè)置兩系統(tǒng)的共享文件夾(我是桌面的workspacec).
進(jìn)入共享文件夾触幼,編譯執(zhí)行一個(gè)c文件測(cè)試一下效果
# vmware-hgfsclient //查看當(dāng)前共享目錄
workspaceC
#cd /mnt
#mkdir hfgs
#vmhgfs-fuse .host:/ /mnt/hgs -o nonempty -o allow_other
//設(shè)置自動(dòng)掛載
#vim /etc/fstab
.host:/ /mnt/hgfs fuse.vmhgfs-fuse allow_other,defaults 0 0
#mount -a //讓配置生效
//進(jìn)入共享文件夾,測(cè)試一下
#cd/hgfs
#cd workspaceC
#gcc -g hello.c -o myhello //編譯hello.c究飞,這里的可執(zhí)行文件不是.exe置谦,而沒有擴(kuò)展名的
#ls
hello.c hello.exe myhello
#./myhello //執(zhí)和可執(zhí)行文件
Hello world! //顯示效果,這里因?yàn)閜rintf()中沒有加\n換行符亿傅,所以與文件目錄顯示在一行媒峡。
Centos7.x中進(jìn)入共享文件夾,比unbuntu要麻煩一些葵擎,所以很多人用ubuntu來做共享測(cè)試
# vmware-hgfsclient //查看共享文件夾情況
workspaceC
//在 /mnt 下創(chuàng)建一個(gè)目錄谅阿,用于掛載共享文件夾,創(chuàng)建hgfs目錄
#mkdir /mnt/hgfs
//查看目錄的創(chuàng)建情況
#ls -l /mnt
# mount -t vmhgfs .host:/workspaceC /mnt/workspaceC //持載共享目錄
//如果內(nèi)核版本是4.0及以后可以執(zhí)行:掛載,我用的是以下這個(gè)命令
# vmhgfs-fuse .host:/ /mnt/hgfs -o subtype=vmhgfs-fuse,allow_other
//自動(dòng)掛載共享目錄 對(duì)于 kernel 4.0 之前的版本:
vim /etc/fstab
.host:/mywinshare /mnt/mylinuxshare vmhgfs defaults 0 0
//貌似舊版內(nèi)核下共享文件夾會(huì)自動(dòng)mount到 /mnt/hgfs 目錄下酬滤,所以也不需要增加 fstab 項(xiàng)签餐。對(duì)于 kernel 4.0 及之后的版本:
.host:/ /mnt/hgfs fuse.vmhgfs-fuse allow_other,defaults 0 0
#mount -a //使自動(dòng)掛載生效
//如果出現(xiàn)if you are sure this is safe, use the 'nonempty' mount option,則
#vmhgfs-fuse .host:/ /mnt/hgfs -o nonempty -o allow_other
//如果后續(xù)不想使用共享文件盯串,可以通過如下命令卸載共享目錄
# umount /mnt/workspaceC
二氯檐,安裝samba服務(wù)器
ubuntn上安裝
sudo apt install -y samba samba-common //有的時(shí)候發(fā)現(xiàn)E安裝不上,更新一下源sudo apt-get update
安裝成功后体捏,需要修改一下samba的配置
sudo cp /etc/samba/smb.conf /etc/samba/smb_bk.conf //備份配置文件
sudo vim /etc/samba/smb.conf //真正修改之
#后面加:
[share]
comment = share
path = /home/zhonghao
browseable = yes
writable = yes
read = yes
directory mask = 0775
create mask = 0775
#修改配置后重啟服務(wù)
sudo service smbd restart
#最后給samba填加一個(gè)用戶
sudo smbpasswd -a zhonghao //該用戶是我系統(tǒng)時(shí)設(shè)置的冠摄,也可以把它做samba的用戶登陸賬號(hào)
centos 7上安裝極samba服務(wù)器
//sudo yum -y install samba
yum install samba samba-client samba-common -y
sudo systemctl start smb nmb //啟動(dòng)Samba應(yīng)用
sudo systemctl enable smb nmb //查看狀態(tài)
ps -ef | grep -E 'smb|nmb' //查看進(jìn)程
[root@Linuxidc-Server ~]# ps -ef | grep -E 'smb|nmb'
root 9885 1 0 14:48 ? 00:00:00 /usr/sbin/nmbd
root 9887 1 0 14:48 ? 00:00:00 /usr/sbin/smbd
root 9888 9887 0 14:48 ? 00:00:00 /usr/sbin/smbd
root 9889 9887 0 14:48 ? 00:00:00 /usr/sbin/smbd
root 9890 9887 0 14:48 ? 00:00:00 /usr/sbin/smbd
root 9959 9627 0 15:02 pts/0 00:00:00 grep --color=auto -E smb|nmb
//查看Samba應(yīng)用服務(wù)端口,smbd應(yīng)用進(jìn)程主要監(jiān)聽139和445端口, nmbd應(yīng)用進(jìn)程主要監(jiān)聽137與138端口几缭。
netstat -tunlp | grep -E 'smbd|nmbd'
cat /etc/samba/smb.conf //查看Samba配置文件
systemctl restart smb nmb //重啟服務(wù)
systemctl status smb.service //查看服務(wù)狀態(tài)
//創(chuàng)建服務(wù)器用戶,本人直接用root用戶了
useradd samba
smbpasswd -a samba //設(shè)置用戶密碼河泳,本人:xxx -a root
//他建共享目錄(win與linux)
su samba
mkdir share //默認(rèn)在 /home/samba/目錄中創(chuàng)建了share文件夾
chmod 777 share //為共享文件夾設(shè)置全權(quán)限
//修改配置文件
su - root
cp /etc/samba/smb.conf /etc/samba/smb.conf.back //備份samba的配置文件
vi /etc/samba/smb.conf //使用vi編輯器配置文件,在配置文件末尾添加:
[share]
path = /home/samba/share //這里填寫共享文件夾的地址
available = yes
browseable = yes
public = yes
writable = yes
win連接遠(yuǎn)程samba服務(wù)器,到目前為止年栓,云服務(wù)器沒有成功過
或按win鍵+R鍵,再輸入如下圖所示
**若點(diǎn)擊share文件夾稍途,則第一次會(huì)彈出對(duì)話框耘沼,輸入賬號(hào):zhonghao,密碼: xiong,若進(jìn)不了玖详,則有可能是防炎嗇打了息楔,centos7上關(guān)閉防火墻 systemctl stop firewalld,Ubuntu上關(guān)閉防火墻 **
關(guān)閉 【ubuntu】防火墻 : sudo ufw status 可以查看當(dāng)前 的防火墻的狀態(tài)
sudo systemctl disable ufw.service 禁用防火墻
進(jìn)制、數(shù)據(jù)類型、常量、變量、運(yùn)算符鸵熟、表達(dá)式、語(yǔ)句和程序
1负甸、進(jìn)制
??計(jì)算機(jī)底層保存和處理的數(shù)據(jù)都是二進(jìn)制數(shù)
數(shù)據(jù)在計(jì)算機(jī)中的表示流强,最終以二進(jìn)制的形式存在 , 就是各種 <黑客帝國(guó)>電影中那些 0101010… 的數(shù)字 ;
如果我們直接操作二進(jìn)制的話 , 面對(duì)這么長(zhǎng)的數(shù)進(jìn)行思考或操作,沒有人會(huì)喜歡呻待。
用16進(jìn)制或8進(jìn)制可以解決這個(gè)問題打月。因?yàn)椋M(jìn)制越大蚕捉,數(shù)的表達(dá)長(zhǎng)度也就越短奏篙。
之所以使用16或8進(jìn)制,而不其它的迫淹,諸如9或20進(jìn)制秘通,
是因?yàn)?、16敛熬,分別是2的3次方肺稀、4次方,便于和二進(jìn)制轉(zhuǎn)換 应民。
10進(jìn)制
??我們最熟悉的10進(jìn)制 , 用 0~9 的數(shù)表示 , 逢10進(jìn)1
16進(jìn)制
??如果是 16 進(jìn)制 , 它就是 由 0-9话原,A-F組成, 與10進(jìn)制的對(duì)應(yīng)關(guān)系是:0-9 對(duì)應(yīng) 0-9诲锹;A-F對(duì)應(yīng)10-15繁仁,字母不區(qū)分大小寫,逢16進(jìn)1归园。
2進(jìn)制 和 8進(jìn)制
?? 2進(jìn)制 由 0-1組成黄虱,逢2進(jìn)1。
?? 8進(jìn)制 由 0-7組成 蔓倍,逢8進(jìn)1悬钳。
二進(jìn)制與十進(jìn)制之間的轉(zhuǎn)換
十進(jìn)制轉(zhuǎn)二進(jìn)制
方法為:十進(jìn)制數(shù)除2取余法,即十進(jìn)制數(shù)除2偶翅,余數(shù)為權(quán)位上的數(shù),得到的商值繼續(xù)除2碉渡,依此步驟繼續(xù)向下運(yùn)算直到商為0為止聚谁。
(具體用法如下圖)
二進(jìn)制轉(zhuǎn)十進(jìn)制
方法為:把二進(jìn)制數(shù)按權(quán)展開、相加即得十進(jìn)制數(shù)滞诺。
(具體用法如下圖)
二進(jìn)制與十六進(jìn)制之間的轉(zhuǎn)換
二進(jìn)制轉(zhuǎn)十六進(jìn)制
方法為:十六進(jìn)制是取四合一形导。注意事項(xiàng)环疼,4位二進(jìn)制轉(zhuǎn)成十六進(jìn)制是從右到左開始轉(zhuǎn)換,不足時(shí)補(bǔ)0朵耕。
(具體用法如下圖)
注意:java中的十六進(jìn)制的數(shù)表示方法為:0x或0X的開頭炫隶,比如0x12C67
十六進(jìn)制轉(zhuǎn)二進(jìn)制
??方法為:十六進(jìn)制數(shù)通過除2取余法,得到二進(jìn)制數(shù)阎曹,對(duì)每個(gè)十六進(jìn)制為4個(gè)二進(jìn)制伪阶,不足時(shí)在最左邊補(bǔ)零。
(具體用法如下圖)
補(bǔ)碼:在計(jì)算機(jī)底層數(shù)據(jù)是使用補(bǔ)碼的形式來存放和運(yùn)算的处嫌。
一個(gè)正數(shù)的補(bǔ)碼和它的原碼的形式是相同的栅贴。
負(fù)數(shù)的補(bǔ)碼形式:除第一位為1外,其他各位取反加1
所以補(bǔ)碼的設(shè)計(jì)目的是:
⑴使符號(hào)位能與有效值部分一起參加運(yùn)算,從而簡(jiǎn)化運(yùn)算規(guī)則.
⑵使減法運(yùn)算轉(zhuǎn)換為加法運(yùn)算熏迹,進(jìn)一步簡(jiǎn)化計(jì)算機(jī)中運(yùn)算器的線路設(shè)計(jì)檐薯,所有這些轉(zhuǎn)換都是在計(jì)算機(jī)的最底層進(jìn)行運(yùn)算的時(shí)候使用,而在我們使用的匯編注暗、C坛缕、java等其他高級(jí)語(yǔ)言中使用的都是原碼。
補(bǔ)碼在計(jì)算機(jī)底層中的運(yùn)算例子:
負(fù)數(shù)的補(bǔ)碼就是除符號(hào)位外捆昏,取反加1赚楚,而正數(shù)不變,正數(shù)的原碼反碼補(bǔ)碼是一樣的.
下面是補(bǔ)碼的運(yùn)算:
( 1 )- ( 1 )= ( 1 )+ ( -1 )
=(00000001)補(bǔ)+ (11111111)補(bǔ)
= (00000000)補(bǔ)= ( 0 )正確
( 1 )- ( 2)= ( 1 )+ ( -2 )
= (00000001)補(bǔ)+ (11111110)補(bǔ)
= (11111111)補(bǔ)= ( -1 ) 正確
(-1) = (10000001)原碼=(11111110 )反碼 =((11111110 )+ 1)補(bǔ)碼
C語(yǔ)言中數(shù)據(jù)類型
??在這里屡立,我們主要任務(wù)是了解基本類型直晨,后面的構(gòu)造類型、指針有專門的章節(jié)具體講解:
short膨俐、int勇皇、long、char焚刺、float敛摘、double ...關(guān)鍵字代表C 語(yǔ)言里的基本數(shù)據(jù)類型,數(shù)據(jù)類型可以看作是“模子”
????什么是“模子”? 舉個(gè)例子:
大家應(yīng)該見過蜂窩煤:
做蜂窩煤的這個(gè)東西叫藕煤器乳愉,拿著它在和好的煤堆里這么一咔兄淫,一個(gè)蜂窩煤出來了。
一般的半徑12cm蔓姚,12 個(gè)孔捕虽。不同型號(hào)的藕煤器咔出來的蜂窩煤大小不一樣,孔數(shù)也不一樣坡脐。
這個(gè)藕煤器其實(shí)就是個(gè)模子泄私。
????現(xiàn)在我們聯(lián)想一下,short、int晌端、long捅暴、char、float咧纠、double 這六個(gè)東東其實(shí)就很像不同類型的藕煤器蓬痒!
拿著它們?cè)趦?nèi)存上咔咔咔,咔出來的不是蜂窩煤漆羔,而是不同大小的內(nèi)存空間梧奢,
這些內(nèi)存空間如果需要反復(fù)的在程序中獲取來用,可以給它關(guān)聯(lián)一個(gè)名字钧椰。
這個(gè)名字就是我們后面要介紹的常量或變量的名字粹断。
????內(nèi)存空間或者磁盤空間里,都是存放數(shù)據(jù)用的嫡霞,描述在這些設(shè)備里的數(shù)據(jù)的存儲(chǔ)單位:
位bit:是計(jì)算機(jī)中最小的儲(chǔ)存單位
字節(jié)byte:一個(gè)byte是由8個(gè)bit組成瓶埋,它是最小的可尋址單元,B也表示字節(jié)诊沪,它是byte的簡(jiǎn)寫
1KB (Kilobyte 千字節(jié))=1024B养筒;
1MB (Megabyte 兆字節(jié) 簡(jiǎn)稱“兆”)=1024KB;
1GB (Gigabyte 吉字節(jié) 又稱“千兆”)=1024MB端姚;
1TB (Trillionbyte 萬億字節(jié) 太字節(jié))=1024GB晕粪;
1PB(Petabyte 千萬億字節(jié) 拍字節(jié))=1024TB;
1EB(Exabyte渐裸,百億億字節(jié)巫湘,艾字節(jié))=1024PB;
1ZB(Zettabyte昏鹃,十萬億億字節(jié)尚氛,澤字節(jié))= 1024EB;
1YB(Yottabyte洞渤,一億億億字節(jié)阅嘶,堯字節(jié))= 1024ZB;
1BB(Brontobyte载迄,一千億億億字節(jié))= 1024YB讯柔;
注意:不同的系統(tǒng)平臺(tái)數(shù)據(jù)類型所占空間大小可能會(huì)有所不同,具體平臺(tái)可以用sizeof關(guān)鍵字測(cè)試一下:
一般情況下护昧,Windows系統(tǒng)是:
short 咔出來的內(nèi)存大小是2 個(gè)byte魂迄;
int 咔出來的內(nèi)存大小是4 個(gè)byte;
long 咔出來的內(nèi)存大小是4 個(gè)byte惋耙;
float 咔出來的內(nèi)存大小是4 個(gè)byte极祸;
double 咔出來的內(nèi)存大小是8 個(gè)byte慈格;
char 咔出來的內(nèi)存大小是1 個(gè)byte怠晴。
printf("%d",sizeof(int)); //4
變量和常量
????字符常量要用單引號(hào)括起來遥金,例如上面的'A',注意單引號(hào)只能括一個(gè)字符而不能像雙引號(hào)那樣括一串字符蒜田,
字符常量也可以是一個(gè)轉(zhuǎn)義序列稿械,例如'\n',這時(shí)雖然單引號(hào)括了兩個(gè)字符冲粤,但實(shí)際上只表示一個(gè)字符美莫。
其他的一些轉(zhuǎn)義符號(hào):
符號(hào)常量
在C語(yǔ)言中,可以用一個(gè)標(biāo)識(shí)符來表示一個(gè)常量梯捕,稱之為符號(hào)常量厢呵。
符號(hào)常量在使用之前必須先定義,其一般形式為:
#define 標(biāo)識(shí)符 常量
其中#define是一條預(yù)處理命令(預(yù)處理命令都以"#"開頭)傀顾,稱為宏定義命令襟铭,宏這塊后面慢慢理解。
一經(jīng)定義短曾,以后在程序中所有出現(xiàn)該標(biāo)識(shí)符的地方均代之以該常量值寒砖。
習(xí)慣上符號(hào)常量的標(biāo)識(shí)符用大寫字母,變量標(biāo)識(shí)符用小寫字母嫉拐,以示區(qū)別哩都。
#include <stdio.h>
#include <stdlib.h>
#define PRICE 30
int main(void){
printf("Hello world!\n");
printf("%o \n",100);
printf("%d\n",sizeof(int));
printf("%d\n",PRICE);
printf("價(jià)格常量值= %d\n",PRICE);
printf("八進(jìn)制= %o\n",PRICE);
printf("十六進(jìn)制= %x\n",PRICE);
exit(0);
}
//結(jié)果
Hello world!
144
4
30
價(jià)格常量值= 30
八進(jìn)制= 36
十六進(jìn)制= 1e
++和移位運(yùn)算符
#include <stdio.h>
#include <stdlib.h>
int main(void){
int a = 10;
//a++;
printf("%d\n",a++); //10,先用,再運(yùn)算
printf("%d\n",++a); //12婉徘,先運(yùn)算漠嵌,再用
printf("%d\n",a); //12
//邏輯運(yùn)算符,返回布爾值盖呼,返1表示true,返0表示false
printf("%d\n",12>2); //1
//移位運(yùn)算
int c = 8,b=1;
c=c<<2;
b=b>>1;
printf("%d %d\n",c,b); //4 0
exit(0);
}
其它位運(yùn)算符
運(yùn)算符意義示例對(duì)于每個(gè)位位置的結(jié)果(1=設(shè)定儒鹿,0=清除)
<<向左移位x<<yx 的每個(gè)位向左移動(dòng) y 個(gè)位,相當(dāng)于乘以2的n次方 >>向右移位x>>yx 的每個(gè)位向右移動(dòng) y 個(gè)位塌计,相當(dāng)于除以2的n次方
& 位 AND x&y 如果 x 和 y 都為 1挺身,則得到 1;如果 x 或 y 任何一個(gè)為 0锌仅,或都為0章钾,則得到 0
| 位 OR x|y 如果 x 或 y 為 1,或都為 1热芹,則得到 1贱傀;如果 x 和 y 都為 0,則得到 0
^ 位 XOR x^y 如果 x 或 y 的值不同伊脓,則得到 1府寒;如果兩個(gè)值相同魁衙,則得到 0
~ 位 NOT(I的補(bǔ)碼) ~x 如果 x 為 0,則得到 1株搔,如果 x 是 1剖淀,則得到 0
注意:位運(yùn)算符的操作數(shù)必須是整數(shù)類型。
在了解位運(yùn)算符的基本規(guī)則下纤房,要求進(jìn)步掌握:
??1)將某個(gè)整數(shù)num 的p個(gè)位置的二進(jìn)制位變成1:num | (1<<p);
??2)將某個(gè)整數(shù)num 的p個(gè)位置的二進(jìn)制位變成0:num & ~(1<<p);
全局變量和局部變量
??前面纵隔,我們一直將變量定義在 main 函數(shù)里面,其實(shí)炮姨,變量也可以定義在 main 函數(shù)外面:
#include <stdio.h>
//在main函數(shù)外部定義變量
int n = 100;
char c = '@';
int main(){
//在main函數(shù)內(nèi)部定義變量
float f = 89.5;
char str[32] = "helloworld"; //前面沒用過捌刮,涉及倒后面的數(shù)組,暫時(shí)先用一下舒岸,表示定義字符串變量绅作。
//輸出變量
printf("n: %d\nc: %c\nf: %f\nstr: %s\n", n, c, f, str);
return 0;
}
在函數(shù)外部定義的變量叫做全局變量(Global Variable)
在函數(shù)內(nèi)部定義的變量叫做局部變量(Local Variable)
全局變量和局部變量的區(qū)別:
局部變量的作用域也僅限于函數(shù)內(nèi)部,出了函數(shù)就不能使用了蛾派;
全局變量的默認(rèn)作用域是整個(gè)程序俄认,也就是所有的代碼文件,包括源文件(.c文件)和頭文件(.h文件)碍脏。
大體上了解C語(yǔ)言的內(nèi)存布局
????由C語(yǔ)言代碼(文本文件)形成可執(zhí)行程序(二進(jìn)制文件)梭依,需要經(jīng)過預(yù)處理-編譯-匯編-連接幾個(gè)階段階段。編譯過程把C語(yǔ)言文本文件生成匯編程序典尾,匯編過程把匯編程序形成二進(jìn)制機(jī)器代碼役拴,連接過程則將各個(gè)源文件生成的二進(jìn)制機(jī)器代碼文件組合成一個(gè)文件。
合成的統(tǒng)一文件钾埂,它由幾個(gè)部分組成河闰。
各個(gè)部分會(huì)存放在不同的內(nèi)存區(qū)域中:
(1)代碼段(Code或Text)
????在C語(yǔ)言中,程序語(yǔ)句進(jìn)行編譯后褥紫,形成機(jī)器代碼姜性。在執(zhí)行程序的過程中,CPU的程序計(jì)數(shù)器指向代碼段的每一條機(jī)器代碼髓考,并由處理器依次運(yùn)行部念。
基本數(shù)學(xué)運(yùn)算(+,-)氨菇,邏輯運(yùn)算(&&儡炼,||),位運(yùn)算(&查蓉,|乌询,^)等都屬于代碼段的內(nèi)容。
(2)只讀數(shù)據(jù)段(RO data)
????常量和下面要講的常變量豌研,不能被改變的數(shù)據(jù)放這里妹田。
(3)已初始化讀寫數(shù)據(jù)段(RW data)
????在程序中一般為已經(jīng)初始化的全局變量唬党,已經(jīng)初始化的靜態(tài)局部變量(static修飾的已經(jīng)初始化的變量)
(4)未初始化數(shù)據(jù)段(BSS)
????只是在程序中聲明,但是沒有初始化值的變量鬼佣,這些變量在程序運(yùn)行之前不需要占用內(nèi)存空間驶拱。因此它只會(huì)在目標(biāo)文件中被標(biāo)識(shí),將會(huì)在程序運(yùn)行時(shí)產(chǎn)生沮趣。
(5)堆(heap)
????堆內(nèi)存只在程序運(yùn)行時(shí)出現(xiàn)屯烦,一般由程序員分配和釋放。在具有操作系統(tǒng)的情況下房铭,如果程序沒有釋放,操作系統(tǒng)可能在程序(例如一個(gè)進(jìn)程)結(jié)束后回收內(nèi)存温眉。
(6)棧(stack)
????棧內(nèi)存只在程序運(yùn)行時(shí)出現(xiàn)缸匪,在函數(shù)內(nèi)部使用的變量
變量前的修飾符
?1.const關(guān)鍵字
const 意思是“恒定不變的”!
???用 const 定義的變量的值是不允許改變的类溢。這也就意味著必須在定義的時(shí)候就給它賦初值凌蔬。
說 const 定義的是變量,但又相當(dāng)于常量诞帐;說它定義的是常量唇聘,但又有變量的屬性柬泽,所以叫常變量。
const int a = 10;
//上面的寫法等價(jià)于:
int const a = 10;
???const 和 define比較:
??????從功能上說const 和 define確實(shí)很像辩诞,但它們又有明顯的不同,define是預(yù)處理命令定義的常量纺涤,而const是普通變量的定義译暂。
?2.static關(guān)鍵字
???static關(guān)鍵字表示修飾全局變量的作用域只能是本身的編譯單元(一個(gè)文件的編譯)。
???static修飾的局部變量表示變量不放在棧內(nèi)存里撩炊,放在讀寫數(shù)據(jù)段里外永,所以相當(dāng)于本編譯單元里的全局變量。
還是通過例子來理解:
/*test.c*/
#include <stdio.h>
static int i = 100;
void fun(){
printf("extern.c file i:%d\n",i);
}
/*main.c*/
#include <stdio.h>
#include <stdlib.h>
int i;
int main(void){
printf("main.c file i:%d\n",i);
exit(0);
}
//聯(lián)合編譯,自動(dòng)生成一個(gè)a.exe文件
C:\Users\Administrator\Desktop\workspaceC>gcc main.c test.c
C:\Users\Administrator\Desktop\workspaceC>a
main.c file i:0
const作用在局部變量中拧咳,相當(dāng)于當(dāng)前編譯單元中的全局變量
#include <stdio.h>
#include <stdlib.h>
void fun(){
static int m =10;
m++;
printf("m:%d\n",m);
}
int main(void){
fun();
fun();
fun();
exit(0);
}
//gcc -g const.c -o const.ext 或開發(fā)IDE中運(yùn)行
[Running] cd "c:\Users\Administrator\Desktop\workspaceC\" && gcc const.c -o const && "c:\Users\Administrator\Desktop\workspaceC\"const
m:11
m:12
m:13
3.volatile關(guān)鍵字
???volatile和編譯器優(yōu)化關(guān)系:
???由于內(nèi)存訪問速度遠(yuǎn)不及CPU處理速度伯顶,為提高機(jī)器整體性能,需要優(yōu)化措施:
1)在硬件上:CPU在運(yùn)算時(shí)會(huì)直接用的設(shè)備由近到遠(yuǎn)骆膝,主要有寄存器祭衩、高速緩存和內(nèi)存。
??? CPU與寄存器交互速度是最快的谭网,其次是高速緩存汪厨,再次是內(nèi)存。
2)執(zhí)行程序的編譯器常用的一個(gè)優(yōu)化方法:將內(nèi)存變量緩存到寄存器愉择。
???由于訪問寄存器要比訪問內(nèi)存單元快的多劫乱,編譯器在存取變量時(shí)织中,為提高存取速度,編譯器優(yōu)化有時(shí)會(huì)先把變量讀取到一個(gè)寄存器中衷戈;以后再取變量值運(yùn)算時(shí)就直接從寄存器中取值狭吼。但如果這個(gè)變量的值變化很頻繁,就有可能造成CPU運(yùn)行的數(shù)據(jù)和變量里的實(shí)時(shí)數(shù)據(jù)不一致殖妇,造成運(yùn)算結(jié)果錯(cuò)誤刁笙,volatile的作用就是提醒編譯器它后面所定義的變量隨時(shí)都有可能改變,告訴編譯器對(duì)該變量不做優(yōu)化谦趣,要運(yùn)算時(shí)都會(huì)直接從變量?jī)?nèi)存地址中讀取數(shù)據(jù)疲吸,從而避免不一致的錯(cuò)誤。
??????注意:頻繁地使用volatile很可能會(huì)增加代碼尺寸和降低性能,因此要合理的使用volatile,一般不用它前鹅。
4.extern關(guān)鍵字
???關(guān)鍵字extern摘悴,僅僅是聲明這個(gè)變量/函數(shù)可能在別的源文件里定義。
???局部變量的聲明不能有extern的修飾舰绘。在全局變量上蹂喻,我們可以理解寫和沒寫基本是一樣的。
???通過例子來理解:
/*test.c*/
#include <stdio.h>
int i = 100;
void fun(){
printf("extern.c file i:%d\n",i);
}
/*main.c*/
#include <stdio.h>
#include <stdlib.h>
extern int i; //此處不寫extern也沒關(guān)系捂寿,因?yàn)楫?dāng)我們聯(lián)合編譯時(shí)口四,也可找到基它文件i賦的值
int main(void){
printf("main.c file i:%d\n",i);
exit(0);
}
//聯(lián)合編譯,自動(dòng)生成一個(gè)a.exe文件
C:\Users\Administrator\Desktop\workspaceC>gcc main.c test.c
C:\Users\Administrator\Desktop\workspaceC>a
main.c file i:100
5.register關(guān)鍵字
register修飾符會(huì)給編譯程序暗示我修飾的變量將被頻繁地使用,如果可能的話秦陋,應(yīng)將其保存在CPU的寄存器中蔓彩,以加快其存儲(chǔ)讀取速度。
使用register修飾符有幾點(diǎn)限制
(1)register變量必須是能被CPU所接受的類型踱侣。
這通常意味著register變量必須是一個(gè)單個(gè)的值粪小,并且長(zhǎng)度應(yīng)該小于或者等于整型的長(zhǎng)度。
不過抡句,有些機(jī)器的寄存器也能存放浮點(diǎn)數(shù)探膊。
(2)因?yàn)閞egister變量可能不存放在內(nèi)存中,所以不能用“&”來獲取register變量的地址待榔。
(3)只有局部變量和形式參數(shù)可以作為寄存器變量逞壁,其它(如全局變量)不行。
(4)局部靜態(tài)變量不能定義為寄存器變量锐锣。不能寫成:register static int a, b, c;
(5)由于寄存器的數(shù)量有限(不同的cpu寄存器數(shù)目不一)腌闯,不能定義任意多個(gè)寄存器變量,而且某些寄存器只能接受特定類型的數(shù)據(jù)(如指針和浮點(diǎn)數(shù))雕憔,因此真正起作用的register修飾符的數(shù)目和類型都依賴于運(yùn)行程序的機(jī)器姿骏,而任何多余的register修飾符都將被編譯程序所忽略。
注意:
早期的C編譯程序不會(huì)把變量保存在寄存器中斤彼,除非你命令它這樣做分瘦,這時(shí)register修飾符是C語(yǔ)言的一種很有價(jià)值的補(bǔ)充蘸泻。然而,隨著編譯程序設(shè)計(jì)技術(shù)的進(jìn)步嘲玫,在決定哪些變量應(yīng)該被存到寄存器中時(shí)悦施,現(xiàn)在的C編譯環(huán)境能比程序員做出更好的決定。實(shí)際上去团,許多編譯程序都會(huì)忽略register修飾符抡诞,因?yàn)楸M管它完全合法,但它僅僅是暗示而不是命令土陪。
6.auto關(guān)鍵字
???auto被解釋為一個(gè)自動(dòng)存儲(chǔ)變量的關(guān)鍵字昼汗,也就是申明一塊臨時(shí)的變量?jī)?nèi)存。
變量旺坠,沒加修飾符的時(shí)候乔遮,默認(rèn)就是atuo這個(gè)關(guān)鍵字。
注意:靜態(tài)變量取刃、全局變量申明后沒初始化會(huì)有一個(gè)默認(rèn)的初始值0,局部變量沒有初始化值出刷,系統(tǒng)隨機(jī)賦值一個(gè)垃圾值璧疗,所以局部變量一定要賦值。
基本輸入輸出函數(shù)
???輸入輸出(Input and Output, IO)是用戶和程序“交流”的過程馁龟。
???在控制臺(tái)程序中崩侠,輸出一般是指將數(shù)據(jù)(包括數(shù)字、字符等)顯示在屏幕上坷檩,輸入一般是指獲取用戶在鍵盤上輸入的數(shù)據(jù)却音。
???主要講解在stdio.h頭文件申明的幾個(gè)標(biāo)準(zhǔn)輸入輸出函數(shù),
1--printf 矢炼,scanf:最基本輸入輸出函數(shù)系瓢, 是最靈活、最復(fù)雜句灌、最常用的輸入輸出函數(shù)
2--getchar夷陋,putchar:針對(duì)字符的輸入和輸出函數(shù),有針對(duì)性用起來簡(jiǎn)單點(diǎn)
3--gets胰锌,puts:針對(duì)字符串的輸入輸出函數(shù)骗绕,有針對(duì)性用起來簡(jiǎn)單點(diǎn)
1、printf函數(shù),scanf函數(shù)
???如果在程序中要使用 printf资昧,那么就必須要包含頭文件 stdio.h酬土,此函數(shù)包含在該頭文件中的。
printf是基本的輸出函數(shù)格带,其功能是將程序運(yùn)行的結(jié)果輸出到屏幕上撤缴。
printf 函數(shù)的原型為:
# include <stdio.h>
int printf(const char *format, ...);
//const char *format :常變量 變量類型字符類型 指針變量刹枉,char *format暫時(shí)你就理解為字符串
//... :可變參數(shù),參數(shù)的個(gè)數(shù)不確定
printf 的格式有三種:
1) printf("字符串\n");
# include <stdio.h>
int main(void)
{
printf("Hello World!\n"); // \n表示換行
return 0;
}
2) printf("字符串可以包含輸出控制符"腹泌,輸出參數(shù));
# include <stdio.h>
int main(void)
{
int i = 10;
printf("%d\n", i); /*%d是輸出控制符嘶卧,d 表示十進(jìn)制,后面的 i 是輸出參數(shù)*/
return 0;
}
???這句話的意思是將變量 i 以十進(jìn)制輸出凉袱。
??????那么現(xiàn)在有一個(gè)問題:i 本身就是十進(jìn)制芥吟,為什么還要將 i 以十進(jìn)制輸出呢?
???因?yàn)槌绦蛑须m然寫的是 i=10专甩,但是在內(nèi)存中并不是將 10 這個(gè)十進(jìn)制數(shù)存放進(jìn)去钟鸵,而是將 10 的二進(jìn)制代碼存放進(jìn)去了。計(jì)算機(jī)只能執(zhí)行二進(jìn)制 0涤躲、1 代碼棺耍,而 0、1 代碼本身并沒有什么實(shí)際的含義种樱,它可以表示任何類型的數(shù)據(jù)蒙袍。所以輸出的時(shí)候要強(qiáng)調(diào)是以哪種進(jìn)制形式輸出。所以就必須要有“輸出控制符”嫩挤,以告訴操作系統(tǒng)應(yīng)該怎樣解讀二進(jìn)制數(shù)據(jù)害幅。
3) printf("輸出控制符1 輸出控制符2…", 輸出參數(shù)1, 輸出參數(shù)2, …);
# include <stdio.h>
int main(void)
{
int i = 10;
int j = 3;
printf("%d %d\n", i, j);
return 0;
}
注意:printf 中雙引號(hào)內(nèi)除了輸出控制符和轉(zhuǎn)義字符\n外,所有其余的普通字符全部都原樣輸出岂昭∫韵郑“輸出控制符”和“輸出參數(shù)”無論在“順序上”還是在“個(gè)數(shù)上”一定要一一對(duì)應(yīng)。
常用的輸出控制符主要有以下幾個(gè):
%d 按十進(jìn)制整型數(shù)據(jù)的實(shí)際長(zhǎng)度輸出约啊。
%ld 輸出長(zhǎng)整型數(shù)據(jù)邑遏。
%md m 為指定的輸出字段的寬度。如果數(shù)據(jù)的位數(shù)小于 m恰矩,則左端補(bǔ)以空格记盒,若大于 m,則按實(shí)際位數(shù)輸出枢里。
%-md 有-表示左對(duì)齊輸出孽鸡,如省略表示右對(duì)齊輸出。
%0md 有0表示指定空位填0,如省略表示指定空位填空格栏豺。要寫在m的前面彬碱,不能和-一起用。
%u 輸出無符號(hào)整型(unsigned)奥洼。
%c 用來輸出一個(gè)字符巷疼。
%f 用來輸出小數(shù)。不指定字段寬度,由系統(tǒng)自動(dòng)指定嚼沿,整數(shù)部分全部輸出估盘,小數(shù)部分輸出 6 位,超過 6 位的四舍五入骡尽。
%.nf 輸出實(shí)數(shù)時(shí)小數(shù)點(diǎn)后保留 n 位遣妥,注意 n 前面有個(gè)點(diǎn),不寫n默認(rèn)值是6攀细。
%o 八進(jìn)制輸出
%s 用來輸出字符串箫踩。用 %s 輸出字符串同前面直接輸出字符串是一樣的。但是此時(shí)要先定義字符數(shù)組或字符指針存儲(chǔ)或指向字符串谭贪。
%x(或 %X 或 %#x 或 %#X) 以十六進(jìn)制形式輸出整數(shù)境钟,這個(gè)很重要。加#,則輸出前綴有 'ox'
%x俭识、%X慨削、%#x、%#X 的區(qū)別
一定要掌握 %x(或 %X 或 %#x 或 %#X)套媚,
因?yàn)檎{(diào)試的時(shí)候經(jīng)常要將內(nèi)存中的二進(jìn)制代碼全部輸出缚态,然后用十六進(jìn)制顯示出來。
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int i = 47;
printf("%x\n", i); //2f
printf("%X\n", i); //2F
printf("%#x\n", i); //0x2f
printf("%#X\n", i); //0X2F
return 0;
}
如何輸出 %d堤瘤、\ 和雙引號(hào)
printf 中有輸出控制符%d猿规,轉(zhuǎn)義字符前面有反斜杠\,還有雙引號(hào)宙橱。那么大家有沒有想過這樣一個(gè)問題:
怎樣將這三個(gè)符號(hào)通過 printf 輸出到屏幕上呢?
要輸出%d只需在前面再加上一個(gè)%蘸拔,要輸出\只需在前面再加上一個(gè)\师郑,要輸出雙引號(hào)也只需在加上一個(gè)\即可。
程序如下:
printf("%%d\n"); //%d
printf("\\\n"); // \
printf("\"\"\n"); // ""
int n = 234;
float f = 9.8;
char c = '@';
char *str = "helloworld";
printf("%10d%14.9f%5c%8s", n, f, c, str); // 234 9.800000191 @helloworld
return 0;
scanf函數(shù)
??scanf是從鍵盤獲得用戶輸入调窍,和 printf 的功能正好相反宝冕。
#include <stdio.h>
#include <stdlib.h>
int main(void){
int a =1,b=2,c=3,d=4;
scanf("%d",&a); // &:取地址符,&a表示獲取到a變量對(duì)應(yīng)在內(nèi)存中的地址
scanf("%d",&b);
printf("變量a: %d和變量b: %d的和: %d\n",a,b,a+b);
exit(0);
}
注意:對(duì)于 scanf()邓萨,輸入數(shù)據(jù)的格式要和控制字符串的格式保持一致地梨。
printf輸出中文亂碼是編碼是問題,解決辦法:終端輸入:chcp 65001 回車缔恳。本人這里是系統(tǒng)默認(rèn)的編譯cmd宝剖,不是windows的cmd,所以沒有出現(xiàn)中文亂碼.
內(nèi)存地址的理解:
????前面反復(fù)講計(jì)算機(jī)里的數(shù)據(jù)是以二進(jìn)制的形式保存在內(nèi)存/磁盤中的,字節(jié)(Byte)是最小的可操作單位歉甚。
??????為了便于管理万细,我們給內(nèi)存中每個(gè)字節(jié)分配了一個(gè)編號(hào),使用該字節(jié)時(shí)纸泄,只要知道編號(hào)就可以赖钞,就像每個(gè)學(xué)生都有學(xué)號(hào)腰素,老師會(huì)讓學(xué)生回答問題,喊學(xué)號(hào)就可以了雪营。字節(jié)的編號(hào)是有順序的弓千,從 0 開始,接下來是 1献起、2洋访、3……
這個(gè)編號(hào),就叫做內(nèi)存的地址(Address)征唬。
int a;會(huì)在內(nèi)存中分配四個(gè)字節(jié)的空間捌显,我們將第一個(gè)字節(jié)的地址稱為變量 a 的地址,也就是&a的值总寒。
對(duì)于前面講到的整數(shù)扶歪、浮點(diǎn)數(shù)、字符摄闸,都可以使用 & 獲取它們對(duì)應(yīng)的變量的地址善镰,
scanf 會(huì)根據(jù)地址把讀取到用戶在鍵盤上敲擊的數(shù)據(jù)寫入內(nèi)存。
我們不妨將變量的地址輸出看一下:
#include <stdio.h>
#include <stdlib.h>
int main(void)
{
int a='F';
int b=12;
int c=452;
printf("&a=%#X, &b=%#X, &c=%#X\n", &a, &b, &c); //&a=0X61FE4C, &b=0X61FE48, &c=0X61FE44
exit(0);
}
輸入其它數(shù)據(jù)
???除了輸入整數(shù)年枕,scanf() 還可以輸入單個(gè)字符炫欺、字符串、小數(shù)等熏兄,請(qǐng)看下面的演示:
#include <stdio.h>
#include <stdlib.h>
int main(void){
char letter;
int age;
char str[30];
float price;
scanf("letter=%c", &letter);
scanf("%d", &age);
scanf("%s", &str);
scanf("%f", &price);
printf("%c\n", letter);
printf("%d品洛, %s,%g\n", age, str, price);
return 0;
}
//編譯scanf.c,自動(dòng)生成a.exe文件摩桶,運(yùn)行a可執(zhí)行文件桥状,回車后,程序會(huì)阻塞程序運(yùn)行硝清,讓我們輸入數(shù)據(jù)
PS C:\Users\Administrator\Desktop\workspaceC\inputout>gcc scan.c
PS C:\Users\Administrator\Desktop\workspaceC\inputout> ./a
//這里輸入時(shí)辅斟,一定要按規(guī)定的格式輸入,不然會(huì)出現(xiàn)怪現(xiàn)像芦拿。
letter=a
12
xiongshaowen
12.23
a
12士飒, xiongshaowen,12.23
PS C:\Users\Administrator\Desktop\workspaceC\inputout>
最后需要注意的一點(diǎn)是蔗崎,scanf() 讀取字符串時(shí)以空格為分隔酵幕,遇到空格就認(rèn)為當(dāng)前字符串結(jié)束了,所以一般情況下無法讀取含有空格的字符串蚁趁,想要讀取空格要作其它處理
2-getchar()和putchar()函數(shù)
getchar()字符輸入函數(shù)裙盾,它就是scanf("%c", c)的替代品,除了更加簡(jiǎn)潔,沒有其它優(yōu)勢(shì)了番官;
或者說庐完,getchar() 就是 scanf() 的一個(gè)簡(jiǎn)化版本。
putchar(c)就是printf("%c\n", c);簡(jiǎn)化版本了徘熔。
#include <stdio.h>
int main()
{
char c = getchar();
putchar(c);
return 0;
}
3-gets()和puts()函數(shù)
gets() 這個(gè)專用的字符串輸入函數(shù)门躯,puts(cc)專用打印輸出字符串函數(shù)。
#include <stdio.h>
int main()
{
char aa[30], bb[30], cc[30];
gets(aa);
puts(aa);//使用 puts() 函數(shù)連換行符 '\n' 都省了酷师,使用 puts() 顯示字符串時(shí)讶凉,系統(tǒng)會(huì)自動(dòng)在其后添加一個(gè)換行符
gets(bb);
puts(bb)
gets(cc);
puts(cc);
return 0;
}
gets() 是有緩沖區(qū)的,每次按下回車鍵山孔,就代表當(dāng)前輸入結(jié)束了懂讯,
gets() 開始從緩沖區(qū)中讀取內(nèi)容针姿,這一點(diǎn)和 scanf() 是一樣的塌鸯。
gets() 和 scanf() 的主要區(qū)別是:
scanf() 讀取字符串時(shí)以空格為分隔,遇到空格就認(rèn)為當(dāng)前字符串結(jié)束了挣轨,所以無法讀取含有空格的字符串串前。
gets() 認(rèn)為空格也是字符串的一部分瘫里,只有遇到回車鍵時(shí)才認(rèn)為字符串輸入結(jié)束。
總結(jié):gets() 能讀取含有空格的字符串荡碾,而 scanf() 不能谨读。
scanf() 可以一次性讀取多份類型相同或者不同的數(shù)據(jù),
getchar() 和 gets() 每次只能讀取一份特定類型的數(shù)據(jù)坛吁,不能一次性讀取多份數(shù)據(jù)劳殖。
基本輸入輸出函數(shù)進(jìn)階知識(shí)點(diǎn):
認(rèn)識(shí)緩沖區(qū)(緩存)
???緩沖區(qū)(Buffer)又稱為緩存(Cache),是內(nèi)存空間的一部分拨脉。
也就是說闷尿,計(jì)算機(jī)在內(nèi)存中預(yù)留了一定的存儲(chǔ)空間,用來暫時(shí)保存輸入或輸出的數(shù)據(jù)女坑,這部分預(yù)留的空間就叫做緩沖區(qū)(緩存)。
???有時(shí)候统舀,從鍵盤輸入的內(nèi)容匆骗,或者將要輸出到顯示器上的內(nèi)容,會(huì)暫時(shí)進(jìn)入緩沖區(qū)誉简,待時(shí)機(jī)成熟碉就,再一股腦將緩沖區(qū)中的所有內(nèi)容“倒出”,我們才能看到變量的值被刷新闷串,或者屏幕產(chǎn)生變化瓮钥。
???有時(shí)候,用戶希望得到最及時(shí)的反饋,輸入輸出的內(nèi)容就不能進(jìn)入緩沖區(qū)碉熄。
緩沖區(qū)的必要性:
???緩沖區(qū)是為了讓低速的輸入輸出設(shè)備和高速的用戶程序能夠協(xié)調(diào)工作桨武,能提升程序效率,并降低輸入輸出設(shè)備的讀寫次數(shù)锈津。
緩沖區(qū)的類型
根據(jù)緩沖區(qū)對(duì)應(yīng)的是輸入設(shè)備還是輸出設(shè)備呀酸,可以分為輸入緩沖區(qū)和輸出緩沖區(qū)。
根據(jù)數(shù)據(jù)刷新(也可以稱為清空緩沖區(qū)琼梆,就是將緩沖區(qū)中的數(shù)據(jù)“倒出”)的時(shí)機(jī)性誉,
可以分為全緩沖、行緩沖茎杂、不帶緩沖错览。
1) 全緩沖
在這種情況下,當(dāng)緩沖區(qū)被填滿以后才進(jìn)行真正的輸入輸出操作煌往。緩沖區(qū)的大小都有限制的倾哺,比如 1KB、4MB 等携冤,數(shù)據(jù)量達(dá)到最大值時(shí)就清空緩沖區(qū)悼粮。
2) 行緩沖
在這種情況下,當(dāng)在輸入或者輸出的過程中遇到換行符時(shí)曾棕,才執(zhí)行真正的輸入輸出操作扣猫。
//對(duì)于 printf() 函數(shù):
#include <stdio.h>
#include <unistd.h>
int main()
{
printf("AAAAAAA"); //\n不僅僅有換行這個(gè)格式的作用,還有一個(gè)隱藏的功能:刷新緩存
sleep(5); //程序暫停5秒鐘
printf("BBBBBBBBBBB\n");
return 0;
}
???windows操縱系統(tǒng)和Linux操縱系統(tǒng)對(duì)printf的緩沖是否默認(rèn)開啟有差別翘地,win默認(rèn)關(guān)閉printf的緩沖區(qū)申尤,而其它像scanf()默認(rèn)是打開的,linux默認(rèn)開啟衙耕,
手動(dòng)開啟:setvbuf(stdout, buf, _IOLBF, 512);
存在緩沖和不存在緩沖允許結(jié)果是不一樣的昧穿!
下圖是兩種操作系統(tǒng)效果圖
下面的代碼開啟了緩沖區(qū)buf,后橙喘,win和linux執(zhí)行效果是一樣的
#include <stdio.h>
#include <unistd.h>
char buf[512];
int main()
{
setvbuf(stdout,buf,_IOLBF,512); //開啟行緩沖區(qū)
printf("AAAAAAA\n"); //\n不僅僅有換行這個(gè)格式的作用时鸵,還有一個(gè)隱藏的功能:刷新緩存
sleep(5); //程序暫停5秒鐘
printf("BBBBBBBBBBB\n");
return 0;
}
我們可以手動(dòng)刷新緩沖區(qū)的,下面代碼就是用了 fflush(stdout);
#include <stdio.h>
#include <unistd.h>
char buf[512];
int main()
{
setvbuf(stdout,buf,_IOLBF,512); //開啟行緩沖區(qū)
printf("AAAAAAA"); //\n不僅僅有換行這個(gè)格式的作用厅瞎,還有一個(gè)隱藏的功能:刷新緩存
fflush(stdout); //手動(dòng)刷新緩沖區(qū)饰潜,即不用在printf()中加'\n'
sleep(5); //程序暫停5秒鐘
printf("BBBBBBBBBBB\n");
return 0;
}
3) 不帶緩沖
不帶緩沖區(qū),數(shù)據(jù)就沒有地方緩存和簸,必須立即進(jìn)行輸入輸出彭雾。
getche()、getch() 就不帶緩沖區(qū)锁保,輸入一個(gè)字符后立即就執(zhí)行了薯酝,根本不用按下回車鍵半沽。
這兩個(gè)方法是Windows下特有的方法,Linux和Mac上沒有吴菠,就不舉例了者填。
scanf()緩沖區(qū)問題
???首先聲明一下,這個(gè)的緩沖區(qū)不管在那都是默認(rèn)打開的橄务,并且緩沖區(qū)中有\(zhòng)n符幔托,所以如下代碼運(yùn)行時(shí)是有異常的
#include <stdio.h>
int main()
{
int a = 1, b = 2;
scanf("a=%d", &a);
scanf("b=%d", &b);
printf("a=%d, b=%d\n", a, b);
return 0;
}
處理方法:
#include <stdio.h>
int main()
{
int a = 1, b = 2;
scanf("a=%d", &a);
scanf("%d", &b); //%d開頭,前面有空格也好蜂挪,換行已好默認(rèn)忽略重挑。
printf("a=%d, b=%d\n", a, b);
return 0;
}
//或
#include <stdio.h>
int main()
{
int a = 1, b = 2;
scanf("a=%d", &a);
scanf("%*[\n]"); scanf("%*[ ]"); //在下次讀取前清空緩沖區(qū)
scanf("b=%d", &b);
printf("a=%d, b=%d\n", a, b);
return 0;
}
scanf() 高級(jí)用法
1) 指定讀取長(zhǎng)度
在前面講過printf() 中可以指定最小輸出寬度!
就是在格式控制符的中間加上一個(gè)數(shù)字棠涮,例如谬哀,%10d表示輸出的整數(shù)至少占用 10 個(gè)字符的位置:
如果整數(shù)的寬度不足 10,那么在左邊以空格補(bǔ)齊严肪;
如果整數(shù)的寬度超過了 10史煎,那么以整數(shù)本身的寬度來輸出,10 不再起作用驳糯。
其實(shí)篇梭,scanf() 也有類似的用法,也可以在格式控制符的中間加一個(gè)數(shù)字酝枢,用來表示讀取數(shù)據(jù)的最大長(zhǎng)度恬偷,
例如:
%2d表示最多讀取兩位整數(shù);
%10s表示讀取的字符串的最大長(zhǎng)度為 10帘睦,或者說袍患,最多讀取 10 個(gè)字符。
#include <stdio.h>
int main(){
int n;
float f;
char str[23];
scanf("%2d", &n);
scanf("%*[^\n]"); scanf("%*c"); //清空緩沖區(qū)
scanf("%5f", &f);
scanf("%*[^\n]"); scanf("%*c"); //清空緩沖區(qū)
scanf("%22s", str);
printf("n=%d, f=%g, str=%s\n", n, f, str);
return 0;
}
限制讀取數(shù)據(jù)的長(zhǎng)度在實(shí)際開發(fā)中非常有用竣付,最典型的一個(gè)例子就是讀取字符串:我們?yōu)樽址峙涞膬?nèi)存是有限的诡延,用戶輸入的字符串過長(zhǎng)就存放不了了,就會(huì)沖刷掉其它的數(shù)據(jù)古胆,從而導(dǎo)致程序出錯(cuò)甚至崩潰肆良;
在用 gets() 函數(shù)讀取字符串的時(shí)候, gets() 不能控制讀取到的字符串的長(zhǎng)度逸绎,風(fēng)險(xiǎn)極高妖滔。
就目前學(xué)到的知識(shí)而言,雖然 scanf() 可以控制字符串的長(zhǎng)度桶良,但是字符串中卻不能包含空白符,這是硬傷沮翔,所以 scanf() 暫時(shí)還無法替代 gets()陨帆。不過后面還有scanf() 的更高級(jí)用法曲秉,屆時(shí) scanf() 就可以完全替代 gets(),并且比 gets() 更加智能疲牵。
2) 匹配特定的字符
%s 控制符有兩個(gè)缺點(diǎn):
%s 不能讀取特定的字符承二,比如只想讀取小寫字母,或者十進(jìn)制數(shù)字等纲爸,%s 就無能為力亥鸠;
%s 讀取到的字符串中不能包含空白符,有些情況會(huì)比較尷尬识啦,例如负蚊,無法將多個(gè)單詞存放到一個(gè)字符串中,因?yàn)閱卧~之間就是以空格為分隔的颓哮,%s 遇到空格就讀取結(jié)束了家妆。
要想解決以上問題,可以使用 scanf() 的另外一種字符匹配方式冕茅,就是%[xxx]伤极,[ ]包圍起來的是需要讀取的字符規(guī)則。例如姨伤,%[abcd]表示只讀取字符abcd哨坪,遇到其它的字符就讀取結(jié)束;
注意乍楚,這里并不強(qiáng)調(diào)字符的順序当编,只要字符在 abcd 范圍內(nèi)都可以匹配成功,所以你可以輸入 abcd炊豪、dcba凌箕、ccdc、bdcca 等词渤。
#include <stdio.h>
int main(){
char str[30];
scanf("%[abcd]", str);
printf("%s\n", str);
return 0;
}
3)使用連接符
連字符左邊的字符對(duì)應(yīng)一個(gè) ASCII 碼牵舱,連字符右邊的字符也對(duì)應(yīng)一個(gè) ASCII 碼,位于這兩個(gè) ASCII 碼范圍以內(nèi)的字符就是要讀取的字符缺虐。注意芜壁,連字符左邊的 ASCII 碼要小于右邊的,如果反過來高氮,那么它的行為是未定義的慧妄。
常用的連字符舉例:
%[A-Z]表示讀取 ABC...XYZ 范圍內(nèi)的字符,也即大寫字母剪芍;
%[0-9]表示讀取 012...789 范圍內(nèi)的字符塞淹,也即十進(jìn)制數(shù)字。
%[a-z]表示讀取 abc...xyz 范圍內(nèi)的字符罪裹,也即小寫字母饱普;
你也可以將它們合并起來运挫,例如:
%[a-zA-Z]表示讀取大寫字母和小寫字母,也即所有英文字母套耕;
%[a-zA-Z0-9]表示讀取所有的英文字母和十進(jìn)制數(shù)字谁帕;
%[0-9a-f]表示讀取十六進(jìn)制數(shù)字。
#include <stdio.h>
int main(){
char str[30];
scanf("%[a-zA-Z]", str); //只讀取字母
printf("%s\n", str);
return 0;
}
4)不匹配某些字符
假如現(xiàn)在有一種需求冯袍,就是讀取換行符以外的所有字符匈挖,或者讀取 0~9 以外的所有字符,該怎么實(shí)現(xiàn)呢康愤?
總不能把剩下的字符都羅列出來吧儡循,一是麻煩,二是不現(xiàn)實(shí)翘瓮。
scanf() 允許我們?cè)?[ ]中直接指定某些不能匹配的字符贮折,具體方法就是在不匹配的字符前面加上^,例如:
%[^\n]表示匹配除換行符以外的所有字符资盅,遇到換行符就停止讀鹊鏖;
%[^0-9]表示匹配除十進(jìn)制數(shù)字以外的所有字符呵扛,遇到十進(jìn)制數(shù)字就停止讀取每庆。
#include <stdio.h>
int main(){
char str1[30], str2[30];
scanf("%[^0-9]", str1);
scanf("%*[^\n]"); scanf("%*c"); //清空緩沖區(qū)
scanf("%[^\n]", str2);
printf("str1=%s \nstr2=%s\n", str1, str2);
return 0;
}
5) 丟棄讀取到的字符
在前面的代碼中,每個(gè)格式控制符都要對(duì)應(yīng)一個(gè)變量今穿,把讀取到的數(shù)據(jù)放入對(duì)應(yīng)的變量中缤灵。其實(shí)你也可以不這樣做,scanf() 允許把讀取到的數(shù)據(jù)直接丟棄蓝晒,不往變量中存放腮出,具體方法就是在 % 后面加一個(gè),例如:
%d表示讀取一個(gè)整數(shù)并丟棄芝薇;
%[a-z]表示讀取小寫字母并丟棄胚嘲;
%[^\n]表示將換行符以外的字符全部丟棄。
#include <stdio.h>
int main(){
int n;
char str[30];
scanf("%[^\n]", str);
scanf("%*[^\n]"); scanf("%*c"); //清空緩沖區(qū)
scanf("%d",&n);
printf("n=%d, str=%s\n", n, str);
return 0;
}
解決問題洛二,scanf函數(shù)能獲取帶空格的字符串?dāng)?shù)據(jù)馋劈,完全替換gets函數(shù)的寫法:
#include <stdio.h>
int main()
{
char str[32];
scanf("%32[^\n]",&str); //sancf替換gets函數(shù)的寫法!
printf("str=%s\n",str);
return 0; // exit(0);
}
流程控制
???C語(yǔ)言中的流程控制可以總結(jié)為三大結(jié)構(gòu):順序結(jié)構(gòu)晾嘶、選擇結(jié)構(gòu)和循環(huán)結(jié)構(gòu)
1-順序結(jié)構(gòu)就是讓程序按照從頭到尾的順序依次執(zhí)行每一條C語(yǔ)言代碼妓雾,不重復(fù)執(zhí)行,也不跳過任何代碼垒迂。
2-選擇結(jié)構(gòu)也稱分支結(jié)構(gòu)械姻,就是讓程序有選擇性的執(zhí)行代碼,可以跳過沒用的代碼机断,只執(zhí)行有用的代碼楷拳。
3-循環(huán)結(jié)構(gòu)就是讓程序不斷地重復(fù)執(zhí)行同一段代碼材部,在操場(chǎng)轉(zhuǎn)圈圈一樣。
???順序結(jié)構(gòu)普通的程序就是依次執(zhí)行每一條C語(yǔ)言代碼唯竹,我們前面寫的所有Demo都是這種結(jié)構(gòu)。
選擇結(jié)構(gòu)
1苦丁、if else語(yǔ)句
#include <stdio.h>
int main()
{
int age = 0;
printf("請(qǐng)輸入你的年齡:");
scanf("%d", &age);
if(age>=18){
printf("你已經(jīng)成年浸颓!\n"); //只有一條語(yǔ)句的時(shí)候,大括號(hào)可以省略
}else{
printf("你還未成年旺拉!\n");
}
return 0;
}
多個(gè)if else語(yǔ)句
#include <stdio.h>
int main(){
char c;
printf("Input a character:");
c=getchar(); //對(duì)鍵盤的按鍵進(jìn)行監(jiān)聽
if(c<32)
printf("This is a control character\n");
else if(c>='0'&&c<='9')
printf("This is a digit\n");
else if(c>='A'&&c<='Z')
printf("This is a capital letter\n");
else if(c>='a'&&c<='z')
printf("This is a small letter\n");
else
printf("This is an other character\n");
return 0;
}
if語(yǔ)句的嵌套
#include <stdio.h>
int main(){
int a,b;
printf("Input two numbers:");
scanf("%d %d",&a,&b);
if(a!=b){ //!=表示不等于
if(a>b) printf("a>b\n");
else printf("a<b\n");
}else{
printf("a=b\n");
}
return 0;
}
2产上、switch case語(yǔ)句
C語(yǔ)言雖然沒有限制 if else 能夠處理的分支數(shù)量,
但當(dāng)分支過多時(shí)蛾狗,用 if else 處理會(huì)不太方便晋涣,代碼量也稍微有點(diǎn)冗余。
#include <stdio.h>
int main(){
int a;
printf("Input integer number:");
scanf("%d",&a);
switch(a){
case 1: printf("Monday\n"); break;
case 2: printf("Tuesday\n"); break;
case 3: printf("Wednesday\n"); break;
case 4: printf("Thursday\n"); break;
case 5: printf("Friday\n"); break;
case 6: printf("Saturday\n"); break;
case 7: printf("Sunday\n"); break;
default:printf("error\n"); break;
}
return 0;
}
???case 后面必須是一個(gè)整數(shù)沉桌,或者是結(jié)果為整數(shù)的表達(dá)式谢鹊,但不能包含任何變量。
???default 不是必須的留凭。當(dāng)沒有 default 時(shí)佃扼,如果所有 case 都匹配失敗,那么就什么都不執(zhí)行蔼夜。
?和:組成的條件運(yùn)算符
if(a>b){
max = a;
}else{
max = b;
}
//上面的寫法等價(jià)于:
max = (a>b) ? a : b;
循環(huán)結(jié)構(gòu)
1兼耀、while循環(huán)
通過例子來講while循環(huán):
用 while 循環(huán)計(jì)算1加到100的值:
#include <stdio.h>
int main(){
int i=1, sum=0;
while(i<=100){
sum+=i;
i++;
}
printf("%d\n",sum);
return 0;
}
while循環(huán)使用注意兩點(diǎn):
1循環(huán)條件恒定成立時(shí)的話,while 循環(huán)會(huì)一直執(zhí)行下去求冷,永不結(jié)束瘤运,成為“死循環(huán)”。
2 循環(huán)條件不成立的話匠题,while 循環(huán)就一次也不會(huì)執(zhí)行拯坟。
2、do while循環(huán)
用do-while計(jì)算1加到100的值:
#include <stdio.h>
int main(){
int i=1, sum=0;
do{
sum+=i;
i++;
}while(i<=100);
printf("%d\n", sum);
return 0;
}
大體上和while循環(huán)差不多梧躺,唯一的差別:
1)while循環(huán):先判斷控制循環(huán)的邏輯條件似谁,在執(zhí)行循環(huán)體,邏輯條件為false掠哥,就不會(huì)執(zhí)行循環(huán)體巩踏。
2)do while循環(huán):不管邏輯條件是true還是false,都先執(zhí)行一次循環(huán)體续搀,然后在判斷條件決定是否繼續(xù)執(zhí)行塞琼。
3、for循環(huán)
對(duì)比while循環(huán)禁舷,下面語(yǔ)句①②③都是必不可少的彪杉,被放到了不同的地方毅往,代碼結(jié)構(gòu)較為松散,容易漏掉派近!
#include <stdio.h>
int main(){
int i, sum=0;
int i = 1; //語(yǔ)句① 控制循環(huán)的變量
while(i<=100 /*語(yǔ)句②*/ ){ //判斷循環(huán)要不要繼續(xù)的條件
sum+=i;
i++; //語(yǔ)句③ 更新控制循環(huán)的變量的值
}
printf("%d\n",sum);
return 0;
}
在實(shí)際開發(fā)中常常用for循環(huán)的寫法代替上面講的while循環(huán)攀唯,盡管效果沒什么差別:
#include <stdio.h>
int main(){
int sum=0;
for(int i=1/*語(yǔ)句①*/; i<=100/*語(yǔ)句②*/; i++/*語(yǔ)句③*/){
sum+=i;
}
printf("%d\n",sum);
return 0;
}
上面是for循環(huán)規(guī)規(guī)矩矩的寫法,下面我們?cè)诳纯床灰?guī)矩的寫法:
- 修改“從1加到100的和”的代碼渴丸,省略“表達(dá)式1(初始化條件)”
int i = 1, sum = 0;
for( ; i<=100; i++){
sum+=i;
}
- 省略了“表達(dá)式2(循環(huán)條件)”侯嘀,如果不做其它處理就會(huì)成為死循環(huán)。例如:
for(int i=1; ; i++) sum=sum+i;
相當(dāng)于:
int i=1;
while(1){
sum=sum+i;
i++;
}
- 省略了“表達(dá)式3”谱轨,這時(shí)可在循環(huán)體中加入修改變量的語(yǔ)句戒幔。例如:
for(int i=1; i<=100; ){
sum=sum+i;
i++;
}
- 省略了“表達(dá)式1”和“表達(dá)式3”。例如:
int i=0;
for( ; i<=100 ; ){
sum=sum+i;
i++;
}
相當(dāng)于:
int i=0;
while(i<=100){
sum=sum+i;
i++;
}
- 3個(gè)表達(dá)式可以同時(shí)省略土童。例如:
for( ; ; ) 語(yǔ)句
相當(dāng)于:
while(1) 語(yǔ)句
注意:盡量不要在循環(huán)體中定義條變量诗茎,這會(huì)大量浪費(fèi)內(nèi)存,可以先定義一個(gè)變量献汗,再在循環(huán)中重新賦值敢订。
4、break和continue關(guān)鍵字
當(dāng) break 關(guān)鍵字用于 while雀瓢、for 循環(huán)時(shí)枢析,會(huì)終止循環(huán)。
break 關(guān)鍵字通常和 if 語(yǔ)句一起使用,即滿足條件時(shí)便跳出循環(huán)。
continue 語(yǔ)句的作用是跳過循環(huán)體中剩余的語(yǔ)句而強(qiáng)制進(jìn)入下一次循環(huán)棍弄。
continue語(yǔ)句只用在 while足绅、for 循環(huán)中,常與 if 條件語(yǔ)句一起使用,判斷條件是否成立。
#include <stdio.h>
int main(){
int i=1, sum=0;
while(1){ //循環(huán)條件為死循環(huán)
sum+=i;
i++;
if(i>100) break; //如果i>100整個(gè)循環(huán)結(jié)束了,不同于return語(yǔ)句饮睬,會(huì)執(zhí)行下面的語(yǔ)句(循環(huán)外的)
}
printf("%d\n", sum);
return 0;
}
#include <stdio.h>
int main(){
char c = 0;
while(c!='\n'){ //回車鍵結(jié)束循環(huán)
c=getchar();
if(c=='4' || c=='5'){ //按下的是數(shù)字鍵4或5
continue; //跳過當(dāng)次循環(huán),進(jìn)入下次循環(huán),即輸入一個(gè)字符串后篮奄,過濾掉4,5字符
}
putchar(c);
}
return 0;
}
5捆愁、循環(huán)嵌套舉例
例子1:簡(jiǎn)單的for循環(huán)嵌套
#include <stdio.h>
int main()
{
int i, j;
for(i=1; i<=4; i++){ //外層for循環(huán)
for(j=1; j<=4; j++){ //內(nèi)層for循環(huán)
printf("i=%d, j=%d\n", i, j);
}
printf("\n");
}
return 0;
}
//執(zhí)行結(jié)果
[Running] cd "c:\Users\Administrator\Desktop\workspaceC\if_for\" && gcc xunhuan.c -o xunhuan && "c:\Users\Administrator\Desktop\workspaceC\if_for\"xunhuan
i=1, j=1
i=1, j=2
i=1, j=3
i=1, j=4
i=2, j=1
i=2, j=2
i=2, j=3
i=2, j=4
i=3, j=1
i=3, j=2
i=3, j=3
i=3, j=4
…………。
#include <stdio.h>
int main()
{
int i=1, j=0;
while(1){ // 外層循環(huán)
j=1;
while(1){ // 內(nèi)層循環(huán)
printf("%-4d", i*j);
j++;
if(j>4) break; //跳出內(nèi)層循環(huán)
}
printf("\n");
i++;
if(i>4) break; // 跳出外層循環(huán)
}
/*int i, j;
for(i=1; i<=4; i++){ //外層for循環(huán)
for(j=1; j<=4; j++){ //內(nèi)層for循環(huán)
printf("%-4d", i*j);
}
printf("\n");
}*/
return 0;
}
//執(zhí)行結(jié)果
[Running] cd "c:\Users\Administrator\Desktop\workspaceC\if_for\" && gcc while.c -o while && "c:\Users\Administrator\Desktop\workspaceC\if_for\"while
1 2 3 4
2 4 6 8
3 6 9 12
4 8 12 16
例子3:輸出九九乘法表窟却。
#include <stdio.h>
int main(){
int i, j;
for(i=1; i<=9; i++){ //外層for循環(huán)
for(j=1; j<=i; j++){ //內(nèi)層for循環(huán)
printf("%d*%d=%-3d ", i, j, i*j);
}
printf("\n");
}
return 0;
}
循環(huán)問題
1.錯(cuò)誤:只允許在 C99 模式下使用‘for’循環(huán)初始化聲明
for (int i = 0; i < (sizeof(a) / sizeof(a[0])); i++) 昼丑。。夸赫。菩帝。
處理:即把int i=0;定義在循環(huán)體外。
數(shù)組
???數(shù)組(Array)就是一些列具有相同類型的數(shù)據(jù)的集合,這些數(shù)據(jù)在內(nèi)存中依次挨著存放呼奢,彼此之間沒有縫隙宜雀。
C語(yǔ)言數(shù)組屬于構(gòu)造數(shù)據(jù)類型。
???為什么要在編程里設(shè)計(jì)數(shù)組這種東西:為了減少變量數(shù)目握础,讓開發(fā)更有效率辐董,能讓多個(gè)數(shù)據(jù)綁定一個(gè)變量名。
#include <stdio.h>
#include <stdlib.h>
int main()
{
int a1=20, a2=345, a3=700, a4=22;
int b1=55, b2=66, b3=77, b4=88;
int c1=99, c2=11, c3=12, c4=13;
int d1=14, d2=15, d3=16, d4=17;
printf("%-9d %-9d %-9d %-9d\n", a1, a2, a3, a4);
printf("%-9d %-9d %-9d %-9d\n", b1, b2, b3, b4);
printf("%-9d %-9d %-9d %-9d\n", c1, c2, c3, c4);
printf("%-9d %-9d %-9d %-9d\n", d1, d2, d3, d4);
return 0;
}
有了數(shù)組后禀综,上面的一堆代碼可以這樣寫:
int a[16] = {20, 345, 700, 22, 55, 66, 77, 88, 99, 11, 12, 13, 14, 15, 16, 17};
for (int i = 0; i < (sizeof(a) / sizeof(a[0])); i++) {
printf("%-9d", a[i]);
if((i+1)%4==0) printf("\n");
}
數(shù)組的定義方式:
dataType arrayName[length];
dataType 為數(shù)據(jù)類型郎哭,arrayName 為數(shù)組變量名稱,length 為數(shù)組長(zhǎng)度菇存。
例如:
int a[16]; //定義一個(gè)長(zhǎng)度為16的整數(shù)類型數(shù)組
float m[12]; //定義一個(gè)長(zhǎng)度為 12 的浮點(diǎn)型數(shù)組
char ch[9]; //定義一個(gè)長(zhǎng)度為 9 的字符型數(shù)組
注意:C語(yǔ)言中的數(shù)組是靜態(tài)的,數(shù)組一旦被定義后邦蜜,占用的內(nèi)存空間就是固定的依鸥,容量就是不可改變的,既不能在任何位置插入元素悼沈,也不能在任何位置刪除元素贱迟,只能讀取和修改元素,我們將這樣的數(shù)組稱為靜態(tài)數(shù)組絮供。
和其他變量的定義一樣衣吠,數(shù)組變量定義的時(shí)候,也可以同時(shí)做初始化:
int a[4] = {20, 345, 700, 22};
對(duì)于數(shù)組的初始化需要注意以下幾點(diǎn):
可以只給部分元素賦值壤靶。當(dāng){ }中值的個(gè)數(shù)少于元素個(gè)數(shù)時(shí)缚俏,只給前面部分元素賦值。例如:
int a[10]={12, 19, 22 , 993, 344};
表示只給 a[0]~a[4] 5個(gè)元素賦值贮乳,而后面 5 個(gè)元素自動(dòng)初始化為 0忧换。只能給元素逐個(gè)賦值,不能給數(shù)組整體賦值向拆。例如給 10 個(gè)元素全部賦值為 1亚茬,只能寫作:
int a[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
而不能寫作:
int a[10] = 1;如給全部元素賦值,那么在定義數(shù)組時(shí)可以不給出數(shù)組長(zhǎng)度浓恳。例如:
int a[] = {1, 2, 3, 4, 5};
等價(jià)于
int a[5] = {1, 2, 3, 4, 5};
????數(shù)組的定義刹缝,本質(zhì)是根據(jù)數(shù)據(jù)長(zhǎng)度分配對(duì)應(yīng)個(gè)數(shù)的內(nèi)存空間格子,一個(gè)格子放置一個(gè)數(shù)組元素颈将,數(shù)組中的每個(gè)元素對(duì)應(yīng)的內(nèi)存格子都有一個(gè)序號(hào)梢夯,這個(gè)序號(hào)從0開始,而不是從我們熟悉的1開始吆鹤,稱為下標(biāo)(Index)厨疙。
要獲取數(shù)組元素時(shí),我們通過數(shù)組變量名稱+下標(biāo)的形式:
arrayName[index],例如沾凄,a[0] 表示第0個(gè)元素梗醇,a[3] 表示第4個(gè)元素。
????修改數(shù)組里邊的小標(biāo)對(duì)應(yīng)的內(nèi)存格子里的數(shù)據(jù):
arrayName[index] = 修改后的值;
????在使用數(shù)組過程中撒蟀,我們經(jīng)常會(huì)使用循環(huán)結(jié)構(gòu)將數(shù)據(jù)放入數(shù)組中叙谨,或者使用循環(huán)結(jié)構(gòu)輸出數(shù)組中元素值,
????下面我們就來演示一下如何將 1~10 這十個(gè)數(shù)字放入數(shù)組中:
#include <stdio.h>
int main(){
int nums[10];
//將1~10放入數(shù)組中
for(int i=0; i<10; i++){
nums[i] = (i+1);
}
//依次輸出數(shù)組元素保屯,這種做法叫數(shù)組遍歷
for(int i=0; i<10; i++){
printf("%d ", nums[i]);
}
return 0;
}
需要注意的是:
- 數(shù)組中每個(gè)元素的數(shù)據(jù)類型必須相同手负,對(duì)于int a[4];,每個(gè)元素都必須為 int姑尺。
- 數(shù)組長(zhǎng)度 length 最好是整數(shù)或者常量表達(dá)式竟终,例如 10、204 等切蟋,這樣在所有編譯器下都能運(yùn)行通過统捶;
如果 length 中包含了變量,例如 n柄粹、4m 等喘鸟,在某些編譯器下就會(huì)報(bào)錯(cuò),某些不會(huì)驻右。 - 訪問數(shù)組元素時(shí)什黑,下標(biāo)的取值范圍為 0 ≤ index < length,過大或過小都會(huì)越界堪夭,導(dǎo)致數(shù)組溢出愕把,
發(fā)生不可預(yù)測(cè)的情況,我們要避免數(shù)組下標(biāo)溢出森爽。
二維數(shù)組
二維數(shù)組的定義礼华、初始化、賦值拗秘、取值圣絮、遍歷
數(shù)組可以看作是一行連續(xù)的數(shù)據(jù),只有一個(gè)下標(biāo)雕旨,稱為一維數(shù)組扮匠。
在實(shí)際問題中有很多數(shù)據(jù)是二維的或多維的,因此C語(yǔ)言允許構(gòu)造多維數(shù)組凡涩。多維數(shù)組元素有多個(gè)下標(biāo)棒搜,以確定它在數(shù)組中的位置。
二維數(shù)組定義的一般形式是:
????dataType arrayName[length1][length2];
????我們可以將二維數(shù)組看做一個(gè) Excel 表格活箕,有行有列力麸,length1 表示行數(shù),length2 表示列數(shù),要在二維數(shù)組中定位某個(gè)元素克蚂,必須同時(shí)指明行和列闺鲸。
例如:int a[3][4];
a[0][0], a[0][1], a[0][2], a[0][3]
a[1][0], a[1][1], a[1][2], a[1][3]
a[2][0], a[2][1], a[2][2], a[2][3]
二維數(shù)組的初始化(賦值)
????二維數(shù)組的初始化可以按行分段賦值,也可按行連續(xù)賦值埃叭。
例如摸恍,對(duì)于數(shù)組 a[5][3],按行分段賦值應(yīng)該寫作:
int a[5][3]={ {80,75,92}, {61,65,71}, {59,63,70}, {85,87,90}, {76,77,85} };
按行連續(xù)賦值應(yīng)該寫作:
int a[5][3]={80, 75, 92, 61, 65, 71, 59, 63, 70, 85, 87, 90, 76, 77, 85};
????這兩種賦初值的結(jié)果是完全相同的赤屋。
單個(gè)運(yùn)算賦值和取值:
a[0][0] = 12立镶;
printf("a[0][0]=%d\n",a[0][0])
二維數(shù)組遍歷:
例: 用二維數(shù)據(jù)保存 5 個(gè)人,每個(gè)人有 3 門課程的考試成績(jī)类早,求該小組各科的平均分和總平均分媚媒。
#include <stdio.h>
int main(void) {
int i=0,j=0; //循環(huán)控制變量
int sum = 0; //保存每門課的總成績(jī);
double average =0; //總平均分
double v[3]; //放3門課涩僻,每門課的平均分
int a[5][3]; //存放每人沒門課成績(jī)的二維數(shù)組
printf("請(qǐng)輸入5人3門課的成績(jī):\n");
for(i=0;i<3;i++){ //外循環(huán)遍歷的課程欣范,3門課
for(j=0;j<5;j++){ //內(nèi)循環(huán)遍歷的5個(gè)人
scanf("%d",&a[j][i]);
sum += a[j][i];
}
//每循環(huán)一次,就能得到一門課的所有人的成績(jī)綜合
v[i] = sum/5.0;
sum = 0;
}
average = (v[0] + v[1] + v[2])/3.0;
for(int k=0;k<3;k++){
printf("第%d門課的平均成績(jī):%f\n",k+1,v[k]);
}
printf("總平均成績(jī)是:%f\n",average);
int jj =0;
for(int ii=0;ii<5;ii++){
for(jj=0;jj<3;jj++){
printf("%-5d",a[ii][jj]);
}
printf("\n");
}
return 0;
}
//執(zhí)行結(jié)果
請(qǐng)輸入5個(gè)3門課的成績(jī):
78
89
56
65
25
69
78
52
36
45
57
89
69
70
28
第1門課的平均成績(jī):62.600000
第2門課的平均成績(jī):56.000000
第3門課的平均成績(jī):62.600000
總平均分成績(jī)是: 60.400000
78 69 57
89 78 89
56 52 69
65 36 70
25 45 28
C語(yǔ)言字符數(shù)組和字符串
用來存放字符的數(shù)組稱為字符數(shù)組令哟,例如:
char a[10]; //一維字符數(shù)組
char b[5][10]; //二維字符數(shù)組
char c[20]={'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a','m'}; // 給部分?jǐn)?shù)組元素賦值
char d[]={'c', ' ', 'p', 'r', 'o', 'g', 'r', 'a', 'm' }; //對(duì)全體元素賦值時(shí)可以省去長(zhǎng)度
字符串定義和初始化:
C語(yǔ)言規(guī)定,可以將字符串直接賦值給字符數(shù)組妨蛹,例如:
char str[30] = {"helloworld"};
char str[30] = "helloworld"; //這種形式更加簡(jiǎn)潔屏富,實(shí)際開發(fā)中常用
為了方便,你也可以不指定數(shù)組長(zhǎng)度蛙卤,從而寫作:
char str[] = {"helloworld"};
char str[] = "helloworld"; //這種形式更加簡(jiǎn)潔狠半,實(shí)際開發(fā)中常用
????給字符數(shù)組賦值時(shí),我們通常使用這種寫法颤难,將字符串一次性地賦值(可以指明數(shù)組長(zhǎng)度神年,也可以不指明),而不是一個(gè)字符一個(gè)字符地賦值行嗤,那樣做太麻煩了已日。
????這里需要留意一個(gè)坑,字符數(shù)組只有在定義時(shí)才能將整個(gè)字符串一次性地賦值給它栅屏,一旦定義完了飘千,就只能一個(gè)字符一個(gè)字符地賦值了。
請(qǐng)看下面的例子:
char str[7];
str = "abc123"; //錯(cuò)誤
//正確
str[0] = 'a'; str[1] = 'b'; str[2] = 'c';
str[3] = '1'; str[4] = '2'; str[5] = '3';
字符串結(jié)束標(biāo)志要我門注意
????C語(yǔ)言的字符串結(jié)尾標(biāo)志的解決方案有點(diǎn)奇妙栈雳,或者說有點(diǎn)奇葩护奈。
在C語(yǔ)言中,字符串總是以'\0'作為結(jié)尾哥纫,所以'\0'也被稱為字符串結(jié)束標(biāo)志霉旗,或者字符串結(jié)束符。
????'\0'是 ASCII 碼表中的第 0 個(gè)字符,英文稱為 NUL厌秒,中文稱為“空字符”读拆。該字符既不能顯示,也沒有控制功能简僧,輸出該字符不會(huì)有任何效果建椰,它在C語(yǔ)言中唯一的作用就是作為字符串結(jié)束標(biāo)志。
????由" "包圍的字符串會(huì)自動(dòng)在末尾添加'\0'岛马。例如棉姐,"abc123"從表面看起來只包含了 6 個(gè)字符,其實(shí)不然啦逆,C語(yǔ)言會(huì)在最后隱式地添加一個(gè)'\0'伞矩,這個(gè)過程是在后臺(tái)默默地進(jìn)行的,所以我們感受不到夏志。
char str[] = "abc123";
printf("%d",sizeof(str)); //7
要特別注意'\0'乃坤,要為'\0'留個(gè)位置,進(jìn)行指定長(zhǎng)度的字符數(shù)組定義沟蔑,字符數(shù)組的長(zhǎng)度至少要比字符串的長(zhǎng)度大 1:
char str[7] = "abc123";
????C語(yǔ)言為了提高效率湿诊,保證操作的靈活性,并不會(huì)對(duì)數(shù)組越界行為進(jìn)行檢查瘦材,即使越界了厅须,也能夠正常編譯,只有在運(yùn)行期間才可能會(huì)發(fā)生問題食棕。在項(xiàng)目開發(fā)中朗和,一定一定要注意,這點(diǎn)特別容易導(dǎo)致程序錯(cuò)誤簿晓。
????另外需要注意的是眶拉,逐個(gè)字符地給數(shù)組賦值并不會(huì)自動(dòng)添加'\0',例如:
char str[] = {'a', 'b', 'c'};
數(shù)組 str 的長(zhǎng)度為 3憔儿,而不是 4忆植,因?yàn)樽詈鬀]有'\0'。這樣賦值我們建議:
char str[] = {'a', 'b', 'c','\0'};
另外谒臼,在定義字符串變量的時(shí)候唱逢,如不是定義的同時(shí)不需要賦有意義初始值,我們建議這樣寫:
char str[30] = {0}; //將所有元素都初始化為 0屋休,或者說 '\0'
string.h中聲明的其他部分字符串處理函數(shù)
1)字符串長(zhǎng)度函數(shù) strlen()
所謂字符串長(zhǎng)度坞古,就是字符串包含了多少個(gè)字符(不包括最后的結(jié)束符'\0')。例如"abc"的長(zhǎng)度是 3劫樟,而不是 4痪枫。
在C語(yǔ)言中织堂,我們使用string.h頭文件中的 strlen() 函數(shù)來求字符串的長(zhǎng)度,比如:
#include <stdio.h>
#include <string.h> //記得引入該頭文件
int main(){
char str[] = "helloworld";
long len = strlen(str);
printf("The lenth of the string is %ld.\n", len);
return 0;
}
2)字符串連接函數(shù) strcat()
strcat 意思是把兩個(gè)字符串拼接在一起奶陈,語(yǔ)法格式為:
strcat(arrayName1, arrayName2);
arrayName1易阳、arrayName2 為需要拼接的字符串。
strcat() 將把 arrayName2 連接到 arrayName1 后面吃粒,并刪除原來 arrayName1 最后的結(jié)束標(biāo)志'\0'潦俺。
這意味著,arrayName1 必須足夠長(zhǎng)徐勃,要能夠同時(shí)容納 arrayName1 和 arrayName2事示,否則會(huì)越界。
????strcat() 的返回值為 arrayName1 的地址僻肖。
#include <stdio.h>
#include <string.h>
int main(){
char str1[11]="hello";
char str2[6] = "world";
strcat(str1, str2);
puts(str1);
return 0;
}
3)字符串復(fù)制函數(shù) strcpy()
strcpy 意思是字符串復(fù)制肖爵,也即將字符串從一個(gè)地方復(fù)制到另外一個(gè)地方,語(yǔ)法格式為:
strcpy(arrayName1, arrayName2);
strcpy() 會(huì)把 arrayName2 中的字符串拷貝到 arrayName1 中臀脏,字符串結(jié)束標(biāo)志'\0'也一同拷貝劝堪。
請(qǐng)看下面的例子:
#include <stdio.h>
#include <string.h>
int main(){
char str1[6] = "hello";
char str2[6] = "world";
strcpy(str1, str2);
printf("str1: %s\n", str1);
return 0;
}
將 str2 復(fù)制到 str1 后,str1 中原來的內(nèi)容就被覆蓋了揉稚。
另外秒啦,strcpy() 要求 arrayName1 要有足夠的長(zhǎng)度,否則不能全部裝入所拷貝的字符串搀玖。
4)字符串比較函數(shù) strcmp()
strcmp 意思是字符串比較余境,語(yǔ)法格式為:
strcmp(arrayName1, arrayName2);
arrayName1 和 arrayName2 是需要比較的兩個(gè)字符串。
字符本身沒有大小之分巷怜,strcmp() 以各個(gè)字符對(duì)應(yīng)的 ASCII 碼值值進(jìn)行比較。
strcmp() 從兩個(gè)字符串的第 0 個(gè)字符開始比較暴氏,如果它們相等延塑,就繼續(xù)比較下一個(gè)字符,直到遇見不同的字符答渔,或者到字符串的末尾关带。
返回值:
若 arrayName1 和 arrayName2 相同,則返回0沼撕;
若 arrayName1 大于 arrayName2宋雏,則返回大于 0 的值;
若 arrayName1 小于 arrayName2务豺,則返回小于0 的值磨总。
#include <stdio.h>
#include <string.h>
int main(){
char a[] = "aBcDeF";
char b[] = "AbCdEf";
char c[] = "aacdef";
char d[] = "aBcDeF";
printf("a VS b: %d\n", strcmp(a, b));
printf("a VS c: %d\n", strcmp(a, c));
printf("a VS d: %d\n", strcmp(a, d));
return 0;
}
函數(shù)
????函數(shù)的本質(zhì)就是一個(gè)可以重復(fù)使用的代碼塊,它使得我們的程序更加模塊化笼沥,提高代碼的復(fù)用性蚪燕。
????將常用的代碼以固定的格式封裝(包裝)成一個(gè)獨(dú)立的模塊娶牌,只要知道這個(gè)模塊的名字就可以重復(fù)使用它,這個(gè)模塊就叫做函數(shù)(Function)馆纳。
1诗良、函數(shù)的定義和調(diào)用
1. C語(yǔ)言無參函數(shù)的定義
語(yǔ)法:
dataType functionName(void){
//body
return xxx;
}
//有很多時(shí)候,我們也寫成:
dataType functionName(){
//body
return xxx;
}
????dataType 是返回值類型鲁驶,它可以是C語(yǔ)言中的任意數(shù)據(jù)類型鉴裹,例如 int、float钥弯、char 等径荔。
????functionName 是函數(shù)名,它是標(biāo)識(shí)符的一種寿羞,命名規(guī)則前面講過猖凛。
():放參數(shù)的地方,無參數(shù)就是括號(hào)里沒東西绪穆,函數(shù)名后面的括號(hào)( )不能少辨泳。
????body 是函數(shù)體,它是函數(shù)需要執(zhí)行的代碼玖院,是函數(shù)的主體部分菠红。即使只有一個(gè)語(yǔ)句,函數(shù)體也要由{ }包圍难菌。
????如果有返回值试溯,在函數(shù)體中使用 return 語(yǔ)句返回。
????return 出來的數(shù)據(jù)的類型要和 dataType 一致郊酒。
比如:
int onePlusTwo(){
return 1+2;
}
無參函數(shù)的調(diào)用遇绞,注意先申明后使用的基本規(guī)則:
函數(shù)名();
onePlusTwo();
??括號(hào)里什么都沒有和有void的區(qū)別
??這樣我們就能知道,onePlusTwo()這種形式不是說onePlusTwo這個(gè)函數(shù)不允許帶任何參數(shù)燎窘,而是指它帶什么類型的參數(shù)摹闽,有多少個(gè),是不確定的褐健。
??而如果我們使用了onePlusTwo(void)這種形式付鹿,則就完全限定了onePlusTwo的參數(shù)是不允許帶任何參數(shù)的。
2)無返回值函數(shù)的定義
語(yǔ)法格式:
void functionName(){
//body
}
void是C語(yǔ)言中的一個(gè)關(guān)鍵字蚜迅,表示“空類型”或“無類型”舵匾,比如:
void hello(){
printf ("Hello,world \n");
//沒有返回值就不需要 return 語(yǔ)句
}
3)有參函數(shù)的定義
????如果函數(shù)需要接收用戶傳遞的數(shù)據(jù),那么定義時(shí)就要帶上參數(shù)谁不。語(yǔ)法:
dataType functionName(dataType1 param1, dataType2 param2 ... ){
//body
return xxx;
}
????dataType1 param1, dataType2 param2 ...是參數(shù)列表坐梯。函數(shù)可以只有一個(gè)參數(shù),也可以有多個(gè)刹帕,多個(gè)參數(shù)之間由,分隔烛缔。參數(shù)本質(zhì)上也是變量馏段,定義時(shí)要指明類型和名稱。
????數(shù)據(jù)通過參數(shù)傳遞到函數(shù)內(nèi)部進(jìn)行處理践瓷,處理完成以后再通過返回值講函數(shù)處理結(jié)果返回到調(diào)用函數(shù)的地方院喜。
int onePlusTwo(int a,int b){
return a+b;
}
有參函數(shù)的調(diào)用:
onePlusTwo(2,3);
int rs = onePlusTwo(2,3);
????函數(shù)定義時(shí)給出的參數(shù)稱為形式參數(shù),簡(jiǎn)稱形參晕翠;函數(shù)調(diào)用時(shí)給出的參數(shù)(也就是傳遞的數(shù)據(jù))稱為實(shí)際參數(shù)喷舀,簡(jiǎn)稱實(shí)參。函數(shù)調(diào)用時(shí)淋肾,將實(shí)參的值傳遞給形參硫麻,相當(dāng)于一次賦值操作。
????原則上講樊卓,實(shí)參的類型和數(shù)目要與形參保持一致拿愧。如果能夠進(jìn)行自動(dòng)類型轉(zhuǎn)換,或者進(jìn)行了強(qiáng)制類型轉(zhuǎn)換碌尔,那么實(shí)參類型也可以不同于形參類型浇辜,例如將 int 類型的實(shí)參傳遞給 float 類型的形參就會(huì)發(fā)生自動(dòng)類型轉(zhuǎn)換。
注意:函數(shù)不能嵌套定義,但可以相互調(diào)用
。
【示例】計(jì)算sum = 1! + 2! + 3! + ... + (n-1)! + n!
分析:可以編寫兩個(gè)函數(shù)傻挂,一個(gè)用來計(jì)算階乘,一個(gè)用來計(jì)算累加的和熊镣。
#include <stdio.h>
//求階乘
long factorial(int n){
int i;
long result=1;
for(i=1; i<=n; i++){
result *= i;
}
return result;
}
// 求累加的和
long sum(long n){
int i;
long result = 0;
for(i=1; i<=n; i++){
//在定義過程中出現(xiàn)嵌套調(diào)用
result += factorial(i);
}
return result;
}
int main(){
printf("1!+2!+...+9!+10! = %ld\n", sum(10)); //main() --> printf() --> sum() --> factorial()
return 0;
}
//執(zhí)行結(jié)果
//1!+2!+...+9!+10! = 4037913
2、庫(kù)函數(shù)和自定義函數(shù)
????C語(yǔ)言在發(fā)布時(shí)已經(jīng)為我們封裝好了很多函數(shù)募书,它們被分門別類地放到了不同的頭文件中聲明(頭文件可能只是申明沒有定義)绪囱,使用函數(shù)時(shí)引入對(duì)應(yīng)的頭文件即可。
????這些C語(yǔ)言自帶的函數(shù)稱為庫(kù)函數(shù)(Library Function)莹捡。庫(kù)(Library)是編程中的一個(gè)基本概念鬼吵,可以簡(jiǎn)單地認(rèn)為它是一很多函數(shù)的倉(cāng)庫(kù),在磁盤上往往是一個(gè)文件夾道盏。
????C語(yǔ)言自帶的庫(kù)稱為標(biāo)準(zhǔn)庫(kù)(Standard Library)而柑,其他公司或個(gè)人開發(fā)的庫(kù)稱為第三方庫(kù)文捶。
????除了庫(kù)函數(shù)荷逞,我們還可以編寫自己的函數(shù),拓展程序的功能粹排。自己編寫的函數(shù)稱為自定義函數(shù)种远。
3、C語(yǔ)言函數(shù)聲明以及函數(shù)原型
????C語(yǔ)言代碼由上到下依次執(zhí)行顽耳,原則上函數(shù)定義要出現(xiàn)在函數(shù)調(diào)用之前坠敷,否則就會(huì)報(bào)錯(cuò)妙同。
????但在實(shí)際開發(fā)中,經(jīng)常會(huì)在函數(shù)定義之前使用它們膝迎,這個(gè)時(shí)候就需要提前聲明粥帚。
????所謂聲明(Declaration),就是告訴編譯器我要使用這個(gè)函數(shù)限次,你現(xiàn)在沒有找到它的定義不要緊芒涡,請(qǐng)不要報(bào)錯(cuò),稍后我會(huì)把定義補(bǔ)上卖漫。
函數(shù)聲明的格式非常簡(jiǎn)單费尽,相當(dāng)于去掉函數(shù)定義中的函數(shù)體,并在最后加上分號(hào);
dataType functionName( dataType1 param1, dataType2 param2 ... );
//也可以不寫形參羊始,只寫數(shù)據(jù)類型:
dataType functionName( dataType1, dataType2 ... );
????函數(shù)聲明給出了函數(shù)名旱幼、返回值類型、參數(shù)列表(重點(diǎn)是參數(shù)類型)等與該函數(shù)有關(guān)的信息突委,稱為函數(shù)原型(Function Prototype)
柏卤。
??有了函數(shù)聲明,函數(shù)定義就可以出現(xiàn)在任何地方了鸯两,甚至是其他文件闷旧、靜態(tài)鏈接庫(kù)、動(dòng)態(tài)鏈接庫(kù)等钧唐。
【實(shí)例1】定義一個(gè)函數(shù) sum()忙灼,計(jì)算從 m 加到 n 的和,并將 sum() 的定義放到 main() 后面钝侠。
#include <stdio.h>
//函數(shù)聲明
int sum(int m, int n); //也可以寫作int sum(int, int);
int main(){
int begin = 5, end = 86;
int result = sum(begin, end);
printf("The sum from %d to %d is %d\n", begin, end, result);
return 0;
}
//函數(shù)定義
int sum(int m, int n){
int i, sum=0;
for(i=m; i<=n; i++){
sum+=i;
}
return sum;
}
????然而在實(shí)際開發(fā)中该园,往往都是上千行、上萬行帅韧、上百萬行的代碼里初,將這些代碼都放在一個(gè)源文件中簡(jiǎn)直是災(zāi)難,不但檢索麻煩忽舟,而且打開文件也很慢双妨,所以必須將這些代碼分散到多個(gè)文件中。
????對(duì)于多個(gè)文件的程序叮阅,通常是將函數(shù)定義放到源文件(.c文件)中刁品,將函數(shù)的聲明放到頭文件(.h文件)中,使用函數(shù)時(shí)引入對(duì)應(yīng)的頭文件就可以浩姥,編譯器會(huì)在鏈接階段找到函數(shù)體挑随。
4、C語(yǔ)言遞歸函數(shù)
一個(gè)函數(shù)在它的函數(shù)體內(nèi)調(diào)用它自身稱為遞歸調(diào)用勒叠,這種函數(shù)稱為遞歸函數(shù)兜挨。
執(zhí)行遞歸函數(shù)將反復(fù)調(diào)用其自身膏孟,每調(diào)用一次就進(jìn)入新的一層,當(dāng)最內(nèi)層的函數(shù)執(zhí)行完畢后拌汇,再一層一層地由里到外退出柒桑。
下面我們通過一個(gè)求階乘的例子,看看遞歸函數(shù)到底是如何運(yùn)作的噪舀。階乘 n! 的計(jì)算公式如下:
#include <stdio.h>
//求n的階乘
long factorial(int n) {
if (n == 0 || n == 1) {
return 1;
}else {
return factorial(n - 1) * n; // 遞歸調(diào)用
}
}
int main() {
int a;
printf("Input a number: ");
scanf("%d", &a);
printf("Factorial(%d) = %ld\n", a, factorial(a));
return 0;
}
?遞歸函數(shù)有巨大的時(shí)間開銷和內(nèi)存開銷幕垦,有這種玩兒法,但不推薦大家在項(xiàng)目中去使用傅联,一般情況下我們都是用循環(huán)去解決問題先改,你仔細(xì)想想其實(shí)據(jù)遞歸不就是另類的循環(huán)嗎:
//求階乘
long factorial(int n){
int i;
long result=1;
for(i=1; i<=n; i++){
result *= i;
}
return result;
}
return 0和exit(0)的區(qū)別
簡(jiǎn)單來說:
exit(0):退出程序;
return 0:退出函數(shù)蒸走,返回值仇奶。
詳細(xì)說:
1)return返回函數(shù)值,是關(guān)鍵字比驻; exit 是一個(gè)函數(shù)该溯。
2)return是語(yǔ)言級(jí)別的,它表示了調(diào)用堆棧的返回别惦;而exit是系統(tǒng)調(diào)用級(jí)別的狈茉,它表示了一個(gè)進(jìn)程的結(jié)束。
3)return是函數(shù)的退出(返回)掸掸;exit是進(jìn)程的退出氯庆。
4)return是C語(yǔ)言提供的,exit是操作系統(tǒng)提供的(或者函數(shù)庫(kù)中給出的)扰付。
5)return用于結(jié)束一個(gè)函數(shù)的執(zhí)行堤撵,將函數(shù)的執(zhí)行信息傳出給其他調(diào)用函數(shù)使用;exit函數(shù)是退出應(yīng)用程序羽莺。
6)非主函數(shù)中調(diào)用return和exit效果很明顯实昨,但是在main函數(shù)中調(diào)用return和exit的現(xiàn)象就很模糊,多數(shù)情況下現(xiàn)象都是一致的盐固。
指針
1荒给、什么是指針
???C語(yǔ)言用變量來存儲(chǔ)數(shù)據(jù),用函數(shù)定義一段可以重復(fù)使用的代碼刁卜,它們最終都要放到內(nèi)存中才能供 CPU 使用志电。
???不同類型的數(shù)據(jù)占用的字節(jié)數(shù)不一樣,例如 int 占用 4 個(gè)字節(jié)长酗,char 占用 1 個(gè)字節(jié)溪北。
???為了正確地訪問這些數(shù)據(jù)和指令桐绒,必須為每個(gè)字節(jié)空間上都編上號(hào)碼夺脾,CPU運(yùn)算時(shí)根據(jù)編號(hào)可以準(zhǔn)確地找到對(duì)應(yīng)的數(shù)據(jù)和指令之拨,這個(gè)編號(hào)前面講過稱為內(nèi)存地址(Address)。
????CPU 讀取指令和數(shù)據(jù)時(shí)需要的是地址咧叭,不是變量名和函數(shù)名蚀乔!
變量名和函數(shù)名只是地址的一種助記符,當(dāng)源程序文件被編譯和鏈接成可執(zhí)行程序后菲茬,它們都會(huì)被替換成地址吉挣。
???假設(shè)變量 a、b婉弹、c 在內(nèi)存中的地址分別是 0X1000睬魂、0X2000、0X3000镀赌,
那么加法運(yùn)算c = a + b;將會(huì)被轉(zhuǎn)換成類似的形式:0X3000 = (0X1000) + (0X2000);
??( )表示取值操作氯哮,整個(gè)表達(dá)式的意思是,取出地址 0X1000 和 0X2000 上的值商佛,將它們相加喉钢,把相加的結(jié)果保存到地址為 0X3000 的內(nèi)存空間。
???普通變量名良姆、函數(shù)名肠虽、字符串變量名和數(shù)組變量名在本質(zhì)上是一樣的,它們都是地址的助記符玛追,在編寫代碼的過程中税课,我們認(rèn)為普通變量名表示的是數(shù)據(jù)本身,而函數(shù)名痊剖、字符串變量名和數(shù)組變量名表示的是代碼塊或數(shù)據(jù)塊的首地址伯复。
??????指針就是內(nèi)存地址,是變量的地址邢笙,或函數(shù)的入口地址啸如。
指針變量的定義和使用
???如果一個(gè)變量里儲(chǔ)的值是另外一份存儲(chǔ)數(shù)據(jù)的內(nèi)存塊的首地址,我們就稱它為指針變量氮惯。
???指針變量的值就是保存了某份數(shù)據(jù)的內(nèi)存空間的首地址叮雳,這樣的一份數(shù)據(jù)可以是數(shù)組、字符串妇汗、函數(shù)帘不,也可以是另外的一個(gè)普通變量或指針變量。
?定義指針變量
定義指針變量與定義普通變量非常類似杨箭,不過要在變量名前面加星號(hào)*寞焙,格式為:
datatype *name;
或者
datatype *name = value;
*表示這是一個(gè)指針變量,datatype表示該指針變量所指向的數(shù)據(jù)的類型 。例如:
int *p1;
p1 是一個(gè)指向 int 類型數(shù)據(jù)的指針變量捣郊,至于 p1 究竟指向哪一份數(shù)據(jù)辽狈,應(yīng)該由賦予它的值決定。再如:
int a = 100;
int *p_a = &a;
???在定義指針變量 p_a 的同時(shí)對(duì)它進(jìn)行初始化呛牲,并將變量 a 的地址賦予它刮萌,此時(shí) p_a 就指向了 a。
??????和普通變量一樣娘扩,指針變量也可以被多次寫入着茸,只要你想,隨時(shí)都能夠改變指針變量的值琐旁,請(qǐng)看下面的代碼:
//定義普通變量
float a = 99.5, b = 10.6;
char c = '@', d = '#';
//定義指針變量
float *p1 = &a;
char *p2 = &c;
//修改指針變量的值
p1 = &b;
p2 = &d;
???*
是一個(gè)特殊符號(hào)涮阔,表明一個(gè)變量是指針變量,定義 p1灰殴、p2 時(shí)必須帶澎语。
而給 p1、p2 賦值時(shí)验懊,因?yàn)橐呀?jīng)知道了它是一個(gè)指針變量擅羞,就沒必要多此一舉再帶上,
后邊可以像使用普通變量一樣來使用指針變量义图。
也就是說减俏,定義指針變量時(shí)必須帶,給指針變量賦值時(shí)不能帶碱工。
???假設(shè)變量 a娃承、b、c怕篷、d 的地址分別為 0X1000历筝、0X1004、0X2000廊谓、0X2004梳猪,下面的示意圖很好地反映了 p1、p2 指向的變化:
???需要強(qiáng)調(diào)的是蒸痹,p1春弥、p2 的類型分別是float *和char *,而不是float和char叠荠,它們是完全不同的數(shù)據(jù)類型匿沛!
???指針變量也可以連續(xù)定義,例如:
int *a, *b, *c; //a榛鼎、b逃呼、c 的類型都是 int*
通過指針變量取得和修改指針變量指向的內(nèi)存地址里保存的數(shù)據(jù)
???指針變量存儲(chǔ)了數(shù)據(jù)的地址鳖孤,通過指針變量能夠獲得該地址上的數(shù)據(jù),格式為:pointer;
這里的稱為指針運(yùn)算符抡笼,用來取得某個(gè)地址上的數(shù)據(jù)苏揣,請(qǐng)看下面的例子:
#include <stdio.h>
int main(){
int a = 15;
int *p_a = &a;
printf("%d, %d\n", a, *p_a); //兩種方式都可以輸出a的值
return 0;
}
假設(shè)變量 a、p_a 的地址分別為 0X1000蔫缸、0XF0A0,它們的指向關(guān)系如下圖所示:
???程序被編譯和鏈接后际起,a拾碌、p_a 被替換成相應(yīng)的地址。使用 *p_a 的話街望,要先通過地址 0XF0A0 取得變量 p_a 保存的值校翔,這個(gè)值是變量 a 的地址,然后再通過這個(gè)值取得變量 a 的數(shù)據(jù)灾前,前后共有兩次運(yùn)算防症;而使用 a 的話,可以通過地址 0X1000 直接取得它的數(shù)據(jù)哎甲,只需要一步運(yùn)算蔫敲。
???也就是說,使用指針是間接獲取數(shù)據(jù)炭玫,使用變量名是直接獲取數(shù)據(jù)奈嘿,前者比后者的代價(jià)要高。
???指針除了可以獲取內(nèi)存上的數(shù)據(jù)吞加,也可以修改內(nèi)存上的數(shù)據(jù)裙犹,例如:
#include <stdio.h>
int main(){
int a = 15, b = 99, c = 222;
int *p = &a; //定義指針變量
*p = b; //通過指針變量修改內(nèi)存上的數(shù)據(jù)
c = *p; //通過指針變量獲取內(nèi)存上的數(shù)據(jù)
printf("%d, %d, %d, %d,%#X\n", a, b, c, *p, p); //99,99,99,99,0X61FE0C
return 0;
}
‘*’總結(jié):
1)定義指針變量時(shí)的和使用指針變量時(shí)的意義完全不同。
int *p = &a;
*p = 100;
2)給指針變量本身賦值時(shí)不能加*衔憨。
int *p;
p = &a;
*p = 100;
3)指針變量也可以出現(xiàn)在普通變量能出現(xiàn)的任何表達(dá)式中叶圃。
int x, y, *px = &x, *py = &y;
y = *px + 5; //表示把x的內(nèi)容加5并賦給y,*px+5相當(dāng)于(*px)+5
y = ++*px; //px的內(nèi)容加上1之后賦給y践图,++*px相當(dāng)于++(*px)
y = *px++; //相當(dāng)于y=*(px++)
py = px; //把一個(gè)指針的值賦給另一個(gè)指針
關(guān)于 * 和 & 的謎題
???假設(shè)有一個(gè) int 類型的變量 a掺冠,pa 是指向它的指針,那么*
&a和&*
pa分別是什么意思呢码党?
???*
&a可以理解為*
(&a)赫舒,&a表示取變量 a 的地址(等價(jià)于 pa),*
(&a)表示取這個(gè)地址上的數(shù)據(jù)(等價(jià)于 *pa)闽瓢,繞來繞去接癌,又回到了原點(diǎn),*
&a仍然等價(jià)于 a扣讼。
&*
pa可以理解為&(*
pa)缺猛,*
pa表示取得 pa 指向的數(shù)據(jù)(等價(jià)于 a),&(*
pa)表示數(shù)據(jù)的地址(等價(jià)于 &a),所以&*pa等價(jià)于 pa荔燎。
對(duì)星號(hào)的總結(jié)*
在我們目前所學(xué)到的語(yǔ)法中耻姥,星號(hào)*主要有三種用途:
1)表示乘法,例如int a = 3, b = 5, c; c = a * b;有咨,這是最容易理解的琐簇。
2)表示定義一個(gè)指針變量,以和普通變量區(qū)分開座享,例如int a = 100; int *p = &a;婉商。
3)表示獲取指針指向的數(shù)據(jù),是一種間接操作渣叛,例如int a, b, *p = &a; *p = 100; b = *p;丈秩。
3、指針變量的運(yùn)算
???指針變量保存的是地址淳衙,而地址本質(zhì)上是一個(gè)整數(shù)蘑秽,所以指針變量可以進(jìn)行運(yùn)算,例如加法箫攀、減法肠牲、比較等,請(qǐng)看下面的代碼:
#include <stdio.h>
int main(){
int a = 10, *pa = &a, *paa = &a;
double b = 99.9, *pb = &b;
char c = '@', *pc = &c;
//最初的值
printf("&a=%d, &b=%d, &c=%d\n", &a, &b, &c); //&a=-101024084, &b=-101024096, &c=-101024097
printf("pa=%d, pb=%d, pc=%d\n", pa, pb, pc); //&a=-101024084, &b=-101024096, &c=-101024097
//加法運(yùn)算
pa++; pb++; pc++;
printf("pa=%d, pb=%d, pc=%d\n", pa, pb, pc); //pa=-101024080, pb=-101024088, pc=-101024096
//減法運(yùn)算
pa -= 2; pb -= 2; pc -= 2;
printf("pa=%d, pb=%d, pc=%d\n", pa, pb, pc); //pa=-101024088, pb=-101024104, pc=-101024098
//比較運(yùn)算
if(pa == paa){
printf("pa和paa都指向同一個(gè)地址的數(shù)據(jù):%d\n", *paa);
}else{
printf("pa和paa都指向的不是同一個(gè)地址的數(shù)據(jù),*pa:%d,*paa:%d\n", *pa,*paa); //*pa:4199705,*paa:10
}
return 0;
}
從運(yùn)算結(jié)果可以看出:pa靴跛、pb埂材、pc 每次加 1,它們的地址分別增加 4汤求、8俏险、1,正好是 int扬绪、double竖独、char 類型的長(zhǎng)度;減 2 時(shí)挤牛,地址分別減少 8莹痢、16、2墓赴,正好是 int竞膳、double、char 類型長(zhǎng)度的 2 倍诫硕。
----這很奇怪坦辟,指針變量加減運(yùn)算的結(jié)果跟數(shù)據(jù)類型的長(zhǎng)度有關(guān),而不是簡(jiǎn)單地加 1 或減 1章办,這是為什么呢锉走?
-----以 a 和 pa 為例滨彻,a 的類型為 int,占用 4 個(gè)字節(jié)挪蹭,pa 是指向 a 的指針亭饵,如下圖所示:
剛開始的時(shí)候,pa 指向 a 的開頭梁厉,通過 *pa 讀取數(shù)據(jù)時(shí)辜羊,從 pa 指向的位置向后移動(dòng) 4 個(gè)字節(jié),把這 4 個(gè)字節(jié)的內(nèi)容作為要獲取的數(shù)據(jù)词顾,這 4 個(gè)字節(jié)也正好是變量 a 占用的內(nèi)存八秃。
如果pa++;使得地址加 1 的話,就會(huì)變成如下圖所示的指向關(guān)系:
這個(gè)時(shí)候 pa 指向整數(shù) a 的中間计技,*pa 使用的是紅色虛線畫出的 4 個(gè)字節(jié)喜德,其中前 3 個(gè)是變量 a 的山橄,后面 1 個(gè)是其它數(shù)據(jù)的垮媒,把它們“攪和”在一起顯然沒有實(shí)際的意義,取得的數(shù)據(jù)也會(huì)非常怪異航棱。
如果pa++;使得地址加 4 的話睡雇,正好能夠完全跳過整數(shù) a,指向它后面的內(nèi)存饮醇,
-----我們知道它抱,數(shù)組中的所有元素在內(nèi)存中是連續(xù)排列的,如果一個(gè)指針指向了數(shù)組中的某個(gè)元素朴艰,那么加 1 就表示指向下一個(gè)元素观蓄,減 1 就表示指向上一個(gè)元素,這樣指針的加減運(yùn)算就具有了現(xiàn)實(shí)的意義祠墅,這個(gè)有意義的應(yīng)用我們后面在具體寫demo來研究侮穿。
------不過C語(yǔ)言并沒有規(guī)定變量的存儲(chǔ)方式,如果連續(xù)定義多個(gè)變量毁嗦,它們有可能是挨著的亲茅,也有可能是分散的,這取決于變量的類型狗准、編譯器的實(shí)現(xiàn)以及具體的編譯模式克锣,所以對(duì)于指向普通變量的指針,做加減運(yùn)算沒有意義腔长,因?yàn)椴恢浪竺嬷赶虻氖鞘裁磾?shù)據(jù)袭祟。
------指針變量除了可以參與加減運(yùn)算,還可以參與比較運(yùn)算。當(dāng)對(duì)指針變量進(jìn)行比較運(yùn)算時(shí)轴咱,比較的是指針變量本身的值,也就是數(shù)據(jù)的地址分瘾。如果地址相等想鹰,那么兩個(gè)指針就指向同一份數(shù)據(jù)紊婉,否則就指向不同的數(shù)據(jù)。
-----另外需要說明的是辑舷,不能對(duì)指針變量進(jìn)行乘法喻犁、除法、取余等其他運(yùn)算何缓,除了會(huì)發(fā)生語(yǔ)法錯(cuò)誤肢础,也沒有實(shí)際的含義。
4碌廓、數(shù)組指針
數(shù)組(Array)是一系列具有相同類型的數(shù)據(jù)的集合传轰,每一份數(shù)據(jù)叫做一個(gè)數(shù)組元素(Element)。數(shù)組中的所有元素在內(nèi)存中是連續(xù)排列的谷婆,整個(gè)數(shù)組占用的是一塊內(nèi)存慨蛙。以int arr[] = { 99, 15, 100, 888, 252 };為例,該數(shù)組在內(nèi)存中的分布如下圖所示:
定義數(shù)組時(shí)纪挎,要給出數(shù)組名和數(shù)組長(zhǎng)度期贫,數(shù)組變量名可以被認(rèn)為就是一個(gè)指針,它指向數(shù)組的第 0 個(gè)元素异袄。在C語(yǔ)言中通砍,我們將第 0 個(gè)元素的地址稱為數(shù)組的首地址。以上面的數(shù)組為例烤蜕,下圖是 arr 的指向:
結(jié)合我們前面分析的指針變量的加減運(yùn)算封孙,循環(huán)遍歷數(shù)組元素,我們還可以這樣干:
#include <stdio.h>
int main(){
int arr[] = { 99, 15, 100, 888, 252 };
int len = sizeof(arr) / sizeof(int); //求數(shù)組長(zhǎng)度
int i;
for(i=0; i<len; i++){
printf("%d ", *(arr+i) ); //*(arr+i)等價(jià)于arr[i]
}
printf("\n");
return 0;
}