最美情侣中文字幕电影,在线麻豆精品传媒,在线网站高清黄,久久黄色视频

歡迎光臨散文網 會員登陸 & 注冊

小甲魚C語言《帶你學C帶你飛》學習筆記(1)

2020-03-15 09:15 作者:康康Loong  | 我要投稿

視頻鏈接https://www.bilibili.com/video/av27744141

由于篇幅限制,本筆記分兩部分:

小甲魚C語言《帶你學C帶你飛》學習筆記(1)-本文

小甲魚C語言《帶你學C帶你飛》學習筆記(2)-傳送門

本筆記是本人根據(jù)小甲魚視頻講課內容記錄,一些程序做了小修改。根據(jù)自身補充了一部分拓展內容。如有錯誤,歡迎一起討論????????——正能量的康sir?

喜歡我的分享,不放點個關注、點個贊、投個幣,謝謝:)? ?????????????

P2第一個程序

編譯型:c語言——》匯編——》機器語言——》CPU執(zhí)行

解釋型:java——》字節(jié)碼——》解釋器——》CPU執(zhí)行

???????????????????????????????????????

#include<stdio.h>

int main(){

printf("hello world!");

return 0;

}

?

linux環(huán)境下

編輯:vi test.c

編譯:gcc test.c -o test

執(zhí)行:./test

?

windows環(huán)境下

Dev C++

CodeBlocks

P3打印

printf("要打印的內容");

printf格式化輸出函數(shù),print打印,f即format格式化

\n換行

每行寫不完行尾用\,下一行為上一行的延續(xù)。函數(shù)、關鍵字中間也可以用,注意下行開頭如果有空格、縮進也會被解釋,會導致識別不了。

轉義字符


P4變量

確定目標并提供存放的空間

?

命名規(guī)則:

變量名只能是英文字母(A-Z,a-z)和數(shù)字(0-9)或者下劃線(_)組成

第一個字母必須是字母或者下劃線開頭(不能是數(shù)字)

變量名區(qū)分大小寫

不能使用關鍵字



32個關健字



1999年c99標準增加5個


2011年c11標準增加7個

?

數(shù)據(jù)類型

char字符型,占用1字節(jié)

int整型,通常反映了所用機器中整數(shù)的自然長度

float單精度浮點型

double雙精度浮點型

?

聲明變量語法

數(shù)據(jù)類型 變量名

如int a

P5常量和宏定義

常量:

整數(shù)常量

實型常量 小數(shù)等

字符常量 包括普通字符、轉義字符

字符串常量

符號常量 使用之前必須先定義

?

定義符號常量 宏定義

#define標識符 常量

?

示例

#include<stdio.h>

#define NAME "小甲魚"

#define YEAR 2010

int main (){

printf("%s成立于%d年",NAME,YEAR);

}

?

標識符:和關鍵字命名規(guī)則一樣

?

字符串常量 使用null結尾,轉義符是\0

P6數(shù)據(jù)類型

1.基本類型

整數(shù)型????? int、short int、long int、long long int???????? (short int<=int<=long int<=long long int)

浮點型????? float、double、long double

字符型????? char

布爾型????? _Bool

枚舉型????? enum

2.指針類型

3.構造類型

數(shù)組型

結構型

聯(lián)合性

4.空類型

?

sizeof運算符

用于獲取數(shù)據(jù)類型或表達式的長度

sizeof(變量或對象),可以不加括號,空格隔開。如sizeof(a)或sizeof a

sizeof(類型),如sizeof(int)

?

signed和unsigned類型限定符 限定char類型和任何整數(shù)類型的取值范圍

signed帶符號位,可以存放負數(shù)

unsigned不帶符號位,只能存放正數(shù)和0

[signed] short [int]

unsigned short [int]

[signed] int

unsigned int

[signed] long [int]

unsigned long [int]

[signed] long long [int]

unsigned long long [int]

示例

#include<stdio.h>

int main(){

short i;

unsigned short j;

i=-1;

j=-1;

printf("%d\n",i);

printf("%u\n",j);

}

輸出

-1

65535

P7取值范圍

比特位

CPU能夠讀懂的最小單位——比特位,單位bit縮寫b

字節(jié)

內存機構的最小尋址單位——字節(jié),單位Byte縮寫B(tài)

1Byte=8bit

1個字節(jié)等于8比特

1字節(jié)最大:二進制11111111 十進制255 十六進制FF

?

n個連續(xù)的1等于2的n次方減一

#include<stdio.h>

#include<math.h>

int main(){

int result = pow(2,32)-1;

printf("result=%d\n",result);

return 0;

}

?

符號位

存放signed類型的存儲單元中,左邊第一位表示符號位。如果該位為0表示該整數(shù)是一個正數(shù);

如果該位為1,表示該整數(shù)是一個負數(shù)。

一個32位的整數(shù)變量,除去左邊第一個符號位,剩下的表示值得只有31個比特位

事實上計算機是用補碼的形式來存放整數(shù)的值。

正數(shù)的補碼是該數(shù)的二進制形式

負數(shù)的補碼:

(1)先取得該數(shù)的絕對值的二進制形式 -7? 絕對值7的二進制10000111

(2)將第一步的值按位取反????????????????????????????????????????????????????????? ?11111000

(3)最后將第二步的值加1?????????????????????????????????????????????????????????? ?11111001

補碼的最大值127最小值-128

01111111????????? 127

00000001????????? 1

00000000????????? 0

11111111????????? -1

11111110????????? -2

10000000????????? -128

?


基本數(shù)據(jù)類型的取值范圍

?

P8字符和字符串

ASCII字符表

字符串:

聲明字符串???????????????????? char變量名[數(shù)量];

賦值??????????????????????????????? 變量名[索引號]='字符';

聲明+賦值 定義字符串 char name[5]={'a','b','c'};

?

#include<stdio.h>

int main(){

char a[9]={'b','i','l','i','b','i','l','i','\0'};

printf("%s\n",a);

return 0;

}

?

#include<stdio.h>

