第一章
1.C語言學習中的重難點
- 運算符:自增、自減運算符(重點)
- 進制:各種進制轉(zhuǎn)換、原碼、反碼、補碼
- 數(shù)組:一維數(shù)組(重點)、二位數(shù)組、多維數(shù)組
- 循環(huán):多重循環(huán)的嵌套、排序、查找(重點)
- 函數(shù):遞歸函數(shù)、遞歸調(diào)用
- 指針:一級指針(重點)、多級指針、指針和數(shù)組、函數(shù)、結(jié)構(gòu)體之間的關(guān)系
- 內(nèi)存管理:C語言的內(nèi)存管理問題、內(nèi)存泄露、野指針
- 有參宏及條件編譯(重點)
- 多文件開發(fā):多文件編譯、多文件開發(fā)(重點中的重點)
第二章
prinf函數(shù)
printf的格式控制的完整格式:
"% - 0 m.n l或h 格式字符"
-
下面對組成格式說明的各項加以說明:
%:表示格式說明的起始符號,不可缺少。
-:有-表示左對齊輸出(右側(cè)補空格),如省略表示右對齊輸出(左側(cè)補空格)。
0:有0表示指定空位填0,如省略表示指定空位不填。
m.n:m指域?qū)?即對應的輸出項在輸出設(shè)備上所占的字符數(shù)。N指精度。用于說明輸出的實型 數(shù)的小數(shù)位數(shù)。對數(shù)值型的來說,未指定n時,隱含的精度為n=6位。
l或h:l對整型指long型,對實型指double型。h用于將整型的格式字符修正為short型。
格式字符(格式字符用以指定輸出項的數(shù)據(jù)類型和輸出格式)
d格式:用來輸出十進制整數(shù)。有以下幾種用法:
%d %hd %ld
o格式:以無符號八進制形式輸出整數(shù)
x格式:以無符號十六進制形式輸出整數(shù)
u格式:以無符號十進制形式輸出整數(shù)
c格式:輸出一個字符
s格式:用來輸出一個串。有幾中用法
prinf函數(shù)的域?qū)拞栴}
- %d:按整型數(shù)據(jù)的實際長度輸出。如果想輸出指定寬度可以指定域?qū)?/li>
- %md-->m域?qū)?打印出來以后,在控制臺上,顯示m位
- 如果我們要打印的數(shù)的位數(shù)如果超過我們設(shè)定 m 則原樣輸出
- 如果我們要打印的數(shù)的位數(shù)如果小于我們設(shè)定的位數(shù),則補空白,具體如下:
- 如果m為正數(shù),則左對齊(左側(cè)補空白)
- 如果m為負數(shù),則右對齊(右側(cè)補空白)
// 如果m為負數(shù),則右對齊(右側(cè)補空白)
printf("|%-5d|",88);
輸出結(jié)果:|88 |
轉(zhuǎn)義字符問題
- 如果想輸出字符"%",則應該在“格式控制”字符串中用連續(xù)兩個%表示
printf("%f%%", 1.0/3);
輸出結(jié)果: 0.333333%。
\n 換行,相當于敲一下回車。
\t 跳到下一個tab位置,相當于按一下鍵盤上的tab鍵。 \b 退格,相當于按一下backspace。
\r 使光標回到本行開頭。
\f 換頁,光標移到到下頁開頭。
\\ 輸出\字符,也就是在屏幕上顯示一個\字符。
\' 輸出'字符,也就是在屏幕上顯示一個'字符。
\" 輸出"字符,也就是在屏幕上顯示一個"字符。
第三章
1.類型轉(zhuǎn)換
- 類型轉(zhuǎn)換分為:
- 隱式數(shù)據(jù)類型轉(zhuǎn)換
- 顯示數(shù)據(jù)類型轉(zhuǎn)換
自動轉(zhuǎn)換(隱式轉(zhuǎn)換):
自動轉(zhuǎn)換發(fā)生在不同數(shù)據(jù)類型的量混合運算時,由編譯系統(tǒng)自動完成。
-
自動轉(zhuǎn)換遵循以下規(guī)則:
- 相同數(shù)據(jù)類型的值才能進行運算(比如加法運算),而且運算結(jié)果依然是同一種數(shù)據(jù)類型。系統(tǒng)會自動對占用內(nèi)存較少的類型做一個“自動類型提升”的操作
- 若參與運算量的類型不同,則先轉(zhuǎn)換成同一類型,然后進行運算。
- 轉(zhuǎn)換按數(shù)據(jù)長度增加的方向進行,以保證精度不降低。如int型和long型運算時,先把int量轉(zhuǎn) 成long型后再進行運算。
- 所有的浮點運算都是以雙精度進行的,即使僅含float單精度量運算的表達式,也要先轉(zhuǎn)換成 double型,再作運算。
- char型和short型參與運算時,必須先轉(zhuǎn)換成int型。
- 在賦值運算中,賦值號兩邊量的數(shù)據(jù)類型不同時,賦值號右邊量的類型將轉(zhuǎn)換為左邊量的類型。如果右邊量的數(shù)據(jù)類型長度比左邊長時,將丟失一部分數(shù)據(jù),這樣會降低精度,丟失的部分 按四舍五入向前舍入。
強制類型轉(zhuǎn)換(顯示轉(zhuǎn)換):
-
強制類型轉(zhuǎn)換是通過類型轉(zhuǎn)換運算來實現(xiàn)的
- 其一般形式為:(類型說明符) (表達式)
-
強制類型轉(zhuǎn)換注意點
- 將大范圍的數(shù)據(jù)賦值給小范圍變量時,系統(tǒng)會自動做一個強制類型轉(zhuǎn)換的操作,這樣容易丟失精度
- 類型說明符和表達式都必須加括號(單個變量可以不加括號),如把(int)(x+y)寫成(int)x+y則成了把x轉(zhuǎn)換成int型之后再與y相加了。
- 無論是強制轉(zhuǎn)換或是自動轉(zhuǎn)換,都只是為了本次運算的需要而對變量的數(shù)據(jù)長度進行的臨時性轉(zhuǎn)換,而不改變數(shù)據(jù)說明時對該變量定義的類型。
2.sizeof 運算符
- sizeof可以用來計算一個變量或一個常量、一種數(shù)據(jù)類型所占的內(nèi)存字節(jié)數(shù)
- 格式: 用法:sizeof(常量/變量)
> - 注意: sizeof不是一個函數(shù), 是一個運算符. (面試題)
>
3.自增自剪
自增的兩種寫法
int result = 10;
result++;
++result;
自剪的兩種寫法
result--;
--result;
- 自增自減寫在前面和后面的區(qū)別
- 如果++寫在變量的前面, 那么會先將變量自增再用自增之后的結(jié)果參與運算
- 如果++寫在變量的后面, 那么會先將變量的值參與運算再將變量自增
**總結(jié)一句話: ++在前, 先自增再運算, ++在后, 先運算再自增**
4.邏輯運算符的短路問題
- 與短路:&& 只要第一個條件表達為假那么后面的條件表達就不參與運算了
int a = 10;
int b = 20;
int result = (a > 19) && (b++ > 6);
printf("a = %d,b = %d, result = %d\n", a, b, result);
輸出結(jié)果:a = 10,b = 20, result = 0
- 或短路:|| 只要第一個條件表達式為真那么后面的條件表達式就不參與運算了
int a = 10;
int b = 20;
int result = (a > 19) || (b++ > 6);
printf("a = %d,b = %d, result = %d\n", a, b, result);
輸出結(jié)果:a = 10,b = 21, result = 1
第四章
1.whie循環(huán)的注意點
- 當一個變量與一個常量進行== 或 != 的時候,通常把常量寫在前面
int num = 3;
while (3 == num) {
printf("num = %d\n",num);
num++;
}
2.break關(guān)鍵字
-
使用場合
- switch
- 循環(huán)結(jié)構(gòu)
-
注意:
- break離開應用范圍,存在是沒有意義的。
```
if(1)
{
break; // 沒有意義
}
```
- 在多層循環(huán)中,一個break語句只向外跳一層
```
while(1)
{
while(2)
{
break;// 只對while2有效, 不會影響while1
}
}
```
3.continue關(guān)鍵字
-
使用場合:
- 循環(huán)結(jié)構(gòu)
- 練習: 把100~200之間的不能被3整除的數(shù)輸出
for(int i = 100; i<= 200; i++)
{
if(i %3 == 0) continue;
printf("i = %d", i);
}
4.for循環(huán)的其他形式
-
表達式省略(三個表達式都可以省略)
- 如:for(; ;) 語句相當于while(1) 語句,即不設(shè)初值,不判斷條件(認為表達式2為真值),循環(huán) 變量不增值。無終止地執(zhí)行循環(huán)體。
-
循環(huán)控制無關(guān)的任意表達式
- 表達式1和表達式3可以是一個簡單的表達式,也可以是逗號表達式,即包含一個以上的簡單表達 式,中間用逗號間隔。
```
for(sum=0,i=1;i<=100;i++) sum=sum+i;
或
for(i=0,j=100;i<=j;i++,j--) k=i+j;
// 表達式1和表達式3都是逗號表達式,各包含兩個賦值表達式,即同時設(shè)兩個初值,使兩個變量增值.
```
5.for嵌套實現(xiàn)
1.
*
**
***
****
*****
2.
*****
****
***
**
*
-
分析
- 應該打印5行
- 最多打印5列
- 每一行和每一列關(guān)系是什么? 列數(shù)<=行數(shù)
實現(xiàn)
for(int i = 0; i< 5; i++){
for(int j = 0; j <= i; j++){
printf("*\t");
}
printf("\n");
}
for(int i = 0; i< 5; i++){
for(int j = i; j < 5; j++){
printf("*\t");
}
printf("\n");
}
- 規(guī)律
- 尖尖朝上,改變內(nèi)循環(huán)的條件表達式,讓內(nèi)循環(huán)的條件表達式隨著外循環(huán)的i值變化
- 尖尖朝下,改變內(nèi)循環(huán)的初始化表達式,讓內(nèi)循環(huán)的初始化表達式隨著外循環(huán)的i值變化
第五章
1.遞歸函數(shù)
- 自己調(diào)用自己
- 存在一個條件能夠讓遞歸結(jié)束
- 問題的規(guī)模能夠縮小
void getNumber2()
{
int number = -1;
printf("請輸入一個正數(shù)abc\n");
scanf("%d", &number);
if (number < 0) {
// 負數(shù)
getNumber2();
}else{
// 正數(shù)
printf("number = %d\n", number);
}
}
- 能用循環(huán)實現(xiàn)的功能,用遞歸都可以實現(xiàn)
- 遞歸就是自己搞自己,性能差
- 注意:遞歸一定要有一個明確的結(jié)束條件,否則會造成死循環(huán)
2.用遞歸法求n的階乘
- 分析
4!=4*3*2*1
=4*3!
=4*3*2!
=4*3*2*1!
n!=(n-1)!*n;
(n-1)!=(n-2)!*(n-1);
... ...
1!=1; 作為遞歸的結(jié)束條件
3.Xcode運行原理
- 當我們點擊運行后xcode自動幫我們做如下事情:
- 編譯--->.o(目標文件)--->鏈接--->.out 執(zhí)行
4.常見的UNIX命令
- Mac系統(tǒng)采用的是UNIX文件系統(tǒng),所有的文件都放在根目錄/下面,因此沒有Windows中分C盤、D盤 的概念
- 因為Mac系統(tǒng)是基于UNIX系統(tǒng)的,因此可以在“終端”中輸入一些UNIX指令來操作Mac系統(tǒng) 常用的UNIX指令:(需要經(jīng)常使用才不容易忘記)
ls :列出當前目錄下的所有內(nèi)容(文件\文件夾) pwd :顯示出當前目錄的名稱
cd :改變當前操作的目錄
who :顯示當前用戶名
clear :清除所有內(nèi)容
mkdir : 創(chuàng)建一個新目錄
rm: 刪除文件
rm -r: 刪除文件夾 -f 強制刪除
touch: 創(chuàng)建文件
vi /open:打開、創(chuàng)建文件
-q 退出
-wq 保存并退出
-q!強制退出
i 進入編輯模式
esc 退出編輯模式
:wq!
cat/more 都可以查看文件內(nèi)容
5.手動編譯的步驟
- 1)打開終端(實用工具----->終端)
- 2)找到源文件所在的位置
- 3)開始編譯:
- 命令: cc -c main.c
- 4)鏈接
- 命令: cc main.o
- 5)執(zhí)行
- 執(zhí)行文件命令:./a.out
第六章
1.進制
-
二進制 0、1 逢二進一
- 書寫形式:需要以0b或者0B開頭,比如0b101
-
八進制 0、1、2、3、4、5、6、7 逢八進一
- 書寫形式:在前面加個0,比如045
-
十六進制 0、1、2、3、4、5、6、7、8、9、A、B、C、D、E、F 逢十六進一
- 16進制就是逢16進1,但我們只有0~9這十個數(shù)字,所以我們用A,B,C,D,E,F這五個字母來分 別表示10,11,12,13,14,15。字母不區(qū)分大小寫。
- 書寫形式:在前面加個0x或者0X,比如0x45
8 進制轉(zhuǎn) 10 進制
-
方法:
- 每一位八進制位的值 * 8的(當前二進制進制位索引)
- 索引從0開始, 從右至左
- 將所有位求出的值相加
***口訣:不看你怎么存,只看你怎去取***
2.原碼反碼補碼
- 正數(shù):3碼合一。 對于負數(shù):反碼==除符號位以外的各位取反。補碼=反 碼+1)
3.位運算
-
位運算都是針對二進制的
- 1.& 按位與
- 特點:只有對應的兩位都是1才返回1 否則返回0
- 口訣: 一假則假
- 規(guī)律:任何數(shù)按位與上1結(jié)果還是那個數(shù)
* 2.| 按位或
* 特點:只要對應的兩位其中一位是1就返回1
* 口訣:一真則真
* 3.^ **按位異或**
* 特點:對應的兩位不相同返回1 相同返回0
```
1001
^ 0101
_______
1100
// **多個整數(shù)按位異或的結(jié)果和順序無關(guān)**
1001
^ 0101
_______
1100
1100
^ 0110
_______
1010
1001
^ 0110
_______
1111
1111
^ 0101
_______
1010
// **相同整數(shù)按位異或結(jié)果是0**
1001
^ 1001
_______
0000
// **任何整數(shù)按位異或上0結(jié)果不變**
1001
^ 0000
_______
1001
// **任何整數(shù)按位異或上另一個整數(shù)兩次結(jié)果還是那個數(shù)**
1001
^ 1001
____________
0000
0000
^0101
______
0101
```
- 4.~ 按位取反
- 特點: 0變1 1變0
<< 左移
a << n 把整數(shù)a的二進制位往左邊移n位
移出的位砍掉,低位補0, 發(fā)現(xiàn)左移會把原有的數(shù)值變大
9 << 1 = 18 9 * 2(1) = 18
9 << 2 = 36 9 * 2(2) = 26
9 << n = 9 * 2(n)
左移的應用場景:當要計算某個數(shù)乘以2的n次方的時候就用左移,效率最高
0000 0000 0000 0000 0000 0000 0000 0000
100 0000 0000 0000 0000 0000 0000 10010
注意點:左移有可能改變數(shù)值的正負性
>> 右移
a >> n 把整數(shù)a的二進制位往右邊移n位
移出的位砍掉, 缺少的以為最高位是0就補0是1就補1(是在當前操作系統(tǒng)下)
9 >> 1 = 4 9 / 2(1) = 4
9 >> 2 = 2 9 / 2(2) = 2
右移的應用場景:當要計算某個數(shù)除以2的N次方的時候就用右移,效率最高
0000 0000 0000 0000 0000 0000 0000 0000
000000 0000 0000 0000 0000 0000 0000 10
4.數(shù)組
1.數(shù)組內(nèi)部存儲細節(jié)
-
存儲方式:
- 1)計算機會給數(shù)組分配一塊連續(xù)的存儲空間
- 2)數(shù)組名代表數(shù)組的首地址,從首地址位置,依次存入數(shù)組的第1個、第2個....、第n個元素
- 3)每個元素占用相同的字節(jié)數(shù)(取決于數(shù)組類型)
- 4)并且數(shù)組中元素之間的地址是連續(xù)。
2.數(shù)組的地址
- 在內(nèi)存中,內(nèi)存從大到小進行尋址,為數(shù)組分配了存儲空間后,數(shù)組的元素自然的從上往下排列 存儲,整個數(shù)組的地址為首元素的地址。
- 數(shù)組a的地址是ffc1,a[0]的地址是ffc1,a[1]的地址是ffc5
- 因此a == &a[0],即第一個元素的地址就是整個數(shù)組的地址
// 注意: 由于內(nèi)存尋址是從大到小, 所以存儲數(shù)據(jù)也是從大到小的存儲(先存儲二進制的高位, 再存儲低位)
// 高位 --> 低位
// 00000000 00000000 00000000 00001001
第七章
1.數(shù)組練習
- 要求從鍵盤輸入6個0~9的數(shù)字,排序后輸出
(填坑法)
// 1.定義數(shù)組保存用戶輸入的數(shù)據(jù)
int nums[10] = {0};
// 2.接收用戶的數(shù)據(jù)
int value = -1;
for (int i = 0; i < 6; i++) {
printf("請輸入第%i個數(shù)據(jù)\n", i + 1);
scanf("%i", &value); // 2, 2, 1, 2
// 7, 3, 6, 1
// nums[value] = 1;
nums[value] = nums[value] + 1;
}
for (int i = 0; i < 10; i++) { // i == 7
// printf("nums[%i] = %i\n", i , nums[i]);
/*
if (nums[i] != 0) {
printf("%i\n", i); // 1, 2, 2, 2
}
*/
for (int j = 0; j < nums[i]; j++) { // j == 1
printf("%i\n", i); // 1, 1, 2, 3, 3, 6
}
}
- 數(shù)組的選擇排序
- 已知一個無序的數(shù)組, 里面有5個元素, 要求對數(shù)組進行排序
int nums[8] = {99, 12, 88, 34, 5, 44, 12, 100}
int length = sizeof(nums) / sizeof(nums[0]);
printf("length = %i\n", length);
for (int i = 0; i < length; i++) {
printf("nums[%i] = %i\n", i, nums[i]);
}
// length - 1是為了防止角標越界
// length - 1因為最后一個元素已經(jīng)沒有可以比較的了
// 0, 1, 2, 3, 4
for (int i = 0; i < length - 1; i++) {
for (int j = i+1; j < length; j++) {
// printf("*");
// printf("i = %i, j = %i\n", i, j);
if (nums[i] > nums[j]) {
int temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
}
// printf("\n");
}
printf("--------------\n");
for (int i = 0; i < length; i++) {
printf("nums[%i] = %i\n", i, nums[i]);
}
- 數(shù)組的冒泡排序
/*
思路:
1.先分析如何比較
2.找出比較的規(guī)律比較完一次之后第二次比較會少一次
3.打印倒三角
4.打印需要比較的角標
5.比較并交換位置
6.將常量替換為變量(length)
*/
// 已知一個無序的數(shù)組, 里面有5個元素, 要求對數(shù)組進行排序
int nums[6] = {99, 12, 88, 34, 5, 7};
int length = sizeof(nums) / sizeof(nums[0]);
for (int i = 0; i < length; i++) {
printf("nums[%i] = %i\n", i, nums[i]);
}
for (int i = 0; i < length - 1; i++) {
for (int j = 0; j < length - 1 - i; j++) {
// printf("*");
// printf("%i == %i\n", j, j+1);
if (nums[j] > nums[j + 1]) {
int temp = nums[j];
nums[j] = nums[j + 1];
nums[j + 1] = temp;
}
}
// printf("\n");
}
printf("----------\n");
for (int i = 0; i < length; i++) {
printf("nums[%i] = %i\n", i, nums[i]);
}
-
數(shù)組的折半查找
- 方法1
```
nt min, max, mid;
min = 0;
max = length - 1;
// 只要還在我們的范圍內(nèi)就需要查找
while (min <= max) {
// 計算中間值
mid = (min + max) / 2;
if (key > nums[mid]) {
min = mid + 1;
}else if (key < nums[mid])
{
max = mid - 1;
}else
{
return mid;
}
}
return -1;
```
* 方法2
```
int min, max, mid;
min = 0;
max = length - 1;
mid = (min + max) / 2;
while (key != nums[mid]) {
// 判斷如果要找的值, 大于了取出的值, 那么min要改變
if (key > nums[mid]) {
min = mid + 1;
// 判斷如果要找的值, 小雨了取出的值, 那么max要改變
}else if (key < nums[mid])
{
max = mid - 1;
}
// 超出范圍, 數(shù)組中沒有需要查找的值
if (min > max) {
return -1;
}
// 每次改變完min和max都需要重新計算mid
mid = (min + max) / 2;
}
// printf("aaaaaa\n");
return mid;
```
- 進制查表法
// 轉(zhuǎn)換所有的進制
// value就是需要轉(zhuǎn)換的數(shù)值
// base就是需要&上的數(shù)
// offset就是需要右移的位數(shù)
// 1.定義一個數(shù)組, 用于保存十六進制中所有的取值
char charValues[] = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f'};
// 2.定義一個數(shù)組, 用于保存查詢后的結(jié)果
char results[32] = {'0'};
// 3.定義一個變量, 用于記錄當前需要存儲到查詢結(jié)果數(shù)組的索引
int pos = sizeof(results)/ sizeof(results[0]);
while (value != 0) {
// 1.取出1位的值
int res = value & base;// 1 7 15
// 2.利用取出來得值到表中查詢對應的結(jié)果
char c = charValues[res];
// 3.存儲查詢的結(jié)果
results[--pos] = c;
// 4.移除二進制被取過的1位
value = value >> offset;// 1 3 4
}
// 4.打印結(jié)果
for (int i = pos; i < 32; i++) {
printf("%c", results[i]);
}
printf("\n");
第八章
1.字符串
-
字符串是位于雙引號中的字符序列
- 在內(nèi)存中以“\0”結(jié)束,所占字節(jié)比實際多一個
使用的格式字符串為“%s”,表示輸入、輸出的是一個字符串 字符串的輸出
-
輸出
*%s的本質(zhì)就是根據(jù)傳入的name的地址逐個去取數(shù)組中的元素然后輸出,直到遇到\0位置
char ch[] = "lnj";
printf("%s\n", ch);
\0引發(fā)的臟讀
char name[] = {'c', 'o', 'o', 'l' , '\0'};
char name2[] = {'l', 'n', 'j'};
printf("name2 = %s\n", name2);
輸出結(jié)果: lnjcool
- 注意點:
- 需要明白的一點就是字符串以\0結(jié)尾, 沒有\(zhòng)0就不是字符串
- 只要是用雙引號括起來的都是字符串
- 字符串的本質(zhì)就是數(shù)組
2.指針與數(shù)組
-
在指針指向數(shù)組元素時,允許以下運算:
- 加一個整數(shù)(用+或+=),如p+1
- 減一個整數(shù)(用-或-=),如p-1
- 自加運算,如p++,++p
- 自減運算,如p--,--p
- 如果指針變量p已指向數(shù)組中的一個元素,則p+1指向同一數(shù)組中的下一個元素,p-1指向同 一數(shù)組中的上一個元素。
-
結(jié)論:
- 引用一個數(shù)組元素,可用下面兩種方法:
- 下標法,如a[i]形式
- 指針法, *(p+i)形式
- 注意:
- 引用一個數(shù)組元素,可用下面兩種方法:
數(shù)組名雖然是數(shù)組的首地址,但是,數(shù)組名所所保存的數(shù)組的首地址是不可以更改的
-
兩指針變量相減所得之差是兩個指針所指數(shù)組元素之間相差的元素個數(shù)。
- 實際上是兩個指針值(地址)相減之差再除以該數(shù)組元素的長度(字節(jié)數(shù))。
- (pointer2地址值 -pointer地址值) / sizeof(所指向數(shù)據(jù)類型)
-
注意:
- 指針之間可以相減,但不可以相加(相加無意義)
-
只要一個指針指向了數(shù)組, 那么訪問數(shù)組就有3種方式:
- : ages[0];
- : p[0];
- : *(p + 0);
- 保存字符串的兩種方式:
-
char str[] = "lnj";
- 存儲的位置: 棧
- 特點: 相同的字符串會重復的分配存儲空間
- 字符串可以修改
-
char *str = "lnj"
- 存儲的位置: 常量區(qū)
- 特點: 相同的字符串不會重復的分配存儲空間
- 字符串不可以修改
- 存儲的位置: 常量區(qū)
-
3.字符串指針
1、使用字符數(shù)組來保存的字符串是保存棧里的,保存棧里面東西是可讀可寫,所有我 們可以改變里面的字符當把一個字符串常量賦值一個字符數(shù)組的時候,那么它會把字符串常量 中的沒有字符都放到字符數(shù)組里面
2、使用字符指針來保存字符串,它保存的是字符串常量地址,常量區(qū)是只讀的,所 以我們不可以修改字符串中的字符
注意
不能夠直接接收鍵盤輸入
char *str;
scanf("%s", str);
錯誤的原因是:str是一個野指針,他并沒有指向某一塊內(nèi) 存空間,所以不允許這樣寫如果給str分配內(nèi)存空間是可以這樣用 的
4.指向函數(shù)的指針
- 由于這類指針變量存儲的是一個函數(shù)的入口地址,所以對它們作加減運算(比如p++)是無意義的
- 函數(shù)調(diào)用中"(指針變量名)"的兩邊的括號不可少,其中的不應該理解為求值運算,在此處它 只是一種表示符號
int sum(int v1, int v2)
{
return v1 + v2;
}
int minus(int v1, int v2)
{
return v1 - v2;
}
// 讓demo接受一個指向函數(shù)的指針
// 以后我們只需要給demo函數(shù)傳遞對應的指針, 那么函數(shù)內(nèi)部就可以調(diào)用不同的函數(shù)
int demo3(int v1, int v2, int (*p)(int, int))
{
return p(v1, v2);
}
int main(int argc, const char * argv[]) {
// 定義一個方法, 給你兩個數(shù), 用戶要求你做加法你就做加法, 用戶要求你做減法, 那你就做減法
printf("mins = %i\n", demo3(20, 10, minus));
printf("sum = %i\n", demo3(20, 10, sum));
return 0;
}
第九章
1.構(gòu)造類型
構(gòu)造數(shù)據(jù)類型:構(gòu)造數(shù)據(jù)類型是根據(jù)已定義的一個或多個數(shù)據(jù)類型用構(gòu)造的方法來定義的。也就是說,一個構(gòu)造類型的值可以分解成若干個“成員”或“元素”。每個“成員”都是一個基 本數(shù)據(jù)類型或又是一個構(gòu)造類型。
在C語言中,構(gòu)造類型有以下幾種:
數(shù)組類型
結(jié)構(gòu)體類型
共用體(聯(lián)合)類型
2.結(jié)構(gòu)體
- 注意:
- struct Dog sd2;
- 特別注意: 結(jié)構(gòu)體和數(shù)組有一點區(qū)別, 數(shù)組不能先定義再進行一次性的初始化, 而結(jié)構(gòu)體可以
- 只不過需要明確的告訴系統(tǒng){}中是一個結(jié)構(gòu)體
- sd2 = (struct Dog){"xq", 8, 8.8}; // 數(shù)組? 結(jié)構(gòu)體?
* 指定將數(shù)據(jù)賦值給指定的屬性
* struct Dog sd3 = {.height = 1.77, .name = "ww", .age = 33};
1.定義結(jié)構(gòu)體類型并不會分配存儲空間
-
2.只有定義結(jié)構(gòu)體變量才會真正的分配存儲空間
- 結(jié)構(gòu)體第0個屬性的地址就是結(jié)構(gòu)體的地址
- 和數(shù)組一樣, 結(jié)構(gòu)體內(nèi)存尋址從大到小, 存儲數(shù)組是從小到大(先存儲第0個屬性, 再一次存儲其它屬性)
-
結(jié)構(gòu)體如何開辟存儲空間
看上去, 結(jié)構(gòu)體分配存儲空間是將所有屬性占用的存儲空間的總和加在一起后再分配注意:其實結(jié)構(gòu)體分配存儲空間本質(zhì)上并不是將所有屬性占用的存儲空間的總和加在一起后再分配而是會獲取結(jié)構(gòu)體類型中占用內(nèi)存最大的屬性的大小, 然后取該大小的倍數(shù)特例:如果剩余的存儲空間"不夠"存儲將要存儲的數(shù)據(jù), 那么就會重新開辟8個字節(jié)的存儲空間, 并且將需要存儲的數(shù)據(jù)放到新開辟的存儲空間中如果剩余的存儲空間"夠"存儲將要存儲的數(shù)據(jù), 那么就不會開辟了
3.結(jié)構(gòu)體指針
- 結(jié)構(gòu)指針變量說明的一般形式為:
struct 結(jié)構(gòu)名 *結(jié)構(gòu)指針變量名
- 示例
```
// 定義一個結(jié)構(gòu)體類型
struct Student {
char *name;
int age;
};
// 定義一個結(jié)構(gòu)體變量
struct Student stu = {“NJ", 27};
// 定義一個指向結(jié)構(gòu)體的指針變量
struct Student *p;
// 指向結(jié)構(gòu)體變量stu
p = &stu;
/*
這時候可以用3種方式訪問結(jié)構(gòu)體的成員
*/
// 方式1:結(jié)構(gòu)體變量名.成員名
printf("name=%s, age = %d \n", stu.name, stu.age);
// 方式2:(*指針變量名).成員名
printf("name=%s, age = %d \n", (*p).name, (*p).age);
// 方式3:指針變量名->成員名
printf("name=%s, age = %d \n", p->name, p->age);
return 0; }
```
4.枚舉類型
枚舉類型是一種基本數(shù)據(jù)類型,而不是一種構(gòu)造類型,因為它不能再分解為任何 基本類型。
要求定義一個枚舉來保持一年四季
// 1.定義枚舉類型
// 定義枚舉類型的規(guī)范
// 枚舉類型的取值一般以k開頭 后面跟上枚舉類型的名稱 跟上當前取值的含義
// 和結(jié)構(gòu)體一樣, 枚舉類型的名稱首字母大寫
enum Season
{
kSeasonSpring,
kSeasonSummer,
kSeasonAutumn,
kSeasonWinter
};
enum Gender
{
kGenderMale,
kGenderFemale
};
enum Season es;
es = kSeasonAutumn;
enum Gender eg;
eg = kGenderFemale;
第十章
1.static和extern關(guān)鍵字-對變量的作用
-
static 與 extern對局部變量的作用
- 延長局部變量的生命周期,從程序啟動到程序退出,但是它并沒有改變變量的作用域
- 定義變量的代碼在整個程序運行期間僅僅會執(zhí)行一次
-
extern用在函數(shù)內(nèi)部
- 不是定義局部變量,它用在函數(shù)內(nèi)部是聲明一個全局變量
- static 全局變量的作用
* 聲明一個內(nèi)部變量 static int a;
* 定義一個內(nèi)部變量 static int a = 10;
* 由于靜態(tài)全局變量的作用域局限于一個源文件內(nèi),只能為該源文件內(nèi)的函數(shù)公用,因此可以 避免在其它源文件中引起錯誤。
- extern:
- 用于聲明一個外部全局變量
- 聲明只需要在使用變量之前聲明就可以了
- static:
- 用于定義一個內(nèi)部全局變量
- 聲明和定義的區(qū)別:
- 聲明不會開辟存儲空間
- 定義會開辟存儲空間
2.宏定義
-
注意點:
- 宏名一般用大寫字母,以便與變量名區(qū)別開來,但用小寫也沒有語法錯誤
- 2)對程序中用雙引號擴起來的字符串內(nèi)的字符,不進行宏的替換操作
-
注意點:
- 1)宏名和參數(shù)列表之間不能有空格,否則空格后面的所有字符串都作為替換的字符串.
- 宏定義在什么時候替換
- 源代碼 --> 預處理 -->匯編 -->二進制 -->可執(zhí)行程序
不能有空格
#define average (a, b) ((a+b)/2)
提前結(jié)束宏定義的作用域
//#undef COUNT
3.條件編譯
-
條件編譯和選則結(jié)構(gòu)if的共同點
- 都可以對給定的條件進行判斷, 添加滿足或者不滿足都可以執(zhí)行特定的代碼
條件編譯和選則結(jié)構(gòu)if的共區(qū)別
-
1.生命周期不同
if 運行時#if 編譯之前
-
2.#if需要一個明確的結(jié)束符號 #endif
為什么需要一個明確的結(jié)束符號?如果省略掉#endif, 那么系統(tǒng)就不知道條件編譯的范圍, 那么會將滿足條件之后的第二個條件之后的所有內(nèi)容都清除
-
3.if會將所有的代碼都編譯到二進制中
-
if只會將滿足條件的部分一直到下一個條件的部分 編譯到二進制中
-
-
應用場景:
- 用于調(diào)試和發(fā)布階段進行測試
- 調(diào)試階段: 程序?qū)懘a的階段
- 發(fā)布階段: 上傳到AppStore的階段
#define DEBUG 1 // 0是調(diào)試階段 1是發(fā)布階段
#if DEBUG == 0
// 調(diào)試階段
#define NJLog(format, ...) printf(format,## __VA_ARGS__)
#else
// 發(fā)布階段
#define NJLog(format, ...)
#endif
- 注意點: 條件編譯不能用來判斷變量, 因為不在同一個生命周期
- 君生我未生, 我生君已老
- 一般情況下, 條件編譯是和宏定義結(jié)合在一起使用的
#ifdef SCORE // 判斷是否定義了后面的宏
printf("score\n");
#elif COUNT
printf("count\n");
#else
printf("OTHER\n");
#endif
#ifndef SCORE // 是不是沒有定義名稱叫做SCORE的宏
printf("no score\n");
#else
printf("score\n");
#endif
*/
/*
#if defined(SCORE) // 判斷是否定義了SCORE這個宏
printf("score\n");
#else
printf("no score\n");
#endif
#if !defined(SCORE) // 判斷是否沒有定義SCORE這個宏
printf("no score\n");
#else
printf("score\n");
#endif
4.typedef
-
什么是typedef, 它有什么作用
typedef可以給一個已知的數(shù)據(jù)類型起別名 (外號)
利用typedef給數(shù)據(jù)類型起別名的格式:
typedef 原有的數(shù)據(jù)類型 別名(外號);
-
注意:
- typedef不僅能給系統(tǒng)原有的數(shù)據(jù)類型起別名, 也可以給一個自定義的數(shù)據(jù)類型起別名
- 利用typedef給數(shù)據(jù)類型起別名, 并不會生成一個新的數(shù)據(jù)類型, 僅僅是給原有的類型起了一個別名而已
注意: 如果是給指向函數(shù)的指針起別名, 那么指向函數(shù)的指針的指針名稱就是它的別名
functionPotinter == int(*functionPotinter)(int , int)
typedef int(*functionPotinter)(int , int);
5.const
如果const寫在指針類型的左邊, 那么意味著指向的內(nèi)存空間中的值不能改變, 但是指針的指向可以改變
如果const寫在指針的數(shù)據(jù)類型和*號之間, 那么意味著指向的內(nèi)存空間中的值不能改變, 但是指針的指向可以改變
如果const寫在指針的右邊(數(shù)據(jù)類型 * const), 那么意味著指針的指向不可以改變, 但是指針指向的存儲空間中的值可以改變
規(guī)律:
如果const寫在指針變量名的旁邊, 那么指針的指向不能變, 而指向的內(nèi)存空間的值可以變
如果const寫在數(shù)據(jù)類型的左邊或者右邊, 那么指針的指向可以改變, 但是指向的內(nèi)存空間的值不能改變
**const對基本數(shù)據(jù)類型的作用, 可以讓基本數(shù)據(jù)類型的變量變?yōu)槌A?/p>