友情提示:先關注收藏,再檢視,13 萬字保姆級 C 語言從入門到精通教學。
什麼是計算機 ?
計算機的發明者是誰 ?
- 現在世界上***公認***的第一臺現代電子計算機是1946年在美國賓夕法尼亞大學誕生的ENIAC(Electronic Numerical Integrator And Calculator)
計算機是一種電器, 所以計算機只能識別兩種狀態, 一種是通電一種是斷電
正是因為如此, 最初ENIAC的程式是由很多開關和連線電線來完成的。但是這樣導致***改動一次程式要花很長時間***(需要人工重新設定很多開關的狀態和連線線)
為了提高效率,工程師們想能不能把程式和資料都放在記憶體中, 數學家馮·諾依曼將這個思想以數學語言系統闡述,提出了儲存程式計算機模型(這是所謂的馮·諾依曼機)
那利用數學語言如何表示計算機能夠識別的通電和斷電兩種狀態呢?
0和1更準確的是應該是高電平和低電平, 但是這個不用瞭解, 只需要知道計算機只能識別0和1以及儲存的資料都是由0和1組成的即可。
計算機程式是為了告訴計算機"做某件事或解決某個問題"而用"***計算機語言***編寫的命令集合(語句)
只要讓計算機執行這個程式,計算機就會自動地、有條不紊地進行工作,計算機的一切操作都是由程式控制的,離開程式,計算機將一事無成
現實生活中你如何告訴別人如何做某件事或者解決某個問題?
10111000 00000001 00000000 00000101 00000001 00000000
MOV AX, 1 ADD AX, 1
1 + 1
「初,世間無語言,僅電路與連線。及大牛出,天地開,始有 FORTRAN、 LISP、ALGOL 隨之, 乃有萬種語」
當你想了解底層原理時,你才會發現後悔當初沒有學習C語言
當你想學習一門新的語言時, 你才會發現後悔當初沒有學習C語言
當你使用一些高階框架、甚至系統框架時發現提供的API都是C語言編寫的, 你才發現後悔當初沒有學習C語言
學好數理化,走遍天下都不拍
學好C語言,再多語言都不怕
學習本套課程之前 | 學習本套課程中 | 學習本套課程之後 |
---|---|---|
[外連圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片儲存下來直接上傳(img-gHyaoC72-1623039894713)(https://upload-images.jianshu.io/upload_images/647982-c724f6cd01191121.png?imageMogr2/auto-orient/strip)] |
整合式開發環境(IDE,Integrated Development Environment )是用於提供程式開發環境的應用程式,一般包括程式碼編輯器、編譯器、偵錯器和圖形化使用者介面等工具。整合了程式碼編寫功能、分析功能、編譯功能、偵錯功能等一體化的開發軟體服務套。
切記囫圇吞棗, 不要糾結裡面的東西都是什麼含義, 初學者安裝成功就是一種成功
下載Qt Creator離線安裝包:
以管理身份執行離線安裝包
下一步,下一步,下一步,等待ing…
注意安裝路徑中最好不要出現中文
對於初學者而言全選是最簡單的方式(重點!!!)
設定Qt Creator開發環境變數
你的安裝路徑\5.11.0\mingw53_32\bin
你的安裝路徑\Tools\mingw530_32\bin
手機有很多功能, 「開機」,「關機」,「打電話」,「傳簡訊」,"拍照"等等
手機中的每一個功能就相當於C語言程式中的一個程式段(函數)
眾多功能中總有一個會被先執行,不可能多個功能一起執行
想使用手機必須先執行手機的開機功能
所以C語言程式也一樣,由眾多功能、眾多程式段組成, 眾多C語言程式段中總有一個會被先執行, 這個先執行的程式段我們稱之為"主函數"
一個C語言程式由多個"函數"構成,每個函數有自己的功能
一個程式***有且只有一個主函數***
如果一個程式沒有主函數,則這個程式不具備執行能力
程式執行時系統會***自動呼叫***主函數,而其它函數需要開發者***手動呼叫***
主函數有固定書寫的格式和範寫
int main() {
// insert code here...
return 0;
}
int call() {
return 0;
}
int main() {
call();
return 0;
}
int call(){
return 0;
}
int main(){
call();
printf();
return 0;
}
printf("hello world\n");
#include <stdio.h>
int call(){
return 0;
}
int main(){
call();
printf("hello world\n");
return 0;
}
int main(){
printf("hello world\n") // 如果沒有分號編譯時會報錯
return 0;
}
int main(){
// 如果沒有分號,多條語句合併到一行時, 系統不知道從什麼地方到什麼地方是一條完整語句
printf("hello world\n") return 0;
}
int main(){
printf("hello world\n"); // 這裡的分號如果是中文的分號就會報錯
return 0;
}
int main(){
return 0;
}
int main(){ // 編譯時會報錯, 重複定義
return 0;
}
int call(){ // 編譯時報錯, 因為只有call函數, 沒有main函數
return 0;
}
int mian(){ // 編譯時報錯, 因為main函數的名稱寫錯了,還是相當於沒有main函數
return 0;
}
#include <stdio.h>
main(){ // 不會報錯
printf("hello world\n");
return 0;
}
#include <stdio.h>
void main(){ // 不會報錯
printf("hello world\n");
return 0;
}
int main(){ // 不會報錯
printf("hello world\n");
}
#include <stdio.h>
int main(){
printf("hello world\n");
return 0;
}
Tips:
語法錯誤:編譯器會直接報錯
邏輯錯誤:沒有語法錯誤,只不過執行結果不正確
*** ***
*********
*******
****
**
printf(" *** *** \n");
printf("*********\n");
printf(" *******\n");
printf(" ****\n");
printf(" **\n");
printf(" *** *** \n*********\n *******\n ****\n **\n");
int i = 0;
while (1) {
if (i % 2 == 0) {
printf(" *** *** \n");
printf("*********\n");
printf(" *******\n");
printf(" ****\n");
printf(" **\n");
}else
{
printf("\n");
printf(" ** ** \n");
printf(" *******\n");
printf(" *****\n");
printf(" **\n");
}
sleep(1);
i++;
system("cls");
}
_ooOoo_
o8888888o
88" . "88
(| -_- |)
O\ = /O
____/`---'\____
. ' \\| |// `.
/ \\||| : |||// \
/ _||||| -:- |||||- \
| | \\\ - /// | |
| \_| ''\---/'' | |
\ .-\__ `-` ___/-. /
___`. .' /--.--\ `. . __
."" '< `.___\_<|>_/___.' >'"".
| | : `- \`.;`\ _ /`;.`/ - ` : | |
\ \ `-. \_ __\ /__ _/ .-` / /
======`-.____`-.___\_____/___.-`____.-'======
`=---='
.............................................
佛祖保佑 有無BUG
━━━━━━神獸出沒━━━━━━
┏┓ ┏┓
┏┛┻━━━━━━┛┻┓
┃ ┃
┃ ━ ┃
┃ ┳┛ ┗┳ ┃
┃ ┃
┃ ┻ ┃
┃ ┃
┗━┓ ┏━┛Code is far away from bug with the animal protecting
┃ ┃ 神獸保佑,程式碼無bug
┃ ┃
┃ ┗━━━┓
┃ ┣┓
┃ ┏━━┛┛
┗┓┓┏━┳┓┏┛
┃┫┫ ┃┫┫
┗┻┛ ┗┻┛
━━━━━━感覺萌萌噠━━━━━━
´´´´´´´´██´´´´´´´
´´´´´´´████´´´´´´
´´´´´████████´´´´
´´`´███▒▒▒▒███´´´´´
´´´███▒●▒▒●▒██´´´
´´´███▒▒▒▒▒▒██´´´´´
´´´███▒▒▒▒██´ 專案:第一個C語言程式
´´██████▒▒███´´´´´ 語言: C語言
´██████▒▒▒▒███´´ 編輯器: Qt Creator
██████▒▒▒▒▒▒███´´´´ 版本控制:git-github
´´▓▓▓▓▓▓▓▓▓▓▓▓▓▒´´ 程式碼風格:江哥style
´´▒▒▒▒▓▓▓▓▓▓▓▓▓▒´´´´´
´.▒▒▒´´▓▓▓▓▓▓▓▓▒´´´´´
´.▒▒´´´´▓▓▓▓▓▓▓▒
..▒▒.´´´´▓▓▓▓▓▓▓▒
´▒▒▒▒▒▒▒▒▒▒▒▒
´´´´´´´´´███████´´´´´
´´´´´´´´████████´´´´´´´
´´´´´´´█████████´´´´´´
´´´´´´██████████´´´´ 大部分人都在關注你飛的高不高,卻沒人在乎你飛的累不累,這就是現實!
´´´´´´██████████´´´ 我從不相信夢想,我,只,相,信,自,己!
´´´´´´´█████████´´
´´´´´´´█████████´´´
´´´´´´´´████████´´´´´
________▒▒▒▒▒
_________▒▒▒▒
_________▒▒▒▒
________▒▒_▒▒
_______▒▒__▒▒
_____ ▒▒___▒▒
_____▒▒___▒▒
____▒▒____▒▒
___▒▒_____▒▒
███____ ▒▒
████____███
█ _███_ _█_███
——————————————————————————女神保佑,程式碼無bug——————————————————————
#include<stdio.h>
int main() {
printf("南哥帶你裝B帶你飛");
return 0;
}
#include<iostream>
using namespace std;
int main() {
cout << "南哥帶你裝B帶你飛" << endl;
return 0;
}
#import <Foundation/Foundation.h>
int main() {
NSLog(@"南哥帶你裝B帶你飛");
return 0;
}
class Test
{
public static viod main()
{
system.out.println("南哥帶你裝B帶你飛");
}
}
package main
import "fmt" //引入fmt庫
func main() {
fmt.Println("南哥帶你裝B帶你飛")
}
void printMap(char map[6][7] , int row, int col);
int main(int argc, const char * argv[])
{
char map[6][7] = {
{'#', '#', '#', '#', '#', '#', '#'},
{'#', ' ', ' ', ' ', '#' ,' ', ' '},
{'#', 'R', ' ', '#', '#', ' ', '#'},
{'#', ' ', ' ', ' ', '#', ' ', '#'},
{'#', '#', ' ', ' ', ' ', ' ', '#'},
{'#', '#', '#', '#', '#', '#', '#'}
};
int row = sizeof(map)/sizeof(map[0]);
int col = sizeof(map[0])/ sizeof(map[0][0]);
printMap(map, row, col);
int pRow = 2;
int pCol = 1;
int endRow = 1;
int endCol = 6;
while ('R' != map[endRow][endCol]) {
printf("親, 請輸入相應的操作\n");
printf("w(向上走) s(向下走) a(向左走) d(向右走)\n");
char run;
run = getchar();
switch (run) {
case 's':
if ('#' != map[pRow + 1][pCol]) {
map[pRow][pCol] = ' ';
pRow++;//3
map[pRow][pCol] = 'R';
}
break;
case 'w':
if ('#' != map[pRow - 1][pCol]) {
map[pRow][pCol] = ' ';
pRow--;
map[pRow][pCol] = 'R';
}
break;
case 'a':
if ('#' != map[pRow][pCol - 1]) {
map[pRow][pCol] = ' ';
pCol--;
map[pRow][pCol] = 'R';
}
break;
case 'd':
if ('#' != map[pRow][pCol + 1]) {
map[pRow][pCol] = ' ';
pCol++;
map[pRow][pCol] = 'R';
}
break;
}
printMap(map, row, col);
}
printf("你太牛X了\n");
printf("想挑戰自己,請購買完整版本\n");
return 0;
}
void printMap(char map[6][7] , int row, int col)
{
system("cls");
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
printf("%c", map[i][j]);
}
printf("\n");
}
}
/*
R代表一個人
#代表一堵牆
// 0123456
####### // 0
# # // 1
#R ## # // 2
# # # // 3
## # // 4
####### // 5
分析:
>1.儲存地圖(二維陣列)
>2.輸出地圖
>3.操作R前進(控制小人行走)
3.1.接收使用者輸入(scanf/getchar)
w(向上走) s(向下走) a(向左走) d(向右走)
3.2.判斷使用者的輸入,控制小人行走
3.2.1.替換二維陣列中儲存的資料
(
1.判斷是否可以修改(如果不是#就可以修改)
2.修改現有位置為空白
3.修改下一步為R
)
3.3.輸出修改後的二維陣列
4.判斷使用者是否走出出口
*/
// 宣告列印地圖方法
void printMap(char map[6][7] , int row, int col);
int main(int argc, const char * argv[])
{
// 1.定義二維陣列儲存迷宮地圖
char map[6][7] = {
{'#', '#', '#', '#', '#', '#', '#'},
{'#', ' ', ' ', ' ', '#' ,' ', ' '},
{'#', 'R', ' ', '#', '#', ' ', '#'},
{'#', ' ', ' ', ' ', '#', ' ', '#'},
{'#', '#', ' ', ' ', ' ', ' ', '#'},
{'#', '#', '#', '#', '#', '#', '#'}
};
// 2.計算地圖行數和列數
int row = sizeof(map)/sizeof(map[0]);
int col = sizeof(map[0])/ sizeof(map[0][0]);
// 3.輸出地圖
printMap(map, row, col);
// 4.定義變數記錄人物位置
int pRow = 2;
int pCol = 1;
// 5.定義變數記錄出口的位置
int endRow = 1;
int endCol = 6;
// 6.控制人物行走
while ('R' != map[endRow][endCol]) {
// 6.1提示使用者如何控制人物行走
printf("親, 請輸入相應的操作\n");
printf("w(向上走) s(向下走) a(向左走) d(向右走)\n");
char run;
run = getchar();
// 6.2根據使用者輸入控制人物行走
switch (run) {
case 's':
if ('#' != map[pRow + 1][pCol]) {
map[pRow][pCol] = ' ';
pRow++;//3
map[pRow][pCol] = 'R';
}
break;
case 'w':
if ('#' != map[pRow - 1][pCol]) {
map[pRow][pCol] = ' ';
pRow--;
map[pRow][pCol] = 'R';
}
break;
case 'a':
if ('#' != map[pRow][pCol - 1]) {
map[pRow][pCol] = ' ';
pCol--;
map[pRow][pCol] = 'R';
}
break;
case 'd':
if ('#' != map[pRow][pCol + 1]) {
map[pRow][pCol] = ' ';
pCol++;
map[pRow][pCol] = 'R';
}
break;
}
// 6.3重新輸出行走之後的地圖
printMap(map, row, col);
}
printf("你太牛X了\n");
printf("想挑戰自己,請購買完整版本\n");
return 0;
}
/**
* @brief printMap
* @param map 需要列印的二維陣列
* @param row 二維陣列的行數
* @param col 二維陣列的列數
*/
void printMap(char map[6][7] , int row, int col)
{
// 為了保證視窗的乾淨整潔, 每次列印都先清空上一次的列印
system("cls");
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
printf("%c", map[i][j]);
}
printf("\n");
}
}
單行註釋
多行註釋
// 南哥 // it666.com
// /* 江哥 */
// 帥哥
/*
// 作者:LNJ
// 描述:第一個C語言程式作用:這是一個主函數,C程式的入口點
*/
/*
哈哈哈
/*嘻嘻嘻*/
呵呵呵
*/
/*
R代表一個人
#代表一堵牆
// 0123456
####### // 0
# # // 1
#R ## # // 2
# # # // 3
## # // 4
####### // 5
分析:
>1.儲存地圖(二維陣列)
>2.輸出地圖
>3.操作R前進(控制小人行走)
3.1.接收使用者輸入(scanf/getchar)
w(向上走) s(向下走) a(向左走) d(向右走)
3.2.判斷使用者的輸入,控制小人行走
3.2.1.替換二維陣列中儲存的資料
(
1.判斷是否可以修改(如果不是#就可以修改)
2.修改現有位置為空白
3.修改下一步為R
)
3.3.輸出修改後的二維陣列
4.判斷使用者是否走出出口
*/
// 2.計算地圖行數和列數
int row = sizeof(map)/sizeof(map[0]);
int col = sizeof(map[0])/ sizeof(map[0][0]);
/**
* @brief printMap
* @param map 需要列印的二維陣列
* @param row 二維陣列的行數
* @param col 二維陣列的列數
*/
void printMap(char map[6][7] , int row, int col)
{
system("cls");
for (int i = 0; i < row; i++) {
for (int j = 0; j < col; j++) {
printf("%c", map[i][j]);
}
printf("\n");
}
}
// 1.定義二維陣列儲存迷宮地圖
char map[6][7] = {
{'#', '#', '#', '#', '#', '#', '#'},
{'#', ' ', ' ', ' ', '#' ,' ', ' '},
{'#', 'R', ' ', '#', '#', ' ', '#'},
{'#', ' ', ' ', ' ', '#', ' ', '#'},
{'#', '#', ' ', ' ', ' ', ' ', '#'},
{'#', '#', '#', '#', '#', '#', '#'}
};
// 2.計算地圖行數和列數
int row = sizeof(map)/sizeof(map[0]);
int col = sizeof(map[0])/ sizeof(map[0][0]);
// 3.輸出地圖
printMap(map, row, col);
// 4.定義變數記錄人物位置
int pRow = 2;
int pCol = 1;
// 5.定義變數記錄出口的位置
int endRow = 1;
int endCol = 6;
// 6.控制人物行走
while ('R' != map[endRow][endCol]) {
... ...
}
1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
---|---|---|---|---|---|---|---|
char | short | int | long | float | double | if | else |
return | do | while | for | switch | case | break | continue |
default | goto | sizeof | auto | register | static | extern | unsigned |
signed | typedef | struct | enum | union | void | const | volatile |
這些不用專門去記住,用多了就會了。在編譯器裡都是有特殊顏色的。 我們用到時候會一個一個講解這個些關鍵字怎麼用,現在瀏覽下,有個印象就OK了
fromNo22 | from#22 | my_Boolean | my-Boolean | 2ndObj | GUI | lnj |
Mike2jack | 江哥 | _test | test!32 | haha(da)tt | jack_rose | jack&rose |
生活中無時無刻都在跟資料打交道
在我們使用計算機的過程中,也會接觸到各種各樣的資料
靜態的資料
動態的資料
既然硬碟的儲存空間這麼大,為何不把所有的應用程式載入到硬碟中去執行呢?
1 B(Byte位元組) = 8 bit(位)
// 00000000 就是一個位元組
// 111111111 也是一個位元組
// 10101010 也是一個位元組
// 任意8個0和1的組合都是一個位元組
1 KB(KByte) = 1024 B
1 MB = 1024 KB
1 GB = 1024 MB
1 TB = 1024 GB
整型常數
實型常數
字元常數
字串常數
自定義常數
常數型別練習
123 | 1.1F | 1.1 | .3 | ‘a’ | 「a」 | 「李南江」 |
int a;
float b;
char ch;
int a,b,c;
int value;
value = 998; // 賦值
int value; value = 998; // 初始化
int a = 10; int b = 4, c = 2;
int a, b = 10; //部分初始化
int c, d, e;
c = d = e =0;
int i = 10;
i = 20; // 修改變數的值
int a = 10;
int b = a; // 相當於把a中儲存的10拷貝了一份給b
int a = 10, c = 11;
printf("a=%d, c=%d", a, c);
double height = 1.75;
char blood = 'A';
printf("height=%.2f, 血型是%c", height, blood);
int main(){
int i = 998; // 作用域開始
return 0;// 作用域結束
}
int main(){
{
int i = 998; // 作用域開始
}// 作用域結束
printf("i = %d\n", i); // 不能使用
return 0;
}
int main(){
{
{
int i = 998;// 作用域開始
}// 作用域結束
printf("i = %d\n", i); // 不能使用
}
return 0;
}
int i = 666;
int main(){
printf("i = %d\n", i); // 可以使用
return 0;
}// 作用域結束
int call(){
printf("i = %d\n", i); // 可以使用
return 0;
}
int main(){
int i = 998; // 作用域開始
int i = 666; // 報錯, 重複定義
return 0;
}// 作用域結束
int i = 666;
int i = 998; // 報錯, 重複定義
int main(){
return 0;
}
int i = 666;
int main(){
int i = 998; // 不會報錯
return 0;
}
int main(){
int i = 998; // 不會報錯
return 0;
}
int call(){
int i = 666; // 不會報錯
return 0;
}
型別 | 16位元編譯器 | 32位元編譯器 | 64位元編譯器 |
---|---|---|---|
char | 1 | 1 | 1 |
int | 2 | 4 | 4 |
float | 4 | 4 | 4 |
double | 8 | 8 | 8 |
short | 2 | 2 | 2 |
long | 4 | 4 | 8 |
long long | 8 | 8 | 8 |
void* | 2 | 4 | 8 |
int main(){
int number;
int value;
number = 22;
value = 666;
}
#include <stdio.h>
int main(){
int number;
int value;
number = 22;
value = 666;
printf("&number = %p\n", &number); // 0060FEAC
printf("&value = %p\n", &value); // 0060FEA8
}
先不要著急, 剛開始接觸C語言, 我先了解這麼多就夠了. 後面會再次更深入的講解儲存的各種細節。
printf("格式控制字串",輸出項列表 );
printf("a = %d, b = %d",a, b);
%[標誌][輸出寬度][.精度][長度]型別
printf("a = %型別", a);
型別 | 含義 |
---|---|
d | 有符號10進位制整型 |
i | 有符號10進位制整型 |
u | 無符號10進位制整型 |
o | 無符號8進位制整型 |
x | 無符號16進位制整型 |
X | 無符號16進位制整型 |
f | 單、雙精度浮點數(預設保留6位小數) |
e / E | 以指數形式輸出單、雙精度浮點數 |
g / G | 以最短輸出寬度,輸出單、雙精度浮點數 |
c | 字元 |
s | 字串 |
p | 地址 |
#include <stdio.h>
int main(){
int a = 10;
int b = -10;
float c = 6.6f;
double d = 3.1415926;
double e = 10.10;
char f = 'a';
// 有符號整數(可以輸出負數)
printf("a = %d\n", a); // 10
printf("a = %i\n", a); // 10
// 無符號整數(不可以輸出負數)
printf("a = %u\n", a); // 10
printf("b = %u\n", b); // 429496786
// 無符號八進位制整數(不可以輸出負數)
printf("a = %o\n", a); // 12
printf("b = %o\n", b); // 37777777766
// 無符號十六進位制整數(不可以輸出負數)
printf("a = %x\n", a); // a
printf("b = %x\n", b); // fffffff6
// 無符號十六進位制整數(不可以輸出負數)
printf("a = %X\n", a); // A
printf("b = %X\n", b); // FFFFFFF6
// 單、雙精度浮點數(預設保留6位小數)
printf("c = %f\n", c); // 6.600000
printf("d = %lf\n", d); // 3.141593
// 以指數形式輸出單、雙精度浮點數
printf("e = %e\n", e); // 1.010000e+001
printf("e = %E\n", e); // 1.010000E+001
// 以最短輸出寬度,輸出單、雙精度浮點數
printf("e = %g\n", e); // 10.1
printf("e = %G\n", e); // 10.1
// 輸出字元
printf("f = %c\n", f); // a
}
printf("a = %[寬度]型別", a);
#include <stdio.h>
int main(){
// 實際位數小於指定寬度
int a = 1;
printf("a =|%d|\n", a); // |1|
printf("a =|%5d|\n", a); // | 1|
// 實際位數大於指定寬度
int b = 1234567;
printf("b =|%d|\n", b); // |1234567|
printf("b =|%5d|\n", b); // |1234567|
}
printf("a = %[標誌][寬度]型別", a);
標誌 | 含義 |
---|---|
- | 左對齊, 預設右對齊 |
+ | 當輸出值為正數時,在輸出值前面加上一個+號, 預設不顯示 |
0 | 右對齊時, 用0填充寬度.(預設用空格填充) |
空格 | 輸出值為正數時,在輸出值前面加上空格, 為負數時加上負號 |
# | 對c、s、d、u型別無影響 |
# | 對o型別, 在輸出時加字首o |
# | 對x型別,在輸出時加字首0x |
#include <stdio.h>
int main(){
int a = 1;
int b = -1;
// -號標誌
printf("a =|%d|\n", a); // |1|
printf("a =|%5d|\n", a); // | 1|
printf("a =|%-5d|\n", a);// |1 |
// +號標誌
printf("a =|%d|\n", a); // |1|
printf("a =|%+d|\n", a);// |+1|
printf("b =|%d|\n", b); // |-1|
printf("b =|%+d|\n", b);// |-1|
// 0標誌
printf("a =|%5d|\n", a); // | 1|
printf("a =|%05d|\n", a); // |00001|
// 空格標誌
printf("a =|% d|\n", a); // | 1|
printf("b =|% d|\n", b); // |-1|
// #號
int c = 10;
printf("c = %o\n", c); // 12
printf("c = %#o\n", c); // 012
printf("c = %x\n", c); // a
printf("c = %#x\n", c); // 0xa
}
printf("a = %[精度]型別", a);
#include <stdio.h>
int main(){
double a = 3.1415926;
printf("a = %.2f\n", a); // 3.14
}
printf("a = %.*f", a);
#include <stdio.h>
int main(){
double a = 3.1415926;
printf("a = %.*f", 2, a); // 3.14
}
#include <stdio.h>
int main(){
// 1234.567871093750000
float a = 1234.567890123456789;
// 1234.567890123456900
double b = 1234.567890123456789;
printf("a = %.15f\n", a); // 前8位元數位是準確的, 後面的都不準確
printf("b = %.15f\n", b); // 前16位元數位是準確的, 後面的都不準確
}
printf("a = %[長度]型別", a);
長度 | 修飾型別 | 含義 |
---|---|---|
hh | d、i、o、u、x | 輸出char |
h | d、i、o、u、x | 輸出 short int |
l | d、i、o、u、x | 輸出 long int |
ll | d、i、o、u、x | 輸出 long long int |
#include <stdio.h>
int main(){
char a = 'a';
short int b = 123;
int c = 123;
long int d = 123;
long long int e = 123;
printf("a = %hhd\n", a); // 97
printf("b = %hd\n", b); // 123
printf("c = %d\n", c); // 123
printf("d = %ld\n", d); // 123
printf("e = %lld\n", e); // 123
}
printf("%f%%", 3.1415);
#include <stdio.h>
int main(){
printf("%f%%", 3.1415); // 輸出結果3.1415%
}
scanf("格式控制字串", 地址列表);
scanf("%d", &num);
#include <stdio.h>
int main(){
int number;
scanf("%d", &number); // 接收一個整數
printf("number = %d\n", number);
}
#include <stdio.h>
int main(){
float num;
// 例如:輸入 Tab 空格 回車 回車 Tab 空格 3.14 , 得到的結果還是3.14
scanf("%f", &num);
printf("num = %f\n", num);
}
#include <stdio.h>
int main(){
int number;
// 使用者必須輸入number = 數位 , 否則會得到一個意外的值
scanf("number = %d", &number);
printf("number = %d\n", number);
}
#include <stdio.h>
int main(){
int number;
scanf("%d", &number);
printf("number = %d\n", number);
int value;
scanf("%d", &value);
printf("value = %d\n", value);
}
#include <stdio.h>
int main(){
int number;
int value;
// 可以輸入 數位 空格 數位, 或者 數位 回車 數位
scanf("%d%d", &number, &value);
printf("number = %d\n", number);
printf("value = %d\n", value);
}
#include <stdio.h>
int main(){
int number;
int value;
// 輸入 數位,數位 即可
scanf("%d,%d", &number, &value);
printf("number = %d\n", number);
printf("value = %d\n", value);
}
#include <stdio.h>
int main(){
int number;
// 輸入完畢之後按下回車無法結束輸入
scanf("%d\n", &number);
printf("number = %d\n", number);
}
#include <stdio.h>
int main(){
int num1;
int num2;
char ch1;
scanf("%d%c%d", &num1, &ch1, &num2);
printf("num1 = %d, ch1 = %c, num2 = %d\n", num1, ch1, num2);
char ch2;
int num3;
scanf("%c%d",&ch2, &num3);
printf("ch2 = %c, num3 = %d\n", ch2, num3);
}
fflush(stdin);
setbuf(stdin, NULL);
#include <stdio.h>
int main(){
int num1;
int num2;
char ch1;
scanf("%d%c%d", &num1, &ch1, &num2);
printf("num1 = %d, ch1 = %c, num2 = %d\n", num1, ch1, num2);
//fflush(stdin); // 清空輸入快取區
setbuf(stdin, NULL); // 清空輸入快取區
char ch2;
int num3;
scanf("%c%d",&ch2, &num3);
printf("ch2 = %c, num3 = %d\n", ch2, num3);
}
#include <stdio.h>
int main(){
char ch = 'a';
putchar(ch); // 輸出a
}
#include <stdio.h>
int main(){
char ch;
ch = getchar();// 獲取一個字元
printf("ch = %c\n", ch);
}
和數學中的運運算元一樣, C語言中的運運算元是告訴程式執行特定算術或邏輯操作的符號
什麼是表示式
優先順序 | 名稱 | 符號 | 說明 |
---|---|---|---|
3 | 乘法運運算元 | * | 雙目運運算元,具有左結合性 |
3 | 除法運運算元 | / | 雙目運運算元,具有左結合性 |
3 | 求餘運運算元 (模運運算元) | % | 雙目運運算元,具有左結合性 |
4 | 加法運運算元 | + | 雙目運運算元,具有左結合性 |
4 | 減法運運算元 | - | 雙目運運算元,具有左結合性 |
#include <stdio.h>
int main(){
int a = 10;
int b = 5;
// 加法
int result = a + b;
printf("%i\n", result); // 15
// 減法
result = a - b;
printf("%i\n", result); // 5
// 乘法
result = a * b;
printf("%i\n", result); // 50
// 除法
result = a / b;
printf("%i\n", result); // 2
// 算術運運算元的結合性和優先順序
// 結合性: 左結合性, 從左至右
int c = 50;
result = a + b + c; // 15 + c; 65;
printf("%i\n", result);
// 優先順序: * / % 大於 + -
result = a + b * c; // a + 250; 260;
printf("%i\n", result);
}
#include <stdio.h>
int main(){
// 整數除以整數, 結果還是整數
printf("%i\n", 10 / 3); // 3
// 參與運算的任何一個數是小數, 結果就是小數
printf("%f\n", 10 / 3.0); // 3.333333
}
#include <stdio.h>
int main(){
// 10 / 3 商等於3, 餘1
int result = 10 % 3;
printf("%i\n", result); // 1
// 左邊小於右邊, 那麼結果就是左邊
result = 2 % 10;
printf("%i\n", result); // 2
// 被除數是正數結果就是正數,被除數是負數結果就是負數
result = 10 % 3;
printf("%i\n", result); // 1
result = -10 % 3;
printf("%i\n", result); // -1
result = 10 % -3;
printf("%i\n", result); // 1
}
優先順序 | 名稱 | 符號 | 說明 |
---|---|---|---|
14 | 賦值運運算元 | = | 雙目運運算元,具有右結合性 |
14 | 除後賦值運運算元 | /= | 雙目運運算元,具有右結合性 |
14 | 乘後賦值運運算元 (模運運算元) | *= | 雙目運運算元,具有右結合性 |
14 | 取模後賦值運運算元 | %= | 雙目運運算元,具有右結合性 |
14 | 加後賦值運運算元 | += | 雙目運運算元,具有右結合性 |
14 | 減後賦值運運算元 | -= | 雙目運運算元,具有右結合性 |
#include <stdio.h>
int main(){
// 簡單的賦值運運算元 =
// 會將=右邊的值賦值給左邊
int a = 10;
printf("a = %i\n", a); // 10
}
#include <stdio.h>
int main(){
// 複合賦值運運算元 += -= *= /= %=
// 將變數中的值取出之後進行對應的操作, 操作完畢之後再重新賦值給變數
int num1 = 10;
// num1 = num1 + 1; num1 = 10 + 1; num1 = 11;
num1 += 1;
printf("num1 = %i\n", num1); // 11
int num2 = 10;
// num2 = num2 - 1; num2 = 10 - 1; num2 = 9;
num2 -= 1;
printf("num2 = %i\n", num2); // 9
int num3 = 10;
// num3 = num3 * 2; num3 = 10 * 2; num3 = 20;
num3 *= 2;
printf("num3 = %i\n", num3); // 20
int num4 = 10;
// num4 = num4 / 2; num4 = 10 / 2; num4 = 5;
num4 /= 2;
printf("num4 = %i\n", num4); // 5
int num5 = 10;
// num5 = num5 % 3; num5 = 10 % 3; num5 = 1;
num5 %= 3;
printf("num5 = %i\n", num5); // 1
}
#include <stdio.h>
int main(){
int number = 10;
// 賦值運運算元優先順序是14, 普通運運算元優先順序是3和4, 所以先計算普通運運算元
// 普通運運算元中乘法優先順序是3, 加法是4, 所以先計算乘法
// number += 1 + 25; number += 26; number = number + 26; number = 36;
number += 1 + 5 * 5;
printf("number = %i\n", number); // 36
}
優先順序 | 名稱 | 符號 | 說明 |
---|---|---|---|
2 | 自增運運算元(在後) | i++ | 單目運運算元,具有左結合性 |
2 | 自增運運算元(在前) | ++i | 單目運運算元,具有右結合性 |
2 | 自減運運算元(在後) | i– | 單目運運算元,具有左結合性 |
2 | 自減運運算元(在前) | –i | 單目運運算元,具有右結合性 |
#include <stdio.h>
int main(){
int number = 10;
number++;
printf("number = %i\n", number); // 11
++number;
printf("number = %i\n", number); // 12
}
#include <stdio.h>
int main(){
int number = 10;
// ++在後, 先參與表示式運算, 再自增
// 表示式運算時為: 3 + 10;
int result = 3 + number++;
printf("result = %i\n", result); // 13
printf("number = %i\n", number); // 11
}
#include <stdio.h>
int main(){
int number = 10;
// ++在前, 先自增, 再參與表示式運算
// 表示式運算時為: 3 + 11;
int result = 3 + ++number;
printf("result = %i\n", result); // 14
printf("number = %i\n", number); // 11
}
#include <stdio.h>
int main(){
int number = 10;
// --在後, 先參與表示式運算, 再自減
// 表示式運算時為: 10 + 3;
int result = number-- + 3;
printf("result = %i\n", result); // 13
printf("number = %i\n", number); // 9
}
#include <stdio.h>
int main(){
int number = 10;
// --在前, 先自減, 再參與表示式運算
// 表示式運算時為: 9 + 3;
int result = --number + 3;
printf("result = %i\n", result); // 12
printf("number = %i\n", number); // 9
}
++(a+b); 5++;
int i = 10;
int b = i++; // 不推薦
或者
int b = ++i; // 不推薦
或者
int a = 10;
int b = ++a + a++; // 不推薦
int i = 10;
int b = i; // 推薦
i++;
或者;
i++;
int b = i; // 推薦
或者
int a = 10;
++a;
int b = a + a; // 推薦
a++;
同一個表示式中同一個變數自增或自減後如何運算
, 不同編譯器得到結果也不同, 在企業開發中千萬不要這樣寫 int a = 1;
// 下列程式碼利用Qt執行時6, 利用Xcode執行是5
// 但是無論如何, 最終a的值都是3
// 在C語言中這種程式碼沒有意義, 不用深究也不要這樣寫
// 特點: 參與運算的是同一個變數, 參與運算時都做了自增自減操作, 並且在同一個表示式中
int b = ++a + ++a;
printf("b = %i\n", b);
sizeof可以用來計算一個變數或常數、資料型別所佔的記憶體位元組數
sizeof的幾種形式
sizeof(10);
char c = 'a'; sizeof(c);
sizeof 10;
char c = 'a'; sizeof c;
sizeof(float);
如果是資料型別不能省略括號
sizeof面試題:
#include <stdio.h>
int main(){
int a = 10;
double b = 3.14;
// 由於sizeof的優先順序比+號高, 所以會先計算sizeof(a);
// a是int型別, 所以佔4個位元組得到結果4
// 然後再利用計算結果和b相加, 4 + 3.14 = 7.14
double res = sizeof a+b;
printf("res = %lf\n", res); // 7.14
}
表示式1,表示式2,… …,表示式n;
int result = a+1,b=3*4;
#include <stdio.h>
int main(){
int a = 10, b = 20, c;
// ()優先順序高於逗號運運算元和賦值運運算元, 所以先計算()中的內容
// c = (11, 21);
// ()中是一個逗號表示式, 結果是最後一個表示式的值, 所以計算結果為21
// 將逗號表示式的結果賦值給c, 所以c的結果是21
c = (a + 1, b + 1);
printf("c = %i\n", c); // 21
}
優先順序 | 名稱 | 符號 | 說明 |
---|---|---|---|
6 | 大於運運算元 | > | 雙目運運算元,具有左結合性 |
6 | 小於運運算元 | < | 雙目運運算元,具有左結合性 |
6 | 大於等於運運算元 | >= | 雙目運運算元,具有左結合性 |
6 | 小於等於運運算元 | <= | 雙目運運算元,具有左結合性 |
7 | 等於運運算元 | == | 雙目運運算元,具有左結合性 |
7 | 不等於運運算元 | != | 雙目運運算元,具有左結合性 |
#include <stdio.h>
int main(){
int result = 10 > 5;
printf("result = %i\n", result); // 1
result = 5 < 10;
printf("result = %i\n", result); // 1
result = 5 > 10;
printf("result = %i\n", result); // 0
result = 10 >= 10;
printf("result = %i\n", result); // 1
result = 10 <= 10;
printf("result = %i\n", result); // 1
result = 10 == 10;
printf("result = %i\n", result); // 1
result = 10 != 9;
printf("result = %i\n", result); // 1
}
#include <stdio.h>
int main(){
// == 優先順序 小於 >, 所以先計算>
// result = 10 == 1; result = 0;
int result = 10 == 5 > 3;
printf("result = %i\n", result); // 0
}
#include <stdio.h>
int main(){
// == 和 != 優先順序一樣, 所以按照結合性
// 關係運算子是左結合性, 所以從左至右計算
// result = 0 != 3; result = 1;
int result = 10 == 5 != 3;
printf("result = %i\n", result); // 1
}
int result1 = 3 > 4 + 7
int result2 = (3>4) + 7
int result3 = 5 != 4 + 2 * 7 > 3 == 10
#include <stdio.h>
int main(){
float a = 0.1;
float b = a * 10 + 0.00000000001;
double c = 1.0 + + 0.00000000001;
printf("b = %f\n", b);
printf("c = %f\n", c);
int result = b == c;
printf("result = %i\n", result); // 0
}
優先順序 | 名稱 | 符號 | 說明 |
---|---|---|---|
2 | 邏輯非運運算元 | ! | 單目運運算元,具有右結合性 |
11 | 邏輯與運運算元 | && | 雙目運運算元,具有左結合性 |
12 | 邏輯或運運算元 | \|\| | 雙目運運算元,具有左結合性 |
! 條件A;
#include <stdio.h>
int main(){
// ()優先順序高, 先計算()裡面的內容
// 10==10為真, 所以result = !(1);
// !代表真變假, 假變真,所以結果是假0
int result = !(10 == 10);
printf("result = %i\n", result); // 0
}
條件A && 條件B;
#include <stdio.h>
int main(){
// 真 && 真
int result = (10 == 10) && (5 != 1);
printf("result = %i\n", result); // 1
// 假 && 真
result = (10 == 9) && (5 != 1);
printf("result = %i\n", result); // 0
// 真 && 假
result = (10 == 10) && (5 != 5);
printf("result = %i\n", result); // 0
// 假 && 假
result = (10 == 9) && (5 != 5);
printf("result = %i\n", result); // 0
}
#include <stdio.h>
int main(){
int a = 10;
int b = 20;
// 邏輯與, 前面為假, 不會繼續執行後面
int result = (a == 9) && (++b);
printf("result = %i\n", result); // 1
printf("b = %i\n", b); // 20
}
條件A || 條件B;
#include <stdio.h>
int main(){
// 真 || 真
int result = (10 == 10) || (5 != 1);
printf("result = %i\n", result); // 1
// 假 || 真
result = (10 == 9) || (5 != 1);
printf("result = %i\n", result); // 1
// 真 || 假
result = (10 == 10) || (5 != 5);
printf("result = %i\n", result); // 1
// 假 || 假
result = (10 == 9) || (5 != 5);
printf("result = %i\n", result); // 0
}
#include <stdio.h>
int main(){
int a = 10;
int b = 20;
// 邏輯或, 前面為真, 不會繼續執行後面
int result = (a == 10) || (++b);
printf("result = %i\n", result); // 1
printf("b = %i\n", b); // 20
}
int result = 3>5 || 2<4 && 6<1;
三目運運算元,它需要3個資料或表示式構成條件表示式
格式: 表示式1?表示式2(結果A):表示式3(結果B)
考試及格 ? 及格 : 不及格;
求值規則:
範例:
int a = 10;
int b = 20;
int max = (a > b) ? a : b;
printf("max = %d", max);
輸出結果: 20
等價於:
int a = 10;
int b = 20;
int max = 0;
if(a>b){
max=a;
}else {
max=b;
}
printf("max = %d", max);
#include <stdio.h>
int main(){
int a = 10;
int b = 5;
// 先計算 a > b
// 然後再根據計算結果判定返回a還是b
// 相當於int max= (a>b) ? a : b;
int max= a>b ? a : b;
printf("max = %i\n", max); // 10
}
#include <stdio.h>
int main(){
int a = 10;
int b = 5;
int c = 20;
int d = 10;
// 結合性是從右至左, 所以會先計算:後面的內容
// int res = a>b?a:(c>d?c:d);
// int res = a>b?a:(20>10?20:10);
// int res = a>b?a:(20);
// 然後再計算最終的結果
// int res = 10>5?10:(20);
// int res = 10;
int res = a>b?a:c>d?c:d;
printf("res = %i\n", res);
}
強制型別轉換(顯示轉換) | 自動型別轉換(隱式轉換) |
---|---|
(需要轉換的型別)(表示式) | 1.算數轉換 2.賦值轉換 |
// 將double轉換為int
int a = (int)10.5;
// 當前表示式用1.0佔用8個位元組, 2佔用4個位元組
// 所以會先將整數型別2轉換為double型別之後再計算
double b = 1.0 / 2;
// 賦值時左邊是什麼型別,就會自動將右邊轉換為什麼型別再儲存
int a = 10.6;
// 結果為0, 因為參與運算的都是整型
double a = (double)(1 / 2);
// 結果為0.5, 因為1被強制轉換為了double型別, 2也會被自動提升為double型別
double b = (double)1 / 2;
#include <stdio.h>
int main(){
double d = 3.14;
int num = (int)d;
printf("num = %i\n", num); // 3
printf("d = %lf\n", d); // 3.140000
}
交換前
int a = 10; int b = 20;
交換後
int a = 20; int b = 10;
預設情況下程式執行後,系統會按書寫順序從上至下依次執行程式中的每一行程式碼。但是這並不能滿足我們所有的開發需求, 為了方便我們控制程式的執行流程,C語言提供3種流程控制結構,不同的流程控制結構可以實現不同的執行流程。
這3種流程結構分別是順序結構、選擇結構、迴圈結構
順序結構:
選擇結構
迴圈結構
if(表示式) {
語句塊1;
}
後續語句;
if(age >= 18) {
printf("開網路卡\n");
}
printf("買菸\n");
if(表示式){
語句塊1;
}else{
語句塊2;
}
後續語句;
if(age > 18){
printf("開網路卡\n");
}else{
printf("喊家長來開\n");
}
printf("買菸\n");
if(表示式1) {
語句塊1;
}else if(表示式2){
語句塊2;
}else if(表示式3){
語句塊3;
}else{
語句塊4;
}
後續語句;
if(age>40){
printf("給房卡");
}else if(age>25){
printf("給名片");
}else if(age>18){
printf("給網路卡");
}else{
printf("給好人卡");
}
printf("買菸\n");
if(表示式1){
語句塊1;
if(表示式2){
語句塊2;
}
}else{
if(表示式3){
語句塊3;
}else{
語句塊4;
}
}
#include <stdio.h>
int main(){
if(0){
printf("執行了if");
}else{
printf("執行了else"); // 被執行
}
}
// 極其不推薦寫法
int age = 17;
if (age >= 18)
printf("開網路卡\n");
else
printf("喊家長來開\n");
#include <stdio.h>
int main(){
if(0)
if(1)
printf("A\n");
else // 和if(1)匹配
printf("B\n");
else // 和if(0)匹配, 因為if(1)已經被匹配過了
if (1)
printf("C\n"); // 輸出C
else // 和if(1)匹配
printf("D\n");
}
#include <stdio.h>
int main(){
if(1)
int number = 10; // 系統會報錯
printf("number = %i\n", number);
}
#include <stdio.h>
int main(){
if(0){
int number = 10;
}else
int value = 20; // 系統會報錯
printf("value = %i\n", value);
}
// 因為if(10 > 2)後面有一個分號, 所以系統會認為if省略了大括號
// if省略大括號時只能管控緊隨其後的那條語句, 所以只能管控分號
if(10 > 2);
{
printf("10 > 2");
}
// 輸出結果: 10 > 2
#include <stdio.h>
int main(){
int a = 8;
// if(a = 10){// 錯誤寫法, 但不會報錯
if (10 == a){
printf("a的值是10\n");
}else{
printf("a的值不是10\n");
}
}
if練習
實現石頭剪刀布
剪刀石頭布遊戲:
1)定義遊戲規則
剪刀 幹掉 布
石頭 幹掉 剪刀
布 幹掉石頭
2)顯示玩家開始猜拳
3)接收玩家輸入的內容
4)讓電腦隨機產生一種拳
5)判斷比較
(1)玩家贏的情況(顯示玩家贏了)
(2)電腦贏的情況(顯示電腦贏了)
(3)平局(顯示平局)
switch(表示式){
case 常數表示式1:
語句1;
break;
case 常數表示式2:
語句2;
break;
case 常數表示式n:
語句n;
break;
default:
語句n+1;
break;
}
#include <stdio.h>
int main() {
int num = 3;
switch(num){
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
case 3:
printf("星期三\n");
break;
case 4:
printf("星期四\n");
break;
case 5:
printf("星期五\n");
break;
case 6:
printf("星期六\n");
break;
case 7:
printf("星期日\n");
break;
default:
printf("回火星去\n");
break;
}
}
#include <stdio.h>
int main() {
switch(1.1){ // 報錯
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n");
break;
default:
printf("回火星去\n");
break;
}
}
#include <stdio.h>
int main() {
int num = 3;
switch(1){
case 1:
printf("星期一\n");
break;
case 'a':
printf("星期二\n");
break;
case num: // 報錯
printf("星期三\n");
break;
case 4.0: // 報錯
printf("星期四\n");
break;
default:
printf("回火星去\n");
break;
}
}
#include <stdio.h>
int main() {
switch(1){
case 1: // 報錯
printf("星期一\n");
break;
case 1: // 報錯
printf("星期一\n");
break;
default:
printf("回火星去\n");
break;
}
}
#include <stdio.h>
int main() {
switch(1){
case 1:{
int num = 10;
printf("num = %i\n", num);
printf("星期一\n");
break;
}
case 2:
printf("星期一\n");
break;
default:
printf("回火星去\n");
break;
}
}
#include <stdio.h>
int main() {
int num = 2;
switch(num){
case 1:
printf("星期一\n");
break;
case 2:
printf("星期二\n"); // 被輸出
case 3:
printf("星期三\n"); // 被輸出
default:
printf("回火星去\n"); // 被輸出
break;
}
}
#include <stdio.h>
int main() {
switch(1){
case 1:
printf("星期一\n");
break;
case 2:
printf("星期一\n");
break;
}
}
#include <stdio.h>
int main() {
switch(3){
case 1:
printf("星期一\n");
break;
default:
printf("Other,,,\n");
break;
case 2:
printf("星期一\n");
break;
}
}
#include <stdio.h>
int main() {
int a = -1;
scanf("%d", &a);
if(a > 100){
printf("使用者輸入的資料大於100");
}else{
printf("使用者輸入的資料不大於100");
}
}
#include <stdio.h>
int main() {
int a = -1;
scanf("%d", &a);
// 挺(T)萌(M)的(D)搞不定啊
switch (a) {
case 101:
case 102:
case 103:
case 104:
case 105:
printf("大於\n");
break;
default:
printf("不大於\n");
break;
}
}
要求使用者輸入一個分數,根據輸入的分數輸出對應的等級
A 90~100
B 80~89
C 70~79
D 60~69
E 0~59
while ( 迴圈控制條件 ) {
迴圈體中的語句;
能夠讓迴圈結束的語句;
....
}
構成迴圈結構的幾個條件
範例:
int count = 0;
while (count < 3) { // 迴圈控制條件
printf("發射子彈~嗶嗶嗶嗶\n"); // 需要反覆執行的語句
count++; // 能夠讓迴圈結束的語句
}
#include <stdio.h>
int main(){
int count = 4;
// 1.判斷迴圈控制條件是否為真,此時為假所以跳過迴圈語句
while (count < 3) {
printf("發射子彈~嗶嗶嗶嗶\n");
count++;
}
// 2.執行迴圈語句後面的程式碼, 列印"迴圈執行完畢"
printf("迴圈執行完畢\n");
}
#include <stdio.h>
int main(){
int count = 0;
// 1.判斷迴圈控制條件是否為真,此時0 < 3為真
// 4.再次判斷迴圈控制條件是否為真,此時1 < 3為真
// 7.再次判斷迴圈控制條件是否為真,此時2 < 3為真
// 10.再次判斷迴圈控制條件是否為真,此時3 < 3為假, 跳過迴圈語句
while (count < 3) {
// 2.執行迴圈體中的程式碼, 列印"發子彈"
// 5.執行迴圈體中的程式碼, 列印"發子彈"
// 8.執行迴圈體中的程式碼, 列印"發子彈"
printf("發射子彈~嗶嗶嗶嗶\n");
// 3.執行"能夠讓迴圈結束的語句" count = 1
// 6.執行"能夠讓迴圈結束的語句" count = 2
// 9.執行"能夠讓迴圈結束的語句" count = 3
count++;
}
// 11.執行迴圈語句後面的程式碼, 列印"迴圈執行完畢"
printf("迴圈執行完畢\n");
}
#include <stdio.h>
int main(){
while (1) { // 死迴圈
printf("發射子彈~嗶嗶嗶嗶\n");
// 沒有能夠讓迴圈結束的語句
}
}
#include <stdio.h>
int main(){
while (1) // 死迴圈
printf("發射子彈~嗶嗶嗶嗶\n");
// 沒有能夠讓迴圈結束的語句
}
#include <stdio.h>
int main(){
while (1) // 死迴圈
int num = 10; // 報錯
// 沒有能夠讓迴圈結束的語句
}
#include <stdio.h>
int main(){
int count = 0;
while (count < 3);{ // 死迴圈
printf("發射子彈~嗶嗶嗶嗶\n");
count++;
}
}
// 死迴圈一般在作業系統級別的應用程式會比較多, 日常開發中很少用
while (1);
do {
迴圈體中的語句;
能夠讓迴圈結束的語句;
....
} while (迴圈控制條件 );
int count = 0;
do {
printf("發射子彈~嗶嗶嗶嗶\n");
count++;
}while(count < 10);
do-while迴圈執行流程
應用場景
#include<stdio.h>
int main()
{
int num = -1;
do{
printf("請輸入密碼,驗證您的身份\n");
scanf("%d", &num);
}while(123456 != num);
printf("主人,您終於回來了\n");
}
for(初始化表示式;迴圈條件表示式;迴圈後的操作表示式) {
迴圈體中的語句;
}
for(int i = 0; i < 10; i++){
printf("發射子彈~嗶嗶嗶嗶\n");
}
for迴圈執行流程
for迴圈注意點:
for(;;);
for和while應用場景
int count = 0; // 初始化表示式
while (count < 10) { // 條件表示式
printf("發射子彈~嗶嗶嗶嗶 %i\n", count);
count++; // 迴圈後增量表示式
}
// 如果初始化表示式的值, 需要在迴圈之後使用, 那麼就用while
printf("count = %i\n", count);
// 注意: 在for迴圈初始化表示式中定義的變數, 只能在for迴圈後面的{}中存取
// 所以: 如果初始化表示式的值, 不需要在迴圈之後使用, 那麼就用for
// 因為如果初始化表示式的值, 在迴圈之後就不需要使用了 , 那麼用while會導致效能問題
for (int count = 0; count < 10; count++) {
printf("發射子彈~嗶嗶嗶嗶 %i\n", count);
}
// printf("count = %i\n", count);
// 如果需要使用初始化表示式的值, 也可以將初始化表示式寫到外面
int count = 0;
for (; count < 10; count++) {
printf("發射子彈~嗶嗶嗶嗶\n", count);
}
printf("count = %i\n", count);
C語言中提供了四大跳轉語句, 分別是return、break、continue、goto
break:
應用場景:
break注意點:
if(1) {
break; // 會報錯
}
while(1) {
while(2) {
break;// 只對while2有效, 不會影響while1
}
printf("while1迴圈體\n");
}
while(2){
break;
printf("打我啊!");// 執行不到
}
if(1) {
continue; // 會報錯
}
#include <stdio.h>
int main(){
int num = 0;
// loop:是定義的標記
loop:if(num < 10){
printf("num = %d\n", num);
num++;
// goto loop代表跳轉到標記的位置
goto loop;
}
}
#include <stdio.h>
int main(){
while (1) {
while(2){
goto lnj;
}
}
lnj:printf("跳過了所有迴圈");
}
while(條件表示式) {
while迴圈結構 or dowhile迴圈結構 or for迴圈結構
}
for(初始化表示式;迴圈條件表示式;迴圈後的操作表示式) {
while迴圈結構 or dowhile迴圈結構 or for迴圈結構
}
do {
while迴圈結構 or dowhile迴圈結構 or for迴圈結構
} while (迴圈控制條件 );
for (row=0; row<100; row++) {
// 低效率:長迴圈在最外層
for ( col=0; col<5; col++ ) {
sum = sum + a[row][col];
}
}
for (col=0; col<5; col++ ) {
// 高效率:長迴圈在最內層
for (row=0; row<100; row++) {
sum = sum + a[row][col];
}
}
好友列表1
好友1
好友2
好友列表2
好友1
好友2
好友列表3
好友1
好友2
for (int i = 0; i < 4; i++) {
printf("好友列表%d\n", i+1);
for (int j = 0; j < 4; j++) {
printf(" 角色%d\n", j);
}
}
****
****
****
// 3行4列
// 外迴圈控制行數
for (int i = 0; i < 3; i++) {
// 內迴圈控制列數
for (int j = 0; j < 4; j++) {
printf("*");
}
printf("\n");
}
*
**
***
****
*****
/*
最多列印5行
最多列印5列
每一行和每一列關係是什麼? 列數<=行數
*/
for(int i = 0; i< 5; i++) {
for(int j = 0; j <= i; j++) {
printf("*");
}
printf("\n");
}
*****
****
***
**
*
for(int i = 0; i< 5; i++) {
for(int j = i; j < 5; j++) {
printf("*");
}
printf("\n");
}
1
12
123
for (int i = 0; i < 3; i++) {
for (int j = 0; j <= i; j++) {
printf("%d", j+1);
}
printf("\n");
}
1
22
333
for (int i = 1; i <= 3; i++) {
for (int j = 1; j <= i; j++) {
printf("%d", i);
}
printf("\n");
}
--*
-***
*****
for (int i = 0; i <= 5; i++) {
for (int j = 0; j < 5 - i; j++) {
printf("-");
}
for (int m = 0; m < 2*i+1; m++) {
printf("*");
}
printf("\n");
}
1 * 1 = 1
1 * 2 = 2 2 * 2 = 4
1 * 3 = 3 2 * 3 = 6 3 * 3 = 9
for (int i = 1; i <= 9; i++) {
for (int j = 1; j <= i; j++) {
printf("%d * %d = %d \t", j, i, (j * i));
}
printf("\n");
}
定義函數的目的
自定義函數的書寫格式
返回值型別 函數名(引數型別 形式引數1,引數型別 形式引數2,…) {
函數體;
返回值;
}
int main(){
printf("hello world\n");
retrun 0;
}
void 函數名() {
函數體;
}
// 1.沒有返回值/沒有形參
// 如果一個函數不需要返回任何資料給呼叫者, 那麼返回值型別就是void
void printRose() {
printf(" {@}\n");
printf(" |\n");
printf(" \\|/\n"); // 注意: \是一個特殊的符號(轉意字元), 想輸出\必須寫兩個斜線
printf(" |\n");
// 如果函數不需要返回資料給呼叫者, 那麼函數中的return可以不寫
}
返回值型別 函數名() {
函數體;
return 值;
}
int getMax() {
printf("請輸入兩個整數, 以逗號隔開, 以回車結束\n");
int number1, number2;
scanf("%i,%i", &number1, &number2);
int max = number1 > number2 ? number1 : number2;
return max;
}
型別 變數名,型別 變數2,......
void 函數名(引數型別 形式引數1,引數型別 形式引數2,…) {
函數體;
}
void printMax(int value1, int value2) {
int max = value1 > value2 ? value1 : value2;
printf("max = %i\n", max);
}
返回值型別 函數名(引數型別 形式引數1,引數型別 形式引數2,…) {
函數體;
return 0;
}
int printMax(int value1, int value2) {
int max = value1 > value2 ? value1 : value2;
return max;
}
void test() {
}
void test() { // 報錯
}
int max(int number1, int number2) // 形式引數
{
return number1 > number2 ? number1 : number2;
}
int main() {
int num = 99;
// 88, num, 22+44均能得到一個確定的值, 所以都可以作為實參
max(88, num, 22+44); // 實際引數
return 0;
}
int max(int number1, int number2) { // 形式引數
return number1 > number2 ? number1 : number2;
}
int main() {
// 函數需要2個形參, 但是我們只傳遞了一個實參, 所以報錯
max(88); // 實際引數
return 0;
}
void change(double number1, double number2) {// 形式引數
// 輸出結果: 10.000000, 20.000000
// 自動將實參轉換為double型別後儲存
printf("number1 = %f, number2 = %f", number1, number2);
}
int main() {
change(10, 20);
return 0;
}
void change(int number1, int number2) { // 形式引數
number1 = 250; // 不會影響實參
number2 = 222;
}
int main() {
int a = 88;
int b = 99;
change(a, b);
printf("a = %d, b = %d", a, b); // 輸出結果: 88, 99
return 0;
}
max(int number1, int number2) {// 形式引數
return number1 > number2 ? number1 : number2;
}
int height() {
return 3.14;
}
int main() {
double temp = height();
printf("%lf", temp);// 輸出結果: 3.000000
}
int max(int number1, int number2) {// 形式引數
return number1 > number2 ? number1 : number2;
printf("執行不到"); // 執行不到
return 250; // 執行不到
}
int max( int a, int b );
int max( int, int );
// 函數宣告
void getMax(int v1, int v2);
int main(int argc, const char * argv[]) {
getMax(10, 20); // 呼叫函數
return 0;
}
// 函數實現
void getMax(int v1, int v2) {
int max = v1 > v2 ? v1 : v2;
printf("max = %i\n", max);
}
// 函數宣告
void getMax(int v1, int v2);
void getMax(int v1, int v2);
void getMax(int v1, int v2); // 不會報錯
int main(int argc, const char * argv[]) {
getMax(10, 20); // 呼叫函數
return 0;
}
// 函數實現
void getMax(int v1, int v2) {
int max = v1 > v2 ? v1 : v2;
printf("max = %i\n", max);
}
int main(int argc, const char * argv[]) {
void getMax(int v1, int v2); // 函數宣告, 不會報錯
getMax(10, 20); // 呼叫函數
return 0;
}
// 函數實現
void getMax(int v1, int v2) {
int max = v1 > v2 ? v1 : v2;
printf("max = %i\n", max);
}
// 函數實現
void getMax(int v1, int v2) {
int max = v1 > v2 ? v1 : v2;
printf("max = %i\n", max);
}
int main(int argc, const char * argv[]) {
getMax(10, 20); // 呼叫函數
return 0;
}
int main(int argc, const char * argv[]) {
int res = getMin(5, 3); // 不會報錯
printf("result = %d\n", res );
return 0;
}
int getMin(int num1, int num2) {// 返回int, 不用宣告
return num1 < num2 ? num1 : num2;
}
void function(int x){
function(x);
}
void getNumber(){
int number = -1;
while (number < 0) {
printf("請輸入一個正數\n");
scanf("%d", &number);
}
printf("number = %d\n", number);
}
void getNumber2(){
int number = -1;
printf("請輸入一個正數abc\n");
scanf("%d", &number);
if (number < 0) {
// 負數
getNumber2();
}else{
// 正數
printf("number = %d\n", number);
}
}
遞迴和迴圈區別
遞迴練習
什麼是進位制?
常見的進位制
進位制書寫的格式和規律
練習
00011 0x001 0x7h4 10.98 0986 .089-109
+178 0b325 0b0010 0xffdc 96f 96.0f 96.oF -.003
01100100
索引從右至左, 從零開始
第0位: 0 * 2^0 = 0;
第1位: 0 * 2^1 = 0;
第2位: 1 * 2^2 = 4;
第3位: 0 * 2^3 = 0;
第4位元: 0 * 2^4 = 0;
第5位: 1 * 2^5 = 32;
第6位: 1 * 2^6 = 64;
第7位: 0 * 2^7 = 0;
最終結果為: 0 + 0 + 4 + 0 + 0 + 32 + 64 + 0 = 100
從右至左每3位劃分為8進位制的1位, 不夠前面補0
001 100 100
第0位: 100 等於十進位制 4
第1位: 100 等於十進位制 4
第2位: 001 等於十進位制 1
最終結果: 144就是轉換為8進位制的值
從右至左每4位元劃分為16進位制的1位, 不夠前面補0
0110 0100
第0位: 0100 等於十進位制 4
第1位: 0110 等於十進位制 6
最終結果: 64就是轉換為16進位制的值
十進位制 --> 十進位制
12345 = 10000 + 2000 + 300 + 40 + 5
= (1 * 10 ^ 4) + (2 * 10 ^ 3) + (3 * 10 ^ 2) + (4 * 10 ^ 1) + (5 * 10 ^ 0)
= (1 * 10000) + (2 + 1000) + (3 * 100) + (4 * 10) + (5 * 1)
= 10000 + 2000 + 300 + 40 + 5
= 12345
規律:
其它進位制轉換為十進位制的結果 = 係數 * 基數 ^ 索引 之和
係數: 每一位的值就是一個係數
基數: 從x進位制轉換到十進位制, 那麼x就是基數
索引: 從最低位以0開始, 遞增的數
二進位制 --> 十進位制
543210
101101 = (1 * 2 ^ 5) + (0 * 2 ^ 4) + (1 * 2 ^ 3) + (1 * 2 ^ 2) + (0 * 2 ^ 1) + (1 * 2 ^ 0)
= 32 + 0 + 8 + 4 + 0 + 1
= 45
八進位制 --> 十進位制
016 = (0 * 8 ^ 2) + (1 * 8 ^ 1) + (6 * 8 ^ 0)
= 0 + 8 + 6
= 14
十六進位制 --> 十進位制
0x11f = (1 * 16 ^ 2) + (1 * 16 ^ 1) + (15 * 16 ^ 0)
= 256 + 16 + 15
= 287
基數
取餘, 倒敘讀取 十進位制 --> 二進位制
100 --> 1100100
100 / 2 = 50 0
50 / 2 = 25 0
25 / 2 = 12 1
12 / 2 = 6 0
6 / 2 = 3 0
3 / 2 = 1 1
1 / 2 = 0 1
十進位制 --> 八進位制
100 --> 144
100 / 8 = 12 4
12 / 8 = 1 4
1 / 8 = 0 1
十進位制 --> 十六進位制
100 --> 64
100 / 16 = 6 4
6 / 16 = 0 6
// 整數部分(除2取餘)
12
/ 2
------
6 // 餘0
/ 2
------
3 // 餘0
/ 2
------
1 // 餘1
/ 2
------
0 // 餘1
//12 --> 1100
// 小數部分(乘2取整數積)
0.125
* 2
------
0.25 //0
0.25
* 2
------
0.5 //0
0.5
* 2
------
1.0 //1
0.0
// 0.125 --> 0.001
// 12.8125 --> 1100.001
// 整數部分(乘以2的n次方, n從0開始)
0 * 2^0 = 0
0 * 2^1 = 0
1 * 2^2 = 4
1 * 2^3 = 8
// 1100 == 8 + 4 + 0 + 0 == 12
// 小數部分(乘以2的負n次方, n從0開始)
0 * (1/2) = 0
0 * (1/4) = 0
1 * (1/8) = 0.125
// .100 == 0 + 0 + 0.125 == 0.125
// 1100.001 --> 12.125
0.8125
* 2
--------
1.625 // 1
0.625
* 2
--------
1.25 // 1
0.25
* 2
--------
0.5 // 0
* 2
--------
1.0 // 1
0.0
// 0. 8125 --> 0.1101
1*(1/2) = 0.5
1*(1/4)=0.25
0*(1/8)=0
1*(1/16)=0.0625
//0.1101 --> 0.5 + 0.25 + 0 + 0.0625 == 0.8125
0000 0000 0000 0000 0000 0000 0000 1100
0000 0000 0000 0000 0000 0000 0000 1100
0000 0000 0000 0000 0000 0000 0000 1100
除了符號位
以外的其它位取反 0000 0000 0000 0000 0000 0000 0000 1100 // 12二進位制
1000 0000 0000 0000 0000 0000 0000 1100 // -12原碼
1111 1111 1111 1111 1111 1111 1111 0011 // -12反碼
1111 1111 1111 1111 1111 1111 1111 0100 // -12二補數
1111 1111 1111 1111 1111 1111 1111 0100 // -12二補數
1111 1111 1111 1111 1111 1111 1111 0011 // -12反碼
1000 0000 0000 0000 0000 0000 0000 1100 // -12原碼
// 1 + 1
0000 0000 0000 0000 0000 0000 0000 0001 // 1原碼
+0000 0000 0000 0000 0000 0000 0000 0001 // 1原碼
---------------------------------------
0000 0000 0000 0000 0000 0000 0000 0010 == 2
// 1 - 1; 1 + (-1);
0000 0000 0000 0000 0000 0000 0000 0001 // 1原碼
+1000 0000 0000 0000 0000 0000 0000 0001 // -1原碼
---------------------------------------
1000 0000 0000 0000 0000 0000 0000 0010 == -2
// 1 - 1; 1 + (-1);
0000 0000 0000 0000 0000 0000 0000 0001 // 1反碼
1111 1111 1111 1111 1111 1111 1111 1110 // -1反碼
---------------------------------------
1111 1111 1111 1111 1111 1111 1111 1111 // 計算結果反碼
1000 0000 0000 0000 0000 0000 0000 0000 // 計算結果原碼 == -0
// 1 - 1; 1 + (-1);
0000 0000 0000 0000 0000 0000 0000 0001 // 1二補數
1111 1111 1111 1111 1111 1111 1111 1111 // -1二補數
---------------------------------------
10000 0000 0000 0000 0000 0000 0000 0000 // 計算結果二補數
0000 0000 0000 0000 0000 0000 0000 0000 // == 0
符號 | 名稱 | 運算結果 |
---|---|---|
& | 按位元與 | 同1為1 |
| | 按位元或 | 有1為1 |
^ | 按位元互斥或 | 不同為1 |
~ | 按位元取反 | 0變1,1變0 |
<< | 按位元左移 | 乘以2的n次方 |
>> | 按位元右移 | 除以2的n次方 |
9&5 = 1
1001
&0101
------
0001
9|5 = 13
1001
|0101
------
1101
9^5 = 12
1001
^0101
------
1100
~9 =-10
0000 0000 0000 0000 0000 1001 // 取反前
1111 1111 1111 1111 1111 0110 // 取反後
// 根據負數二補數得出結果
1111 1111 1111 1111 1111 0110 // 二補數
1111 1111 1111 1111 1111 0101 // 反碼
1000 0000 0000 0000 0000 1010 // 原始碼 == -10
偶數: 的二進位制是以0結尾
8 -> 1000
10 -> 1010
奇數: 的二進位制是以1結尾
9 -> 1001
11 -> 1011
任何數和1進行&操作,得到這個數的最低位
1000
&0001
-----
0000 // 結果為0, 代表是偶數
1011
&0001
-----
0001 // 結果為1, 代表是奇數
enum Unix {
S_IRUSR = 256,// 100000000 使用者可讀
S_IWUSR = 128,// 10000000 使用者可寫
S_IXUSR = 64,// 1000000 使用者可執行
S_IRGRP = 32,// 100000 組可讀
S_IWGRP = 16,// 10000 組可寫
S_IXGRP = 8,// 1000 組可執行
S_IROTH = 4,// 100 其它可讀
S_IWOTH = 2,// 10 其它可寫
S_IXOTH = 1 // 1 其它可執行
};
// 假設設定使用者許可權為可讀可寫
printf("%d\n", S_IRUSR | S_IWUSR); // 384 // 110000000
a = a^b;
b = b^a;
a = a^b;
2<<1; //相當於 2 *= 2 // 4
0010
<<0100
2<<2; //相當於 2 *= 2^2; // 8
0010
<<1000
2>>1; //相當於 2 /= 2 // 1
0010
>>0001
4>>2; //相當於 4 /= 2^2 // 1
0100
>>0001
#include <stdio.h>
void printBinary(int num);
int main(int argc, const char * argv[]) {
printBinary(13);
}
void printBinary(int num){
int len = sizeof(int)*8;
int temp;
for (int i=0; i<len; i++) {
temp = num; //每次都在原數的基礎上進行移位運算
temp = temp>>(31-i); //每次移動的位數
int t = temp&1; //取出最後一位
if(i!=0&&i%4==0)printf(" "); printf("%d",t);
}
}
- 地址匯流排: 地址匯流排寬度決定了CPU可以存取的實體地址空間(定址能力)
- 例如: 地址匯流排的寬度是1位, 那麼表示可以存取 0 和 1的記憶體
- 例如: 地址匯流排的位數是2位, 那麼表示可以存取 00、01、10、11的記憶體
- 資料匯流排: 資料匯流排的位數決定CPU單次通訊能交換的資訊數量
- 例如: 資料匯流排:的寬度是1位, 那麼一次可以傳輸1位二進位制資料
- 例如: 地址匯流排的位數是2位,那麼一次可以傳輸2位二進位制資料
- 控制匯流排: 用來傳送各種控制訊號
寫入流程
讀取流程
變數的儲存原則
10的二進位制: 0b00000000 00000000 00000000 00001010
高位元組← →低位元組
字元 | 意義 |
---|---|
\b | 退格(BS)當前位置向後回退一個字元 |
\r | 回車(CR),將當前位置移至本行開頭 |
\n | 換行(LF),將當前位置移至下一行開頭 |
\t | 水平製表(HT),跳到下一個 TAB 位置 |
\0 | 用於表示字串的結束標記 |
\ | 代表一個反斜線字元 \ |
\" | 代表一個雙引號字元" |
\’ | 代表一個單引號字元’ |
char ch1 = 'a';
printf("%i\n", ch1); // 97
char ch2 = 97;
printf("%c\n", ch2); // a
char c = '我'; // 錯誤寫法
char ch = 'ab'; // 錯誤寫法
char ch1 = '6'; // 儲存的是ASCII碼 64
char ch2 = 6; // 儲存的是數位 6
#include <stdio.h>
int main()
{
// char佔1個位元組, char的取值範圍 -2^7~2^7
char num = 129;
printf("size = %i\n", sizeof(num)); // 1
printf("num = %i\n", num); // -127
// short int 佔2個位元組, short int的取值範圍 -2^15~2^15-1
short int num1 = 32769;// -32767
printf("size = %i\n", sizeof(num1)); // 2
printf("num1 = %hi\n", num1);
// int佔4個位元組, int的取值範圍 -2^31~2^31-1
int num2 = 12345678901;
printf("size = %i\n", sizeof(num2)); // 4
printf("num2 = %i\n", num2);
// long在32位元佔4個位元組, 在64位元佔8個位元組
long int num3 = 12345678901;
printf("size = %i\n", sizeof(num3)); // 4或8
printf("num3 = %ld\n", num3);
// long在32位元佔8個位元組, 在64位元佔8個位元組 -2^63~2^63-1
long long int num4 = 12345678901;
printf("size = %i\n", sizeof(num4)); // 8
printf("num4 = %lld\n", num4);
// 由於short/long/long long一般都是用於修飾int, 所以int可以省略
short num5 = 123;
printf("num5 = %lld\n", num5);
long num6 = 123;
printf("num6 = %lld\n", num6);
long long num7 = 123;
printf("num7 = %lld\n", num7);
return 0;
}
#include <stdio.h>
int main()
{
// 1.預設情況下所有型別都是由符號的
int num1 = 9;
int num2 = -9;
int num3 = 0;
printf("num1 = %i\n", num1);
printf("num2 = %i\n", num2);
printf("num3 = %i\n", num3);
// 2.signed用於明確說明, 當前儲存的資料可以是有符號的, 一般情況下很少使用
signed int num4 = 9;
signed int num5 = -9;
signed int num6 = 0;
printf("num4 = %i\n", num4);
printf("num5 = %i\n", num5);
printf("num6 = %i\n", num6);
// signed也可以省略資料型別, 但是不推薦這樣編寫
signed num7 = 9;
printf("num7 = %i\n", num7);
// 3.unsigned用於明確說明, 當前不能儲存有符號的值, 只能儲存0和正數
// 應用場景: 儲存銀行存款,學生分數等不能是負數的情況
unsigned int num8 = -9;
unsigned int num9 = 0;
unsigned int num10 = 9;
// 注意: 不看怎麼存只看怎麼取
printf("num8 = %u\n", num8);
printf("num9 = %u\n", num9);
printf("num10 = %u\n", num10);
return 0;
}
signed short int num1 = 666;
signed unsigned int num2 = 666; // 報錯
陣列,從字面上看,就是一組資料的意思,沒錯,陣列就是用來儲存一組資料的
陣列的幾個名詞
相同資料型別
資料的有序
的集合陣列的應用場景
#include <stdio.h>
int main(int argc, const char * argv[]) {
/*
// 需求: 儲存2個人的分數
int score1 = 99;
int score2 = 60;
// 需求: 儲存全班同學的分數(130人)
int score3 = 78;
int score4 = 68;
...
int score130 = 88;
*/
// 陣列: 如果需要儲存`一組``相同型別`的資料, 就可以定義一個陣列來儲存
// 只要定義好一個陣列, 陣列內部會給每一塊小的儲存空間一個編號, 這個編號我們稱之為 索引, 索引從0開始
// 1.定義一個可以儲存3個int型別的陣列
int scores[3];
// 2.通過陣列的下標往陣列中存放資料
scores[0] = 998;
scores[1] = 123;
scores[2] = 567;
// 3.通過陣列的下標從陣列中取出存放的資料
printf("%i\n", scores[0]);
printf("%i\n", scores[1]);
printf("%i\n", scores[2]);
return 0;
}
// int 元素型別
// ages 陣列名稱
// [10] 元素個數
int ages[10];
int ages[3] = {4, 6, 9};
int nums[] = {1,2,3,5,6};
int nums[10] = {1,2};
int nums[5] = {[4] = 3,[1] = 2};
int nums[] = {[4] = 3};
int nums[3];
nums[0] = 1;
nums[1] = 2;
nums[2] = 3;
int nums[5];
printf("%d\n", nums[0]);
printf("%d\n", nums[1]);
printf("%d\n", nums[2]);
printf("%d\n", nums[3]);
printf("%d\n", nums[4]);
輸出結果:
0
0
1606416312
0
1606416414
int ages[3];
ages = {4, 6, 9}; // 報錯
// 找到下標為0的元素, 賦值為10
ages[0]=10;
// 取出下標為2的元素儲存的值
int a = ages[2];
printf("a = %d", a);
int ages[4] = {19, 22, 33, 13};
for (int i = 0; i < 4; i++) {
printf("ages[%d] = %d\n", i, ages[i]);
}
int ages[4] = {19, 22, 33, 13};
int length = sizeof(ages)/sizeof(int);
printf("length = %d", length);
輸出結果: 4
int ages[4] = {19, 22, 33, 13};
for (int i = 0; i < 4; i++) {
printf("ages[%d] = %d\n", i, ages[i]);
}
int ages[4] = {19, 22, 33, 13};
for (int i = 3; i >=0; i--) {
printf("ages[%d] = %d\n", i, ages[i]);
}
儲存方式:
範例
#include <stdio.h>
int main()
{
int num = 9;
char cs[] = {'l','n','j'};
printf("cs = %p\n", &cs); // cs = 0060FEA9
printf("cs[0] = %p\n", &cs[0]); // cs[0] = 0060FEA9
printf("cs[1] = %p\n", &cs[1]); // cs[1] = 0060FEAA
printf("cs[2] = %p\n", &cs[2]); // cs[2] = 0060FEAB
int nums[] = {2, 6};
printf("nums = %p\n", &nums); // nums = 0060FEA0
printf("nums[0] = %p\n", &nums[0]);// nums[0] = 0060FEA0
printf("nums[1] = %p\n", &nums[1]);// nums[1] = 0060FEA4
return 0;
}
- 注意:字元在記憶體中是以對應ASCII碼值的二進位制形式儲存的,而非上述的形式。
char cs1[2] = {1, 2};
char cs2[3] = {3, 4, 5};
cs2[3] = 88; // 注意:這句存取到了不屬於cs1的記憶體
printf("cs1[0] = %d\n", cs1[0] );
輸出結果: 88
為什麼上述會輸出88, 自己按照"陣列內部儲存細節"畫圖腦補
int ages4['A'] = {19, 22, 33};
printf("ages4[0] = %d\n", ages4[0]);
int ages5[5 + 5] = {19, 22, 33};
printf("ages5[0] = %d\n", ages5[0]);
int ages5['A' + 5] = {19, 22, 33};
printf("ages5[0] = %d\n", ages5[0]);
// 沒有指定元素個數,錯誤
int a[];
// []中不能放變數
int number = 10;
int ages[number]; // 老版本的C語言規範不支援
printf("%d\n", ages[4]);
int number = 10;
int ages2[number] = {19, 22, 33} // 直接報錯
// 只能在定義陣列的時候進行一次性(全部賦值)的初始化
int ages3[5];
ages10 = {19, 22, 33};
// 一個長度為n的陣列,最大下標為n-1, 下標範圍:0~n-1
int ages4[4] = {19, 22, 33}
ages4[8]; // 陣列角標越界
void change(int val)// int val = number
{
val = 55;
}
int main(int argc, const char * argv[])
{
int ages[3] = {1, 5, 8};
printf("ages[0] = %d", ages[0]);// 1
change(ages[0]);
printf("ages[0] = %d", ages[0]);// 1
}
- 用陣列元素作函數引數不要求形參也必須是陣列元素
void change2(int array[3])// int array = 0ffd1
{
array[0] = 88;
}
int main(int argc, const char * argv[])
{
int ages[3] = {1, 5, 8};
printf("ages[0] = %d", ages[0]);// 1
change(ages);
printf("ages[0] = %d", ages[0]);// 88
}
void change(int array[])
{
array[0] = 88;
}
void prtArray(double array[3]) // 錯誤寫法
{
for (int i = 0; i < 3; i++) {
printf("array[%d], %f", i, array[i]);
}
}
int main(int argc, const char * argv[])
{
int ages[3] = {1, 5, 8};
prtArray(ages[0]);
}
void printArray(int array[])
{
printf("printArray size = %lu\n", sizeof(array)); // 8
int length = sizeof(array)/ sizeof(int); // 2
printf("length = %d", length);
}
計數排序是一個非基於比較的排序演演算法,該演演算法於1954年由 Harold H. Seward 提出。它的優勢在於在對一定範圍內的整數排序
時,快於任何比較排序演演算法。
排序思路:
簡單程式碼實現:
int main()
{
// 待排序陣列
int nums[5] = {3, 1, 2, 0, 3};
// 用於排序陣列
int newNums[4] = {0};
// 計算待排序陣列長度
int len = sizeof(nums) / sizeof(nums[0]);
// 遍歷待排序陣列
for(int i = 0; i < len; i++){
// 取出待排序陣列當前值
int index = nums[i];
// 將待排序陣列當前值作為排序陣列索引
// 將用於排序陣列對應索引原有值+1
newNums[index] = newNums[index] +1;
}
// 計算待排序陣列長度
int len2 = sizeof(newNums) / sizeof(newNums[0]);
// 輸出排序陣列索引, 就是排序之後結果
for(int i = 0; i < len2; i++){
for(int j = 0; j < newNums[i]; j++){
printf("%i\n", i);
}
}
/*
// 計算待排序陣列長度
int len2 = sizeof(newNums) / sizeof(newNums[0]);
// 還原排序結果到待排序陣列
for(int i = 0; i < len2; i++){
int index = 0;
for(int i = 0; i < len; i++){
for(int j = 0; j < newNums[i]; j++){
nums[index++] = i;
}
}
}
*/
return 0;
}
選擇排序(Selection sort)是一種簡單直觀的排序演演算法。它的工作原理如下。首先在未排序序列中找到最小元素,存放到排序序列的起始位置,然後,再從剩餘未排序元素中繼續尋找最小元素,然後放到排序序列末尾。以此類推,直到所有元素均排序完畢。
排序思路:
程式碼實現:
// 選擇排序
void selectSort(int numbers[], int length) {
// 外迴圈為什麼要-1?
// 最後一位不用比較, 也沒有下一位和它比較, 否則會出現錯誤存取
for (int i = 0; i < length; i++) {
for (int j = i; j < length - 1; j++) {
// 1.用當前元素和後續所有元素比較
if (numbers[i] < numbers[j + 1]) {
// 2.一旦發現小於就交換位置
swapEle(numbers, i, j + 1);
}
}
}
}
// 交換兩個元素的值, i/j需要交換的索引
void swapEle(int array[], int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
// 氣泡排序
void bubbleSort(int numbers[], int length) {
for (int i = 0; i < length; i++) {
// -1防止`角標越界`: 存取到了不屬於自己的索引
for (int j = 0; j < length - i - 1; j++) {
// 1.用當前元素和相鄰元素比較
if (numbers[j] < numbers[j + 1]) {
// 2.一旦發現小於就交換位置
swapEle(numbers, j, j + 1);
}
}
}
}
// 交換兩個元素的值, i/j需要交換的索引
void swapEle(int array[], int i, int j) {
int temp = array[i];
array[i] = array[j];
array[j] = temp;
}
int main()
{
// 待排序陣列
int nums[5] = {3, 1, 2, 0, 3};
// 0.計算待排序陣列長度
int len = sizeof(nums) / sizeof(nums[0]);
// 1.從第一個元素開始依次取出所有用於比較元素
for (int i = 1; i < len; i++)
{
// 2.取出用於比較元素
int temp = nums[i];
int j = i;
while(j > 0){
// 3.判斷元素是否小於前一個元素
if(temp < nums[j - 1]){
// 4.讓前一個元素向後移動一位
nums[j] = nums[j - 1];
}else{
break;
}
j--;
}
// 5.將元素插入到空出來的位置
nums[j] = temp;
}
}
int main()
{
// 待排序陣列
int nums[5] = {3, 1, 2, 0, 3};
// 0.計算待排序陣列長度
int len = sizeof(nums) / sizeof(nums[0]);
// 1.從第一個元素開始依次取出所有用於比較元素
for (int i = 1; i < len; i++)
{
// 2.遍歷取出前面元素進行比較
for(int j = i; j > 0; j--)
{
// 3.如果前面一個元素大於當前元素,就交換位置
if(nums[j-1] > nums[j]){
int temp = nums[j];
nums[j] = nums[j - 1];
nums[j - 1] = temp;
}else{
break;
}
}
}
}
int main()
{
// 待排序陣列
int nums[5] = {3, 1, 2, 0, 3};
// 0.計算待排序陣列長度
int len = sizeof(nums) / sizeof(nums[0]);
// 2.計算步長
int gap = len / 2;
do{
// 1.從第一個元素開始依次取出所有用於比較元素
for (int i = gap; i < len; i++)
{
// 2.遍歷取出前面元素進行比較
int j = i;
while((j - gap) >= 0)
{
printf("%i > %i\n", nums[j - gap], nums[j]);
// 3.如果前面一個元素大於當前元素,就交換位置
if(nums[j - gap] > nums[j]){
int temp = nums[j];
nums[j] = nums[j - gap];
nums[j - gap] = temp;
}else{
break;
}
j--;
}
}
// 每個小陣列排序完成, 重新計算步長
gap = gap / 2;
}while(gap >= 1);
}
江哥提示:
對於初學者而言, 排序演演算法一次不易於學習太多, 咋們先來5個玩一玩, 後續繼續講解其它5個
實現步驟
在有序表中,取中間元素作為比較物件,若給定值與中間元素的要查詢的數相等,則查詢成功;
若給定值小於中間元素的要查詢的數,則在中間元素的左半區繼續查詢;
若給定值大於中間元素的要查詢的數,則在中間元素的右半區繼續查詢。
不斷重複上述查詢過 程,直到查詢成功,或所查詢的區域無資料元素,查詢失敗。
程式碼實現
int findKey(int values[], int length, int key) {
// 定義一個變數記錄最小索引
int min = 0;
// 定義一個變數記錄最大索引
int max = length - 1;
// 定義一個變數記錄中間索引
int mid = (min + max) * 0.5;
while (min <= max) {
// 如果mid對應的值 大於 key, 那麼max要變小
if (values[mid] > key) {
max = mid - 1;
// 如果mid對應的值 小於 key, 那麼min要變
}else if (values[mid] < key) {
min = mid + 1;
}else {
return mid;
}
// 修改完min/max之後, 重新計算mid的值
mid = (min + max) * 0.5;
}
return -1;
}
#include <stdio.h>
void toBinary(int num)
{
total(num, 1, 1);
}
void toOct(int num)
{
total(num, 7, 3);
}
void toHex(int num)
{
total(num, 15, 4);
}
void total(int num , int base, int offset)
{
// 1.定義表用於查詢結果
char cs[] = {
'0', '1', '2', '3', '4', '5',
'6', '7', '8', '9', 'a', 'b',
'c', 'd', 'e', 'f'
};
// 2.定義儲存結果的陣列
char rs[32];
// 計算最大的角標位置
int length = sizeof(rs)/sizeof(char);
int pos = length;//8
while (num != 0) {
int index = num & base;
rs[--pos] = cs[index];
num = num >> offset;
}
for (int i = pos; i < length; i++) {
printf("%c", rs[i]);
}
printf("\n");
}
int main()
{
toBinary(9);
return 0;
}
二維數的初始化可分為兩種:
定義的同時初始化
int a[2][3]={ {80,75,92}, {61,65,71}};
int a[2][3];
a[0][0] = 80;
a[0][1] = 75;
a[0][2] = 92;
a[1][0] = 61;
a[1][1] = 65;
a[1][2] = 71;
int a[2][3]={ {80,75,92}, {61,65,71}};
int a[2][3]={ 80,75,92,61,65,71};
int a[][3]={{1,2,3},{4,5,6}};
int a[][3]={1,2,3,4,5,6};
int a[][3]={{1},{4,5}};
int a[][3]={1,2,3,4};
- 注意: 有些人可能想不明白,為什麼可以省略行數,但不可以省略列數。也有人可能會問,可不可以只指定行數,但是省略列數?其實這個問題很簡單,如果我們這樣寫:
int a[2][] = {1, 2, 3, 4, 5, 6}; // 錯誤寫法
大家都知道,二維陣列會先存放第1行的元素,由於不確定列數,也就是不確定第1行要存放多少個元素,所以這裡會產生很多種情況,可能1、2是屬於第1行的,也可能1、2、3、4是第一行的,甚至1、2、3、4、5、6全部都是屬於第1行的
int a[2][3]={[1][2]=10};
int a[2][3]={[1]={1,2,3}}
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
printf("%c", cs[0][0]);// 第一個[0]取出一維陣列, 第二個[0]取出一維陣列中對應的元素
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
for (int i = 0; i < 2; i++) { // 外迴圈取出一維陣列
// i
for (int j = 0; j < 3; j++) {// 內迴圈取出一維陣列的每個元素
printf("%c", cs[i][j]);
}
printf("\n");
}
注意: 必須強調的是,a[0],a[1],a[2]不能當作下標變數使用,它們是陣列名,不是一個單純的下標變數
#include <stdio.h>
int main()
{
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
// cs == &cs == &cs[0] == &cs[0][0]
printf("cs = %p\n", cs); // 0060FEAA
printf("&cs = %p\n", &cs); // 0060FEAA
printf("&cs[0] = %p\n", &cs[0]); // 0060FEAA
printf("&cs[0][0] = %p\n", &cs[0][0]); // 0060FEAA
return 0;
}
#include <stdio.h>
// 和一位陣列一樣, 只看形參是基本型別還是陣列型別
// 如果是基本型別在函數中修改形參不會影響實參
void change(char ch){
ch = 'n';
}
int main()
{
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
printf("cs[0][0] = %c\n", cs[0][0]); // a
change(cs[0][0]);
printf("cs[0][0] = %c\n", cs[0][0]); // a
return 0;
}
#include <stdio.h>
// 和一位陣列一樣, 只看形參是基本型別還是陣列型別
// 如果是陣列型別在函數中修改形參會影響實參
void change(char ch[]){
ch[0] = 'n';
}
int main()
{
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
printf("cs[0][0] = %c\n", cs[0][0]); // a
change(cs[0]);
printf("cs[0][0] = %c\n", cs[0][0]); // n
return 0;
}
#include <stdio.h>
// 和一位陣列一樣, 只看形參是基本型別還是陣列型別
// 如果是陣列型別在函數中修改形參會影響實參
void change(char ch[][3]){
ch[0][0] = 'n';
}
int main()
{
char cs[2][3] = {
{'a', 'b', 'c'},
{'d', 'e', 'f'}
};
printf("cs[0][0] = %c\n", cs[0][0]); // a
change(cs);
printf("cs[0][0] = %c\n", cs[0][0]); // n
return 0;
}
void test(char cs[2][]) // 錯誤寫法
{
printf("我被執行了\n");
}
void test(char cs[2][3]) // 正確寫法
{
printf("我被執行了\n");
}
void test(char cs[][3]) // 正確寫法
{
printf("我被執行了\n");
}
void test(char cs[2][3])
{
int row = sizeof(cs); // 輸出4或8
printf("row = %zu\n", row);
}
void test(char cs[2][3])
{
size_t col = sizeof(cs[0]); // 輸出3
printf("col = %zd\n", col);
}
玩家通過鍵盤錄入 w,s,a,d控制小人向不同方向移動,其中w代表向上移動,s代表向 下移動,a代表向左移動,d 代表向右移動,當小人移動到出口位置,玩家勝利
思路:
1.定義二維陣列存放地圖
######
#O #
# ## #
# # #
## #
######
char name[9] = "lnj"; //在記憶體中以「\0」結束, \0ASCII碼值是0
char name1[9] = {'l','n','j','\0'};
char name2[9] = {'l','n','j',0};
// 當陣列元素個數大於儲存字元內容時, 未被初始化的部分預設值是0, 所以下面也可以看做是一個字串
char name3[9] = {'l','n','j'};
//省略元素個數時, 不能省略末尾的\n
// 不正確地寫法,結尾沒有\0 ,只是普通的字元陣列
char name4[] = {'l','n','j'};
// "中間不能包含\0", 因為\0是字串的結束標誌
// \0的作用:字串結束的標誌
char name[] = "c\0ool";
printf("name = %s\n",name);
輸出結果: c
char chs[] = "lnj";
printf("%s\n", chs);
char name[] = {'c', 'o', 'o', 'l' , '\0'};
char name2[] = {'l', 'n', 'j'};
printf("name2 = %s\n", name2); // 輸出結果: lnjcool
char ch[10];
scanf("%s",ch);
char ch[] = "lnj";
puts(ch); //輸出結果: lnj
- puts函數完全可以由printf函數取代。當需要按一定格式輸出時,通常使用printf函數
char ch[30];
gets(ch); // 輸入:lnj
puts(ch); // 輸出:lnj
- 可以看出當輸入的字串中含有空格時,輸出仍為全部字串。說明gets函數並不以空格作為字串輸入結束的標誌,而只以回車作為輸入結束。這是與scanf函數不同的。
- 注意gets很容易導致陣列下標越界,是一個不安全的字串操作函數
char name[] = "it666";
int size = sizeof(name);// 包含\0
printf("size = %d\n", size); //輸出結果:6
char name[] = "it666";
size_t len = strlen(name2);
printf("len = %lu\n", len); //輸出結果:5
/**
* 自定義方法計算字串的長度
* @param name 需要計算的字串
* @return 不包含\0的長度
*/
int myStrlen2(char str[])
{
// 1.定義變數儲存字串的長度
int length = 0;
while (str[length] != '\0')
{
length++;//1 2 3 4
}
return length;
}
/**
* 自定義方法計算字串的長度
* @param name 需要計算的字串
* @param count 字串的總長度
* @return 不包含\0的長度
*/
int myStrlen(char str[], int count)
{
// 1.定義變數儲存字串的長度
int length = 0;
// 2.通過遍歷取出字串中的所有字元逐個比較
for (int i = 0; i < count; i++) {
// 3.判斷是否是字串結尾
if (str[i] == '\0') {
return length;
}
length++;
}
return length;
}
char oldStr[100] = "welcome to";
char newStr[20] = " lnj";
strcat(oldStr, newStr);
puts(oldStr); //輸出: welcome to lnj"
- 本程式把初始化賦值的字元陣列與動態賦值的字串連線起來。要注意的是,字元陣列1應定義足 夠的長度,否則不能全部裝入被連線的字串。
- 格式: strcpy(字元陣列名1,字元陣列名2)
- 功能:把字元陣列2中的字串拷貝到字元陣列1中。串結束標誌「\0」也一同拷貝。字元數名2, 也可以是一個字串常數。這時相當於把一個字串賦予一個字元陣列。char oldStr[100] = "welcome to";
char newStr[50] = " lnj";
strcpy(oldStr, newStr);
puts(oldStr); // 輸出結果: lnj // 原有資料會被覆蓋
- 本函數要求字元陣列1應有足夠的長度,否則不能全部裝入所拷貝的字串。
char oldStr[100] = "0";
char newStr[50] = "1";
printf("%d", strcmp(oldStr, newStr)); //輸出結果:-1
char oldStr[100] = "1";
char newStr[50] = "1";
printf("%d", strcmp(oldStr, newStr)); //輸出結果:0
char oldStr[100] = "1";
char newStr[50] = "0";
printf("%d", strcmp(oldStr, newStr)); //輸出結果:1
char names[2][10] = { {'l','n','j','\0'}, {'l','y','h','\0'} };
char names2[2][10] = { {"lnj"}, {"lyh"} };
char names3[2][10] = { "lnj", "lyh" };
什麼是地址
地址與記憶體單元中的資料是兩個完全不同的概念
變數地址:
int num = 6; // 佔用4個位元組
//那麼變數num的地址為: 0ff06
char c = 'a'; // 佔用1個位元組
//那麼變數c的地址為:0ff05
在計算機中所有資料都儲存在記憶體單元中,而每個記憶體單元都有一個對應的地址, 只要通過這個地址就能找到對應單元中儲存的資料.
由於通過地址能找到所需的變數單元,所以我們說該地址指向了該變數單元。將地址形象化的稱為「指標」
記憶體單元的指標(地址)和記憶體單元的內容是兩個不同的概念。
int age;// 定義一個普通變數
num = 10;
int *pnAge; // 定義一個指標變數
pnAge = &age;
char ch = 'a';
char *p; // 一個用於指向字元型變數的指標
p = &ch;
int num = 666;
int *q; // 一個用於指向整型變數的指標
q = #
- 其中,*表示這是一個指標變數
- 變數名即為定義的指標變數名
- 型別說明符表示本指標變數所指向的變數的資料型別
int a = 5;
int *p = &a;
int a = 5;
int *p;
p=&a;
int *p=NULL;
int *q=0;
int *p;
p = 250; // 錯誤寫法
int *p;
*p=&a; //錯誤寫法
注意點:
指標的指向是可以改變的
int a = 5;
int *p = &a;
int b = 10;
p = &b; // 修改指標指向
int *p=NULL; // 定義指標變數
int a = 5;
int *p = &a;
printf("a = %d", *p); // 存取指標變數
在同一種編譯器環境下,一個指標變數所佔用的記憶體空間是固定的。
雖然在同一種編譯器下, 所有指標占用的記憶體空間是一樣的,但不同型別的變數卻佔不同的位元組數
char c = 'a';
char *cp;
cp = &c;
char **cp2;
cp2 = &cp;
printf("c = %c", **cp2);
int ***m1; //取值***m1
int *****m2; //取值*****m2
##陣列指標的概念及定義
printf(「%p %p」, &(a[0]), a); //輸出結果:0x1100, 0x1100
- 注意: 陣列名a不代表整個陣列,只代表陣列首元素的地址。
- 「p=a;」的作用是「把a陣列的首元素的地址賦給指標變數p」,而不是「把陣列a各元素的值賦給 p」
int main (void)
{
int a[5] = {2, 4, 6, 8, 22};
int *p;
// p = &(a[0]);
p = a;
printf(「%d %d\n」,a[0],*p); // 輸出結果: 2, 2
}
- 如果指標變數p已指向陣列中的一個元素,則p+1
指向
同一陣列中的下一個元素,p-1指向
同 一陣列中的上一個元素。
int x[10];
x++; //錯誤
int* p = x;
p++; //正確
char string[]=」I love lnj!」;
printf("%s\n",string);
// 陣列名儲存的是陣列第0個元素的地址, 指標也可以儲存第0個元素的地址
char *str = "abc"
har *str = "lnj";
for(int i = 0; i < strlen(str);i++)
{
printf("%c-", *(str+i)); // 輸出結果:l-n-j
}
// + 使用字元陣列來儲存的字串是儲存棧裡的,儲存棧裡面東西是可讀可寫,所有可以修改字串中的的字元
// + 使用字元指標來儲存字串,它儲存的是字串常數地址,常數區是唯讀的,所以我們不可以修改字串中的字元
char *str = "lnj";
*(str+2) = 'y'; // 錯誤
// 錯誤的原因是:str是一個野指標,他並沒有指向某一塊記憶體空間
// 所以不允許這樣寫如果給str分配記憶體空間是可以這樣用 的
char *str;
scanf("%s", str);
返回值型別 (*指標變數名)(形參1, 形參2, ...);
int sum(int a,int b)
{
return a + b;
}
int (*p)(int,int);
p = sum;
指標函數定義技巧
應用場景
注意點:
struct 結構體名{
型別名1 成員名1;
型別名2 成員名2;
……
型別名n 成員名n;
};
struct Student {
char *name; // 姓名
int age; // 年齡
float height; // 身高
};
定好好結構體型別之後, 我們就可以利用我們定義的結構體型別來定義結構體變數
格式: struct 結構體名 結構體變數名;
先定義結構體型別,再定義變數
struct Student {
char *name;
int age;
};
struct Student stu;
struct Student {
char *name;
int age;
} stu;
struct {
char *name;
int age;
} stu;
- 第三種方法與第二種方法的區別在於,第三種方法中省去了結構體型別名稱,而直接給出結構變數,這種結構體最大的問題是結構體型別不能複用
結構體變數名.成員名
struct Student {
char *name;
int age;
};
struct Student stu;
// 存取stu的age成員
stu.age = 27;
printf("age = %d", stu.age);
struct Student {
char *name;
int age;
};
struct Student stu = {「lnj", 27};
struct Student {
char *name;
int age;
};
struct Student stu = {.age = 35, .name = 「lnj"};
struct Student {
char *name;
int age;
};
struct Student stu;
stu.name = "lnj";
stu.age = 35;
struct Student {
char *name;
int age;
};
struct Student stu;
stu2 = (struct Student){"lnj", 35};
//定義一個全域性結構體,作用域到檔案末尾
struct Person{
int age;
char *name;
};
int main(int argc, const char * argv[])
{
//定義區域性結構體名為Person,會遮蔽全域性結構體
//區域性結構體作用域,從定義開始到「}」塊結束
struct Person{
int age;
};
// 使用區域性結構體型別
struct Person pp;
pp.age = 50;
pp.name = "zbz";
test();
return 0;
}
void test() {
//使用全域性的結構體定義結構體變數p
struct Person p = {10,"sb"};
printf("%d,%s\n",p.age,p.name);
}
struct 結構體型別名稱 陣列名稱[元素個數]
struct Student {
char *name;
int age;
};
struct Student stu[2];
struct Student {
char *name;
int age;
};
struct Student stu[2] = {{"lnj", 35},{"zs", 18}};
struct Student {
char *name;
int age;
};
struct Student stu[2];
stu[0] = {"lnj", 35};
stu[1] = {"zs", 18};
struct 結構名 *結構指標變數名
// 定義一個結構體型別
struct Student {
char *name;
int age;
};
// 定義一個結構體變數
struct Student stu = {「lnj", 18};
// 定義一個指向結構體的指標變數
struct Student *p;
// 指向結構體變數stu
p = &stu;
/*
這時候可以用3種方式存取結構體的成員
*/
// 方式1:結構體變數名.成員名
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;
}
- (pstu)兩側的括號不可少,因為成員符「.」的優先順序高於「」。
- 如去掉括號寫作pstu.num則等效於(pstu.num),這樣,意義就完全不對了。
+多實際的計算機系統對基本型別資料在記憶體中存放的位置有限制,它們會要求這些資料的起始地址的值是 某個數k的倍數,這就是所謂的記憶體對齊,而這個k則被稱為該資料型別的對齊模數(alignment modulus)。
- 這種強制的要求一來簡化了處理器與記憶體之間傳輸系統的設計,二來可以提升讀取資料的速度。比如這麼一種處理器,它每次讀寫記憶體的時候都從某個8倍數的地址開始,一次讀出或寫入8個位元組的資料,假如軟體能 保證double型別的資料都從8倍數地址開始,那麼讀或寫一個double型別資料就只需要一次記憶體操作。否則,我們就可能需要兩次記憶體操作才能完成這個動作,因為資料或許恰好橫跨在兩個符合對齊要求的8位元組 記憶體塊上
struct Person{
int age; // 4
char ch; // 1
double score; // 8
};
struct Person p;
printf("sizeof = %i\n", sizeof(p)); // 16
struct Person{
int age; // 4
double score; // 8
char ch; // 1
};
struct Person p;
printf("sizeof = %i\n", sizeof(p)); // 24
struct Date{
int month;
int day;
int year;
}
struct stu{
int num;
char *name;
char sex;
struct Date birthday;
Float score;
}
- 注意:
- 結構體不可以巢狀自己變數,可以巢狀指向自己這種型別的指標
struct Student { int age; struct Student stu; };
struct Date {
int year;
int month;
int day;
};
struct Student {
char *name;
struct Date birthday;
};
struct Student stu;
stu.birthday.year = 1986;
stu.birthday.month = 9;
stu.birthday.day = 10;
struct Person{
char *name;
int age;
};
struct Person p1 = {"lnj", 35};
struct Person p2;
p2 = p1;
p2.name = "zs"; // 修改p2不會影響p1
printf("p1.name = %s\n", p1.name); // lnj
printf("p2.name = %s\n", p2.name); // zs
#include <stdio.h>
struct Person{
char *name;
int age;
};
void test(struct Person per);
int main()
{
struct Person p1 = {"lnj", 35};
printf("p1.name = %s\n", p1.name); // lnj
test(p1);
printf("p1.name = %s\n", p1.name); // lnj
return 0;
}
void test(struct Person per){
per.name = "zs";
}
union 共用體名{
資料型別 屬性名稱;
資料型別 屬性名稱;
... ....
};
union 共用體名 共用體變數名稱;
union Test{
int age;
char ch;
};
union Test t;
printf("sizeof(p) = %i\n", sizeof(t));
t.age = 33;
printf("t.age = %i\n", t.age); // 33
t.ch = 'a';
printf("t.ch = %c\n", t.ch); // a
printf("t.age = %i\n", t.age); // 97
什麼是列舉型別?
列舉型別的定義
enum 列舉名 {
列舉元素1,
列舉元素2,
……
};
// 表示一年四季
enum Season {
Spring,
Summer,
Autumn,
Winter
};
enum Season {
Spring,
Summer,
Autumn,
Winter
};
enum Season s;
enum Season {
Spring,
Summer,
Autumn,
Winter
} s;
enum {
Spring,
Summer,
Autumn,
Winter
} s;
enum Season {
Spring,
Summer,
Autumn,
Winter
} s;
s = Spring; // 等價於 s = 0;
s = 3; // 等價於 s = winter;
printf("%d", s);
enum Season {
Spring,
Summer,
Autumn,
Winter
};
// 也就是說spring的值為0,summer的值為1,autumn的值為2,winter的值為3
enum Season {
Spring = 9,
Summer,
Autumn,
Winter
};
// 也就是說spring的值為9,summer的值為10,autumn的值為11,winter的值為12
auto int num; // 等價於 int num;
register int num;
#include <stdio.h>
void test();
int main()
{
test();
test();
test();
return 0;
}
void test(){
static int num = 0; // 區域性變數
num++;
// 如果不加static輸出 1 1 1
// 如果新增static輸出 1 2 3
printf("num = %i\n", num);
}
// A檔案中的程式碼
int num; // 和B檔案中的num共用
void test(){
printf("ds.c中的 num = %i\n", num);
}
// B檔案中的程式碼
#include <stdio.h>
#include "ds.h"
int num; // 和A檔案中的num共用
int main()
{
num = 666;
test(); // test中輸出666
return 0;
}
// A檔案中的程式碼
static int num; // 不和B檔案中的num共用
void test(){
printf("ds.c中的 num = %i\n", num);
}
// B檔案中的程式碼
#include <stdio.h>
#include "ds.h"
int num; // 不和A檔案中的num共用
int main()
{
num = 666;
test(); // test中輸出0
return 0;
}
#include <stdio.h>
int main()
{
extern int num;
num = 998; // 使用時並沒有儲存空間可用, 所以宣告了也沒用
int num; // 這裡才會開闢
printf("num = %i\n", num);
return 0;
}
#include <stdio.h>
int main()
{
extern int num; // 宣告我們有名稱叫做num變數
num = 998; // 使用時已經有對應的儲存空間
printf("num = %i\n", num);
return 0;
}
int num; // 全域性變數, 程式啟動就會分配儲存空間
內部函數:只能在本檔案中存取的函數
外部函數:可以在本檔案中以及其他的檔案中存取的函數
預設情況下所有的函數都是外部函數
static 作用
static int sum(int num1,int num2);
static int sum(int num1,int num2)
{
return num1 + num2;
}
extern int sum(int num1,int num2);
extern int sum(int num1,int num2)
{
return num1 + num2;
}
- 注意點:
- 由於預設情況下所有的函數都是外部函數, 所以extern一般會省略
- 如果只有函數宣告新增了static與extern, 而定義中沒有新增static與extern, 那麼無效
#include <stdio.h>
int main(){
printf("hello lnj\n");
return 0;
}
#define 識別符號 字串
#include <stdio.h>
// 源程式中所有的宏名PI在編譯預處理的時候都會被3.14所代替
#define PI 3.14
// 根據圓的半徑計radius算周長
float girth(float radius) {
return 2 * PI *radius;
}
int main ()
{
float g = girth(2);
printf("周長為:%f", g);
return 0;
}
#define R 10
int main ()
{
char *s = "Radio"; // 在第1行定義了一個叫R的宏,但是第4行中"Radio"裡面的'R'並不會被替換成10
return 0;
}
#define I 100
int main ()
{
int i[3] = I;
return 0;
}
#define PI 3.14
int main ()
{
printf("%f", PI);
return 0;
}
#undef PI
void test()
{
printf("%f", PI); // 不能使用
}
#define R 3.0
#define PI 3.14
#define L 2*PI*R
#define S PI*R*R
#define String char *
int main(int argc, const char * argv[])
{
String str = "This is a string!";
return 0;
}
#define 宏名(形參表) 字串
// 第1行中定義了一個帶有2個引數的宏average,
#define average(a, b) (a+b)/2
int main ()
{
// 第4行其實會被替換成:int a = (10 + 4)/2;,
int a = average(10, 4);
// 輸出結果為:7是不是感覺這個宏有點像函數呢?
printf("平均值:%d", a);
return 0;
}
#define average (a, b) (a+b)/2
int main ()
{
int a = average(10, 4);
return 0;
}
注意第1行的宏定義,宏名average跟(a, b)之間是有空格的,於是,第5行就變成了這樣:
int a = (a, b) (a+b)/2(10, 4);
這個肯定是編譯不通過的
#include <stdio.h>
// 下面定義一個宏D(a),作用是返回a的2倍數值:
#define D(a) 2*a
// 如果定義宏的時候不用小括號括住引數
int main ()
{
// 將被替換成int b = 2*3+4;,輸出結果10,如果定義宏的時候用小括號括住引數,把上面的第3行改成:#define D(a) 2*(a),注意右邊的a是有括號的,第7行將被替換成int b = 2*(3+4);,輸出結果14
int b = D(3+4);
printf("%d", b);
return 0;
}
#include <stdio.h>
// 下面定義一個宏P(a),作用是返回a的平方
#define Pow(a) (a) * (a) // 如果不用小括號括住計算結果
int main(int argc, const char * argv[]) {
// 程式碼被替換為:int b = (10) * (10) / (2) * (2);
// 簡化之後:int b = 10 * (10 / 2) * 2;,最後變數b為:100
int b = Pow(10) / Pow(2);
printf("%d", b);
return 0;
}
#include <stdio.h>
// 計算結果用括號括起來
#define Pow(a) ( (a) * (a) )
int main(int argc, const char * argv[]) {
// 程式碼被替換為:int b = ( (10) * (10) ) / ( (2) * (2) );
// 簡化之後:int b = (10 * 10) / (2 *2);,最後輸出結果:25
int b = Pow(10) / Pow(2);
printf("%d", b);
return 0;
}
#if 常數表示式
..code1...
#else
..code2...
#endif
#define SCORE 67
#if SCORE > 90
printf("優秀\n");
#else
printf("不及格\n");
#endif
#if 條件1
...code1...
#elif 條件2
...code2...
#else
...code3...
#endif
#define SCORE 67
#if SCORE > 90
printf("優秀\n");
#elif SCORE > 60
printf("良好\n");
#else
printf("不及格\n");
#endif
typedef 原型別名 新型別名;
typedef int INTEGER
INTEGER a; // 等價於 int a;
typedef int Integer;
typedef Integer MyInteger;
用typedef定義陣列、指標、結構等型別將帶來很大的方便,不僅使程式書寫簡單而且使意義更為 明確,因而增強了可讀性。
陣列型別
typedef char NAME[20]; // 表示NAME是字元陣列型別,陣列長度為20。然後可用NAME 說明變數,
NAME a; // 等價於 char a[20];
struct Person{
int age;
char *name;
};
typedef struct Person PersonType;
+ 第二種形式:
typedef struct Person{
int age;
char *name;
} PersonType;
+ 第三種形式:
typedef struct {
int age;
char *name;
} PersonType;
enum Sex{
SexMan,
SexWoman,
SexOther
};
typedef enum Sex SexType;
+ 第二種形式:
typedef enum Sex{
SexMan,
SexWoman,
SexOther
} SexType;
+ 第三種形式:
typedef enum{
SexMan,
SexWoman,
SexOther
} SexType;
// 定義一個結構體並起別名
typedef struct {
float x;
float y;
} Point;
// 起別名
typedef Point *PP;
// 定義一個sum函數,計算a跟b的和
int sum(int a, int b) {
int c = a + b;
printf("%d + %d = %d", a, b, c);
return c;
}
typedef int (*MySum)(int, int);
// 定義一個指向sum函數的指標變數p
MySum p = sum;
typedef char *String;
int main(int argc, const char * argv[])
{
String str = "This is a string!";
return 0;
}
#define String char *
int main(int argc, const char * argv[])
{
String str = "This is a string!";
return 0;
}
typedef char *String1; // 給char *起了個別名String1
#define String2 char * // 定義了宏String2
int main(int argc, const char * argv[]) {
/*
只有str1、str2、str3才是指向char型別的指標變數
由於String1就是char *,所以上面的兩行程式碼等於:
char *str1;
char *str2;
*/
String1 str1, str2;
/*
宏定義只是簡單替換, 所以相當於
char *str3, str4;
*號只對最近的一個有效, 所以相當於
char *str3;
char str4;
*/
String2 str3, str4;
return 0;
}
const int Max=100;
int Array[Max];
void f(const int i) { .........}
+ 編譯器就會知道i是一個常數,不允許修改;
(3)可以避免意義模糊的數位出現,同樣可以很方便地進行引數的調整和修改。 同宏定義一樣,可以做到不變則已,一變都變!如(1)中,如果想修改Max的內容,只需要:const int Max=you want;即可!
(4)可以保護被修飾的東西,防止意外的修改,增強程式的健壯性。 還是上面的例子,如果在 函數體內修改了i,編譯器就會報錯;
void f(const int i) { i=10;//error! }
#define PI 3.14159 //常數宏
const doulbe Pi=3.14159; //此時並未將Pi放入ROM中 ...... double i=Pi; //此時為Pi分配記憶體,以後不再分配!
double I=PI; //編譯期間進行宏替換,分配記憶體
double j=Pi; //沒有記憶體分配
double J=PI; //再進行宏替換,又一次分配記憶體! const定義常數從組合的角度來看,只是給出了對應的記憶體地址,而不是象#define一樣給出的是立即數,所以,const定義的常數在程式執行過程中只有一份拷貝,而#define定義的常數在記憶體 中有若干個拷貝。
int const x=2; 或 const int x=2;
// const對於基本資料型別, 無論寫在左邊還是右邊, 變數中的值不能改變
const int a = 5;
// a = 666; // 直接修改會報錯
// 偷樑換柱, 利用指標指向變數
int *p;
p = &a;
// 利用指標間接修改變數中的值
*p = 10;
printf("%d\n", a);
printf("%d\n", *p);
int const a[5]={1, 2, 3, 4, 5};
const int a[5]={1, 2, 3, 4, 5};
const int a[5]={1, 2, 3, 4, 5};
a[1] = 55; // 錯誤
(3)修飾函數的常引數const修飾符也可以修飾函數的傳遞引數,格式如下:void Fun(const int Var); 告訴編譯器Var在函數體中的無法改變,從而防止了使用者的一些無 意的或錯誤的修改。
(4)修飾函數的返回值: const修飾符也可以修飾函數的返回值,是返回值不可被改變,格式如 下:
const int Fun1();
const MyClass Fun2();
(5)修飾常指標
技巧
先看「*」的位置
如果const 在 *的左側 表示值不能修改,但是指向可以改。
如果const 在 *的右側 表示指向不能改,但是值可以改
如果在「*」的兩側都有const 標識指向和值都不能改。
int ages[10240*10240]; // 程式會崩潰, 棧溢位
#include <stdio.h>
int main()
{
// 儲存在棧中, 記憶體地址從大到小
int a = 10;
int b = 20;
printf("&a = %p\n", &a); // &a = 0060FEAC
printf("&b = %p\n", &b); // &b = 0060FEA8
return 0;
}
int *p = (int *)malloc(10240 * 1024); // 不一定會崩潰
#include <stdio.h>
#include <stdlib.h>
int main()
{
// 儲存在棧中, 記憶體地址從小到大
int *p1 = malloc(4);
*p1 = 10;
int *p2 = malloc(4);
*p2 = 20;
printf("p1 = %p\n", p1); // p1 = 00762F48
printf("p2 = %p\n", p2); // p2 = 00762F58
return 0;
}
函數宣告 | void * malloc(size_t _Size); |
---|---|
所在檔案 | stdlib.h |
函數功能 | 申請堆記憶體空間並返回,所申請的空間並未初始化。 |
常見的初始化方法是 | memset 位元組初始化。 |
引數及返回解析 | |
引數 | size_t _size 表示要申請的字元數 |
返回值 | void * 成功返回非空指標指向申請的空間 ,失敗返回 NULL |
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
/*
* malloc
* 第一個引數: 需要申請多少個位元組空間
* 返回值型別: void *
*/
int *p = (int *)malloc(sizeof(int));
printf("p = %i\n", *p); // 儲存垃圾資料
/*
* 第一個引數: 需要初始化的記憶體地址
* 第二個初始: 需要初始化的值
* 第三個引數: 需要初始化對少個位元組
*/
memset(p, 0, sizeof(int)); // 對申請的記憶體空間進行初始化
printf("p = %i\n", *p); // 初始化為0
return 0;
}
函數宣告 | void free(void *p); |
---|---|
所在檔案 | stdlib.h |
函數功能 | 釋放申請的堆記憶體 |
引數及返回解析 | |
引數 | void* p 指向手動申請的空間 |
返回值 | void 無返回 |
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// 1.申請4個位元組儲存空間
int *p = (int *)malloc(sizeof(int));
// 2.初始化4個位元組儲存空間為0
memset(p, 0, sizeof(int));
// 3.釋放申請的儲存空間
free(p);
return 0;
}
函數宣告 | void *calloc(size_t nmemb, size_t size); |
---|---|
所在檔案 | stdlib.h |
函數功能 | 申請堆記憶體空間並返回,所申請的空間,自動清零 |
引數及返回解析 | |
引數 | size_t nmemb 所需記憶體單元數量 |
引數 | size_t size 記憶體單元位元組數量 |
返回值 | void * 成功返回非空指標指向申請的空間 ,失敗返回 NULL |
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
/*
// 1.申請3塊4個位元組儲存空間
int *p = (int *)malloc(sizeof(int) * 3);
// 2.使用申請好的3塊儲存空間
p[0] = 1;
p[1] = 3;
p[2] = 5;
printf("p[0] = %i\n", p[0]);
printf("p[1] = %i\n", p[1]);
printf("p[2] = %i\n", p[2]);
// 3.釋放空間
free(p);
*/
// 1.申請3塊4個位元組儲存空間
int *p = calloc(3, sizeof(int));
// 2.使用申請好的3塊儲存空間
p[0] = 1;
p[1] = 3;
p[2] = 5;
printf("p[0] = %i\n", p[0]);
printf("p[1] = %i\n", p[1]);
printf("p[2] = %i\n", p[2]);
// 3.釋放空間
free(p);
return 0;
}
函數宣告 | void *realloc(void *ptr, size_t size); |
---|---|
所在檔案 | stdlib.h |
函數功能 | 擴容(縮小)原有記憶體的大小。通常用於擴容,縮小會會導致記憶體縮去的部分資料丟失。 |
引數及返回解析 | |
引數 | void * ptr 表示待擴容(縮小)的指標, ptr 為之前用 malloc 或者 calloc 分配的記憶體地址。 |
引數 | size_t size 表示擴容(縮小)後記憶體的大小。 |
返回值 | void* 成功返回非空指標指向申請的空間 ,失敗返回 NULL。 |
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// 1.申請4個位元組儲存空間
int *p = NULL;
p = realloc(p, sizeof(int)); // 此時等同於malloc
// 2.使用申請好的空間
*p = 666;
printf("*p = %i\n", *p);
// 3.釋放空間
free(p);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
// 1.申請4個位元組儲存空間
int *p = malloc(sizeof(int));
printf("p = %p\n", p);
// 如果能在傳入儲存空間地址後面擴容, 返回傳入儲存空間地址
// 如果不能在傳入儲存空間地址後面擴容, 返回一個新的儲存空間地址
p = realloc(p, sizeof(int) * 2);
printf("p = %p\n", p);
// 2.使用申請好的空間
*p = 666;
printf("*p = %i\n", *p);
// 3.釋放空間
free(p);
return 0;
}
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
// 1.定義連結串列節點
typedef struct node{
int data;
struct node *next;
}Node;
int main()
{
// 2.建立連結串列節點
Node a;
Node b;
Node c;
// 3.初始化節點資料
a.data = 1;
b.data = 3;
c.data = 5;
// 4.連結節點
a.next = &b;
b.next = &c;
c.next = NULL;
// 5.建立連結串列頭
Node *head = &a;
// 6.使用連結串列
while(head != NULL){
int currentData = head->data;
printf("currentData = %i\n", currentData);
head = head->next;
}
return 0;
}
靜態連結串列的意義不是很大,主要原因,資料儲存在棧上,棧的儲存空間有限,不能動態分配。所以連結串列要實現儲存的自由,要動態的申請堆裡的空間。
有一個點要說清楚,我們的實現的連結串列是帶頭節點。至於,為什麼帶頭節點,需等大家對連結串列有個整體的的認知以後,再來體會,會更有意義。
空連結串列
#include <stdio.h>
#include <stdlib.h>
// 1.定義連結串列節點
typedef struct node{
int data;
struct node *next;
}Node;
int main()
{
Node *head = createList();
return 0;
}
// 建立空連結串列
Node *createList(){
// 1.建立一個節點
Node *node = (Node *)malloc(sizeof(Node));
if(node == NULL){
exit(-1);
}
// 2.設定下一個節點為NULL
node->next = NULL;
// 3.返回建立好的節點
return node;
}
#include <stdio.h>
#include <stdlib.h>
// 1.定義連結串列節點
typedef struct node{
int data;
struct node *next;
}Node;
Node *createList();
void printNodeList(Node *node);
int main()
{
Node *head = createList();
printNodeList(head);
return 0;
}
/**
* @brief createList 建立連結串列
* @return 建立好的連結串列
*/
Node *createList(){
// 1.建立頭節點
Node *head = (Node *)malloc(sizeof(Node));
if(head == NULL){
return NULL;
}
head->next = NULL;
// 2.接收使用者輸入資料
int num = -1;
printf("請輸入節點資料\n");
scanf("%i", &num);
// 3.通過迴圈建立其它節點
while(num != -1){
// 3.1建立一個新的節點
Node *cur = (Node *)malloc(sizeof(Node));
cur->data = num;
// 3.2讓新節點的下一個節點指向頭節點的下一個節點
cur->next = head->next;
// 3.3讓頭節點的下一個節點指向新節點
head->next = cur;
// 3.4再次接收使用者輸入資料
scanf("%i", &num);
}
// 3.返回建立好的節點
return head;
}
/**
* @brief printNodeList 遍歷連結串列
* @param node 連結串列指標頭
*/
void printNodeList(Node *node){
Node *head = node->next;
while(head != NULL){
int currentData = head->data;
printf("currentData = %i\n", currentData);
head = head->next;
}
}
#include <stdio.h>
#include <stdlib.h>
// 1.定義連結串列節點
typedef struct node{
int data;
struct node *next;
}Node;
Node *createList();
void printNodeList(Node *node);
int main()
{
Node *head = createList();
printNodeList(head);
return 0;
}
/**
* @brief createList 建立連結串列
* @return 建立好的連結串列
*/
Node *createList(){
// 1.建立頭節點
Node *head = (Node *)malloc(sizeof(Node));
if(head == NULL){
return NULL;
}
head->next = NULL;
// 2.接收使用者輸入資料
int num = -1;
printf("請輸入節點資料\n");
scanf("%i", &num);
// 3.通過迴圈建立其它節點
// 定義變數記錄上一個節點
Node *pre = head;
while(num != -1){
// 3.1建立一個新的節點
Node *cur = (Node *)malloc(sizeof(Node));
cur->data = num;
// 3.2讓新節點連結到上一個節點後面
pre->next = cur;
// 3.3當前節點下一個節點等於NULL
cur->next = NULL;
// 3.4讓當前節點程式設計下一個節點的上一個節點
pre = cur;
// 3.5再次接收使用者輸入資料
scanf("%i", &num);
}
// 3.返回建立好的節點
return head;
}
/**
* @brief printNodeList 遍歷連結串列
* @param node 連結串列指標頭
*/
void printNodeList(Node *node){
Node *head = node->next;
while(head != NULL){
int currentData = head->data;
printf("currentData = %i\n", currentData);
head = head->next;
}
}
#include <stdio.h>
#include <stdlib.h>
// 1.定義連結串列節點
typedef struct node{
int data;
struct node *next;
}Node;
Node *createList();
void printNodeList(Node *node);
void insertNode1(Node *head, int data);
void insertNode2(Node *head, int data);
int main()
{
// 1.建立一個空連結串列
Node *head = createList();
// 2.往空連結串列中插入資料
insertNode1(head, 1);
insertNode1(head, 3);
insertNode1(head, 5);
printNodeList(head);
return 0;
}
/**
* @brief createList 建立空連結串列
* @return 建立好的空連結串列
*/
Node *createList(){
// 1.建立頭節點
Node *head = (Node *)malloc(sizeof(Node));
if(head == NULL){
return NULL;
}
head->next = NULL;
// 3.返回建立好的節點
return head;
}
/**
* @brief insertNode1 尾插法插入節點
* @param head 需要插入的頭指標
* @param data 需要插入的資料
* @return 插入之後的連結串列
*/
void insertNode1(Node *head, int data){
// 1.定義變數記錄最後一個節點
Node *pre = head;
while(pre != NULL && pre->next != NULL){
pre = pre->next;
}
// 2.建立一個新的節點
Node *cur = (Node *)malloc(sizeof(Node));
cur->data = data;
// 3.讓新節點連結到上一個節點後面
pre->next = cur;
// 4.當前節點下一個節點等於NULL
cur->next = NULL;
// 5.讓當前節點程式設計下一個節點的上一個節點
pre = cur;
}
/**
* @brief insertNode1 頭插法插入節點
* @param head 需要插入的頭指標
* @param data 需要插入的資料
* @return 插入之後的連結串列
*/
void insertNode2(Node *head, int data){
// 1.建立一個新的節點
Node *cur = (Node *)malloc(sizeof(Node));
cur->data = data;
// 2.讓新節點的下一個節點指向頭節點的下一個節點
cur->next = head->next;
// 3.讓頭節點的下一個節點指向新節點
head->next = cur;
}
/**
* @brief printNodeList 遍歷連結串列
* @param node 連結串列指標頭
*/
void printNodeList(Node *node){
Node *head = node->next;
while(head != NULL){
int currentData = head->data;
printf("currentData = %i\n", currentData);
head = head->next;
}
}
/**
* @brief destroyList 銷燬連結串列
* @param head 連結串列頭指標
*/
void destroyList(Node *head){
Node *cur = NULL;
while(head != NULL){
cur = head->next;
free(head);
head = cur;
}
}
/**
* @brief listLength 計算連結串列長度
* @param head 連結串列頭指標
* @return 連結串列長度
*/
int listLength(Node *head){
int count = 0;
head = head->next;
while(head){
count++;
head = head->next;
}
return count;
}
/**
* @brief searchList 查詢指定節點
* @param head 連結串列頭指標
* @param key 需要查詢的值
* @return
*/
Node *searchList(Node *head, int key){
head = head->next;
while(head){
if(head->data == key){
break;
}else{
head = head->next;
}
}
return head;
}
void deleteNodeList(Node *head, Node *find){
while(head->next != find){
head = head->next;
}
head->next = find->next;
free(find);
}
/**
* @brief bubbleSort 對連結串列進行排序
* @param head 連結串列頭指標
*/
void bubbleSort(Node *head){
// 1.計算連結串列長度
int len = listLength(head);
// 2.定義變數記錄前後節點
Node *cur = NULL;
// 3.相鄰元素進行比較, 進行氣泡排序
for(int i = 0; i < len - 1; i++){
cur = head->next;
for(int j = 0; j < len - 1 - i; j++){
printf("%i, %i\n", cur->data, cur->next->data);
if((cur->data) > (cur->next->data)){
int temp = cur->data;
cur->data = cur->next->data;
cur->next->data = temp;
}
cur = cur->next;
}
}
}
/**
* @brief sortList 對連結串列進行排序
* @param head 連結串列頭指標
*/
void sortList(Node *head){
// 0.計算連結串列長度
int len = listLength(head);
// 1.定義變數儲存前後兩個節點
Node *sh, *pre, *cur;
for(int i = 0; i < len - 1; i ++){
sh = head; // 頭節點
pre = sh->next; // 第一個節點
cur = pre->next; // 第二個節點
for(int j = 0; j < len - 1 - i; j++){
if(pre->data > cur->data){
// 交換節點位置
sh->next = cur;
pre->next = cur->next;
cur->next = pre;
// 恢復節點名稱
Node *temp = pre;
pre = cur;
cur = temp;
}
// 讓所有節點往後移動
sh = sh->next;
pre = pre->next;
cur = cur->next;
}
}
}
/**
* @brief reverseList 反轉連結串列
* @param head 連結串列頭指標
*/
void reverseList(Node *head){
// 1.將連結串列一分為二
Node *pre, *cur;
pre = head->next;
head->next = NULL;
// 2.重新插入節點
while(pre){
cur = pre->next;
pre->next = head->next;
head->next = pre;
pre = cur;
}
}
文字檔案
文字檔案的每一個位元組存放一個 ASCII 碼,代表一個字元
。這便於對字元的逐個處理,但佔用儲存空間二進位制檔案
資料按其記憶體中的儲存形式原樣存放
#include <stdio.h>
int main()
{
/*
* 以文字形式儲存
* 會將每個字元先轉換為對應的ASCII,
* 然後再將ASCII碼的二進位制儲存到計算機中
*/
int num = 666;
FILE *fa = fopen("ascii.txt", "w");
fprintf(fa, "%d", num);
fclose(fa);
/*
* 以二進位制形式儲存
* 會將666的二進位制直接儲存到檔案中
*/
FILE *fb = fopen("bin.txt", "w");
fwrite(&num, 4, 1, fb);
fclose(fb);
return 0;
}
記憶體示意圖
通過文字工具開啟示意圖
- 文字工具預設會按照ASCII碼逐個直接解碼檔案, 由於文字檔案儲存的就是ASCII碼, 所以可以正常解析顯示, 由於二進位制檔案儲存的不是ASCII碼, 所以解析出來之後是亂碼
struct _iobuf {
char *_ptr; //檔案輸入的下一個位置
int _cnt; //當前緩衝區的相對位置
char *_base; //檔案的起始位置)
int _flag; //檔案標誌
int _file; //檔案的有效性驗證
int _charbuf; //檢查緩衝區狀況,如果無緩衝區則不讀取
int _bufsiz; // 緩衝區大小
char *_tmpfname; //臨時檔名
};
typedef struct _iobuf FILE;
函數宣告 | FILE * fopen ( const char * filename, const char * mode ); |
---|---|
所在檔案 | stdio.h |
函數功能 | 以 mode 的方式,開啟一個 filename 命名的檔案,返回一個指向該檔案緩衝的 FILE 結構體指標。 |
引數及返回解析 | |
引數 | char*filaname :要開啟,或是建立檔案的路徑。 |
引數 | char*mode :開啟檔案的方式。 |
返回值 | FILE* 返回指向檔案緩衝區的指標,該指標是後序操作檔案的控制程式碼。 |
mode | 處理方式 | 當檔案不存在時 | 當檔案存在時 | 向檔案輸入 | 從檔案輸出 |
---|---|---|---|---|---|
r | 讀取 | 出錯 | 開啟檔案 | 不能 | 可以 |
w | 寫入 | 建立新檔案 | 覆蓋原有檔案 | 可以 | 不能 |
a | 追加 | 建立新檔案 | 在原有檔案後追加 | 可以 | 不能 |
r+ | 讀取/寫入 | 出錯 | 開啟檔案 | 可以 | 可以 |
w+ | 寫入/讀取 | 建立新檔案 | 覆蓋原有檔案 | 可以 | 可以 |
a+ | 讀取/追加 | 建立新檔案 | 在原有檔案後追加 | 可以 | 可以 |
注意點:
- Windows如果讀寫的是二進位制檔案,則還要加 b,比如 rb, r+b 等。 unix/linux 不區分文字和二進位制檔案
函數宣告 | int fclose ( FILE * stream ); |
---|---|
所在檔案 | stdio.h |
函數功能 | fclose()用來關閉先前 fopen()開啟的檔案. |
函數功能 | 此動作會讓緩衝區內的資料寫入檔案中, 並釋放系統所提供的檔案資源 |
引數及返回解析 | |
引數 | FILE* stream :指向檔案緩衝的指標。 |
返回值 | int 成功返回 0 ,失敗返回 EOF(-1)。 |
#include <stdio.h>
int main()
{
FILE *fp = fopen("test.txt", "w+");
fclose(fp);
return 0;
}
–
函數宣告 | int fputc (int ch, FILE * stream ); |
---|---|
所在檔案 | stdio.h |
函數功能 | 將 ch 字元,寫入檔案。 |
引數及返回解析 | |
引數 | FILE* stream :指向檔案緩衝的指標。 |
引數 | int : 需要寫入的字元。 |
返回值 | int 寫入成功,返回寫入成功字元,如果失敗,返回 EOF。 |
#include <stdio.h>
int main()
{
// 1.開啟一個檔案
FILE *fp = fopen("test.txt", "w+");
// 2.往檔案中寫入內容
for(char ch = 'a'; ch <= 'z'; ch++){
// 一次寫入一個字元
char res = fputc(ch, fp);
printf("res = %c\n", res);
}
// 3.關閉開啟的檔案
fclose(fp);
return 0;
}
函數宣告 | int fgetc ( FILE * stream ); |
---|---|
所在檔案 | stdio.h |
函數功能 | 從檔案流中讀取一個字元並返回。 |
引數及返回解析 | |
引數 | FILE* stream :指向檔案緩衝的指標。 |
返回值 | int 正常,返回讀取的字元;讀到檔案尾或出錯時,為 EOF。 |
#include <stdio.h>
int main()
{
// 1.開啟一個檔案
FILE *fp = fopen("test.txt", "r+");
// 2.從檔案中讀取內容
char res = EOF;
while((res = fgetc(fp)) != EOF){
printf("res = %c\n", res);
}
// 3.關閉開啟的檔案
fclose(fp);
return 0;
}
函數宣告 | int feof( FILE * stream ); |
---|---|
所在檔案 | stdio.h |
函數功能 | 判斷檔案是否讀到檔案結尾 |
引數及返回解析 | |
引數 | FILE* stream :指向檔案緩衝的指標。 |
返回值 | int 0 未讀到檔案結尾,非零 讀到檔案結尾。 |
#include <stdio.h>
int main()
{
// 1.開啟一個檔案
FILE *fp = fopen("test.txt", "r+");
// 2.從檔案中讀取內容
char res = EOF;
// 注意: 由於只有先讀了才會修改標誌位,
// 所以通過feof判斷是否到達檔案末尾, 一定要先讀再判斷, 不能先判斷再讀
while((res = fgetc(fp)) && (!feof(fp))){
printf("res = %c\n", res);
}
// 3.關閉開啟的檔案
fclose(fp);
return 0;
}
- 注意點:
- feof 這個函數,是去讀標誌位判斷檔案是否結束的。
- 而標誌位只有讀完了才會被修改, 所以如果先判斷再讀標誌位會出現多打一次的的現象
- 所以企業開發中使用feof函數一定要先讀後判斷, 而不能先判斷後讀
#include <stdio.h>
#include <string.h>
void encode(char *name, char *newName, int code);
void decode(char *name, char *newName, int code);
int main()
{
encode("main.c", "encode.c", 666);
decode("encode.c", "decode.c", 666);
return 0;
}
/**
* @brief encode 加密檔案
* @param name 需要加密的檔名稱
* @param newName 加密之後的檔名稱
* @param code 祕鑰
*/
void encode(char *name, char *newName, int code){
FILE *fw = fopen(newName, "w+");
FILE *fr = fopen(name, "r+");
char ch = EOF;
while((ch = fgetc(fr)) && (!feof(fr))){
fputc(ch ^ code, fw);
}
fclose(fw);
fclose(fr);
}
/**
* @brief encode 解密檔案
* @param name 需要解密的檔名稱
* @param newName 解密之後的檔名稱
* @param code 祕鑰
*/
void decode(char *name, char *newName, int code){
FILE *fw = fopen(newName, "w+");
FILE *fr = fopen(name, "r+");
char ch = EOF;
while((ch = fgetc(fr)) && (!feof(fr))){
fputc(ch ^ code, fw);
}
fclose(fw);
fclose(fr);
}
#include <stdio.h>
int main()
{
FILE *fw = fopen("test.txt", "w+");
fputc('a', fw);
fputc('\n', fw);
fputc('b', fw);
fclose(fw);
return 0;
}
函數宣告 | int fputs(char *str,FILE *fp) |
---|---|
所在檔案 | stdio.h |
函數功能 | 把 str 指向的字串寫入 fp 指向的檔案中。 |
引數及返回解析 | |
引數 | char * str : 表示指向的字串的指標。 |
引數 | FILE *fp : 指向檔案流結構的指標。 |
返回值 | int 正常,返 0;出錯返 EOF。 |
#include <stdio.h>
int main()
{
FILE *fw = fopen("test.txt", "w+");
// 注意: fputs不會自動新增\n
fputs("lnj\n", fw);
fputs("it666\n", fw);
fclose(fw);
return 0;
}
#include <stdio.h>
int main()
{
FILE *fp = fopen("test.txt", "w+");
// 注意: fputs寫入時遇到\0就會自動終止寫入
fputs("lnj\0it666\n", fp);
fclose(fp);
return 0;
}
函數宣告 | char *fgets(char *str,int length,FILE *fp) |
---|---|
所在檔案 | stdio.h |
函數功能 | 從 fp 所指向的檔案中,至多讀 length-1 個字元,送入字元陣列 str 中, 如果在讀入 length-1 個字元結束前遇\n 或 EOF,讀入即結束,字串讀入後在最後加一個‘\0’字元。 |
引數及返回解析 | |
引數 | char * str :指向需要讀入資料的緩衝區。 |
引數 | int length :每一次讀數位符的字數。 |
引數 | FILE* fp :檔案流指標。 |
返回值 | char * 正常,返 str 指標;出錯或遇到檔案結尾 返空指標 NULL。 |
#include <stdio.h>
int main()
{
FILE *fp = fopen("test.txt", "w+");
// 注意: fputs不會自動新增\n
fputs("it666\n", fp);
// 將FILE結構體中的讀寫指標重新移動到最前面
// 注意: FILE結構體中讀寫指標每讀或寫一個字元后都會往後移動
rewind(fp);
char str[1024];
// 從fp中讀取4個字元, 存入到str中
// 最多隻能讀取N-1個字元, 會在最後自動新增\0
fgets(str, 4, fp);
printf("str = %s", str); // it6
fclose(fp);
return 0;
}
#include <stdio.h>
int main()
{
FILE *fp = fopen("test.txt", "w+");
// 注意: fputs不會自動新增\n
fputs("lnj\n", fp);
fputs("it666\n", fp);
// 將FILE結構體中的讀寫指標重新移動到最前面
// 注意: FILE結構體中讀寫指標每讀或寫一個字元后都會往後移動
rewind(fp);
char str[1024];
// 從fp中讀取1024個字元, 存入到str中
// 但是讀到第4個就是\n了, 函數會自動停止讀取
// 注意點: \n會被讀取進來
fgets(str, 1024, fp);
printf("str = %s", str); // lnj
fclose(fp);
return 0;
}
#include <stdio.h>
int main()
{
FILE *fp = fopen("test.txt", "w+");
// 注意: fputs不會自動新增\n
fputs("lnj\n", fp);
fputs("it666", fp);
// 將FILE結構體中的讀寫指標重新移動到最前面
// 注意: FILE結構體中讀寫指標每讀或寫一個字元后都會往後移動
rewind(fp);
char str[1024];
// 每次從fp中讀取1024個字元, 存入到str中
// 讀取到檔案末尾自動結束
while(fgets(str, 1024, fp)){
printf("str = %s", str);
}
fclose(fp);
return 0;
}
#include <stdio.h>
int main()
{
FILE *fp = fopen("test.txt", "w+");
// 注意: fputs不會自動新增\n
fputs("12345678910\n", fp);
fputs("12345678910\n", fp);
fputs("12345678910", fp);
// 將FILE結構體中的讀寫指標重新移動到最前面
// 注意: FILE結構體中讀寫指標每讀或寫一個字元后都會往後移動
rewind(fp);
char str[1024];
// 每次從fp中讀取1024個字元, 存入到str中
// 讀取到檔案末尾自動結束
while(fgets(str, 1024, fp) && !feof(fp)){
printf("str = %s", str);
}
fclose(fp);
return 0;
}
12345678910
12345
123
函數宣告 | int fwrite(void *buffer, int num_bytes, int count, FILE *fp) |
---|---|
所在檔案 | stdio.h |
函數功能 | 把buffer 指向的資料寫入fp 指向的檔案中 |
引數 | char * buffer : 指向要寫入資料儲存區的首地址的指標 |
int num_bytes: 每個要寫的欄位的位元組數count | |
int count : 要寫的欄位的個數 | |
FILE* fp : 要寫的檔案指標 | |
返回值 | int 成功,返回寫的欄位數;出錯或檔案結束,返回 0。 |
#include <stdio.h>
#include <string.h>
int main()
{
FILE *fp = fopen("test.txt", "wb+");
// 注意: fwrite不會關心寫入資料的格式
char *str = "lnj\0it666";
/*
* 第一個引數: 被寫入資料指標
* 第二個引數: 每次寫入多少個位元組
* 第三個引數: 需要寫入多少次
* 第四個引數: 已開啟檔案結構體指標
*/
fwrite((void *)str, 9, 1, fp);
fclose(fp);
return 0;
}
函數宣告 | int fread(void *buffer, int num_bytes, int count, FILE *fp) |
---|---|
所在檔案 | stdio.h |
函數功能 | 把fp 指向的檔案中的資料讀到 buffer 中。 |
引數 | char * buffer : 指向要讀入資料儲存區的首地址的指標 |
int num_bytes: 每個要讀的欄位的位元組數count | |
int count : 要讀的欄位的個數 | |
FILE* fp : 要讀的檔案指標 | |
返回值 | int 成功,返回讀的欄位數;出錯或檔案結束,返回 0。 |
#include <stdio.h>
int main()
{
// test.txt中存放的是"lnj\0it666"
FILE *fr = fopen("test.txt", "rb+");
char buf[1024] = {0};
// fread函數讀取成功返回讀取到的位元組數, 讀取失敗返回0
/*
* 第一個引數: 儲存讀取到資料的容器
* 第二個引數: 每次讀取多少個位元組
* 第三個引數: 需要讀取多少次
* 第四個引數: 已開啟檔案結構體指標
*/
int n = fread(buf, 1, 1024, fr);
printf("%i\n", n);
for(int i = 0; i < n; i++){
printf("%c", buf[i]);
}
fclose(fr);
return 0;
}
- 注意點:
- 讀取時num_bytes應該填寫讀取資料型別的最小單位, 而count可以隨意寫
- 如果讀取時num_bytes不是讀取資料型別最小單位, 會引發讀取失敗
- 例如: 儲存的是char型別 6C 6E 6A 00 69 74 36 36 36
如果num_bytes等於1, count等於1024, 那麼依次取出 6C 6E 6A 00 69 74 36 36 36 , 直到取不到為止
如果num_bytes等於4, count等於1024, 那麼依次取出[6C 6E 6A 00][69 74 36 36] , 但是最後還剩下一個36, 但又不滿足4個位元組, 那麼最後一個36則取不到
#include <stdio.h>
#include <string.h>
int main()
{
// test.txt中存放的是"lnj\0it666"
FILE *fr = fopen("test.txt", "rb+");
char buf[1024] = {0};
/*
while(fread(buf, 4, 1, fr) > 0){
printf("%c\n", buf[0]);
printf("%c\n", buf[1]);
printf("%c\n", buf[2]);
printf("%c\n", buf[3]);
}
*/
/*
while(fread(buf, 1, 4, fr) > 0){
printf("%c\n", buf[0]);
printf("%c\n", buf[1]);
printf("%c\n", buf[2]);
printf("%c\n", buf[3]);
}
*/
while(fread(buf, 1, 1, fr) > 0){
printf("%c\n", buf[0]);
}
fclose(fr);
return 0;
}
- 注意: fwrite和fread本質是用來操作二進位制的
- 所以下面用法才是它們的正確開啟姿勢
#include <stdio.h>
int main()
{
FILE *fp = fopen("test.txt", "wb+");
int ages[4] = {1, 3, 5, 6};
fwrite(ages, sizeof(ages), 1, fp);
rewind(fp);
int data;
while(fread(&data, sizeof(int), 1, fp) > 0){
printf("data = %i\n", data);
}
return 0;
}
#include <stdio.h>
typedef struct{
char *name;
int age;
double height;
} Person;
int main()
{
Person p1 = {"lnj", 35, 1.88};
// printf("name = %s\n", p1.name);
// printf("age = %i\n", p1.age);
// printf("height = %lf\n", p1.height);
FILE *fp = fopen("person.stu", "wb+");
fwrite(&p1, sizeof(p1), 1, fp);
rewind(fp);
Person p2;
fread(&p2, sizeof(p2), 1, fp);
printf("name = %s\n", p2.name);
printf("age = %i\n", p2.age);
printf("height = %lf\n", p2.height);
return 0;
}
#include <stdio.h>
typedef struct{
char *name;
int age;
double height;
} Person;
int main()
{
Person ps[] = {
{"zs", 18, 1.65},
{"ls", 21, 1.88},
{"ww", 33, 1.9}
};
FILE *fp = fopen("person.stu", "wb+");
fwrite(&ps, sizeof(ps), 1, fp);
rewind(fp);
Person p;
while(fread(&p, sizeof(p), 1, fp) > 0){
printf("name = %s\n", p.name);
printf("age = %i\n", p.age);
printf("height = %lf\n", p.height);
}
return 0;
}
#include <stdio.h>
#include <stdlib.h>
typedef struct person{
char *name;
int age;
double height;
struct person* next;
} Person;
Person *createEmpty();
void insertNode(Person *head, char *name, int age, double height);
void printfList(Person *head);
int saveList(Person *head, char *name);
Person *loadList(char *name);
int main()
{
// Person *head = createEmpty();
// insertNode(head, "zs", 18, 1.9);
// insertNode(head, "ls", 22, 1.65);
// insertNode(head, "ws", 31, 1.78);
// printfList(head);
// saveList(head, "person.list");
Person *head = loadList("person.list");
printfList(head);
return 0;
}
/**
* @brief loadList 從檔案載入連結串列
* @param name 檔名稱
* @return 載入好的連結串列頭指標
*/
Person *loadList(char *name){
// 1.開啟檔案
FILE *fp = fopen(name, "rb+");
if(fp == NULL){
return NULL;
}
// 2.建立一個空連結串列
Person *head = createEmpty();
// 3.建立一個節點
Person *node = (Person *)malloc(sizeof(Person));
while(fread(node, sizeof(Person), 1, fp) > 0){
// 3.進行插入
// 3.1讓新節點的下一個節點 等於 頭節點的下一個節點
node->next = head->next;
// 3.2讓頭結點的下一個節點 等於 新節點
head->next = node;
// 給下一個節點申請空間
node = (Person *)malloc(sizeof(Person));
}
// 釋放多餘的節點空間
free(node);
fclose(fp);
return head;
}
/**
* @brief saveList 儲存連結串列到檔案
* @param head 連結串列頭指標
* @param name 儲存的檔名稱
* @return 是否儲存成功 -1失敗 0成功
*/
int saveList(Person *head, char *name){
// 1.開啟檔案
FILE *fp = fopen(name, "wb+");
if(fp == NULL){
return -1;
}
// 2.取出頭節點的下一個節點
Person *cur = head->next;
// 3.將所有有效節點儲存到檔案中
while(cur != NULL){
fwrite(cur, sizeof(Person), 1, fp);
cur = cur->next;
}
fclose(fp);
return 0;
}
/**
* @brief printfList 遍歷連結串列
* @param head 連結串列的頭指標
*/
void printfList(Person *head){
// 1.取出頭節點的下一個節點
Person *cur = head->next;
// 2.判斷是否為NULL, 如果不為NULL就開始遍歷
while(cur != NULL){
// 2.1取出當前節點的資料, 列印
printf("name = %s\n", cur->name);
printf("age = %i\n", cur->age);
printf("height = %lf\n", cur->height);
printf("next = %x\n", cur->next);
printf("-----------\n");
// 2.2讓當前節點往後移動
cur = cur->next;
}
}
/**
* @brief insertNode 插入新的節點
* @param head 連結串列的頭指標
* @param p 需要插入的結構體
*/
void insertNode(Person *head, char *name, int age, double height){
// 1.建立一個新的節點
Person *node = (Person *)malloc(sizeof(Person));
// 2.將資料儲存到新節點中
node->name = name;
node->age = age;
node->height = height;
// 3.進行插入
// 3.1讓新節點的下一個節點 等於 頭節點的下一個節點
node->next = head->next;
// 3.2讓頭結點的下一個節點 等於 新節點
head->next = node;
}
/**
* @brief createEmpty 建立一個空連結串列
* @return 連結串列頭指標, 建立失敗返回NULL
*/
Person *createEmpty(){
// 1.定義頭指標
Person *head = NULL;
// 2.建立一個空節點, 並且賦值給頭指標
head = (Person *)malloc(sizeof(Person));
if(head == NULL){
return head;
}
head->next = NULL;
// 3.返回頭指標
return head;
}
函數宣告 | long ftell ( FILE * stream ); |
---|---|
所在檔案 | stdio.h |
函數功能 | 得到流式檔案的當前讀寫位置,其返回值是當前讀寫位置偏離檔案頭部的位元組數. |
引數及返回解析 | |
引數 | FILE * 流檔案控制程式碼 |
返回值 | int 成功,返回當前讀寫位置偏離檔案頭部的位元組數。失敗, 返回-1 |
#include <stdio.h>
int main()
{
char *str = "123456789";
FILE *fp = fopen("test.txt", "w+");
long cp = ftell(fp);
printf("cp = %li\n", cp); // 0
// 寫入一個位元組
fputc(str[0], fp);
cp = ftell(fp);
printf("cp = %li\n", cp); // 1
fclose(fp);
return 0;
}
函數宣告 | void rewind ( FILE * stream ); |
---|---|
所在檔案 | stdio.h |
函數功能 將檔案指標重新指向一個流的開頭。 | |
引數及返回解析 | |
引數 | FILE * 流檔案控制程式碼 |
返回值 | void 無返回值 |
#include <stdio.h>
int main()
{
char *str = "123456789";
FILE *fp = fopen("test.txt", "w+");
long cp = ftell(fp);
printf("cp = %li\n", cp); // 0
// 寫入一個位元組
fputc(str[0], fp);
cp = ftell(fp);
printf("cp = %li\n", cp); // 1
// 新指向一個流的開頭
rewind(fp);
cp = ftell(fp);
printf("cp = %li\n", cp); // 0
fclose(fp);
return 0;
}
函數宣告 | int fseek ( FILE * stream, long offset, int where); |
---|---|
所在檔案 | stdio.h |
函數功能 | 偏移檔案指標。 |
引數及返回解析 | |
參 數 | FILE * stream 檔案控制程式碼 |
long offset 偏移量 | |
int where 偏移起始位置 | |
返回值 | int 成功返回 0 ,失敗返回-1 |
#define SEEK_CUR 1 當前文字
#define SEEK_END 2 檔案結尾
#define SEEK_SET 0 檔案開頭
#include <stdio.h>
int main()
{
FILE *fp = fopen("test.txt", "w+");
fputs("123456789", fp);
// 將檔案指標移動到檔案結尾, 並且偏移0個單位
fseek(fp, 0, SEEK_END);
int len = ftell(fp); // 計算檔案長度
printf("len = %i\n", len);
fclose(fp);
return 0;
}
#include <stdio.h>
int main()
{
FILE *fp;
fp = fopen("file.txt","w+");
fputs("123456789", fp);
fseek( fp, 7, SEEK_SET );
fputs("lnj", fp);
fclose(fp);
return 0;
}
如果覺得文章對你有幫助,點贊、收藏、關注、評論,一鍵四連支援,你的支援就是江哥持續更新的動力