int main(){

//char a[8]={'b','i','l','i','b','i','l','i'};//錯誤 指定的數(shù)組長度不夠時

//char a[]={'b','i','l','i','b','i','l','i'};//錯誤 不指定長度,每個字符用單引號時

//char a[9]={'b','i','l','i','b','i','l','i'};//正確 預留一個位置

//char a[]={'b','i','l','i','b','i','l','i','\0'};//正確 人工添加

char a[]={"bilibili"};//正確 字符串復制

printf("%s\n",a);

return 0;

}

?

拓展

'\0'是由C編譯系統(tǒng)自動加上的。所以在用字符串賦初值時一般無須指定數(shù)組的長度, 而由系統(tǒng)自行處理。

把字符數(shù)組str1中的字符串拷貝到字符數(shù)組str2中。串結束標志'\0'也一同拷貝。

個案

1. 當數(shù)組長度不夠。假設我們指定了數(shù)組長度,如:u8 str1[13]={"cxjr.21ic.org"};

由于字符組str1的長度為13,所以后面的信息會丟失,即'\0'丟失。

2. 如果在給數(shù)組賦值時,把每個字符單獨用引號括起來。也會丟失'\0'。如:

u8 str1[]={'c','x','j','r','.','2','1','i','c','.','o','r','g'};

如果希望數(shù)組以'\0'結束,則可以寫成以下三者之一:

u8 str1[]={"cxjr.21ic.org"}; //字符串賦值

u8 str1[]={'c','x','j','r','.','2','1','i','c','.','o','r','g','\0'}; //人工添加

u8 str1[14]={'c','x','j','r','.','2','1','i','c','.','o','r','g'}; //故意給數(shù)組預留一個空位

P9算術運算符

求余%后面必須是整數(shù)

?

?

?

?

?

?


1+2?????????? +運算符,1、2兩個操作數(shù)(雙目)

?

表達式

用運算符和括號將操作數(shù)連接起來的式子

1+1

a+b

'a'+'b'

a+'b'+pow(a,b)*3/4+5

?

運算符的優(yōu)先性和結合性

?

類型轉換

1+2.0轉化為1.0+2.0

強制轉換

(int)1.2注意不會四舍五入

?

P10關系運算符和邏輯運算符

關系運算符 ?表示 ?比較:

優(yōu)先級高:<、<=、>、>=

優(yōu)先級低:==、!=

?

用關系運算符將兩邊的變量、數(shù)據(jù)、表達式連接起來,稱為關系表達式。

1<2

a>b

a<1+b

'a'+'b'<='c'

(a=3)>(b=5)

結果是布爾型,c語言中正確1,錯誤0

?

邏輯運算符

非???? !???? 優(yōu)先級高?? !a

與???? &&?? 優(yōu)先級中?? a&&b

或???? ||?????? 優(yōu)先級低?? a||b

?

短路求值

又稱最小化求值,是一種邏輯運算符的求值策略。只有當?shù)谝粋€運算數(shù)的值無法確定邏輯運算的結果,才對第二個運算數(shù)進行求值。

C語言對于邏輯與和邏輯或采用短路求值的方式。

#include<stdio.h>

int main(){

int a,b=3;

(a=0)&&(b=5);?? // 因為a為0,0表示假,能確定邏輯與的值,不會再對第二個運算數(shù)求值

printf("a=%d,b=%d\n",a,b);

(a=1)||(b=5);????? //因為a為1,1表示真,能確定邏輯或的值,不會再對第二個運算數(shù)求值

printf("a=%d,b=%d\n",a,b);

return 0;

}

結果

a=0,b=3

a=1,b=3

?

P11 if語句

語句(1):

if(表達式)

{

邏輯值為真所執(zhí)行的語句、程序塊

}

?

語句(2):

if(表達式)

{

邏輯值為真所執(zhí)行的語句、程序塊

}

else

{

邏輯值為假所執(zhí)行的語句、程序塊

}

?

語句(3):

if(表達式1) {……}

else if(表達式2){……}

else if(表達式3){……}

……

else if(表達式n){……}

else {……}

?

P12 switch語句和分支嵌套

?

switch(整型或字符型)

{

case常量表達式1:程序塊1???? break;

case常量表達式2:程序塊2???? break;

case常量表達式3:程序塊3???? break

…….

case常量表達式n:程序塊n???? break;

default:語句或程序塊n+1? 缺省一個break;可不寫

}

?

懸掛else

if(a==1)

if(b==2)

printf("a等于1,b等于2");

else{lprintf("a不等于1")}

這樣即使a等于1,b不等于2,也會輸出a不等于1。因為else就近if

正確做法加{}

if(a==1)

{

if(b==2)

printf("a等于1,b等于2");

}

else{lprintf("a不等于1")}

?

等于號帶來的問題

?

P13while語句和dowhile語句

?

入口條件循環(huán)

while(表達式){

循環(huán)體

}

?

出口條件循環(huán)

do

{

循環(huán)體

}

while(表達式);

?

?

P14for語句和循環(huán)嵌套

for(初始化表達式;循環(huán)條件表達式;循環(huán)調整表達式)

循環(huán)體

?

for(;;) 都可以省略 但是分號不能省

?

C99、C11允許在for語句的表達式1中定義變量?????????? 表達式1、3中可以用逗號運算符寫多個語句

for(int i=0;i<100;i++)

P15break語句和continue語句

break跳出 跳出循環(huán)或switch。不能用于循環(huán)語句和switch語句之外的任何其他語句中。break 只能跳出一層循環(huán)。當有多層循環(huán)嵌套的時候,break只能跳出“包裹”它的最里面的那一層循環(huán),無法一次跳出所有循環(huán)。同樣,在多層 switch 嵌套的程序中,break 也只能跳出其所在的距離它最近的 switch。但多層 switch 嵌套實在是少見。

?

continue 結束本次循環(huán),即跳過循環(huán)體中下面尚未執(zhí)行的語句,然后進行下一次是否執(zhí)行循環(huán)的判定。不能用于switch

P16拾遺

賦值運算符左邊必須是一個Lvalue,變量名就是Lvalue

int a;

a=5

lvalue 識別和定位存儲數(shù)值的標志符

?

復合的賦值運算符

+=

-=

*=

/=

%=

?

自增自減

i++

i—

i++、++i前后的區(qū)別

j=++i先i加1,再把加1后的值給j

j=i++ 先把i的值給j,i再加1

?

逗號運算符

優(yōu)先級最低

