C語(yǔ)言基礎(chǔ)

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í)候用。

image.png

image.png

驗(yàn)證是否成功配置
image.png

記事本上寫第一個(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了。

image.png

image.png

????上面這樣做很麻煩茸苇,只是為了讓大家了解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)試则果。

image.png

image.png

編輯工具

下載安裝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功能


    image.png

    image.png

載入寫好的hello.c文件
??打開vscode載入剛剛的c文件做修,網(wǎng)上網(wǎng)友說編譯c文件要打開文件所在的文件夾霍狰,我嘗試了下,確實(shí)是這樣的饰及。如下圖:

image.png

然后點(diǎn)運(yùn)行--啟動(dòng)調(diào)試蔗坯,他會(huì)跳出一個(gè)launch.json的一個(gè)配置文件:是要修改的
image.png

image.png

{
 // 使用 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è)的辑鲤!

image.png

image.png

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ā)宁赤。

image.png

????安裝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)中

image.png

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).

image.png

進(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ù)器沒有成功過

image.png

image.png

或按win鍵+R鍵,再輸入如下圖所示
image.png

image.png

**若點(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)制轉(zhuǎn)換1.jpeg

二進(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)換2.jpeg

二進(jìn)制轉(zhuǎn)十進(jìn)制
方法為:把二進(jìn)制數(shù)按權(quán)展開、相加即得十進(jìn)制數(shù)滞诺。
(具體用法如下圖)
進(jìn)制轉(zhuǎn)換3.jpeg

二進(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朵耕。
(具體用法如下圖)

進(jìn)制轉(zhuǎn)換4.jpeg

注意: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ǔ)零。
(具體用法如下圖)
進(jìn)制轉(zhuǎn)換4.jpeg

補(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é)具體講解:

clipboard.png

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):

clipboard (1).png

符號(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)部使用的變量

clipboard.png

變量前的修飾符
?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);
}

image.png

