郝斌C語言(180集自學(xué)教程)

零散知識筆記(老師講的基本都在這里了)
算法:
????解體的方法和步驟
如何看懂一個程序,分三布:
????1、流程
????2、每個語句的功能
????3、試數(shù)
1、判斷一個數(shù)字是否為素數(shù)
只能被1或本身整除的數(shù)為素數(shù)
2、判斷一個屬是否為回文數(shù)
正反寫都是一個數(shù) 如 1221??1331.。。。
如何看懂一個程序,分三步:
1、流程
2、每一個語句的功能
3、試數(shù)
如何學(xué)習(xí)一些小算法的程序【如何掌握一個程序】
1、嘗試自己去編程解決它,大部分人都無法自己解決
2、如果解決不了就看答案,把答案看懂,這個要費很大精力,也是學(xué)習(xí)重點
3、看懂之后嘗試自己去修改程序,并且知修改之后程序的 不同輸出結(jié)果的含義
4、按著答案去敲,調(diào)試錯誤
5、不看答案,自己獨立把答案敲出來;
終極秘訣:
如果實在無法徹底理解,就把他背會;
強制類型轉(zhuǎn)化
格式:
(數(shù)據(jù)類型)(表達式)
功能:
把表達式的值強制轉(zhuǎn)化為前面所執(zhí)行的數(shù)據(jù)類型
例子:
(int)(4.5+2.2) 最終值是6
(float)(5) 最終值是 5.00000000
浮點數(shù)的存儲所帶來的問題
float和double都不能保證可以精確的儲存一個小數(shù)
例子:
float i = 99.9;
printf("%f\n", i);?
舉例:
有一個浮點型變量X,如何判斷x的值是否為零
if( |x-0.000001| <= 0.00001)
是零
else
不是零
為什么循環(huán)中更新的變量不能定義成浮點型
進制
1、什么叫進制
逢n進一
2、把r進制轉(zhuǎn)成十進制
3、十進制轉(zhuǎn)成r進制
4、不同進制所代表的數(shù)值之間的關(guān)系
十進制的3981轉(zhuǎn)化化成 十六進制的F8D
是進制的3981和十六進制的F8D所代表的本質(zhì)都是同一個
一些瑣碎的運算符知識
自增或者自減
分類:
前自增?--?++i
后自增 --?i++
前自增和后自增的異同:
相同:
最終都是使i的值加1
不同:
前自增整體表達式的值是i加1之后的值
后自增整體表達式的值是i+1之后的值
為什么會出現(xiàn)自增?
代碼更精煉
自增的速度更快
*學(xué)習(xí)自增要明白的幾個問題
1、我們編程時應(yīng)該盡量屏蔽掉前自增和后自增的差別
2、自增表達式最好不要作為一個更大的表達式的一部分來使用或者說
i++和++i單獨成一個語句, 不要把他作為一個完整復(fù)合語句的一部分來使用
如:
int m = i++ + ++i + i + i++;//這樣不但是錯的而且還是不可移植的
printf("%d %d %d", i++, ++i, i);//同上
break和contiune
break
break如果用于循環(huán)是用來終止循環(huán)的
break如果用于switch,則是用于終止switch
break不能直接用于if,除非if屬于循環(huán)內(nèi)部的一個子句
例子:
int i;
for (i=0; i<3; i++)
{
if (3 > 2)
break;//break雖然是內(nèi)部的語句,但是break終止的是內(nèi)部的循環(huán);?
printf("呵呵!\n");
}?
return 0;
在多層循環(huán)中,break只能終止距離他最近的那個循環(huán)
例子:
for(i=1; i<3; ++i)
{
for(j=1; j<4; ++j)
break;
printf("共和最帥!");?
}?
continue
用于跳過本次循環(huán)余下的語句,
轉(zhuǎn)去判斷是否需要再次循環(huán)。
數(shù)組
為什么需要數(shù)組
為了解決大量同類型數(shù)據(jù)的存儲和使用問題
為了模擬現(xiàn)實世界
數(shù)組的分類
一維數(shù)組
怎樣定義一維數(shù)組:
為n個變量連續(xù)非陪儲存空間
所有變量數(shù)據(jù)類型必須相同
所有變量所占的字節(jié)大小必須相等
例子:
int a[5];
有關(guān)一維數(shù)組的操作
初始化
完全初始化
int a[5] = {1,2,3,4,5};
不完全初始化,未被初始化的元素自動為零
int a[5] = {1,2,3};
不初始化,所有的元素是垃圾值
int a[5];
清零
int a[5] = {0};
*錯誤的寫法
int a[5];
a[5] = {1,2,3,4,5};//錯誤
只有在定義數(shù)組的同時才可以整體賦值,其他情況下整體賦值都是錯誤
int a[5] = {1,2,3,4,5};
a[5] = 100;//error 因為沒有a[5]這個元素,最大只有a[4]
int a[5] = {1,2,3,4,5};
int b[5];
如果要把a數(shù)組中的值全部復(fù)制到b數(shù)組
錯誤的寫法
b = a;//error
正確的寫法
for(i=0; i<5; ++i)
b[i] = a[i];
二維數(shù)組
int a[3][4];
總共是12個元素,可以當(dāng)作3行4列看待,這12個元素的名字依次是
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]
a[i][j] 表示第i+1行 第j+1列的元素
int a[m][n]; 該二維數(shù)組右下角位置的元素只能是a[m-1][n-1]
初始化
int a[3][4] = {1,2,3,4,5,6,7,8,9,10,11,12};
int a[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12};
};
操作 輸出二維數(shù)組的內(nèi)容
int main(void) {
int a[3][4] = {
{1, 2, 3, 4},
{5, 6, 7, 8},
{9, 10, 11, 12}
};
int i, j;
//輸出數(shù)組內(nèi)容
for (i=0; i<3; ++i)
{
for (j=0; j<4; ++j)
printf("%-5d?", a[i][j]); //"-"表示左對齊,“5”表示占5個光標的位置?
printf("\n");
}?
return 0;
對二位數(shù)組排序
求每一行的最大值
判斷矩形的相乘
多維數(shù)組
是否存在多為數(shù)組?
不存在!
因為內(nèi)存是線性一維的
*n維數(shù)組可以當(dāng)作每個元素是n-1維數(shù)組的一維數(shù)組
比如:
int a[3][4];
該數(shù)組是含有3個元素的一維數(shù)組
只不過每個元素都可以再分成4個小元素
int a[3][4][5];
該數(shù)組是含有3個元素的一個數(shù)組
只不過每個元素都是4行5列的二維數(shù)組
函數(shù):
什么叫函數(shù)
邏輯上:能夠完成特定功能的獨立的代碼塊
物理上:
能夠接受數(shù)據(jù)【當(dāng)然也可以不接受數(shù)據(jù)】
能夠?qū)邮艿臄?shù)據(jù)進行處理
能夠?qū)?shù)據(jù)處理的結(jié)果返回【當(dāng)然也可以不反悔任何值】
總結(jié):
函數(shù)是一個工具,他是為了解決大量類似問題而設(shè)計的
函數(shù)可以當(dāng)做一個黑匣子
如何定義函數(shù)
函數(shù)的返回值 函數(shù)的名字(函數(shù)的形參列表)
{
函數(shù)的執(zhí)行體
}
1、函數(shù)定義的本質(zhì)是詳細描述函數(shù)之所以能夠?qū)崿F(xiàn)某個特定功能的具體方法
2、return 表達式; 的含義:
1>終止被調(diào)函數(shù),向主函數(shù)返回表達式的值
2>如果表達式為空,則只終止函數(shù),不向主函數(shù)返回任何值
3>break是用來終止循環(huán)和switch的,return設(shè)計用來終止函數(shù)的
例子:
void f()
{
return;
}
int f()
{
return 10;//終止函數(shù)并返還10
}
3、函數(shù)返回值的類型也成為函數(shù)的類型, 因為如果 函數(shù)名前面的返回值類型 和函數(shù)執(zhí)行體中的return 表達式;中的表達式類型不同的話,則最終函數(shù)的返回值類型 以函數(shù)名前面的返回值類型為準
例子:
int f()
{
return 10.5;//返回值為10,因為定義為int類型
}
函數(shù)的分類
有參函數(shù) 和 無參函數(shù)
有返回值函數(shù) 和 無返回值函數(shù)
庫函數(shù) 和 用戶自定義函數(shù)
值傳遞函數(shù) 和 地址傳遞函數(shù)
普通函數(shù) 和 主函數(shù)(main函數(shù))
一個程序必須且只能有一個主函數(shù)
主函數(shù)可以調(diào)用普通函數(shù) 普通函數(shù)不能調(diào)用主函數(shù)
普通函數(shù)可以相互調(diào)用
主函數(shù)是程序的入口,也是程序的出口
注意的問題
函數(shù)調(diào)用和函數(shù)定義的順序
如果函數(shù)調(diào)用寫在了函數(shù)定義的前面,則必須加函數(shù)的前置聲明
函數(shù)前置的申明:
1、告訴編譯器即將可能出現(xiàn)的若干個字母代表的是一個函數(shù)
2、告訴編譯器即將可能出現(xiàn)的入肝個字母所代表的函數(shù)的形參和返回值的具體情況
3、函數(shù)聲明是一個語句,末尾必須加分號
4、對庫函數(shù)的什么是通過#include<庫函數(shù)所在的文件的:形參和實參
??個數(shù)相同?位置一一對應(yīng) 數(shù)據(jù)類型必須相互兼容
如何在軟件開發(fā)中合理的設(shè)計函數(shù)來解決實際問題
一個函數(shù)的功能盡量獨立,單一
多學(xué)習(xí),多模仿牛人的代碼
函數(shù)是C語言的基本單位,類是java,C#,C++的基本單位
double sqrt(double x);
求x的平方根
int abs(int x)
求x的絕對值
double fabs(double x)//求負的絕對值 比如 double fabs (-5.5)
求x的絕對值
變量的作用域的存儲方式:
按作用域分:
全局變量
在所有函數(shù)外部定義的變量叫局部變量
全局變量使用范圍: 從定義位置開始到整個程序結(jié)束
局部變量
在一個函數(shù)內(nèi)部定義的一個變量或者函數(shù)的形參都稱為局部變量
void f(int i)
{
int?j = 20;
}
i和j都屬于局部變量
局部變量的適用范圍:只能在本函數(shù)內(nèi)部使用
注意的問題:
全局變量和局部變量名沖突問題
在一個函數(shù)每部如果定義的局部變量的名字和全局變量的名一樣時,局部變量會屏蔽掉全局變量
按變量的儲存方式
靜態(tài)變量
自動變量
寄存器變量
指針:
指針和指針變量的關(guān)系
指針的重要性
表示一個復(fù)雜的數(shù)據(jù)結(jié)構(gòu)
快速的傳遞數(shù)據(jù)
使函數(shù)返回以上的值
能夠訪問硬件
能夠方便的處理字符串
是理解面向?qū)ο笳Z言中引用的基礎(chǔ)
總結(jié):指針是C語言的靈魂
指針的定義
地址
內(nèi)存單元的編號
從零開始的非負整數(shù)
范圍:4G 【0 -- 4G-1】
指針
指針就是地址,地址就是指針
地址就是內(nèi)存單元的編碼
指針變量是存放內(nèi)存單元編號的變量,或者說指針變量就是存放地址的變量
指針和指針變量是兩個不同的概念
但是要注意,通常我們敘述時會把指針變量簡稱為指針,實際他們含義并不一樣
指針的本質(zhì)就是操作受限的非負整數(shù)
指針的分類
**1、基本類型指針
int * p;//p是變量的名字, int * 表示p變量存放的時int類型變量的地址?
//int * p;不表示定義了一個名字叫做*p的變量
//int * p;應(yīng)該這樣理解:p是變量名,p變量的數(shù)據(jù)類型是 int *類型
// 所謂int *類型實際就是存放int變量地址的類型?
int i = 3;?
int j;?
p =?&i;//ok
/*
1、p保存了i的地址, 因此p指向i
2、p不是i,i也不是p,更準確地說:修改p的值 不影響i的值,修改i的值也不影響p的值
3、如果一個指針變量指向某個普通變量,則
*指針變量?就完全等同與 普通變量?
例子:
如果p是個指針變量, 并且p存放了普通變量i的地址
則p指向了普通變量i
*p 就完全等同與 i
或者說: 在所有出現(xiàn)*p的地方都可以替換成i
在所有出現(xiàn)i的地方都可以替換成*p?
*p 就是以p的內(nèi)容為地址的變量?
*/?
j = *p;//等價于j = i
printf("i = %d, j = %d\n", i, j);?
附注:
* 的含義
1、乘法
2、定義指針變量
int * p;
//定義了一個名字叫p的變量,int *表示p只能存放int變量地址
3、指針運算符
該運算符放在已經(jīng)定義好的指針變量的前面
如果p是一個已經(jīng)定義好的指針變量
則*p便是 以p的內(nèi)容為地址的變量
如何通過被調(diào)函數(shù)修改主調(diào)函數(shù)普通變量的值
1、實參必須為該變量的地址
2、形參必須為以該指針變量
3、在被調(diào)函數(shù)中通過
*形參名 = ....
??方式就可以修改主調(diào)函數(shù)相關(guān)的數(shù)值
2、指針和數(shù)組
指針和一維數(shù)組
數(shù)組名
一維數(shù)組名是個指針常量
它存放的是一維數(shù)組第一個元素的地址
下標和指針的關(guān)系
如果p是指針變量,則
p[i]永遠等價于*(p+i)
確定一個一位數(shù)組需要幾個參數(shù)【如果一個參數(shù)需要處理一個一維數(shù)組,則需要接受該數(shù)組的那些信息】
需要兩個參數(shù):
數(shù)組第一個元素的地址
數(shù)組的長度
指針變量的運算
指針變量不能相加,不能相乘,也不能相除
如果兩個指針變量指向的是同一塊空間中的不同存儲空間
則這兩個指針變量才可以相減
一個指針變量到底占幾個字節(jié)
預(yù)備知識:
sizeof(數(shù)據(jù)類型)
功能:返回值就該數(shù)據(jù)類型所占的字節(jié)數(shù)
例子:sizeof(int) = 4 sizeof(char) = 1
sizeof(double) = 8
?
假設(shè)p指向char類型(1個字節(jié))
假設(shè)q指向int類型變量(4個字節(jié))
假設(shè)r指向double類型變量(8個字節(jié))
p q r 本身所占的字節(jié)數(shù)是否一樣?
答案:p q r本身所占的字節(jié)數(shù)是一樣的
總結(jié):
一個指針變量,無論他指向的便改良占幾個字節(jié)
給指針便改良本身只占四個字節(jié)
一個變量的地址使用該變量首字節(jié)的地址來表示
指針和二維數(shù)組
3、指針和函數(shù)
*4、指針和結(jié)構(gòu)體
5、多級指針
專題:
動態(tài)內(nèi)存分配
傳統(tǒng)數(shù)組的缺點:
*1、數(shù)組長度必須事先制定,且只能是常整數(shù),不能是變量
例子:
int a[5];//ok
int len = 5; int a[len];//error
*2、傳統(tǒng)形式定義的數(shù)組,該數(shù)組的內(nèi)存程序員無法手動釋放
??數(shù)組一旦定義,系統(tǒng)為該數(shù)組非陪的存儲空間就會一直存在
??除非數(shù)組所在的函數(shù)運行結(jié)束
??在一個函數(shù)期間,系統(tǒng)為該函數(shù)中數(shù)組所分配的空間會一直存在
??知道該函數(shù)運行完畢時,數(shù)組的空間才會被系統(tǒng)釋放
*3、數(shù)組的長度一旦定義,其長度就不能在更改
??數(shù)組的長度不能在函數(shù)運行的過程中動態(tài)的擴充或縮小
4、A函數(shù)定義的數(shù)組,在A函數(shù)運行期間可以被其他函數(shù)使用,
??但A函數(shù)運行完畢之后,A函數(shù)中的數(shù)組將無法在被其他函數(shù)使用
傳統(tǒng)方式定義的數(shù)組不能跨函數(shù)使用
為什么需要動態(tài)分配內(nèi)存
動態(tài)數(shù)組很好的解決了傳統(tǒng)數(shù)組的4個缺陷
傳統(tǒng)數(shù)組也叫靜態(tài)數(shù)組
靜態(tài)內(nèi)存和動態(tài)內(nèi)存的比較
靜態(tài)內(nèi)存是由系統(tǒng)自動分配,由系統(tǒng)自動釋放
靜態(tài)內(nèi)存是在棧中分配的
動態(tài)內(nèi)存使用成需要手動分配,手動釋放
動態(tài)內(nèi)存實在堆分配的
跨函數(shù)使用內(nèi)存的問題
結(jié)構(gòu)體
為什么需要結(jié)構(gòu)體
為了表示一些復(fù)雜的事務(wù),而普通的基本類型無法滿足實際要求
什么叫結(jié)構(gòu)體
把一些基本類型數(shù)據(jù)組合在一起形成一個新的復(fù)合數(shù)據(jù)類型,這個叫做結(jié)構(gòu)體
如何定義結(jié)構(gòu)體
3種方式,推薦第一種
**//第一種方式?
? struct Student
?{
int age;
float score;
char sex;
?};
//第二種方式?
struct Student2
{
int age;
float score;
char sex;
}st2;
//都三種方式?
struct
{
int age;
float score;
char sex;
}st3;
怎樣使用結(jié)構(gòu)體變量
賦值和初始化
頂貼一的同時可以整體賦初值
如果定義完之后,則只能單個的賦初值
如何取出結(jié)構(gòu)體變量中的每一個成員【重點】
1、結(jié)構(gòu)體變量名.成員名
2、指針變量名->成員名
指針變量名->成員們 在計算機內(nèi)部會轉(zhuǎn)換成(*指針變量名).成員名 的方式來執(zhí)行
所以說這兩種方式是等價的
例子:
struct Student
{
int age;
float score;
char sex;
};
int main(void) {
struct Student st = {80, 66, 'F'};//初始值 定義的同時賦初值?
struct Student * pst = &st;//&st不能夠改成st
?
pst->age = 88;//第二種方式?
st.age = 10;//第一種方式
return 0;
}
**1、pst->age 在計算機內(nèi)部會被轉(zhuǎn)換成(*pst).age,沒有問什么這就是->的含義,這是一種硬性規(guī)定
**2、所以pst->age 等價于(*pst).age也等價于st.age
**3、我們之所以知道pst->age等價于st.age,是應(yīng)為pst->age是轉(zhuǎn)換成了(*pst).age來執(zhí)行
**4、pst->age的含義:
pst所指向的那個結(jié)構(gòu)體變量中的age這個成員
結(jié)構(gòu)體變量的運算
結(jié)構(gòu)體變量和結(jié)構(gòu)體變量指針作為函數(shù)參數(shù)傳遞的問題
舉例
動態(tài)構(gòu)造存放學(xué)生信息的結(jié)構(gòu)體數(shù)組