表達式1,表達式,。。。,表達式n

運算過程從左到右

作為一個整體,它的值為最后一個表達式(表達式n)的值

a=(b=3,(c=b+4)+5)

先將b賦值為3,c賦值為b+4的和7,c的值加5,最后賦值給a,a的值12

常見:

1.??????? 多個變量初始化

for表達式1、3

?

三目運算符

表達式1?表達式1:表達式3;

表達式1正確返回表達式2,錯誤返回表達式3

?

goto歷史遺留 不常用

goto A;

A:????? printf("123");

?

注釋

//單行注釋

?

/*多行注釋

多行注釋*/

?

P17數(shù)組

數(shù)組定義:

類型 數(shù)組名[元素個數(shù)]

int a[10];

int N=10;int a[N];

char b[24];

double c[3];

?

數(shù)組不能動態(tài)定義。[]中不能是一個變量,但是這個變量的值是固定的值的話可以

?

訪問數(shù)組中的元素

數(shù)組名[下標]

第一個元素的下標是0,不是1

?

數(shù)組的初始化

1.所有元素賦值為0(只有0可以)

int a[10]={0};?????????????? //只是將第一個元素賦值0,后面會自動初始化為0

2.賦值不同的值用逗號隔開

int a[10]={1,2,3,4,5,6,7,8,9,0};

3.只給一部分元素賦值,未被賦值的元素自動初始化為0.

int a[10]={1,2,3,4,5}

4.只給出各個元素的值,不指定長度,由編譯器自動判斷

int a[]={1,2,3,4,5};

5.C99新特性:指定初始化的元素。未被賦值的元素自動初始化為0

int a[10]={[3]=110,[5]=120,[8]=114};

P18啪啪啪

數(shù)組不能動態(tài)定義。[]中不能是一個變量,但是這個變量的值是固定的值的話可以

?

P19字符串處理函數(shù)

字符數(shù)組

char str1[10]={'F','I','S','H','\0'};

char str2[]={'F','I','S','H','\0'};不指定長度

char str3[]={"FISH"};使用字符串常量初始化字符數(shù)組,可以省略大括號

char str4[]="FISH";

?

字符串處理函數(shù) 在<string.h>

strcat連接字符串

strcmp比較字符串

strcpy拷貝字符串strcpy(str1,str2)把str2復制給str1.注意\0也會復制。要保證str2長度小于str1

strlen獲取字符串長度 數(shù)組元素的個數(shù)

strncat連接字符串(受限)

strncmp比較字符串(受限)

strncpy拷貝字符串(受限)strncpy(str1,str2,元素個數(shù)n) 把str2的n個元素復制給str1,要自己加\0 str1[n]='\0'

?

?

P20二維數(shù)組

定義:

類型 數(shù)組名[行數(shù)][列數(shù)]

訪問:

數(shù)組名[行下標][列下標]

a[0][0]第一行第一列

?

二維數(shù)組初始化

int a[2][3]={1,2,3,4,5,6};

int a[2][3]={{1,2,3},{4,5,6}};

int a[2][3]={{1},{4}}對每行第一個賦值,其余自動為0

int a[2][3]={0}整個二維數(shù)組初始化為0

int a[][3]={1,2,3,4,5,6} 行數(shù)可不寫,自動判斷

C99新特性 指定初始化的元素。

?

?

P21指針

通過變量名訪問變量

指針和指針變量

類型名 *指針變量名

char *pa;定義一個指向字符型的指針變量

int *pb;定義一個指向整型的指針變量

?

獲取某個變量的地址,可以使用取地址運算符&

char *pa=&a;

int *pb=&f;

?

如果需要訪問指針變量指向的數(shù)據(jù),可以使用取值運算符*

printf("%c,%d",*pa,*pb)

?

避免訪問未初始化的指針(野指針)

#include<stdio.h>

int main(){

int *a;??????? //要初始化才對int *pa,a;*pa=&a;*pa=123??????????????????????????? 但是字符串可以int *ps="haha"

*a=123;

return 0;

}

P22指針和數(shù)組

數(shù)組名是數(shù)組第一個元素的地址

?

指向數(shù)組的指針

char *p;

p=a;或p=&a[0];

?

對指針加減運算相當于指向距離指針所在位置向前或向后第n個元素

對比標準的下標訪問數(shù)組元素,使用指針進行間接訪問的方法叫作指針法

p+1并不是將地址加1,而是指向數(shù)組的下一個元素。(根據(jù)定義的類型長度加1個單位的int、char。。。)

?

P23指針數(shù)組和數(shù)組指針

int *p1[5]?? 指針數(shù)組 是數(shù)組 變量類型指針int *

int (*p2)[5] 數(shù)組指針 是指針 指向一個數(shù)組

?

定義數(shù)組指針int (*p)[3]=array

P24指針和二維數(shù)組

數(shù)組array[4][5]

*(array+1)==array[1]指向數(shù)組第二行第一個元素

*(array+1)+3==&array[1][3] 指向數(shù)組第二行第4個元素

結論

*(array+i)==array[i]

*(*(array+i)+j)==array[i][j]

*(*(*(array+i)+j)+k)==array[i][j][k]

?

P25void指針和NULL指針

void指針 稱為通用指針,可以指向任意類型的數(shù)據(jù)。也就是說任何類型的指針都可以賦值給void指針。

int a=1024;

int *pi=&a;

void *pv;

pv=pi;

?

NULL指針 用于指針和對象

#define NULL ((void *)0)

好習慣 不清楚將指針初始化為什么地址時,將她初始化為NULL

NULL不是NUL,NUL是空字符\0

P26指向指針的指針

int num=520;

int *p=&num;

int **pp=&p;兩次解引用

好處:

避免重復分配內存

只需進行一次修改

?

P27常量和指針

常量:1,'a',2.5

或者

#define N 10

或者使用const修飾

const int price = 520

?

指向常量的指針

指針可以修改為指向不同的變量

指針可以修改為指向不同的變量

可以通過解引用來讀取指針指向的數(shù)據(jù)

不可以通過解引用來修改指針指向的數(shù)據(jù)

?

常量指針

指向非常量的常量指針:

指針自身不可被修飾

指針指向的值可以被修改

指向常量的常量指針:

指針自身不可以被修改