注意:對(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……

1045231316-0.png

這個(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ù)锈津。

clipboard.png

緩沖區(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)效果圖

image.png

下面的代碼開啟了緩沖區(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;
}

image.png

我們可以手動(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;
}

image.png

處理方法:

#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. 修改“從1加到100的和”的代碼渴丸,省略“表達(dá)式1(初始化條件)”
int i = 1, sum = 0;
for( ; i<=100; i++){
    sum+=i;
}
  1. 省略了“表達(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++;
}
  1. 省略了“表達(dá)式3”谱轨,這時(shí)可在循環(huán)體中加入修改變量的語(yǔ)句戒幔。例如:
for(int i=1; i<=100; ){
    sum=sum+i;
    i++;
} 
  1. 省略了“表達(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++;
}
  1. 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):

  1. 可以只給部分元素賦值壤靶。當(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忧换。

  2. 只能給元素逐個(gè)賦值,不能給數(shù)組整體賦值向拆。例如給 10 個(gè)元素全部賦值為 1亚茬,只能寫作:
    int a[10] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
    而不能寫作:
    int a[10] = 1;

  3. 如給全部元素賦值,那么在定義數(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;
}

需要注意的是:

  1. 數(shù)組中每個(gè)元素的數(shù)據(jù)類型必須相同手负,對(duì)于int a[4];,每個(gè)元素都必須為 int姑尺。
  2. 數(shù)組長(zhǎng)度 length 最好是整數(shù)或者常量表達(dá)式竟终,例如 10、204 等切蟋,這樣在所有編譯器下都能運(yùn)行通過统捶;
    如果 length 中包含了變量,例如 n柄粹、4
    m 等喘鸟,在某些編譯器下就會(huì)報(bào)錯(cuò),某些不會(huì)驻右。
  3. 訪問數(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 指向的變化:

1IG3J50-1.jpeg

???需要強(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)系如下圖所示:

clipboard (1).png

???程序被編譯和鏈接后际起,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 的指針亭饵,如下圖所示:


1J0563E7-0.jpeg

剛開始的時(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)系:

1J0563064-1.jpeg

這個(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)存饮醇,


1J05622E-2.jpeg

-----我們知道它抱,數(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)存中的分布如下圖所示:

1J35014B-0.jpeg

定義數(shù)組時(shí)纪挎,要給出數(shù)組名和數(shù)組長(zhǎng)度期贫,數(shù)組變量名可以被認(rèn)為就是一個(gè)指針,它指向數(shù)組的第 0 個(gè)元素异袄。在C語(yǔ)言中通砍,我們將第 0 個(gè)元素的地址稱為數(shù)組的首地址。以上面的數(shù)組為例烤蜕,下圖是 arr 的指向:

2.jpeg

結(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;
}

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末讽营,一起剝皮案震驚了整個(gè)濱河市虎忌,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌斑匪,老刑警劉巖呐籽,帶你破解...
    沈念sama閱讀 206,378評(píng)論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異蚀瘸,居然都是意外死亡狡蝶,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評(píng)論 2 382
  • 文/潘曉璐 我一進(jìn)店門贮勃,熙熙樓的掌柜王于貴愁眉苦臉地迎上來贪惹,“玉大人,你說我怎么就攤上這事寂嘉∽嗨玻” “怎么了枫绅?”我有些...
    開封第一講書人閱讀 152,702評(píng)論 0 342
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)硼端。 經(jīng)常有香客問我并淋,道長(zhǎng),這世上最難降的妖魔是什么珍昨? 我笑而不...
    開封第一講書人閱讀 55,259評(píng)論 1 279
  • 正文 為了忘掉前任县耽,我火速辦了婚禮,結(jié)果婚禮上镣典,老公的妹妹穿的比我還像新娘兔毙。我一直安慰自己,他們只是感情好兄春,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評(píng)論 5 371
  • 文/花漫 我一把揭開白布澎剥。 她就那樣靜靜地躺著,像睡著了一般赶舆。 火紅的嫁衣襯著肌膚如雪哑姚。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,036評(píng)論 1 285
  • 那天涌乳,我揣著相機(jī)與錄音蜻懦,去河邊找鬼甜癞。 笑死夕晓,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的悠咱。 我是一名探鬼主播蒸辆,決...
    沈念sama閱讀 38,349評(píng)論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼析既!你這毒婦竟也來了躬贡?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評(píng)論 0 259
  • 序言:老撾萬榮一對(duì)情侶失蹤眼坏,失蹤者是張志新(化名)和其女友劉穎拂玻,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宰译,經(jīng)...
    沈念sama閱讀 43,469評(píng)論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡檐蚜,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評(píng)論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了沿侈。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片闯第。...
    茶點(diǎn)故事閱讀 38,059評(píng)論 1 333
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖缀拭,靈堂內(nèi)的尸體忽然破棺而出咳短,到底是詐尸還是另有隱情填帽,我是刑警寧澤,帶...
    沈念sama閱讀 33,703評(píng)論 4 323
  • 正文 年R本政府宣布咙好,位于F島的核電站篡腌,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏勾效。R本人自食惡果不足惜哀蘑,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評(píng)論 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望葵第。 院中可真熱鬧绘迁,春花似錦、人聲如沸卒密。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評(píng)論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)哮奇。三九已至膛腐,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間鼎俘,已是汗流浹背哲身。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評(píng)論 1 262
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留贸伐,地道東北人勘天。 一個(gè)月前我還...
    沈念sama閱讀 45,501評(píng)論 2 354
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像捉邢,于是被迫代替她去往敵國(guó)和親脯丝。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評(píng)論 2 345

推薦閱讀更多精彩內(nèi)容

  • C是什么樣的語(yǔ)言 為了解決眼前問題由開發(fā)現(xiàn)場(chǎng)的人發(fā)明的 雖然使用方便伏伐,但看上去不怎么順眼宠进。 不熟悉的人糊里糊涂地使...
    JunChow520閱讀 383評(píng)論 0 0
  • C語(yǔ)言的歷史 C語(yǔ)言的原型是A語(yǔ)言(ALGOL 60語(yǔ)言)。 1963年赤惊,劍橋大學(xué)將ALGOL 60語(yǔ)言發(fā)展成為C...
    沐森老師閱讀 191評(píng)論 0 0
  • 1吼旧、C語(yǔ)言的歷史(了解) 歡迎來到C語(yǔ)言的世界!C語(yǔ)言是一種強(qiáng)大的專業(yè)化編程語(yǔ)言未舟,深受業(yè)余和專業(yè)編程人員的歡迎圈暗。在...
    百度搜不到哥的微笑閱讀 780評(píng)論 0 3
  • 姓名:呂彬 學(xué)號(hào):1613014035 【嵌牛導(dǎo)讀】C語(yǔ)言是目前世界上流行掂为、使用非常廣泛的高級(jí)程序設(shè)計(jì)語(yǔ)言。 C語(yǔ)...
    傻彬兒閱讀 949評(píng)論 0 1
  • 1.c語(yǔ)言的發(fā)展歷史 --C語(yǔ)言的原型ALGOL 60語(yǔ)言(也稱為A語(yǔ)言)员串,最早的塊結(jié)構(gòu)語(yǔ)言勇哗。 ...
    積木同學(xué)閱讀 726評(píng)論 0 2