網(wǎng)站滾動效果怎么做站長工具怎么關(guān)掉
?一、數(shù)據(jù)類型 數(shù)據(jù)的輸入輸出
1.數(shù)據(jù)類型 常量變量
1.1 數(shù)據(jù)類型
1.2 常量
?? 程序運行中值不發(fā)生變化的量,常量又可分為整型、實型(也稱浮點型)、字符型和字符串型
1.3 變量
變量代表內(nèi)存中具有特定屬性的存儲單元,用來存放數(shù)據(jù),即變量的值,這些值在程序的執(zhí)行過程中是可以改變的
命名規(guī)則:只能由字母、數(shù)字、下劃線組成,并且第一個字符必須為字母或下劃線。
大寫和小寫是不同的字符。
1.4 整型數(shù)據(jù)
???1.4.1符號常量
#include<stdio.h>
//符號常量
#define PI 3+2//符號常量不需要=賦值,也不需要分號
int main(){int i=PI*2;//i就是一個整型變量printf("i=%d\n",i);//printf是用來輸出的
}
????//最終的輸出結(jié)果是7 ,符號常量PI是直接替換的效果 。3+2*2=7,不等于8
????1.4.2整型變量 int i
? ? 整型變量i是4個字節(jié)
printf("i size=%d\n",sizeof(i));//sizeof可以用來計算某個變量的空間大小
? ? //輸出i size =4
1.5 浮點型數(shù)據(jù)
? ? 1.5.1 浮點型常量
????1.5.2 浮點型變量
? ? ?通過float f來定義浮點變量,f占用4個字節(jié)的空間
#include<stdio.h>
int main(){float f=3e-3;printf("f=%f\n",f);
}
//輸出f=0.003000
1.6 字符型數(shù)據(jù)
1.6.1 字符型常量
用單引號括起來的一個字符是字符型常量,且只能包含一個 字符!例如,'a'、'A'、'l'、' '是正確的字符型常量,而'abc'、"a"、" "是錯誤的字符型常量
轉(zhuǎn)義字符及其作用
\n | 換行 |
\b | 退格 |
\\ | 反斜杠 |
1.6.2 字符數(shù)據(jù)在內(nèi)存中的存儲形式及其使用方法
字符用char定義,一個字符變量占一個字節(jié)
一個字符常量存放到字符型變量中,實際上是把該字符的ASCII碼值存放到內(nèi)存單元中。
#include<stdio.h>
//大寫表小寫
int main(){char c='A'; //ASCII表中A=65 a=97printf("%c\n",c+32);//以字符形式輸出? %c打印出來的都是字符printf("%d\n",c);//以數(shù)值形式輸出? %d打印出來的都是ASCII碼值
}
1.7 字符串型常量
由一對雙引號括起來的字符序列 例如"How do you do"、"CHINA"、"$123.45"、"a"等都是合法的字符串常量
注意"a" 占用的是兩個字節(jié)
"CHINA"占用的是6個字節(jié),最后一個字符為'\0' 輸出是不輸出,因為無法顯示
C | H | I | N | A | \0 |
如上面,為CHINA在內(nèi)存中的存儲結(jié)果,所以字符的字節(jié)都比里面的字母要多一個
1.8 ASCII碼表
2.混合運算
2.1 類型強制轉(zhuǎn)換場景
整型數(shù)進行除法運算時,如果運算結(jié)果為小數(shù),那么浮點數(shù)時一定要進行強制類型轉(zhuǎn)換
#include<stdio.h>
//強制類型轉(zhuǎn)換
int main(){int i=5;float f=i/2;//這里做的是整型運算,因為左右操作數(shù)都是整型float k=(float)i/2; //(float)i 強制類型轉(zhuǎn)換式 是浮點型printf("%f\n",f);printf("%f\n",k);return 0;
}
f得到的是2
k得到的才是2.5
2.2 printf函數(shù)介紹
printf函數(shù)可以輸出各種類型的數(shù)據(jù),包括整型、浮點型、字符型、字符串型等,實際原理是printf函數(shù)將這些類型的數(shù)據(jù)格式化為字符串后,放入標準緩沖區(qū),然后將結(jié)果顯示到屏幕上
語法如下:
#include<studio.h>
int printf(const char*format,…);
printf函數(shù)根據(jù)format給出的格式打印輸出到stdout(標準輸出)和其他參數(shù)中
int age =21;
printf("Hello %s,you are %d years old \n","Bob",age);
代碼的輸出如下:
Hello Bob,you are 21 years old
其中,%s表示在該位置插入首個參數(shù)(一個字符串),%d表示第二個參數(shù)(一個整數(shù))應(yīng)該放在那里,不同的%codes表示不同的變量類型,也可以限制變量的長度,printf函數(shù)如下:
代碼 | 格式 |
%c | 字符 |
%d | 帶符號整數(shù) |
%f | 浮點數(shù) |
%s | 一串字符 |
%u | 無符號整數(shù) |
%x | 無符號十六進制數(shù),用小寫字母 |
%X | 無符號十六進制數(shù),用大寫字母 |
%p | 一個指針 |
%% | 一個'%'符號 |
位于%和格式化命令之間的一個整數(shù)被稱為最小字段寬度說明符,通常會加上空格來控制格式。
- 用%f精度修飾符指定想要的小數(shù)位數(shù),例如,%5.2f會至少顯示5位數(shù)字并帶有2位小數(shù)點的浮點數(shù)。
- 用%s精度修飾符簡單地表示一個最大的長度,以補充句點前的最小字段長度
printf函數(shù)的所有輸出都是右對齊的,除非在%符號后放置了負號
例如:%-5.2f會顯示5位字符、2位小數(shù)位的浮點數(shù)并且左對齊
#include<stdio.h>
//練習printf
int main(){int i=10;float f=96.3;printf("student number=%-3d,score=%5.2f\n",i,f);//默認是右對齊,加一個負號代表左對齊i=100;f=98.21;printf("studentnumber=%3d,score=%5.2f\n",i,f);? //3d表示后面 的數(shù)據(jù)輸出時會占三個空格的位置return 0;
}
3.整型進制轉(zhuǎn)換
3.1整型常量的不同進制表示
計算機中只能存儲二進制數(shù),即0和1,而在對應(yīng)的物理硬件上則是高、低電平.為了更方便地觀察內(nèi)存中的二進制數(shù)情況,除我們正常使用的十進制數(shù)外,計算機還提供了十六進制數(shù)和八進制數(shù).
在計算機中,1字節(jié)為8位,1位即二進制的1位,它存儲0或1。 int型常量的大小為4字節(jié),即32位。
二進制 0和1
0101 0101 1個字節(jié)byte ,有8個位,bit
1KB = 1024 字節(jié)
1MB = 1024Kb
1GB = 1024MB
十進制 0-9
八進制 0-7???????????? 十進制轉(zhuǎn)八進制 除18
十六進制 0-9 A-F?? 十進制轉(zhuǎn)十六進制 除16
123為十進制???? 轉(zhuǎn)二進制? 除2 余數(shù)倒看
2? |123??? 1
2??? |61??? 1
2??? |30??? 0
2??? |15??? 1
2????? |7??? 1
2????? |3??? 1
2????? |1??? 1
???????? 0???????????????? 從下往上為二進制 即0000 0000 0000 0000 0000 0111 1011
123轉(zhuǎn)十六進制
16? |123??? 11???? 由于十六進制沒有11? 所以為7b
??????????? 7
二進制轉(zhuǎn)十六進制
每四位對應(yīng)十六進制的一個數(shù)字
0000 0000 0000 0000 0000 0111 1011? 二進制
?
? ? ?7? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? b
二進制轉(zhuǎn)八進制??
每三位對應(yīng)一個
001 111 011
1???? 7???? 3
八進制轉(zhuǎn)十進制
#include<stdio.h>
int main(){int i=0x7b;? //賦值八進制前面需要加個0 【0173】 十六進制前面需要加個0x 【0x7b】 十進制直接輸123printf("%d\n",i);//十進制輸出 ???123printf("%o\n",i);//%o八進制?? ???173printf("%x\n",i);//%s十六進制 ???7breturn 0;
}
debug查看 打斷點 點開內(nèi)存視圖 輸入&i 回車查看
4.scanf讀取標準輸入
4.1?scanf函數(shù)的原理
C語言未提供輸入/輸出關(guān)鍵字,其輸入和輸出是通過標準函數(shù)庫來實現(xiàn)的。C語言通過scanf 函數(shù)讀取鍵盤輸入,鍵盤輸入又被稱為標準輸入,當scanf函數(shù)讀取標準輸入時,如果還沒有輸入任何內(nèi)容,那么scanf函數(shù)會被卡住(專業(yè)用語為阻塞).如下例
#include<stdio.h>
//scanf用來讀取標準輸入,scanf把標準輸入內(nèi)的內(nèi)容,需要放到某個變量空間里,因此變量必須取地址
//scanf會阻塞,是因為標準輸入緩沖區(qū)是空的
int main(){int i;char c;float f;scanf("%d",&i); //注意一定要取地址&iprintf("i=%d\n",i);//把標準緩沖區(qū)中的整型數(shù)讀走了fflush(stdin);//清空標準輸入緩沖區(qū)? 不清空的話下面的輸出會卡住scanf("%c",&c);printf("c=%c\n",c);//輸出字符變量c//scanf("%f",&f);//printf("f=%f\n",f);return 0;
}
執(zhí)行時輸入20,然后回車,顯示結(jié)果為
20
20
c=c
進程已結(jié)束,退出代碼為0
4.2 多種數(shù)據(jù)類型混合輸入
????????? 當我們讓 scanf函數(shù)一次讀取多種類型的數(shù)據(jù)時,要注意當一行數(shù)據(jù)中存在字符型數(shù)據(jù)讀取時,讀取的字符并不會忽略空格和'\n'(回車符),所以編寫代碼時,我們需要在%d與%c之間加入一個空格。輸入格式和輸出效果如下圖所示, scanf 函數(shù)匹配成功了4個成員,所以返回值為4,我們可以通過返回值來判斷scanf函數(shù)匹配成功了幾個成員,中間任何有一個成員匹配出錯,后面的成員都會匹配出錯.
#include<stdio.h>
//scanf一次讀多種數(shù)據(jù)類型
int main(){int i,ret;? //定義ret變量 是因為scanf是由返回值的float f;char c;ret=scanf("%d %c%f",&i,&c,&f);//ret是指scanf匹配成功的個數(shù)? 注意要在%c之前加個空格 不然后面的無法輸出 遇到%c前面就加空格printf("i=%d,c=%c,f=%5.2f\n",i,c,f);return 0;
}
二、運算符與表達式
1.運算符分類
C語言提供了13種類型的運算符,如下所示。
(1)算術(shù)運算符(+-*/ %) .
(2)關(guān)系運算符(><= >=<= !=).
(3)邏輯運算符(!&& ll) .
(4)位運算符(<< >> ~|^ &).
(5)賦值運算符(=及其擴展賦值運算符).
(6)條件運算符(?:).
(7)逗號運算符(,).
(8)指針運算符(*和&)---講指針時講解
(9)求字節(jié)數(shù)運算符(sizeof).
(10)強制類型轉(zhuǎn)換運算符((類型)).
(11)分量運算符(.->) 。---講結(jié)構(gòu)體時講解
(12)下標運算符([]) ?!?--講數(shù)組時講解
(13)其他(如函數(shù)調(diào)用運算符()) 。---講函數(shù)時講解
2.算術(shù)運算符及算術(shù)表達式
算術(shù)運算符包含+、一、*、/和%,當一個表達式中同時出現(xiàn)這5種運算符時,先進行乘(*)、除(/)、取余(%),取余也稱取模,后進行加(+)、減(-).
也就是乘、除、取余運算符的優(yōu)先級高于加、減運算符.
#include<stdio.h>
//練習算術(shù)運算符
int main(){int? result=4+5*2-6/3+11%4;printf("result=%d\n",result);return 0;
}
result=15
3.關(guān)系運算符與關(guān)系表達式
關(guān)系運算符>、<、==、>=、<=、!=依次為大于、小于、是否等于、大于等于、小于等于和不等于。由關(guān)系運算符組成的表達式稱為關(guān)系表達式。
關(guān)系表達式的值只有真和假,對應(yīng)的值為1和0。由于C語言中沒有布爾類型,所以在C語言中0值代表假,非0值即為真。
例如,關(guān)系表達式3>4為假,因此整體值為0,而關(guān)系表達式5>2為真,因此整體值為1。關(guān)系運算符的優(yōu)先級低于算術(shù)運算符
如果要判斷三個變量a、b、c是否相等,那么不能寫為a==b==c,而應(yīng)寫為a==b &&b==C
#include<stdio.h>
//關(guān)系運算符,優(yōu)先級小于算術(shù)運算符
int main(){int? a;while(scanf("%d",&a)){if(3<a&&a<10)//a大于3同時a小于10要這樣寫 不能寫成3<a<10{printf("a is between 3 and10\n");}else{printf("a is not between 3 and 10\n");}}return 0;
}
運算符的優(yōu)先級表
4.邏輯運算符與邏輯表達式
邏輯運算符!、&&、ll依次為邏輯非、邏輯與、邏輯或,這和數(shù)學上的與、或、非是一致的.邏輯非的優(yōu)先級高于算術(shù)運算符,邏輯與和邏輯或的優(yōu)先級低于關(guān)系運算符.
邏輯表達式的值只有真和假,對應(yīng)的值為1和0.
下例中的代碼是計算一年是否為閏年的例子,因為需要重復(fù)測試,所以我們用了一個 while循環(huán)。
#include<stdio.h>
//記住優(yōu)先級目的,不能夠加多余的括號
int main(){int year,i,j=6;while(scanf("%d",&year)){if(year%4==0&&year%100!=0||year%400==0){printf("%d is leap year\n",year);}else{printf("%d is not leap year\n",year);}}i=!!j;? //邏輯非? 非非j? 自右至左printf("i value=%d\n",i);return 0;
}
//邏輯與和邏輯或短路運算
int main(){int i=0;i&&printf("you can't see me!\n");//當i為假時,不會執(zhí)行邏輯與后的表達式,稱為短路運算? 等價于if(為真){}else{} 簡潔代碼i=1;i||printf("you can't see me!\n");return 0;
}
5.賦值運算符
a = b+ 25;
?a是一個左值,它標識了一個可以存儲結(jié)果值的地點; b+25是一個右值,因為它指定了一個值。
#include<stdio.h>
int main(){int a=1,b=2;b+25=a;? ? ? ? //b+25不能作為左值,因為它并未標識一個特定的位置(并不對應(yīng)特定的內(nèi)存空間)return 0;
}
上面的例子執(zhí)行時會報下面的編譯錯誤
Error:lvalue required as left operand of assignment
5.1復(fù)合賦值運算符
復(fù)合賦值運算符操作是一種縮寫形式,復(fù)合賦值運算符能使對變量的賦值操作變得更加簡潔。
iNum = iNum + 5;
對變量 iNum的賦值進行操作,值為這個變量本身與一個整型常量5相加的結(jié)果.使用復(fù)合語句可以實現(xiàn)同樣的操作。例如,上面的語句可以修改為
iNum+=5;
賦值運算符與復(fù)合賦值運算符的區(qū)別如下:
(1)復(fù)合賦值運算符簡化了程序,可使程序精煉,提升閱讀速度。
(2)復(fù)合賦值運算符提高了編譯效率.
int main(){int a=1,b=2;a+=3;b*=5;printf("a=%d\n",a);printf("b=%d\n",b);return 0;
}
6.求字節(jié)運算符sizeof
sizeof不是一個函數(shù),而是一個運算符,不像其他運算符是一個符號,sizeof是字母組成的,用于求常量或變量所占用的空間大小
#include<stdio.h>
//sizeof運算符
int main(){int i=0;printf("i size is%d\n",sizeof(i));return 0;
}
運行結(jié)果為i size is 4,可以求得整型變量占用的空間大小是4個字節(jié)。
三、選擇、循環(huán)
關(guān)系表達式與邏輯表達式
算術(shù)運算符的優(yōu)先級高于關(guān)系運算符、
關(guān)系運算符的優(yōu)先級高于邏輯與和邏輯或運算符
相同優(yōu)先級的運算符從左至右進行結(jié)合
表達式5>3&&8<4-!0的最終值是多少?其計算過程如下圖所示。
1.if-else語句
int main(){int year;scanf("%d",&year);if(year%4==0&&year%100!=0||year%400==0){printf("yes\n");}else{printf("no\n");}return 0;
}
if語句和else語句也可以多個同時使用(多分支語句)
2.while循環(huán)
while 語句用來實現(xiàn)“當型”循環(huán)結(jié)構(gòu),其一般形式為“while(表達式)語句;”,當表達式的值非0時,執(zhí)行while語句中的內(nèi)嵌語句。
其特點是:先判斷表達式,后執(zhí)行語句。
//計算從1到100的和
int main(){int i=1,total=0;while(i<=100)//在這里加分號會造成死循環(huán){if(i%2==0){i++;continue;//continue下面的代碼均不會得到執(zhí)行}total=total+i;//把i加到total上i++;//i++等價于i+=1;在循環(huán)體內(nèi)沒有讓while判斷表達式趨近于假的操作,死循環(huán)}printf("total=%d\n",total);return 0;
}
3.for循環(huán)
for循環(huán)語句中必須且只能有兩個分號,用于分割表達式1、表達式2、和表達式3
//for循環(huán)實現(xiàn)從1加到100
int main(){int i,total;for(i=1,total=0;i<=100;i++)//for小括號后不要加分號{total+=i;}printf("total=%d\n",total);return 0;
}
for循環(huán)的可讀性比while循環(huán)要好,所以能使用for循環(huán)時不要強制改為while循環(huán)
4.continue語句
continue語句的作用為結(jié)束本次循環(huán),即跳過循環(huán)體中下面尚未執(zhí)行的語句,接著進行是否執(zhí)行下一次循環(huán)的判斷
continue;
//for循環(huán)實現(xiàn)從1加到100
//使用continue
int main(){int i,total;for(i=1,total=0;i<=100;i++)//for小括號后不要加分號{if(i%2==0){continue;//continue下面的代碼均不會得到執(zhí)行 }total+=i;}printf("total=%d\n",total);return 0;
}
5.break語句
break語句的作用是結(jié)束整個循環(huán)過程,不再判斷執(zhí)行循環(huán)的條件是否成立
例:從1開始累加,當累加的和大于2000時,結(jié)束for循環(huán),同時打印此時total的值和i的值,一旦執(zhí)行break語句,下一句要執(zhí)行的就是“printf("total=%d,i=%d\n",total,i);”,break語句也可以用在while循環(huán)和do while循環(huán)中,起到結(jié)束對應(yīng)循環(huán)的作用
int main(){int i,total;for(i=1,total=0;i<=100;i++)//for小括號后不要加分號 {if(total>2000){break;//當和大于2000時,循環(huán)結(jié)束 }total+=i;}printf("total=%d,i=%d\n",total,i);return 0;
}
??四、一維數(shù)組與字符數(shù)組
1.一維數(shù)組
1.1數(shù)組的定義
數(shù)組,是指一組具 有相同數(shù)據(jù)類型的數(shù)據(jù)的有序集合??赏ㄟ^一個符號來 訪問多個元素
一維數(shù)組的定義格式為
類型說明符 數(shù)組名 [常量表達式];
int a[10];
?定義一個整型數(shù)組,數(shù)組名為 a,它有 10 個元素。
?聲明數(shù)組時要遵循以下規(guī)則:
(1)數(shù)組名的命名規(guī)則和變量名的相同,即遵循標識符命名規(guī)則。
(2)在定義數(shù)組時,需要指定數(shù)組中元素的個數(shù),方括號中的常量表達式用來表示元素的 個數(shù),即數(shù)組長度。
(3)常量表達式中可以包含常量和符號常量,但不能包含變量。也就是說,C 語言不允許 對數(shù)組的大小做動態(tài)定義,即數(shù)組的大小不依賴于程序運行過程中變量的值
以下是錯誤的聲明示例(最新的 C 標準支持,但是最好不要這么寫):
int n;
scanf("%d", &n); /* 在程序中臨時輸入數(shù)組的大小 */
int a[n];
數(shù)組聲明的其他常見錯誤如下:
① float a[0]; /* 數(shù)組大小為 0 沒有意義 */
② int b(2)(3); /* 不能使用圓括號 */
③ int k=3, a[k]; /* 不能用變量說明數(shù)組大小*/
1.2 一維數(shù)組在內(nèi)存中的存儲
語句 int mark[100];定義的一維數(shù)組 mark 在內(nèi)存中的存放情況如下圖所示,每個元素都是 整型元素,占用 4 字節(jié),數(shù)組元素的引用方式是“數(shù)組名[下標]”,所以訪問數(shù)組 mark 中的元素 的方式是 mark[0],mark[1],…,mark[99]。注意,沒有元素 mark[100],因為數(shù)組元素是從 0 開 始編號的
一維數(shù)組的初始化方法。
(1)在定義數(shù)組時對數(shù)組元素賦初值。例如,
int a[10]={0,1,2,3,4,5,6,7,8,9};
不能寫成
int a[10];a[10]={0,1,2,3,4,5,6,7,8,9}
(2)可以只給一部分元素賦值。例如,
int a[10]={0,1,2,3,4};
定義 a 數(shù)組有 10 個元素,但花括號內(nèi)只提供 5 個初值,這表示只給前 5 個元素賦初值,后 5 個
元素的值為 0。
(3)如果要使一個數(shù)組中全部元素的值為 0,那么可以寫為
int a[10]={0,0,0,0,0,0,0,0,0,0};
或
int a[10]={0};
(4)在對全部數(shù)組元素賦初值時,由于數(shù)據(jù)的個數(shù)已經(jīng)確定,因此可以不指定數(shù)組的長度。
int a[]={1,2,3,4,5};
2.數(shù)組的訪問越界
下面借助一個數(shù)組的實例來掌握數(shù)組元素的賦值、訪問越界。
int main()
{int a[5]={1,2,3,4,5}; //定義數(shù)組時,數(shù)組長度必須固定int j=20;int i=10;a[5]=6; //越界訪問a[6]=7; //越界訪問會造成數(shù)據(jù)異常printf("i=%d\n",i); //i 發(fā)生改變return 0;
}
下圖顯示了代碼運行情況。在內(nèi)存視圖依次輸入&j、&a、&i 來查看整型變量 j、整型數(shù)組 a、整型變量 i 的地址,即可看到三個變量的地址,這里就像我們給衣柜的每個格子的編號,第一格、第二格……一直到柜子的最后一格。操作系統(tǒng)對內(nèi)存中的每個位置也給予一個編號,對于 Windows 32 位控制臺應(yīng)用程序來說,這個編號的范圍是從 0x00 00 00 00 到 0xFF FF FF FF,總計為 2 的 32 次方,大小為4G。這些編號稱為地址(我們是 64 位程序,地址顯示的是 64)
在變量窗口中輸入sizeof(a),可以看到數(shù)組a的大小為20字節(jié),計算方法其實就是sizeof(int)*5: 數(shù)組中有 5 個整型元素,每個元素的大小為 4 字節(jié),所以共有 20 字節(jié)。訪問元素的順序是依次從 a[0]到 a[4],a[5]=6、a[6]=7 均為訪問越界。下圖顯示了代碼運行情況,從中看出,執(zhí)行到第 12 行時,變量 i 的值被修改了,這就是訪問越界的危險性——未對變量 i 賦值,其值卻發(fā)生了改變
數(shù)組另一個值得關(guān)注的地方是,編譯器并不檢查程序?qū)?shù)組下標的引用是否在數(shù)組的合法范圍內(nèi)。如果下標值 是通過那些已知正確的值計算得來的,那么就無須檢查;如果下標值是由用戶輸入的數(shù)據(jù)產(chǎn)生的, 那么在使用它們之前就必須進行檢查,以確保它們位于有效范圍內(nèi)。
3.數(shù)組的傳遞
//一維數(shù)組的傳遞,數(shù)組長度無法傳遞給子函數(shù)
//C 語言的函數(shù)調(diào)用方式是值傳遞
void print(int b[],int len)
{int i;for(i=0;i<len;i++){printf("%3d",b[i]);}b[4]=20; //在子函數(shù)中修改數(shù)組元素
printf("\n");
}//數(shù)組越界
//一維數(shù)組的傳遞
#define N 5
int main()
{int a[5]={1,2,3,4,5}; //定義數(shù)組時,數(shù)組長度必須固定print(a,5);printf("a[4]=%d\n",a[4]); //a[4]發(fā)生改變return 0;
}
如下圖 1 所示,進入 print 函數(shù),發(fā)現(xiàn)數(shù)組 b 的大小變?yōu)? 字節(jié),如下圖 2 所示,這是因為一維數(shù)組在傳遞時,其長度是傳遞不過去的,所以我們通過 len來傳遞數(shù)組中的元素個數(shù)。
實際數(shù)組名中存儲的是數(shù)組的首地址,在調(diào)用函數(shù)傳遞時,是將數(shù)組的首地址給了變量 b(其實變量 b 是指針類型,指針8個字節(jié)),在 b[]的方括號中填寫任何數(shù)字都是沒有意義的。
這時我們在 print 函數(shù)內(nèi)修改元素 b[4]=20,可以看到數(shù)組 b 的起始地址和 main 函數(shù)中數(shù)組 a 的起始地址相同,即二者在內(nèi)存中位于同一位置,函數(shù)執(zhí)行結(jié)束時,數(shù)組 a 中的元素 a[4]就得到了修改
4.字符數(shù)組初始化及傳遞
字符數(shù)組的定義方法與前面介紹的一維數(shù)組類似。例如,
char c[10];
字符數(shù)組的初始化可以采用以下方式。
(1)對每個字符單獨賦值進行初始化。例如,
c[0]='I';c[1]=' ';c[2]='a';c[3]='m';c[4]='';c[5]='h';c[6]='a';c[7]='p';c[8]='p';c[9]='y';
(2)對整個數(shù)組進行初始化。例如,
char c[10]={'I','a','m','h','a','p','p','y'}
但工作中一般不用以上兩種初始化方式,因為字符數(shù)組一般用來存取字符串。通常采用的初始化方式是 char c[10]= "hello"。因為 C 語言規(guī)定字符串的結(jié)束標志為'\0',而系統(tǒng)會對字符串常量自動加一個'\0',為了保證處理方法一致,一般會人為地在字符數(shù)組中添加'\0',所以字符數(shù)組存儲的字符串長度必須比字符數(shù)組少 1 字節(jié)。例如,char c[10]最長存儲 9 個字符,剩余的 1個字符用來存儲'\0'。
【例】字符數(shù)組初始化及傳遞
#include <stdio.h>
//print 函數(shù)模擬實現(xiàn) printf 的%s 打印效果
void print(char c[])
{int i=0;while(c[i]){printf("%c",c[i]);//當 c[i]為'\0'時,其值是 0,循環(huán)結(jié)束,也可以寫為 c[i]!='\0'。i++;}
printf("\n");
}//字符數(shù)組存儲字符串,必須存儲結(jié)束符'\0'
int main()
{char c[5]={'h','e','l','l','o'};char d[5]="how";printf("%s\n",c); //會發(fā)現(xiàn)打印了亂碼 ,printf 通過%s 打印字符串時,原理是依次輸出每個字符,當讀到結(jié)束符'\0'時,結(jié)束打印;printf("%s\n",d);print(d);return 0;
}
?5.scanf 讀取字符串
#include <stdio.h>
//scanf 讀取字符串時使用%s
int main()
{char c[10];char d[10];scanf("%s",c);printf("%s\n",c);scanf("%s%s",c,d);printf("c=%s,d=%s\n",c,d);return 0;
}
scanf 通過%s 讀取字符串,對 c 和 d 分別輸入"are"和"you"(中間加一個空格),scanf在使用%s 讀取字符串時,會忽略空格和回車(這一點與%d 和%f 類似)。
輸入順序及執(zhí)行結(jié)果如下圖
6.gets 函數(shù)與 puts 函數(shù)
gets 函數(shù)類似于 scanf 函數(shù),用于讀取標準輸入。 scanf 函數(shù)在讀取字符串時遇到空格就認為讀取結(jié)束,所以當輸入的字符串存在空格時,需要使用 gets 函數(shù)進行讀取。gets 函數(shù)的格式如下:
char *gets(char *str);
puts 函數(shù)類似于 printf 函數(shù),用于輸出標準輸出。puts 函數(shù)的格式如下:
int puts(char *str);
函數(shù) puts 把 str(字符串)寫入 STDOU(標準輸出)。puts 會將數(shù)組 c 中存儲的"how areyou"字符串打印到屏幕上,同時打印換行,相對于 printf 函數(shù),puts 只能用于輸出字符串,同時多打印一個換行符,等價于 printf(“%s\n”,c)
//gets 一次讀取一行
int main()
{char c[20];gets(c);puts(c);return 0;
}
7.str 系列字符串操作函數(shù)(機試重要)
str 系列字符串操作函數(shù)主要包括 strlen、strcpy、strcmp、strcat 等。
strlen 函數(shù)用于統(tǒng)計字符串長度,
strcpy 函數(shù)用于將某個字符串復(fù)制到字符數(shù)組中,
strcmp 函數(shù)用于比較兩個字符串的大小,
strcat 函數(shù)用于將兩個字符串連接到一起。
各個函數(shù)的具體格式如下所示:
#include <string.h> //引入頭文件size_t strlen(char *str);
char *strcpy(char *to, const char *from);
int strcmp(const char *str1, const char *str2);
char *strcat(char *str1, const char *str2);
對于傳參類型 char*,直接放入字符數(shù)組的數(shù)組名即可。
#include <stdio.h>
#include <string.h>
//str 系列字符串操作函數(shù)的使用。
int mystrlen(char c[]) {int i = 0;while (c[i++]);return i - 1;
}
//strlen 統(tǒng)計字符串長度int main() {int len; //用于存儲字符串長度char c[20];char d[100] = "world";gets(c);puts(c);len = strlen(c);printf("len=%d\n", len);len = mystrlen(c);printf("mystrlen len=%d\n", len);strcat(c, d);strcpy(d, c); //c 中的字符串復(fù)制給 dputs(d);printf("c?d %d\n", strcmp(c, d));puts(c);return 0;
}
下圖所示為我們輸入"hello"后的執(zhí)行結(jié)果
???五、指針
5.1 指針的定義
內(nèi)存區(qū)域中的每字節(jié)都對應(yīng)一個編號,這個編號就是“地址”.
在程序中定義一個變量,在對程序進行編譯時,系統(tǒng)就會給這個變量分配內(nèi)存單元.
按變量地址存取變量值的方式稱為“直接訪問”,如printf(""%d",i);、 scanf("%d",&i);等,
另一種存取變量值的方式稱為“間接訪問”,即將變量i的地址存放到另一個變量中.
在C語言中,指針變量是一種特殊的變量,它用來存放變量地址。
指針變量的定義格式如下:
基類型? *指針變量名;
例如
int *i_pointer;?
指針與指針變量是兩個概念,一個變量的“地址”成為該變量的“指針”
用來存放一個變量的地址(即指針)的變量,稱為“指針變量”
如下圖的i_pointer,在64位應(yīng)用程序中,sizeof(i_pointer)=8,即占8個字節(jié)
如果考研強調(diào)程序是32位的,則尋址范圍為4字節(jié)?
5.2 取地址操作符與取值操作符,指針本質(zhì)
取地址操作符為&,也稱引用,通過&可以獲取一個變量的地址值;
取值操作符為*,也稱解引用,通過*可以得到一個地址對應(yīng)的數(shù)據(jù)。
如下例,通過&i獲取整型變量i的地址值,然后對整型指針變量p進行初始化, p中存儲的是整型變量i的地址值,所以通過*p(printf 函數(shù)中的*p)就可以獲取整型變量i的值. p中存儲的是一個絕對地址值,
#include <stdio.h>int main() {int i=5;//定義了一個指針變量,i_pointer就是指針變量名//指針變量的初始化一定是某個變量取地址來賦值,不能隨機寫個數(shù)int *i_pointer;i_pointer=&i;printf("i=%d\n",i); //直接訪問printf("*i_pointor=%d",*i_pointer);return 0;
}
注意:
(1)指針變量前面的“*”表示該變量為指針型變量。
例如,float *pointer_1;
指針變量名是pointer_1 ,而不是*pointer_1.
(2)在定義指針變量時必須指定其類型。只有整型變量的地址才能放到指向整型變量的指針變量中。例如,下面的賦值是錯誤的:
float a;
int * pointer_1;
pointer_1=&a;//毫無意義而且會出錯
(3)如果已執(zhí)行了語句
pointer_1=&a; 那&*pointer_1與&a相同,都表示變量a的地址,即pointer_1
即雖然”&“和”*“兩個運算符的優(yōu)先級相同,但是要按自右向左的方向結(jié)合*&a表示,先進行&a的運算,得到a的地址,在進行*運算,*&a和*pointer_1的作用一樣,都等價于變量a,即*&a與a等價
int *a,b,c;表示聲明了一個指針變量,兩個int變量
要聲明三個指針變量需要寫成int *a,*b,*c;
指針的使用場景只有兩個,即傳遞和偏移
5.3 指針的傳遞?
下例的主函數(shù)通過子函數(shù)改變量i的值,但是執(zhí)行程序后發(fā)現(xiàn)i的值并未發(fā)生改變
因為i的地址和j的地址實際并不相同,執(zhí)行change函數(shù)改變的并不是之前定義的i的地址
#include <stdio.h>
void change(int j){ //j是形參j=5;
}
int main() {int i=10;printf("before value i=%d\n",i);//在子函數(shù)內(nèi)去改變主函數(shù)的某個變量值change(i); //C語言的函數(shù)調(diào)用是值傳遞,實參賦值給形參,j=iprintf("after value i=%d\n",i);return 0;
}//輸出結(jié)果都是10
原理如下圖所示,程序的執(zhí)行過程其實就是內(nèi)存的變化過程,當main函數(shù)執(zhí)行時,系統(tǒng)會為其開辟函數(shù)棧空間,當程序走到int i時,main函數(shù)的??臻g就會為變量i分配4字節(jié)大小的空間,調(diào)用change函數(shù)時,系統(tǒng)會為change函數(shù)重新分配新的函數(shù)??臻g,并為形參變量j分配4字節(jié)大小的空間,調(diào)用change(i)實際是將i的值賦值給j,這就是值傳遞,當change()中修改變量j的值后,函數(shù)執(zhí)行結(jié)束,其棧空間就會釋放,j就不再存在,i的值不會改變。
要在子函數(shù)中修改main函數(shù)的值,如下所示,通過指針進行操作?
#include <stdio.h>
void change(int *j){ //j是形參*j=5; //間接訪問得到變量i *j等價于變量i
}//指針的傳遞
int main() {int i=10;printf("before value i=%d\n",i);change(&i); //傳遞變量i的地址 j=&iprintf("after value i=%d\n",i);return 0;
}//執(zhí)行change()后,打印的i的值為5
變量i的地址傳遞給change函數(shù)時,實際效果是j=&i,依然是值傳遞,只是這時候的j是一個指針變量,內(nèi)部存儲的是變量i的地址,所以通過*j就間接訪問到了與變量i相同的區(qū)域,通過*j=5就實現(xiàn)了對變量i的值的改變。
5.4?指針的偏移使用場景
5.4.1 指針的偏移
拿到指針變量后可以對他進行加減,如找到一棟樓是2棟,那往前就是1棟,往后就是3棟
#include <stdio.h>
//指針的偏移使用場景,也就是對指針進行加減
#define N 5 //符號常量N
int main() {int a[N]={1,2,3,4,5}; //數(shù)組名內(nèi)存中存儲了數(shù)組的起始地址,a中存儲的就是一個地址值int *p;//定義指針變量pp=a;//不是取地址a 因為數(shù)組名中本來就有地址int i;for(i=0;i<N;i++){printf("%3d",*(p+i)); //這里*(p+i)和a[i]的結(jié)果是等價的}printf("\n-------------------------------\n");p=&a[4]; //讓p指向最后一個元素for(i=0;i<N;i++){ //逆序輸出printf("%3d",*(p-i));}printf("\n");return 0;
}
? 1 ?2 ?3 ?4 ?5
-------------------------------
? 5 ?4 ?3 ?2 ?1
如下圖所示,數(shù)組名中存儲著數(shù)組的起始地址Ox61fdf0,其類型為整型指針,所以可以將其賦值給整型指針變量p,可以從監(jiān)視窗口中看到p+1的值為Ox61fdf4.指針變量加1后,偏移的長度是其基類型的長度,也就是偏移sizeof(int),這樣通過*p+1就可以得到元素a[1]。編譯器在編譯時,數(shù)組取下標的操作正是轉(zhuǎn)換為指針偏移來完成?
float *p; p的加減也是偏移4個字節(jié)??
?5.4.2?指針與一維數(shù)組
一維數(shù)組中存儲的是數(shù)組的首地址,如下例中數(shù)組名c中存儲的是一個起始地址,所以子函數(shù)change中其實傳入了一個地址,定義一個指針變量時,指針變量的類型要和數(shù)組的數(shù)據(jù)類型保持一致,通過取值操作,可以將“h”改為“H”,這種方法稱為指針法,獲取數(shù)組元素時,也可以通過取下標的方式來獲取數(shù)組元素并進行修改,這種方法稱為下標法。
#include <stdio.h>
//指針與一維數(shù)組的的傳遞
//數(shù)組名作為實參傳遞給子函數(shù)時,是弱化為指針的 (指針默認就是8個字節(jié))
//練習傳遞與偏移void change(char *d){*d='H';d[1]='E'; //*(d+1)='E'與其等價*(d+2)='L';*(d+3)='L';*(d+4)='O';
}
int main(){char c[10]="hello";change(c);puts(c);return 0;
}
5.6指針與malloc動態(tài)內(nèi)存申請,棧與堆的差異
5.6.1 指針與動態(tài)內(nèi)存申請
C語言的數(shù)組長度固定,因為其定義的整型、浮點型、字符型變量、數(shù)組變量都在??臻g中,而棧空間的大小在編譯時是確定的,如果使用的空間大小不確定,就要使用堆空間。
#include <stdio.h>
#include <stdlib.h> //malloc使用的頭文件
#include <string.h> //strcpy使用的頭文件
int main() {int size;//代表要申請多大字節(jié)的空間char *p;//void*類型的指針是不能偏移的,不能進行加減 ,因此不會定義無類型指針scanf("%d",&size); //輸入要申請的空間大小//malloc返回的void*代表無類型指針p= (char*)malloc(size); //malloc返回的是對應(yīng)空間起始地址 要強轉(zhuǎn)malloc的類型與p一致strcpy(p,"malloc success");//strcpy傳進去的也是指針類型的 所以可以直接copyputs(p);free(p);//釋放申請的空間時,必須是最初malloc返回給我們的地址 即free的時候p不能變 不能填p+1這種 會異常printf("free success\n");return 0;
}
注意:指針本身的大小和其指向空間的大小是兩碼事,指針本身大小只跟操作系統(tǒng)的位數(shù)有關(guān),指向空間的大小是字節(jié)定義的,需要定義變量單獨存儲
5.6.2 ??臻g與堆空間的差異
棧是計算機系統(tǒng)提供的數(shù)據(jù)結(jié)構(gòu),計算機會在底層對棧提供支持,分配專門的寄存器存放棧的地址,操作都有專門的指令執(zhí)行,所以棧的效率比較高。
堆則是C/C++函數(shù)庫提供的數(shù)據(jù)結(jié)構(gòu),它的機制很復(fù)雜,例如為了分配一塊內(nèi)存,庫函數(shù)會按照一定的算法在堆內(nèi)存中搜索可用的足夠大小的空間,如果沒有足夠大小的空間就可能調(diào)用系統(tǒng)功能去增加程序數(shù)據(jù)段的內(nèi)存空間,堆的效率要比棧低得多。
棧不能動態(tài),所以要動態(tài)還是要使用堆,注意堆使用完要用free釋放
#include <stdio.h>
#include <stdlib.h> //malloc使用的頭文件
#include <string.h> //strcpy使用的頭文件
//堆和棧的差異
char* print_stack(){char c[100]="I am print_stack func"; //c中存了數(shù)組的起始地址char *p;p=c;puts(p);return p;
}
char* print_malloc(){char *p=(char*) malloc(100); //堆空間在整個進程中一直有效,不因為函數(shù)結(jié)束消亡strcpy(p,"I am print_stack func");puts(p);return p;
}
int main(){char *p;p=print_stack(); //調(diào)用函數(shù)執(zhí)行完之后,操作系統(tǒng)就會直接釋放掉這個棧空間puts(p);//p接到了print_stack()的指針 打印亂碼或nullp=print_malloc();puts(p);free(p);//只有free的時候,堆空間才會釋放return 0;
}/*
int main() {int size;//代表要申請多大字節(jié)的空間char *p;//void*類型的指針是不能偏移的,不能進行加減 ,因此不會定義無類型指針scanf("%d",&size); //輸入要申請的空間大小//malloc返回的void*代表無類型指針p= (char*)malloc(size); //malloc返回的是對應(yīng)空間起始地址 要強轉(zhuǎn)malloc的類型與p一致strcpy(p,"malloc success");//strcpy傳進去的也是指針類型的 所以可以直接copyputs(p);free(p);//釋放申請的空間時,必須是最初malloc返回給我們的地址 即free的時候p不能變 不能填p+1這種 會異常printf("free success\n");return 0;
}
*/
六、函數(shù)
6.1 函數(shù)的聲明與定義—嵌套調(diào)用
6.1.1 函數(shù)的聲明與定義
?函數(shù)間的調(diào)用關(guān)系是,由主函數(shù)調(diào)用其他函數(shù),其他函數(shù)也可以互相調(diào)用,同一個函數(shù)可以背一個或多個函數(shù)調(diào)用任意次。
?下例中有兩個c文件, func.c是子函數(shù)printstar和print_message 的實現(xiàn),也稱定義; main.c是main函數(shù), func.h中存放的是標準頭文件的聲明和 main函數(shù)中調(diào)用的兩個子函數(shù)的聲明,如果不在頭文件中對使用的函數(shù)進行聲明,那么在編譯時會出現(xiàn)警告。
【例1】函數(shù)的嵌套調(diào)用
func.c
#include "func.h"int main() {int a=10;a=print_star(a);print_message();//調(diào)用print_messageprint_star(5);return 0;
}
func.h#ifndef FUNCTION_DEFINE_FUNC_H
#define FUNCTION_DEFINE_FUNC_H
#include <stdio.h> //都需要用到這個頭文件 就都統(tǒng)一寫在這里就行 <>表示從標準庫中找頭文件
void print_message(); //print_message函數(shù)的聲明
int print_star(int i); //print_star函數(shù)聲明。有形參,有返回值
#endif //FUNCTION_DEFINE_FUNC_H
main.c
#include "func.h" //“”表示在當前路徑下找頭文件//print_star的定義
int print_star(int i){printf("************************\n");printf("print_star %d\n",i);return i+3;
}void print_message(){printf("how do you do\n");print_star(6); //調(diào)用print_star
}
C語言的編譯和執(zhí)行有以下特點:
(1)一個C程序由一個或多個程序模塊組成,每個程序模塊作為一個源程序文件。對于較大的程序,通常將程序內(nèi)容分別放在若干源文件中,再由若干源程序文件組成一個C程序。這樣處理便于分別編寫、分別編譯,進而提高調(diào)試效率(復(fù)試有用).一個源程序文件可以為多個C程序共用。
(2)一個源程序文件由一個或多個函數(shù)及其他有關(guān)內(nèi)容(如命令行、數(shù)據(jù)定義等)組成.一個源程序文件是一個編譯單位,在程序編譯時是以源程序文件為單位而不是以函數(shù)為單位進行編譯的.main.c和 func.c分別單獨編譯,在鏈接成為可執(zhí)行文件時, main中調(diào)用的函數(shù)printstar和 print_message才會通過鏈接去找到函數(shù)定義的位置.
(3)C程序的執(zhí)行是從 main函數(shù)開始的,如果在main函數(shù)中調(diào)用其他函數(shù),那么在調(diào)用后會返回到main函數(shù)中,在 main函數(shù)中結(jié)束整個程序的運行.
(4)所有函數(shù)都是平行的,即在定義函數(shù)時是分別進行的,并且是互相獨立的。一個函數(shù)并不從屬于另一函數(shù),即函數(shù)不能嵌套定義,函數(shù)間可以互相調(diào)用,但不能調(diào)用main函數(shù).main函數(shù)是由系統(tǒng)調(diào)用的,例1.1的 main 函數(shù)中調(diào)用print_message 函數(shù),而 print_message函數(shù)中又調(diào)用printstar 函數(shù).我們把這種調(diào)用稱為嵌套調(diào)用.?
6.1.2 函數(shù)的分類與調(diào)用?
函數(shù)分為如下兩種
(1)標準函數(shù):即庫函數(shù),由系統(tǒng)提供的可以直接使用的,如 printf函數(shù)、scanf 函數(shù).不同的C系統(tǒng)提供的庫函數(shù)的數(shù)量和功能會有一些不同,但許多基本的函數(shù)是相同的。
(2)用戶自己定義的函數(shù):用以解決用戶的專門需要.從函數(shù)的形式看,函數(shù)分為如下兩類.
? ?a.無參函數(shù):一般用來執(zhí)行指定的一組操作.調(diào)用無參函數(shù)時,主調(diào)函數(shù)不向被調(diào)用函數(shù)傳遞數(shù)據(jù)。
? ?無參函數(shù)的定義形式如下:
類型標識符 函數(shù)名()
{聲明部分語句部分
}
? 例1中, print_message就是無參函數(shù)。
? b.有參函數(shù):主調(diào)函數(shù)在調(diào)用被調(diào)用函數(shù)時,通過參數(shù)向被調(diào)用函數(shù)傳遞數(shù)據(jù)。
? 有參函數(shù)的定義形式如下:
類型標識符函數(shù)名(形式參數(shù)表列)
{聲明部分語句部分
}
? 例1中,printstar就是有參函數(shù),int i對應(yīng)的i為形參,主調(diào)函數(shù)和被調(diào)用函數(shù)之間存在數(shù)據(jù)傳遞關(guān)系。
6.2 函數(shù)的遞歸調(diào)用
6.2.1 遞歸調(diào)用
函數(shù)自身調(diào)用自身的操作,稱為遞歸函數(shù),遞歸函數(shù)一定要有結(jié)束條件,否則會產(chǎn)生死循環(huán)!
【例1】n的階乘的遞歸調(diào)用實現(xiàn)。
分析:f(n)=n*f(n-1); 如5!=5*4!
#include <stdio.h>int f(int n){//一定要有結(jié)束條件if(1==n){return 1;}return n*f(n-1); //寫公式
}int main() {int n;scanf("%d",&n);printf("f(%d)=%d\n",n, f(n));return 0;
}
【例2】假如有n個臺階,一次只能上1個臺階或2個臺階,請問走到第n個臺階有幾種走法?
#include <stdio.h>
//上臺階 ,到第n個臺階 一次只能上一個或兩個,有多少種走法
//分析 step(3)=step(2)+step(1)
// 3 = 2 + 1
int step(int n){if(1==n||2==n){//當臺階是1個或2個時,遞歸結(jié)束,一個臺階只有一種走法,2個臺階只有兩種走法return n;}return step(n-1)+ step(n-2);
}int main(){int n;scanf("%d",&n);printf("step(%d)=%d",n, step(n));return 0;
}
?遞歸的核心:找公式,寫遞歸結(jié)束條件。
6.3 局部變量與全局變量
6.3.1 全局變量解析-形參-實參解析
在不同的函數(shù)之間傳遞數(shù)據(jù)時,可以使用的方法如下:
(1)參數(shù):通過形式參數(shù)和實際參數(shù)。
(2)返回值:用return 語句返回計算結(jié)果。
(3)全局變量:外部變量。
#include <stdio.h>int i=10;//i是一個全局變量,不建議使用 局部變量全局變量重名不會報錯 容易搞錯
void print(int a)//形參看成一個局部變量
{printf("I am print i=%d\n",i);
}int main() {{int j=5;}//局部變量只在離自己最近的大括號內(nèi)有效int i=5;printf("main i=%d\n",i);for(int k=0;k<-1;){}
// printf("k=%d\n",k);for循環(huán)括號內(nèi)定義的變量,循環(huán)體外不可用print(3);return 0;
}
全局變量存儲,如下圖所示,全局變量i存儲在數(shù)據(jù)段,所以main函數(shù)和 print函數(shù)都是可見的。全局變量不會因為某個函數(shù)執(zhí)行結(jié)束而消失,在整個進程的執(zhí)行過程中始終有效,因此工作中應(yīng)盡量避免使用全局變量!
在函數(shù)內(nèi)定義的變量都稱為局部變量,局部變量存儲在自己的函數(shù)對應(yīng)的??臻g內(nèi),函數(shù)執(zhí)行結(jié)束后,函數(shù)內(nèi)的局部變量所分配的空間將會得到釋放。如果局部變量與全局變量重名,那么將采取就近原則,即實際獲取和修改的值是局部變量的值.
形參與實參的說明如下:
(1)定義函數(shù)中指定的形參,如果沒有函數(shù)調(diào)用,那并不占用內(nèi)存中的存儲單元。只有發(fā)生函數(shù)調(diào)用時,函數(shù)print 中的形參才被分配內(nèi)存單元。在調(diào)用結(jié)束后,形參所占的內(nèi)存單元也會被釋放。
(2)實參可以是常量、變量或表達式,但要求它們有確定的值,例如, print(i+3)在調(diào)用時將實參的值i+3賦給形參。print函數(shù)可以有兩個形參,如 print( inta,int b)
(3)在被定義的函數(shù)中,必須指定形參的類型.如果實參列表中包含多個實參,那么各參數(shù)間用逗號隔開。實參與形參的個數(shù)應(yīng)相等,類型應(yīng)匹配,且實參與形參應(yīng)按順序?qū)?yīng),一一傳遞數(shù)據(jù)。
(4)實參與形參的類型應(yīng)相同或賦值應(yīng)兼容。
(5)實參向形參的數(shù)據(jù)傳遞是單向“值傳遞”,只能由實參傳給形參,而不能由形參傳回給實參.在調(diào)用函數(shù)時,給形參分配存儲單元,并將實參對應(yīng)的值傳遞給形參,調(diào)用結(jié)束后,形參單元被釋放,實參單元仍保留并維持原值.
(6)形參相當于局部變量,因此不能再定義局部變量與形參同名,否則會造成編譯不通。
6.3.2 外部變量
函數(shù)之外定義的變量稱為外部變量.外部變量可以為本文件中的其他函數(shù)共用,它的有效范圍是從定義變量的位置開始到本源文件結(jié)束,所以也稱全程變量。
關(guān)于全局變量需要注意如下幾點:
(1)全局變量在程序的全部執(zhí)行過程中都占用存儲單元,而不是僅在需要時才開辟單元。
(2)使用全局變量過多會降低程序的清晰性。在各個函數(shù)執(zhí)行時都可能改變外部變量的值,程序容易出錯,因此要有限制地使用全局變量(初試時盡量不用)。
(3)因為函數(shù)在執(zhí)行時依賴于其所在的外部變量,如果將一個函數(shù)移到另一個文件中,那么還要將有關(guān)的外部變量及其值一起移過去。然而,如果該外部變量與其他文件的變量同名,那么
就會出現(xiàn)問題,即會降低程序的可靠性和通用性.C語言一般要求把程序中的函數(shù)做成一個封閉體,除可以通過“實參→形參”的渠道與外界發(fā)生聯(lián)系外,沒有其他渠道.
七、結(jié)構(gòu)體與C++引用
7.1?結(jié)構(gòu)體的定義、初始化、結(jié)構(gòu)體數(shù)組
C 語言提供結(jié)構(gòu)體來管理不同類型的數(shù)據(jù)組合。通過將不同類型的數(shù)據(jù)組合成一個整體,方便引用
例如,一名學生有學號、姓 名、性別、年齡、地址等屬性,如果針對學生的學號、姓名、年齡等都單獨定義一個變量,那么在有多名學生時,變量就難以分清。就可通過結(jié)構(gòu)體來管理
聲明一個結(jié)構(gòu)體類型的一般形式為
struct 結(jié)構(gòu)體名 {成員表列};
struct student
{ int num;char name[20];char sex; int age;float score;char addr[30];
};
先聲明結(jié)構(gòu)體類型,再定義變量名。
struct student student1,student2;
【例1】結(jié)構(gòu)體的scanf讀取和輸出
#include <stdio.h>struct student{int num;char name[20];char sex;int age;float score;char addr[30];
};//結(jié)構(gòu)體類型聲明,注意最后一定要加分號int main() {struct student s={1001,"lele",'M',20,85.4,"Shenzhen"};struct student sarr[3];//定義一個結(jié)構(gòu)體數(shù)組變量int i;//結(jié)構(gòu)體輸出必須單獨去訪問內(nèi)部的每個成員s.num=1003; //單獨賦值 輸出的值會改變printf("%d %s %c %d %f %s\n",s.num,s.name,s.sex,s.age,s.score,s.addr);printf("--------------------------------------\n");
// scanf("%d%s %c%d%f%s",&s.num,s.name,&s.sex,&s.age,&s.score,s.addr);for(i=0;i<3;i++){scanf("%d%s %c%d%f%s",&sarr[i].num,sarr[i].name,&sarr[i].sex,&sarr[i].age,&sarr[i].score,sarr[i].addr);}for(i=0;i<3;i++)//結(jié)構(gòu)體數(shù)組的輸出{printf("%d %s %c %d %f %s\n",sarr[i].num,sarr[i].name,sarr[i].sex,sarr[i].age,sarr[i].score,sarr[i].addr);}return 0;
}
結(jié)構(gòu)體類型聲明要放在 main 函數(shù)之前,這樣 main 函數(shù)中才可以使用這個結(jié)構(gòu)體,工作中往往把結(jié)構(gòu)體聲明放在頭文件中。
注意,結(jié)構(gòu)體類型聲明最后一定要加分號,否則會編譯不通。 定義結(jié)構(gòu)體變量時,使用 struct student 來定義,不能只有 struct 或 student,否則也會編譯不通。
結(jié)構(gòu)體的初始化只能在一開始定義,如果 struct student s={1001,"lele",'M',20,85.4,"Shenzhen"}已經(jīng)執(zhí)行,即 struct student s 已經(jīng)定義,就不能再執(zhí) 行 s={1001,"lele",'M',20,85.4,"Shenzhen"}
整型數(shù)據(jù)(%d)、浮點型數(shù)據(jù)(%f)、字符串型數(shù)據(jù)(%s)都會忽略空格,但字符型數(shù)據(jù)(%c)不會忽略空格,所以讀取字符型數(shù)據(jù)就要在待讀取的字符數(shù)據(jù)與其他數(shù)據(jù)之間加入空格。
7.2 結(jié)構(gòu)體對齊
結(jié)構(gòu)體本身的對齊規(guī)則,考研初試只需要記住,結(jié)構(gòu)體的大小必須是其最大成員的整數(shù)倍
結(jié)構(gòu)體對齊,是為了cpu高效的去取內(nèi)存中上的數(shù)據(jù)
#include <stdio.h>struct student_type1{double score;//double是一種浮點類型,8個字節(jié),浮點分為float和double,float占4個字節(jié),記住有這兩種即可short age;//short 是整型,占2個字節(jié)
};struct student_type2{double score;int height;//如果兩個小存儲之和是小于最大長度8,那么它們就結(jié)合在一起short age;
};struct student_type3{int height; //4個字節(jié)char sex;short age;
};
//結(jié)構(gòu)體對齊
int main() {struct student_type1 s1;struct student_type2 s2={1,2,3};struct student_type3 s3;printf("s1 size=%d\n",sizeof(s1));printf("s2 size=%d\n",sizeof(s2));printf("s3 size=%d\n",sizeof(s3));return 0;
}
注意:兩個小存儲挨在一起且和小于最大長度,那兩個小存儲就結(jié)合在一起看?
s1 size=16? ?//double8 + short 8
s2 size=16? ?//double>int+short 所以長度為?double8 + (int+short)8
s3 size=8? ? ?//int>char+short 所以長度為 int 4 + (char+short)4
7.3 結(jié)構(gòu)體指針與typedef的使用
7.3.1 結(jié)構(gòu)體指針
一個結(jié)構(gòu)體變量的指針就是該變量所占據(jù)的內(nèi)存段的起始地址。可以設(shè)置一個指針變量,用它指向一個結(jié)構(gòu)體變量,此時該指針變量的值是結(jié)構(gòu)體變量的起始地址。指針變量也可以用來指向結(jié)構(gòu)體數(shù)組中的元素,從而能夠通過結(jié)構(gòu)體指針快速訪問結(jié)構(gòu)體內(nèi)的每個成員。
#include <stdio.h>struct student{int num;char name[20];char sex;
};
//結(jié)構(gòu)體指針的練習
int main() {struct student s={1001,"wangle",'M'};struct student sarr[3]={1001,"lilei",'M',1005,"zhangsan",'M',1007,"lili",'F'};struct student *p;//定義了一個結(jié)構(gòu)體指針變量p=&s;//*p要加括號去訪問元素是因為.的優(yōu)先級比*高,如果不加括號就會報錯printf("%d %s %c\n",(*p).num,(*p).name,(*p).sex);//方式1訪問通過指針對象去訪問成員printf("%d %s %c\n",p->num,p->name,p->sex);//方式2訪問通過結(jié)構(gòu)體指針去訪問成員,用這種p=sarr;printf("%d %s %c\n",(*p).num,(*p).name,(*p).sex);//方式1訪問通過指針對象去訪問成員printf("%d %s %c\n",p->num,p->name,p->sex);//方式2訪問通過結(jié)構(gòu)體指針去訪問成員,用這種printf("------------------------------\n");p=p+1;printf("%d %s %c\n",(*p).num,(*p).name,(*p).sex);//方式1訪問通過指針對象去訪問成員printf("%d %s %c\n",p->num,p->name,p->sex);//方式2訪問通過結(jié)構(gòu)體指針去訪問成員,用這種return 0;
}
1001 wangle M
1001 wangle M
1002 lilei M
1002 lilei M
-------------------------
1003 zhangsan M
1003 zhangsan M
由上面例子可以看到,p 就是一個結(jié)構(gòu)體指針,可以對結(jié)構(gòu)體 s 取地址并賦給 p,這樣借助成員選擇操作符,就可以通過 p 訪問結(jié)構(gòu)體的每個成員,然后進行打印。
由于數(shù)組名中存儲的是數(shù)據(jù)的首地址,所以可以將 sarr 賦給 p,這樣也可以訪問對應(yīng)的成員。
使用(*p).num 訪問成員要加括號是因為“.”成員選擇的優(yōu)先級高于“*”(即 取值)運算符,所以必須加括號,通過*p 得到 sarr[0],然后獲取對應(yīng)的成員。?
7.3.2 結(jié)構(gòu)體的使用
定義結(jié)構(gòu)體變量時使用的語句是 struct student s,這樣定義結(jié)構(gòu)體變量有些麻煩,每次都需要寫 struct student??梢赃x擇使用 typedef 聲明新的類型名來代替已有的類型名
#include <stdio.h>typedef struct student{int num;char name[20];char sex;
}stu,*pstu; //pstu等價于 struct student*
//typedef的使用,typedef 起別名typedef int INGETER; //在特定的地方使用 給int起別名
int main() {//stu s ={0};//結(jié)構(gòu)體變量初始化為0 stu 等價于struct studentstu s={1001,"wangle",'M'};stu *p=&s;//定義了一個結(jié)構(gòu)體指針pstu p1=&s;//定義了一個結(jié)構(gòu)體指針I(yè)NGETER num=10;printf("num=%d,p->num=%d\n",num,p->num);return 0;
}
7.4 C++的引用
?7.4.1 C++的引用講解
?對于?C++,建源文件時,名字需要叫 main.cpp,以 cpp 后綴結(jié)尾
?使用了引用后,在子函數(shù)內(nèi)的 操作和函數(shù)外操作手法一致,這樣編程效率較高
【例1.1】在子函數(shù)內(nèi)修改主函數(shù)的普通變量的值(C++)
#include <stdio.h>
//C++引用的講解//在子函數(shù)中要修改主函數(shù)中變量的值,就用引用,不需要修改,就不用
void modify_num(int &b){//形參中寫&,要稱為引用b=b+1;
}//在子函數(shù)內(nèi)修改主函數(shù)的普通變量的值
int main() {int a=10;modify_num(a);printf("after modify_num a=%d\n",a);return 0;
}
上面的代碼如果改為純 C,代碼如下:
【例 1.2】在子函數(shù)內(nèi)修改主函數(shù)的普通變量(純 C 代碼)?
#include <stdio.h>
void modify_num(int *b)
{ *b=*b+1;
} int main()
{ int a=10; modify_num(&a); printf("after modify_num a=%d\n",a); return 0;
}
【例 1.3】子函數(shù)內(nèi)修改主函數(shù)的一級指針變量(這是是重要的!)
#include <stdio.h>
void modify_pointer(int *&p,int *q){//引用必須和變量名緊鄰p=q;
}
//子函數(shù)內(nèi)修改主函數(shù)的一級指針變量
int main() {int *p=NULL;int i=10;int *q=&i;modify_pointer(p,q);printf("after modify_pointer *p=%d\n",*p);return 0;//進程已結(jié)束,退出代碼為 -1073741819,不為0,那么代碼進程異常結(jié)束
}
上面的代碼如果改為純 C,就需要使用到二級指針
#include <stdio.h>
void modify_pointer(int **p,int *q)//相對于 C++這里是 int **p;
{ *p=q;//這里的寫法和例 1.2 中的是非常類似的
}
int main()
{int *p=NULL;int i=10;int *q=&i;modify_pointer(&p,q);//相對于 C++這里是&pprintf("after modify_pointer *p=%d\n",*p);return 0;
}
7.4.2 C++的布爾類型
布爾類型在 C 語言沒有,C++有 true 和 false,值是1和0
【例 2.1】 布爾類型也是有值的
#include <stdio.h>
//設(shè)置布爾值的好處是提升了代碼的可閱讀性
int main()
{ bool a=true; bool b= false; printf("a=%d,b=%d\n", a,b); return 0;
}
a=1,b=0