指針指向的值也不可以被修改

?

指向"指向常量的常量指針"的指針

P28函數(shù)

函數(shù)的定義;

類型 函數(shù)名(參數(shù)列表)

{

???????? 函數(shù)體

}

?

函數(shù)的聲明

寫在主函數(shù)mian前面

或者

在mian方法后寫函數(shù),在前面寫函數(shù)聲明

void f1(int);

int main(){

int x=1;

f1(x);

}

void f1(int x){

}

?

?

P29參數(shù)和指針

形參 形式參數(shù)

實參 實際參數(shù)

?

傳值和傳址

?

可變參數(shù)

?

P30指針函數(shù)和函數(shù)指針

指針函數(shù)int *f()

char *f1(char c){

return "字符串"

}

字符數(shù)組只要指名開頭地址。結尾是\0自動判斷

?

不要 返回局部變量的指針

?

?

函數(shù)指針int (*p)()

int square(int num){

return num*num;

}

int main(){

int num=5;

int (*fp)(int);

fp=square;函數(shù)名相當于地址 等價于fp=&square

printf("%d",(*fp)(num))

}

?

P31局部變量和全局變量

不同函數(shù)的變量無法相互訪問

?

在函數(shù)里面定義的?局部變量

在函數(shù)外邊定義的?全局變量

?

如果不對全局變量進行初始化,它會自動初始化為0

如果在函數(shù)內部存在一個與全局變量同名的局部變量,并不會報錯,而是在函數(shù)中屏蔽全局變量(全局變量不起作用)

?

extern關鍵字?告訴程序變量在后面定義了,不要報錯

?

不要大量使用全局變量,因為

1、全局變量從被定義開始,直到程序退出才能被釋放,會占用更多的內存

2、污染命名空間,雖然局部變量會屏蔽全局變量,但這樣一來也會降低程序的可讀性,人們往往很難一下子判斷出每個變量的含義和作用范圍

?

?

P32作用域和鏈接屬性

變量的作用范圍?作用域

?

c語言編譯器可以確認4種不同類型的作用域:

代碼塊作用域block scope

文件作用域file scope

原型作用域prototype scoope

函數(shù)作用域function scope

?

代碼塊作用域

在代碼塊中定義的變量,具有代碼塊作用域。從變量定義的位置開始,到標志該代碼結束的右大括號}處

盡管函數(shù)的形參不在大括號內定義,但其同樣具有代碼塊作用域,隸屬于包含函數(shù)體的代碼塊

?

文件作用域

任何在代碼塊之外聲明的標識符都具有文件作用域。從聲明的位置開始,到文件的結尾處

函數(shù)名也具有文件作用域,因為函數(shù)名本身也在代碼塊之外

?

原型作用域

原型作用域只適用于那些在函數(shù)原型中聲明的參數(shù)名。函數(shù)在聲明的時候可以不寫參數(shù)的名字(但參數(shù)的類型是必須要寫上的),其實函數(shù)原型的參數(shù)名還可以隨便寫一個名字,不必與形式參數(shù)相匹配(當然,這樣做毫無意義)。

void func(int a,int b,int c);

void func(int d,int e,int f){

}

?

函數(shù)作用域

函數(shù)作用域只適用于goto語句的標簽,作用將goto語句的標簽限制在同一個函數(shù)內部,以及防止出現(xiàn)重名標簽。

?

定義和聲明

當一個變量被定義的時候,編譯器為變量申請內存空間并填充一些值

當一個變量被聲明的時候,編譯器就知道該變量被定義在其他地方

聲明通知編譯器該變量名及相關的類型已存在,不需要再為此申請內存空間。

局部變量既是定義又是聲明

定義只能來一次,否則就叫做重復定義某個同名變量;而聲明可以有很多次

?

鏈接屬性

.c編譯.o,.o鏈接lib庫文件

external外部的?多個文件中聲明的同名標識符表示同一個實體

internal內部的?單個文件中聲明的同名標識符表示同一個實體static

none無?聲明的同名標識符被當做獨立不同的實體

只有具有文件作用域的標識符才能擁有external或internal的鏈接屬性,其它作用域的標識符都是none屬性

默認情況下,具備文件作用域的標識符擁有external屬性。也就是說該標識符允許跨文件訪問。對于external屬性的標識符,無論在不同文件中聲明多少次,表示的都是同一個實體

?

?

P33生存期和存儲類型

c語言的變量擁有兩種生存期

靜態(tài)存儲期

自動存儲期

?

具有文件作用域的變量屬于靜態(tài)存儲期,函數(shù)也屬于靜態(tài)存儲期,屬于靜態(tài)存儲期的變量在程序執(zhí)行期間將一直占據(jù)存儲空間,直到程序關閉才釋放。

?

具有代碼塊作用域的變量一般情況下屬于自動存儲期。屬于自動存儲期的變量在代碼塊結束時將自動釋放存儲空間。

?

生存期

?

存儲類型

指存儲變量值的內存類型

auto

register

static

extern

typedef

?

自動變量auto

在代碼塊中聲明的變量默認的存儲類型就是自動變量,使用關鍵字auto來描述,可省略

#include<stdio.h>

int main(){

auto int I,j,k;

return 0;

}

?

寄存器變量register

將一個變量聲明為寄存器變量,那么該變量就有可能被存放于CPU的寄存器中。

寄存器變量和自動變量在很多方面都是一樣的,都擁有代碼塊作用域、自動存儲期和空連接屬性

不過將變量聲明為寄存器變量,那么你就沒有辦法通過取址運算符獲得該變量的地址。

?

靜態(tài)局部變量static

static使得局部變量具有靜態(tài)存儲期,所以它的生存期與全局變量一樣,直到程序結束才釋放

?

作用于文件作用域的static和extern,static關鍵字使得默認具有external鏈接屬性的標識符變成internal鏈接屬性,而extern關鍵字是用于告訴編譯器這個變量或函數(shù)在別的地方已經定義過,先去別的地方找找,不要急著報錯。

?

typedef

?

P34遞歸

漢諾塔

謝爾賓斯基三角形

目錄樹的索引

女神的自拍

?

函數(shù)調用本身

?

遞歸必須有結束條件,否則程序將崩潰

#include<stdio.h>

void f1(){

static int count =10;

printf("HI\n");

if(--count) f1();

}

int main(){

f1();

return 0;

}

?

?

遞歸求階乘

#include<stdio.h>

long fact(int num)??????????????????????? //循環(huán)方法

{

???????????????????????????????????????????????????? long result;

???????????????????????????????????????????????????? for(result=1;num>1;num--)

???????????????????????????????????????????????????? {

???????????????????????????????????????????????????? result*=num;

???????????????????????????????????????????????????? }

???????????????????????????????????????????????????? return result;

}

long fact1(int num)????????????????????? //遞歸方法

{

???????????????????????????????????????????????????? long result;

???????????????????????????????????????????????????? if (num>0)

???????????????????????????????????????????????????? {

???????????????????????????????????????????????????????????? result=num*fact1(num-1);

???????????????????????????????????????????????????? }

???????????????????????????????????????????????????? else

???????????????????????????????????????????????????? {

???????????????????????????????????????????????????????????? result=1;

???????????????????????????????????????????????????? }

???????????????????????????????????????????????????? return result;

}

int main(void)

{

int num;

printf("請輸入一個整數(shù):");

scanf("%d",&num);

printf("%d的階乘是:%d",num,fact1(num));

}???????????????????????????????????????????????????

?

實現(xiàn)遞歸滿足兩個條件

調用函數(shù)本身

設置了正確的結束條件

?

普通程序員用迭代,天才程序員用遞歸,但我們寧可做普通程序員

?

?

P35漢諾塔

?遞歸求解漢諾塔

簡單分為三個步奏:

1、將前63個盤子從X移動到Y上

2、將最底下的第64塊盤子從X移動到Z上

3、將Y上的63個盤子移動到Z上

而1、3都能在此分解為類似的三步

1分為

1.1、將前62個盤子從X移動到Z上

1.2、將最底下的第63塊盤子從X移動到Y上

1.3、將Z上的62個盤子移動到Y上

3分為

3.1、將前62個盤子從Y移動到X上

3.2、將最底下的第64塊盤子從Y移動到Z上

3.3、將X上的62個盤子移動到Y上

.。。。

#include<stdio.h>

void hanoi(int n,char x,char y,char z) //將x上的n個盤子借助y移動到z上

{

???????? if(n==1)

???????? {

????????????????? printf("%c-->%c\n",x,z);

???????? }

???????? else

???????? {

????????????????? hanoi(n-1,x,z,y);

????????????????? printf("%c-->%c\n",x,z);

????????????????? hanoi(n-1,y,x,z);

???????? }

}

int main()

{

???????? int n;

???????? printf("請輸入漢諾塔的層數(shù):");

???????? scanf("%d",&n);

???????? hanoi(n,'X','Y','Z');

???????? return 0;

}

?

P36快速排序

分治法

大事化小,小事化了

?

快速排序

基本思想:通過一趟排序將待排序數(shù)據(jù)分割成獨立的兩部分,其中一部分所有元素均比另一部分的元素小,然后分別對兩部分繼續(xù)進行排序,重復上述步驟直到排序完成。

?

?

P37動態(tài)內存管理1

4個庫函數(shù)stdlib.h

malloc申請動態(tài)內存空間

free釋放動態(tài)內存空間

calloc申請并初始化一系列內存空間

realloc重新分配內存空間

?

malloc

函數(shù)原型void *malloc(size_t size);

向系統(tǒng)中申請分配size個字節(jié)的內存空間,并返回一個指向這塊空間的指針。

函數(shù)調用成功,返回一個指向申請的內存空間的指針,由于返回的類型是void指針,所以它可以被轉換成任何類型的數(shù)據(jù);如果函數(shù)調用失敗,返回值是NULL。另外,如果size參數(shù)設置為0,返回值也可能為NULL,但并不意味著函數(shù)調用失敗。

#include<stdio.h>

#include<stdlib.h>

int main()

{

???????? int *ptr;

???????? ptr=(int *)malloc(sizeof(int));

???????? if(ptr==NULL)

???????? {

????????????????? printf("分配內存失??!\n");

????????????????? exit(1);

???????? }

???????? printf("請輸入一個整數(shù):\n");

???????? scanf("%d",ptr);

???????? printf("請輸入的整數(shù)是:%d\n",*ptr);

???????? return 0;

}

?

free

函數(shù)原型:void free(void *ptr);

free函數(shù)釋放ptr參數(shù)指向的內存空間。該內存空間必須是由malloc、calloc或realloc函數(shù)申請的。否則,該函數(shù)將導致未定義行為。如果ptr參數(shù)是NULL,則不執(zhí)行任何操作。注意:該函數(shù)并不會修改ptr的值,所以調用后仍然指向原來的地方(變?yōu)榉欠臻g)。

在以上程序中添加

free(ptr);

printf("請輸入的整數(shù)是:%d\n",*ptr);//此時輸出的值是無意義的

一個吃滿內存的死循環(huán)

#include<stdio.h>

#include<stdlib.h>

int main()

{

???????? while(1)

???????? {

????????????????? malloc(1024);

???????? }

???????? return 0;

}

?

內存泄露

如果申請的動態(tài)內存沒有及時釋放會怎么樣?

c語言沒有垃圾回收機制

導致內存泄露主要兩種情況:

隱式內存泄露(用完內存塊沒有及時使用free函數(shù)釋放)

丟失內存快地址

#include<stdio.h>

#include<stdlib.h>

int main()

{

???????? int *ptr;

???????? int num=123;

???????? ptr=(int *)malloc(sizeof(int));

???????? if(ptr==NULL)

???????? {

????????????????? printf("分配內存失??!\n");

????????????????? exit(1);

???????? }

???????? printf("請輸入一個整數(shù):\n");

???????? scanf("%d",ptr);

???????? printf("請輸入的整數(shù)是:%d\n",*ptr);

???????? ptr=&num;//ptr又指向了別處,原來malloc申請的內存塊丟失,free不能釋放原來的動態(tài)內存。

???????? free(ptr);//另一個錯誤,free不能釋放這里不是動態(tài)內存的ptr

???????? return 0;

}

P38動態(tài)內存管理2

malloc還可以申請一塊任意尺寸的內存空間

#include<stdio.h>

#include<stdlib.h>

int main()

{

???????? int *ptr=NULL;

???????? int num,i;

???????? printf("請輸入待錄入的整數(shù)的個數(shù):");

???????? scanf("%d",&num);

???????? ptr=(int *)malloc(num*sizeof(int));

???????? for(i=0;i<num;i++)

???????? {

????????????????? printf("請錄入第%d個數(shù):\n",i+1);

????????????????? scanf("%d",&ptr[i]);

???????? }

???????? printf("你錄入的整數(shù)是:\n");

???????? for(i=0;i<num;i++)

???????? {

????????????????? printf("%d ",ptr[i]);

???????? }

???????? free(ptr);

???????? return 0;

}

?

初始化內存空間

以mem開頭的函數(shù)被編入字符串標準庫,函數(shù)的聲明包含在string.h這個頭文件中

memset使用一個常量字節(jié)填充內存空間

memcpy?拷貝內存空間

memmove?拷貝內存空間

memcmp比較內存空間

?

memset示例

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

#define N 10//沒分號

int main()

{

???????? int *ptr=NULL;

???????? int i;

???????? ptr = (int *)malloc(N*sizeof(int));

???????? if(ptr==NULL)

???????? {

????????????????? exit(1);

???????? }

???????? memset(ptr,0,N*sizeof(int));

???????? for(i=0;i<N;i++)

???????? {

????????????????? printf("%d ",ptr[i]);

???????? }

???????? free(ptr);

???????? return 0;

}

?

calloc

函數(shù)原型void *calloc(size_t nmemb,size_t size);

calloc函數(shù)在內存中動態(tài)地申請nmemb個長度為size的連續(xù)內存空間(即申請的總空間尺寸為nmemb *size),這些內存空間全部被初始化為0。

calloc函數(shù)與malloc函數(shù)的一個重要區(qū)別是:

calloc函數(shù)在申請完內存后,自動初始化該內存空間為零;malloc函數(shù)不進行初始化操作,里面數(shù)據(jù)是隨機的。

所以下面兩種寫法是等價的:

calloc()分配內存空間并初始化

int *ptr=(int *)calloc(8,sizeof(int));

malloc()分配內存空間并用memset()初始化

?int *ptr=(int *)malloc(8*sizeof()int);

memset(ptr,0,8*sizeof(int));

?

memcpy示例

#include<stdio.h>

#include<stdlib.h>

#include<string.h>

int main(){

???????? int *ptr1=NULL;

???????? int *ptr2=NULL;

???????? ptr1=(int *)malloc(10 *sizeof(int));

???????? ptr2=(int *)malloc(20 *sizeof(int));

???????? memcpy(ptr2,ptr1,10);

???????? free(ptr1);

???????? //略...對ptr2進行若干操作

???????? free(ptr2);

???????? return 0;

}

?

realloc

函數(shù)原型void *realloc(void *ptr,size_t size);

以下幾點需要注意;

realloc函數(shù)修改ptr指向的內存空間大小為size字節(jié)

如果新分配的內存空間比原來的大,則舊內存塊的數(shù)據(jù)不會發(fā)生改變;如果新的內存空間大小小于舊的內存空間,可能會導致數(shù)據(jù)丟失,慎用!

如果ptr參數(shù)為NULL,那么調用該函數(shù)就相當于調用malloc(size)

如果size參數(shù)為0,并且ptr參數(shù)不為NULL,那么調用該函數(shù)就相當于調用free(ptr)

除非ptr參數(shù)為NULL,否則ptr的值必須由先前調用malloc、calloc或realloc函數(shù)返回

?

?

P39C語言的內存布局


C語言的內存布局規(guī)律

根據(jù)內存地址從低到高分別劃分為:

代碼段(Text segment)

數(shù)據(jù)段(Initialized data segment)

BSS段(Uninitialized data segment)

棧(Stack)

堆(Heap)

?

代碼段

代碼段(Text segment)通常是指用來存放程序執(zhí)行代碼的一塊內存區(qū)域。這部分區(qū)域的大小在程序運行前就已經確定,并且內存區(qū)域通常屬于只讀。在代碼段中,也有可能包含一些只讀的常數(shù)變量,例如字符串常量等。

?

數(shù)據(jù)段

數(shù)據(jù)段(Initialized data segment)通常用來存放已經初始化的全局變量和局部靜態(tài)變量。

?

BSS段

BSS段(Bss segment/Uninitialized data segment)通常是指用來存放程序中未初始化的全局變量的一塊內存區(qū)域。BSS是英文Block Started by Symbol的簡稱,這個區(qū)段中的數(shù)據(jù)在程序運行前將被自動初始化為數(shù)字0。

?

堆是用于存放進程運行中被動態(tài)分配的內存段,它的大小并不固定,可動態(tài)擴展或縮小。當進程調用malloc等函數(shù)分配內存時,新分配的內存就被動態(tài)添加到堆上;當利用free等函數(shù)釋放內存時,被釋放的內存從堆中被剔除。

?

大家平時可能經常聽到堆棧這個詞,一般指的就是這個棧。棧是函數(shù)執(zhí)行的內存區(qū)域,通常和堆共享同一片區(qū)域。

?

堆和棧的區(qū)別

申請方式:

堆由程序員手動申請

棧由系統(tǒng)自動分配

釋放方式:

堆由程序員手動釋放

棧由系統(tǒng)自動釋放

生存周期:

堆的生存周期由動態(tài)申請到程序員主動釋放為止,不同函數(shù)之間均可自由訪問

棧的生存周期由函數(shù)調用開始到函數(shù)返回時結束,函數(shù)之間的局部變量不能互相訪問

發(fā)展方向:

堆和其它區(qū)段一樣,都是從低地址向高地址發(fā)展

棧則相反,是由高地址向低地址發(fā)展

P40高級宏定義

宏定義

文件包含

條件編譯

?

不帶參數(shù)的宏定義

#define PI 3.14

為了和普通的變量進行區(qū)分,宏的名字通常我們約定是全部由大寫字母組成

宏定義只是簡單的進行替換,并且由于預處理是在編譯之前進行,而編譯工作的任務之一就是語法檢查,所以編譯器不會對宏定義進行語法檢查

宏定義不是說明或語句,在末尾不必加分號

宏定義的作用域是從定義的位置開始到整個程序結束

可以用#undef來終止宏定義的作用域

#include<stdio.h>

#define PI 3.14

int main(){

???????? int r;

???????? float s;

???????? printf("請輸入圓的半徑;");

???????? scanf("%d",&r);

???????? //#undef PI

???????? s=PI*r*r;

???????? printf("圓的面積是:%.2f\n",s);

???????? return 0;

}

宏定義允許嵌套

#include<stdio.h>

#define PI 3.14

#define R 6371

#define V PI*R*R*R*4/3

int main(){

???????? printf("地球的體積是:%.2f\n",V);

???????? return 0;

}

?

帶參數(shù)的宏定義

#define MAX(x,y) (((x)>(y))?(x):(y))

注意MAX沒有空格(x,y)

#include<stdio.h>

#define MAX(x,y) (((x)>(y))?(x):(y))

int main(){

???????? int a,b;

???????? printf("請輸入兩個數(shù):");

???????? scanf("%d%d",&a,&b);

???????? printf("較大的數(shù)是:%d\n",MAX(a,b));

???????? return 0;

}

括號不能省

#include<stdio.h>

#define SQUARE(x) x*x

int main(){

???????? int a;

???????? printf("請輸入一個數(shù):");

???????? scanf("%d",&a);

???????? printf("%d的平方是:%d\n",a,SQUARE(a));

???????? printf("%d的平方是:%d\n",a+1,SQUARE(a+1));//如果算x+1的平方會x+1*x+1,宏很傻,直接替換,不會幫你加括號?。保險做法#define SQUARE(x) (x)*(x),也不完美例如計算a++則(a++)*(a++)或造成a加兩次

???????? return 0;

}

?

P41內聯(lián)函數(shù)和一些鮮為人知的技巧

內聯(lián)函數(shù) 解決程序中函數(shù)調用的效率問題。(但會增加編譯時間)

定義函數(shù)前加上inline關鍵字

內聯(lián)函數(shù)執(zhí)行過程是在主函數(shù)中展開,而不是主函數(shù)-子函數(shù)-返回主函數(shù)。

現(xiàn)在的編譯器很聰明,不寫inline,也會自動將一些函數(shù)優(yōu)化成內連函數(shù)

#include<stdio.h>

inline int square(int x)

{

???????? return x*x;

?}

int main(){

???????? int i=1;

???????? while(i<=100){

????????????????????????? printf("%d的平方是:%d\n",i-1,square(i++)); //提高編譯效率,也可以避免想宏定義出現(xiàn)兩次加的錯誤

???????? }

???????? return 0;

}

?

?

#和##

兩個預處理運算符

在帶參數(shù)的宏定義中,#運算符后面應該跟一個參數(shù),預處理會把這個參數(shù)轉化為一個字符串。

#include<stdio.h>

#define STR(s) # s

int main(){

???????? printf("%s\n",STR(FISHC));

???????? return 0;

}

會把多個空格轉化為一個空格

#include<stdio.h>

#define STR(s) # s

int main(){

???????? printf(STR(Hello?? %s num=%d\n),STR(FISHC),520);

???????? return 0;

}

?

##運算符被稱為記號連接運算符,比如我們可以使用

##運算符連接兩個參數(shù)

#include<stdio.h>

#define TOGETHER(x,y) x ## y

int main(){

???????? printf("%d\n",TOGETHER(2,50));

???????? return 0;

}

?

可變參數(shù)

帶參數(shù)的宏定義也可以使用可變參數(shù)

#define SHOWLIST(...) printf(#__VA_ARGS__)

其中…表示可變參數(shù),__VA_ARGS__在預處理中被實際的參數(shù)集所替換(就像參數(shù)列表)(兩邊是兩個下劃線哦)。

#include<stdio.h>

#define SHOWLIST(...) printf(#__VA_ARGS__)

int main(){

???????? SHOWLIST(FishC,520,3.14\n);

???????? return 0;

}

可變參數(shù)允許空參數(shù)

#include<stdio.h>

#define PRINT(format,...) printf(#format,##__VA_ARGS__)

int main(){

???????? PRINT(num=%d\n,520);

???????? PRINT(Hello FishC!\n);//這個里面可變參數(shù)是空的

???????? return 0;

}

?

P42結構體

結構體聲明:

struct 結構體名? //英語單詞structure結構

{

結構體成員1;

結構體成員2;

結構體成員3;

...

};//這里有一個分號

示例

struct Book

{

char title[128];

char author[40];

float price;

unsigned int date;

char publisher[40];

};

?

定義結構體類型變量

struct結構體名稱 結構體變量名;

或者

在聲明結構體時定義

struct結構體名{

.。。。

} 變量名;//不過這時是全局變量

注意:如果

typedef struct結構體名稱{

。。。

}簡稱;或typedef struct結構體名 簡稱;

使用typedef給結構體定義了一個簡稱,并不是變量

?

結構體可以放在函數(shù)外(全局),可以放在函數(shù)內(局部)

?

#include<stdio.h>

struct Book

{

char title[128];

char author[40];

float price;

unsigned int date;

char publisher[40];

} book;

int main(){

???????? //struct Book book;

???????? printf("請輸入書名:");

???????? scanf("%s",book.title); //字符數(shù)組名指向開頭元素地址 不用&

???????? printf("請輸入作者:");

???????? scanf("%s",book.author);

???????? printf("請輸入售價:");

???????? scanf("%f",&book.price);

???????? printf("請輸入出版日期:");

???????? scanf("%d",&book.date);

???????? printf("請輸入出版社:");

???????? scanf("%s",book.publisher);

???????? printf("\n===========數(shù)據(jù)錄入完畢========\n");

???????? printf("書名:%s\n作者:%s\n售價:%.2f\n出版日期:%d\n出版社:%s\n",book.title,book.author,book.price,book.date,book.publisher);

}

?

初始化一個結構體變量

book={

"數(shù)學",

"小明",

21.5,

20200311,

"家里蹲大學出版社"};

?

c99新特性:初始結構體的指定成員值

結構體指定初始化成員使用點號運算符和成員名。

比如我們可以讓程序只初始化Book的price成員;

struct Book book={.price=21.5};

多個可以不按結構體順序進行初始化

struct Book book={.price=21.5,.title="數(shù)學"};

?

結構體的長度與內存對齊

#include<stdio.h>

int main(){

???????? struct A

???????? {

????????????????? char a;

????????????????? int b;

????????????????? char c;

???????? } a={'x',520,'o'};

???????? printf("size of a=%d\n",sizeof(a));//結果結構體a長度為12 ,因為內存對齊(讓CPU更快處理數(shù)據(jù) )

???????? // 如果順序為char a;char c;int b;則長度為8

???????? return 0;


}

拓展:掃碼閱讀 如何手工打包c結構體聲明,減少內存空間占用

?

P43結構體數(shù)組和結構體指針

結構體嵌套

struct Date

{

int year;

int month;

int day;

};

struct Book

{

char title[128];

char author[40];

float price;

struct Date date;

char publisher[40];

} book={

"數(shù)學",

"小明",

21.5,

{2020,3,11},

"家里蹲大學出版社"};

.。。。

printf("日期:%d-%d-%d",book.Date.year,book.Date.month,book.Date.day);

。。。

?

結構體數(shù)組

聲明方法

第一種

struct結構名稱

{

結構體成員;

}數(shù)組名[長度];

?

第二鐘

struct結構體名稱 數(shù)組名[長度];

?

結構體數(shù)組初始化struct Book book[3]={{。。。},{。。。},{。。。}}

?

結構體指針

struct Book *pt;

pt=&book;

?

通過結構體指針訪問成員

(*結構體).成員名 //*優(yōu)先級低于點.?? (*pt).title

結構體指針->成員名 ??pt->title

?

P44傳遞結構體變量和結構體指針

傳遞結構體變量

兩個相同結構體類型的結構體變量可以賦值。book1=book2;

?

#include<stdio.h>

struct Date {

???????? int year;

???????? int month;

???????? int day;

};

struct Book {

???????? char title[128];

???????? char author[40];

???????? float price;

???????? struct Date date;

???????? char publisher[40];

};

struct Book getInput(struct Book book){

???????? printf("請輸入書名:");

???????? scanf("%s",book.title);

???????? printf("請輸入作者:");

???????? scanf("%s",book.author);

???????? printf("請輸入售價:");

???????? scanf("%f",&book.price);

???????? printf("請輸入出版日期:");

???????? scanf("%d-%d-%d",&book.date.year,&book.date.month,&book.date.day);

???????? printf("請輸入出版社:");

???????? scanf("%s",book.publisher);

???????? return book;

?

}

void printBook(struct Book book){

???????? printf("書名:%s\n作者:%s\n售價:%.2f\n出版日期:%d-%d-%d\n出版社:%s\n",book.title,book.author,book.price,book.date.year,book.date.month,book.date.day,book.publisher);

}

int main(){

???????? struct Book book1,book2;

???????? printf("請輸入第一本書的信息");

???????? book1=getInput(book1);

???????? printf("請輸入第二本書的信息");

???????? book2=getInput(book2);

???????? printf("顯示第一本書的信息");

???????? printBook(book1);

???????? printf("顯示第二本書的信息");

???????? printBook(book2);

???????? return 0;

}

?

提高執(zhí)行效率,函數(shù)可以不用結構體傳址,而是傳遞指向結構體變量的指針。

#include<stdio.h>

struct Date {

???????? int year;

???????? int month;

???????? int day;

};

struct Book {

???????? char title[128];

???????? char author[40];

???????? float price;

???????? struct Date date;

???????? char publisher[40];

};

void getInput(struct Book *book){

???????? printf("請輸入書名:");

???????? scanf("%s",book->title);

???????? printf("請輸入作者:");

???????? scanf("%s",book->author);

???????? printf("請輸入售價:");

???????? scanf("%f",&book->price);

???????? printf("請輸入出版日期:");

???????? scanf("%d-%d-%d",&book->date.year,&book->date.month,&book->date.day);

???????? printf("請輸入出版社:");

???????? scanf("%s",book->publisher);

}

void printBook(struct Book *book){

???????? printf("書名:%s\n作者:%s\n售價:%.2f\n出版日期:%d-%d-%d\n出版社:%s\n",book->title,book->author,book->price,book->date.year,book->date.month,book->date.day,book->publisher);

}

int main(){

???????? struct Book book1,book2;

???????? printf("請輸入第一本書的信息");

???????? getInput(&book1);

???????? printf("請輸入第二本書的信息");

???????? getInput(&book2);

???????? printf("顯示第一本書的信息");

???????? printBook(&book1);

???????? printf("顯示第二本書的信息");

???????? printBook(&book2);

???????? return 0;

}

?

動態(tài)申請結構體

使用malloc函數(shù)為結構體分配存儲空間

修改

int main(){

???????? struct Book *book1,*book2;

???????? book1=(struct Book *)malloc(sizeof(struct Book));

???????? book2=(struct Book *)malloc(sizeof(struct Book));

???????? if(book1==NULL||book2==NULL){

????????????????? printf("內存分配失??!\n");

????????????????? exit(1);//需要stdlib.h

???????? }

???????? printf("請輸入第一本書的信息");

???????? getInput(book1);

???????? printf("請輸入第二本書的信息");

???????? getInput(book2);

???????? printf("顯示第一本書的信息");

???????? printBook(book1);

???????? printf("顯示第二本書的信息");

???????? printBook(book2);

???????? free(book1);

???????? free(book2);

???????? return 0;

}

篇幅限制,后面部分在這里——傳送門



小甲魚C語言《帶你學C帶你飛》學習筆記(1)的評論 (共 條)

分享到微博請遵守國家法律
西充县| 兰坪| 营口市| 富平县| 和静县| 眉山市| 富平县| 贡觉县| 丹凤县| 昭平县| 涿鹿县| 巴彦淖尔市| 老河口市| 福鼎市| 洛隆县| 双流县| 墨脱县| 安仁县| 阿拉善左旗| 信宜市| 溧阳市| 申扎县| 凤山县| 绥芬河市| 固原市| 漠河县| 平顺县| 大田县| 临潭县| 西平县| 霸州市| 蒙山县| 会昌县| 兴山县| 和硕县| 尉犁县| 荃湾区| 九江市| 武夷山市| 内黄县| 潼南县|