專升本計算機綜合-C語言篇


C 語言教程

參考於視頻:C語言教程(vs2019版);用於C入門、期末備考。

練習網站:編程入門訓練

C語言教程推薦: C語言入門教程

第一章 入門

  • C 語言是一種通用的、面向過程式的計算機程序設計語言。
  • 1972 年,為了移植與開發 UNIX 操作系統,丹尼斯·里奇在貝爾電話實驗室設計開發了 C 語言。
  • 當前最新的 C 語言標准為 C18 ,在它之前的 C 語言標准有 C17、C11...C99 等。

  • 編譯器安裝,初學建議使用Dev-C++,安裝步驟
  • 注意:如果是C程序,文件后綴保存為.c;如果是C++程序,文件后綴保存為.cpp
  • 筆者使用的VS2017編譯器。安裝步驟!!!

  • 安裝好之后,編譯/執行 C 程序:
#include <stdio.h>

int main()
{
	/* 我的第一個C程序 */
	printf("Hello, World! \n");
	return 0;
}

在這里插入圖片描述

/* #include 包含 <stdio.h> .h head 頭文件 stdio */
// standard i input o out輸出

// 這個文件可能會包含一個標准輸入輸出的頭文件

#include <stdio.h>

int main()
{
	/* 我的第一個C程序 注釋:不被編譯器識別 */
	// 注釋
	printf("Hello, World! \n");

	// print 打印 f format 格式化
	// printf 格式化輸出
	return 0;
}

C 程序主要包括以下部分:

  • 預處理器指令

    • include <stdio.h>

  • 函數

    • int main(){}
  • 變量

  • 語句 & 表達式

    • printf("Hello world! \n");
  • 注釋

    • // 單行注釋
      
      /* 單行注釋 */
      /* 
       多行注釋
       多行注釋
       多行注釋
       */
      

VS注釋與取消注釋快捷鍵

這個是VS最常用的,一定要記住。

  • 注釋: 先 Ctrl+ K,然后 Ctrl+ C
  • 取消注釋: 先 Ctrl+ K,然后 Ctrl + U

在VS中使用scanf(),會報錯。如下:

在這里插入圖片描述

  • 只需進行如下修改即可:
scanf_s("%d", &number);   // 只是vs中要修改,
// 其他的編譯器中輸入函數仍用:
// scanf("%d",&number);
// 進行數據輸入!!!

在這里插入圖片描述

另一種解決方法:修改VS配置,就可以使用scanf()函數,表示輸入了。


第二章 變量與常量

  • 此處先做了解!!!

C語言標識符:

  • C 標識符是用來標識變量、函數,或任何其他用戶自定義項目的名稱。
  • 一個標識符以字母 A-Z 或 a-z 或下划線 _ 開始,后跟零個或多個字母、下划線和數字(0-9)。
  • C 標識符內不允許出現標點字符,比如 @、$ 和 %。
  • C 是區分大小寫的編程語言。

標識符分類:

  • 關鍵字(32個);
  • 預定義標識符;
  • 用戶自定義的標識符。

2.1 變量

  • 變量:在程序運行中不斷變化的量。
  • 它需要定義后才能使用,具體格式如下:

變量的定義格式:

變量類型 變量名;
// 變量在定義時,可以賦初值:
變量類型 變量名 = 初值;
#include <stdio.h>
int main()
{
	int number = 50;
	printf("number = %d \n", number);
	return 0;
}

在這里插入圖片描述

  • 變量名的命名:
    • 1.見名知義,采用英文單詞組合,不要出現拼音;
    • 2.命名的長度應當符合“min-length && max-information”原則;
    • 3.盡量避免名字中出現數字編號,如 Value1,Value2 等;
    • 4.C語言嚴格區分大小寫,注意類似x和X,1(數字 1)和 l(小寫字母 l)之間,0(數字 0)和 o(小寫字母 o)之間的區別;
    • 5.宏定義、枚舉常數、只讀變量全用大寫字母命名,用下划線分割單詞。
#include <stdio.h>
int main()
{
	// 不規范變量名
	int a = 1,b = 2;
	// 規范的命名
	// 1. 見名知義
	int dollor = 100;
	// 2.下划線命名法
	int sun_flower = 100;
	// 3.駝峰命名法
	int sunFlowerValues = 100;
	// 4.匈牙利命名法

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

一般來說,基本數據類型分為整型、浮點型、字符型,C++中包括布爾型;每種類型又可分為若干種類型,常用基本類型可分為如下:

類型 存儲大小 取值范圍 大致范圍
整型 int 4字節 -2,147,483,648 ~ 2,147,483,647 -2 x 109 ~2 x 109
long long 8字節 -263 ~ +(263 -1) -9 x 1018 ~ 9 x 1018
浮點型 float 4字節 -2128 ~ +2128 實際精度6 ~ 7 位
double 8字節 -21024 ~ +21024 實際精度15 ~ 16 位
字符型 char 1字節 -128 ~ 127 -128 ~ 127
布爾型 bool 1字節 0(false) or 1(true) 0(false) or 1(true)
  • 自行測試如下:
#include <stdio.h>
int main()
{
	int number = 50;
	double moon = 99.9;
	printf("number = %d \n", number);
	printf("moon = %lf \n", moon);
	return 0;
}

強制類型轉換:

  • 強制類型轉換格式:
(新類型名) 變量名
#include <stdio.h>

int main()
{
	double r = 12.56;
	int a = 3, b = 5;
	printf("%d \n", (int)r);
	printf("%d \n", a / b);
	printf("%.lf", (double)a / (double)b);
	return 0;
}

2.2 常量

C 常量:

  • 常量是固定值,在程序執行期間不會改變。這些固定的值,又叫做字面量
  • 常量可以是任何的基本數據類型,比如整數常量、浮點常量、字符常量,或字符串字面值,也有枚舉常量。
  • 常量就像是常規的變量,只不過常量的值在定義后不能進行修改。
  • 在 C 中,有兩種簡單的定義常量的方式:

    • 使用 #define 預處理器。
    • 使用 const 關鍵字。

在這里插入圖片描述

#include <stdio.h>
// 宏定義
#define PI 3.14
#define SUN_FLOWER 100;

int main()
{
	// 定義常量時,全大寫。
	const int IPHONE = 999;
	return 0;
}
  • #define 是宏定義,它不能定義常量,但宏定義可以實現在字面意義上和其它定義常量相同的功能,本質的區別就在於 #define 不為宏名分配內存,而 const 也不為常量分配內存,怎么回事呢,其實 const 並不是去定義一個常量,而是去改變一個變量的存儲類,把該變量所占的內存變為只讀!

基本數據類型書寫:

  • 整型:

    • 默認為10進制 ,10 ,20。
    • 以0開頭為8進制,045,021。
    • 以0b開頭為2進制,0b11101101。
    • 以0x開頭為16進制,0x21458adf。
  • 實型

    • 單精度常量:2.3f 。
    • 雙精度常量:2.3,默認為雙精度。
  • 字符型常量

    • 用英文單引號括起來,只保存一個字符'a'、'b' 、'*' 。
    #include<stdio.h>
    
    int main()
    {
    	char character='A';
    	
    	printf("character = %c \n", character);
    	return 0;
    }
    
    • 轉義字符: 一些前面有反斜杠的字符。

      轉義序列 含義
      \ \ 字符
      ' ' 字符
      " " 字符
      ? ? 字符
      \a 警報鈴聲
      \b 退格鍵
      \f 換頁符
      \n 換行符
      \r 回車
      \t 水平制表符
      \v 垂直制表符
      \ooo 一到三位的八進制數
  • 字符串常量

    • 用英文的雙引號引起來 可以保存多個字符:"abc"。

第三章 運算符

3.1 算術運算符

運算符 描述 實例
+ 把兩個操作數相加 A + B 將得到 30
- 從第一個操作數中減去第二個操作數 A - B 將得到 -10
* 把兩個操作數相乘 A * B 將得到 200
/ 分子除以分母 B / A 將得到 2
% 取模運算符,整除后的余數 B % A 將得到 0
++ 自增運算符,整數值增加 1 A++ 將得到 11
-- 自減運算符,整數值減少 1 A-- 將得到 9
#include <stdio.h>

int main()
{
	int number_1 = 1;
	int number_2 = 2;

	int number_3 = number_1 + number_2;
	
	printf("sum = %d \n", number_3);

	return 0;
}

關於前++和后++的區別

  • i++是先用i,然后在讓i+1,然后++i是先對i+1,然后在去使用i。
  • 前加: ++a 先運算,再賦值;
  • 后加: a++ 先賦值,再運算;
  • 給一個例子:
#include <stdio.h>

int main()
{
	int a = 0;
	int b = 0;
	int c = ++a;
	int d = b++;
	
	printf("前加加 = %d , 后加加 = %d\n", c,d);

	return 0;
}

3.2 賦值運算符

運算符 描述 實例
= 簡單的賦值運算符,把右邊操作數的值賦給左邊操作數 C = A + B 將把 A + B 的值賦給 C
+= 加且賦值運算符,把右邊操作數加上左邊操作數的結果賦值給左邊操作數 C += A 相當於 C = C + A
-= 減且賦值運算符,把左邊操作數減去右邊操作數的結果賦值給左邊操作數 C -= A 相當於 C = C - A
*= 乘且賦值運算符,把右邊操作數乘以左邊操作數的結果賦值給左邊操作數 C *= A 相當於 C = C * A
/= 除且賦值運算符,把左邊操作數除以右邊操作數的結果賦值給左邊操作數 C /= A 相當於 C = C / A
%= 求模且賦值運算符,求兩個操作數的模賦值給左邊操作數 C %= A 相當於 C = C % A
<<= 左移且賦值運算符 C <<= 2 等同於 C = C << 2
>>= 右移且賦值運算符 C >>= 2 等同於 C = C >> 2
&= 按位與且賦值運算符 C &= 2 等同於 C = C & 2
^= 按位異或且賦值運算符 C ^= 2 等同於 C = C ^ 2
|= 按位或且賦值運算符 C |= 2 等同於 C = C | 2

3.3 位運算符

運算符 描述 實例
& 按位與操作,按二進制位進行"與"運算。運算規則:0&0=0; 0&1=0; 1&0=0; 1&1=1; (A & B) 將得到 12,即為 0000 1100
| 按位或運算符,按二進制位進行"或"運算。運算規則:0|0=0; 0|1=1; 1|0=1; 1|1=1; (A | B) 將得到 61,即為 0011 1101
^ 異或運算符,按二進制位進行"異或"運算。運算規則:0^0=0; 0^1=1; 1^0=1; 1^1=0; (A ^ B) 將得到 49,即為 0011 0001
~ 取反運算符,按二進制位進行"取反"運算。運算規則:~1=-2; ~0=1; (~A ) 將得到 -61,即為 1100 0011,一個有符號二進制數的補碼形式。
<< 二進制左移運算符。將一個運算對象的各二進制位全部左移若干位(左邊的二進制位丟棄,右邊補0)。 A << 2 將得到 240,即為 1111 0000
>> 二進制右移運算符。將一個數的各二進制位全部右移若干位,正數左補0,負數左補1,右邊丟棄。 A >> 2 將得到 15,即為 0000 1111

3.4 C 中的運算符優先級

類別 運算符 結合性
后綴 () [] -> . ++ - - 從左到右
一元 + - ! ~ ++ - - (type)* & sizeof 從右到左
乘除 * / % 從左到右
加減 + - 從左到右
移位 << >> 從左到右
關系 < <= > >= 從左到右
相等 == != 從左到右
位與 AND & 從左到右
位異或 XOR ^ 從左到右
位或 OR | 從左到右
邏輯與 AND && 從左到右
邏輯或 OR || 從左到右
條件 ?: 從右到左
賦值 = += -= *= /= %=>>= <<= &= ^= |= 從右到左
逗號 , 從左到右
#include <stdio.h>

int main()
{
	int a = 20;
	int b = 10;
	int c = 15;
	int d = 5;
	int e;

	e = (a + b) * c / d;      // ( 30 * 15 ) / 5
	printf("e = %d\n", e);

	e = ((a + b) * c) / d;    // (30 * 15 ) / 5
	printf("e = %d\n", e);

	e = (a + b) * (c / d);   // (30) * (15/5)
	printf("e = %d\n", e);

	e = a + (b * c) / d;     //  20 + (150/5)
	printf("e = %d\n", e);

	return 0;
}

本章練習:運算符

第四章 判斷結構

4.1 if

#include <stdio.h>

int main()
{
	int age;
	printf("請輸入你的年齡:");
	scanf("%d", &age);
	if (age >= 18) {
		// 符合上括號的條件,執行此處代碼。
		printf("成年了!");
	}
	
	return 0;
}

關系運算符:

  • 假設變量 A 的值為 10,變量 B 的值為 20,則:
運算符 描述 實例
== 檢查兩個操作數的值是否相等,如果相等則條件為真。 (A == B) 為假。
!= 檢查兩個操作數的值是否相等,如果不相等則條件為真。 (A != B) 為真。
> 檢查左操作數的值是否大於右操作數的值,如果是則條件為真。 (A > B) 為假。
< 檢查左操作數的值是否小於右操作數的值,如果是則條件為真。 (A < B) 為真。
>= 檢查左操作數的值是否大於或等於右操作數的值,如果是則條件為真。 (A >= B) 為假。
<= 檢查左操作數的值是否小於或等於右操作數的值,如果是則條件為真。 (A <= B) 為真。
#include <stdio.h>

int main()
{
	int a = 10;
	int b = 20;
	if (a==b) {
		// 符合上括號的條件,執行此處代碼。
		printf("a與b相等!");
	}
	
	return 0;
}

邏輯運算符:

  • 假設變量 A 的值為 1,變量 B 的值為 0
運算符 描述 實例
&& 稱為邏輯與運算符。如果兩個操作數都非零,則條件為真。 (A && B) 為假。
|| 稱為邏輯或運算符。如果兩個操作數中有任意一個非零,則條件為真。 (A || B) 為真。
! 稱為邏輯非運算符。用來逆轉操作數的邏輯狀態。如果條件為真則邏輯非運算符將使其為假。 !(A && B) 為真。
#include <stdio.h>

int main()
{
	int a = 1;
	int b = 0;
	if (a&&b) {
		// 符合上括號的條件,執行此處代碼。
		printf("條件為真!");
	}
	
	return 0;
}

4.2 if…else…

C 語言提供了以下類型的判斷語句:

語句 描述
if 語句 一個 if 語句 由一個布爾表達式后跟一個或多個語句組成。
if...else 語句 一個 if 語句 后可跟一個可選的 else 語句,else 語句在布爾表達式為假時執行。
嵌套 if 語句 您可以在一個 ifelse if 語句內使用另一個 ifelse if 語句。
switch 語句 一個 switch 語句允許測試一個變量等於多個值時的情況。
嵌套 switch 語句 您可以在一個 switch 語句內使用另一個 switch 語句。
#include <stdio.h>

int main()
{
	int age;
	int math_score;

	printf("請輸入你的年齡: \n");
	scanf("%d", &age);

	printf("請輸入你的數學成績: \n");
	scanf("%d", &math_score);

	if (age >= 18 && math_score >= 90) {
		printf("成年了,可以玩游戲!");
	}
	else {
		printf("未成年人,禁止玩游戲!!!");
	}
	
	return 0;
}

4.3 if…else if…

#include <stdio.h>

int main()
{
	int age = 90;

	if (age >= 18) {
		printf("滿足成年了! \n");
	}
	else if (age >= 30) {
		printf("滿足結婚年齡!\n");
	}
	else if (age >= 70) {
		printf("滿足養老年齡!\n");
	}
	else if (age >= 100) {
		printf("長壽者!\n");
	}
	else {
		printf("再見!\n");
	}
	return 0;
}

4.4 ? : 運算符(三元運算符)

  • 格式:
Exp1 ? Exp2 : Exp3;
// Exp1、Exp2 和 Exp3 是表達式。請注意,冒號的使用和位置。
// 如果Exp1符合,則執行Exp2;反之,執行Exp3
#include <stdio.h>

int main()
{
	int num;
	printf("輸入一個數字: ");
	scanf("%d", &num);
	(num % 2 == 0) ? printf("偶數 \n") : printf("奇數 \n");

	return 0;
}

4.5 switch

一個 switch 語句允許測試一個變量等於多個值時的情況。每個值稱為一個 case,且被測試的變量會對每個 switch case 進行檢查。

#include <stdio.h>

int main()
{
	char grade;
	printf("請輸入成績字母:");
	scanf("%c", &grade);
	switch (grade)
	{
	case 'A':
		printf("很棒!\n");
		break;
	case 'B':
	case 'C':
		printf("做得好!\n");
		break;
	case 'D':
		printf("您通過了!\n");
		break;
	case 'E':
		printf("最好再試一下\n");
		break;
	default:
		printf("無效的成績\n");
	}
	return 0;
}

附:ASCII碼對照表

  • 空格的ASCII碼值為0;
  • ‘0’的ASCII碼值為48;
  • ‘A’的ASCII碼值為65;
  • ‘a’的ASCII碼值為97;
  • 大小寫字母的ASCII碼值相差32。
ASCII值 控制字符 ASCII值 控制字符 ASCII值 控制字符 ASCII值 控制字符
0 NUT 32 (space) 64 @ 96
1 SOH 33 ! 65 A 97 a
2 STX 34 " 66 B 98 b
3 ETX 35 # 67 C 99 c
4 EOT 36 $ 68 D 100 d
5 ENQ 37 % 69 E 101 e
6 ACK 38 & 70 F 102 f
7 BEL 39 , 71 G 103 g
8 BS 40 ( 72 H 104 h
9 HT 41 ) 73 I 105 i
10 LF 42 * 74 J 106 j
11 VT 43 + 75 K 107 k
12 FF 44 , 76 L 108 l
13 CR 45 - 77 M 109 m
14 SO 46 . 78 N 110 n
15 SI 47 / 79 O 111 o
16 DLE 48 0 80 P 112 p
17 DCI 49 1 81 Q 113 q
18 DC2 50 2 82 R 114 r
19 DC3 51 3 83 S 115 s
20 DC4 52 4 84 T 116 t
21 NAK 53 5 85 U 117 u
22 SYN 54 6 86 V 118 v
23 TB 55 7 87 W 119 w
24 CAN 56 8 88 X 120 x
25 EM 57 9 89 Y 121 y
26 SUB 58 : 90 Z 122 z
27 ESC 59 ; 91 [ 123 {
28 FS 60 < 92 / 124 |
29 GS 61 = 93 ] 125 }
30 RS 62 > 94 ^ 126 `
31 US 63 ? 95 _ 127 DEL

本章練習:判斷

第五章 循環結構

5.1 while循環

  • 只要給定的條件為真,C 語言中的 while 循環語句會重復執行一個目標語句。
  • while的格式如下:
while(條件A){
    ...
}
  • 只要條件A成立,就反復執行省略號的內容。
  • 如果不加大括號,則while循環只作用於while后的第一個完整語塊(while大括號后到第一個分號之間的語句)。
#include<stdio.h>
#define ONE_KILOMETER 1000
int main()
{
	int run_meter = 0;
	while (run_meter <= ONE_KILOMETER) {
		printf("runing...%d \n", run_meter);
		run_meter += 1;
	}
	printf("我他媽終於跑完了!!!\n");
	return 0;
}
  • 死循環
#include<stdio.h>
#define ONE_KILOMETER 1000
int main()
{
	int run_meter = 0;
	while (run_meter <= ONE_KILOMETER) {
		printf("runing...%d \n", run_meter);
	}
	printf("我他媽終於跑完了!!!\n");
	return 0;
}

5.2 break 和 continue 語句

C 語言中 break 語句有以下兩種用法:

  1. break 語句出現在一個循環內時,循環會立即終止,且程序流將繼續執行緊接着循環的下一條語句。
  2. 它可用於終止 switch 語句中的一個 case。
  • break 語句的語法:
break;
#include<stdio.h>
#define GOLD 1000
int main()
{
	int rush = 1;
	while (rush <= GOLD) {
		if (rush == 500) {
			printf("挖到鐵礦!!!扔掉不要!\n");
			rush++;
			break;
		}

		printf("rush = %d \n", rush);
		rush++;
	}
	printf("挖完了!!\n");
	return 0;
}
  • 注:一個break只能跳出一層循環

C 語言中的 continue 語句有點像 break 語句。但它不是強制終止,continue 會跳過當前循環中的代碼,強迫開始下一次循環。

  • continue 語句的語法:
continue;
#include<stdio.h>

int main()
{
	int rush = 1;
	while (rush <= GOLD) {
		if (rush == 500) {
			printf("挖到鐵礦!!!扔掉不要!\n");
			rush++;
			/*
			滿足一定要求的時候,如果使用continue語句,
			就代表:我不要了,滿足條件中的內容。
			---> 但是我還是要繼續干,還是要繼續執行循環,
			但是要注意,一定要在continue之前rush++
			(注意不要遺漏自增變量)
			*/
			continue;
		}

		printf("rush = %d \n", rush);
		rush++;
	}
	printf("挖完了!!\n");
	return 0;
}

5.3 do…while 語句

與while類似。

  • do...while 循環的語法:
do
{
   ...
       
}while( 條件A );
  • do...while語句會先執行省略號的內容一次,然后才判斷條件A是否成立。如果成立,繼續執行省略號的內容,直到某一次條件A不在成立,則退出循環。
#include<stdio.h>

int main()
{
	int rush = 1;
	do {
		/*
		先執行一次循環,后判斷,判斷結果為true,繼續執行do中的代碼塊。
		*/
		rush++;
		printf("rush = %d \n", rush);

	} while (rush <= 1000); // 注意結尾,有分號!

	return 0;
}

5.4 for 語句

  • for 循環的一般形式為:
for(表達式1; 表達式2; 表達式3){
  語句塊
}
  • 它的運行過程為:
    • 先執行“表達式1”。
    • 再執行“表達式2”,如果它的值為真(非0),則執行循環體,否則結束循環。
    • 執行完循環體后再執行“表達式3”。
    • 重復執行步驟 2) 和 3),直到“表達式2”的值為假,就結束循環。
  • 上面的步驟中,2) 和 3) 是一次循環,會重復執行,for 語句的主要作用就是不斷執行步驟 2) 和 3)。
  • “表達式1”僅在第一次循環時執行,以后都不會再執行,可以認為這是一個初始化語句。
  • “表達式2”一般是一個關系表達式,決定了是否還要繼續下次循環,稱為“循環條件”。
  • “表達式3”很多情況下是一個帶有自增或自減操作的表達式,以使循環條件逐漸變得“不成立”。
#include<stdio.h>
#define GOLD 1000
int main()
{
	int rush;  // 聲明一個變量rush

	// 1.初始化的變量
	// 2.判斷條件(滿足或不滿足)
	// 3.自增衡量變量
	
	/* 需注意,不是必選,但是如果沒有,可能會出問題,除非是業務要求。
	比如說死循環...可能就沒有上述三點,但是一個常用的循環,都應該
	具備上述三個要素,除非是特殊情況。 */

	for (rush = 1; rush < GOLD; rush++)
	{
		printf("rush = %d \n", rush);
	}
	printf("挖完了!\n");

	return 0;
}

for循環嵌套:

#include <stdio.h>

int main()
{
	int row, column;
	for (row = 1; row <= 9; row++) {
		for (column = 1; column <= 9; column++) {
			printf("%d*%d=%2d\t", row, column, row*column);
		}
		printf("\n");
	}
	return 0;
}

本章練習:循環

第六章 函數

函數是一組一起執行一個任務的語句。每個 C 程序都至少有一個函數,即主函數 main() ,所有簡單的程序都可以定義其他額外的函數。

  • C 語言中的函數定義的一般形式如下:
返回類型 函數名稱(參數類型 參數)
{
	說明語句 /*函數體*/ 
	執行語句 
}
  • 案例
#include <stdio.h>

// 聲明函數
void sum(int number_1, int number_2);

// 函數名: sum
// 函數的返回值類型: void
// 函數的參數: 有兩個參數,參數的類型均為int

// main 主函數,整個程序的入口
int main()
{
	sum(95, 63); // 調用函數
	sum(5, 6); // 調用函數
	return 0;
}

// 定義函數
// 大括號內為函數體
void sum(int number_1, int number_2) {
	printf("sum = %d \n", number_1 + number_2);
}
  • 案例2
#include <stdio.h>

// main 主函數,整個程序的入口

// 聲明函數
// 形參,形式參數,可理解為無實際意義的參數
int sum(int number_1, int number_2); //此處的number_1,number_2即為形參

// 函數名: sum
// 函數的返回值類型: int
// 函數的參數: 有兩個參數,參數的類型均為int

int main()
{
    // 實參,可理解為具體的值
	int result = sum(95, 63); // 調用函數,此處的95,63即為實參
	printf("%d \n", result);
	return 0;
}

// 定義函數
// 大括號內為函數體
int sum(int number_1, int number_2) {
	int score_sum = number_1 + number_2;

	return score_sum;
}

形參與實參的區別:

參數 位置 使用范圍 傳遞方向
形參 函數定義時括號中的參數 函數體內 形參不可以傳遞給實參
實參 調用時函數是括號中的參數 主調函數 實參可以傳遞給形參

以下為專業說法(了解即可):

  • 形參變量只有在函數被調用時才會分配內存,調用結束后,立刻釋放內存,所以形參變量只有在函數內部有效,不能在函數外部使用。
  • 實參可以是常量、變量、表達式、函數等,無論實參是何種類型的數據,在進行函數調用時,它們都必須有確定的值,以便把這些值傳送給形參,所以應該提前用賦值、輸入等辦法使實參獲得確定值。
  • 實參和形參在數量上、類型上、順序上必須嚴格一致,否則會發生“類型不匹配”的錯誤。當然,如果能夠進行自動類型轉換,或者進行了強制類型轉換,那么實參類型也可以不同於形參類型。
  • 函數調用中發生的數據傳遞是單向的,只能把實參的值傳遞給形參,而不能把形參的值反向地傳遞給實參;換句話說,一旦完成數據的傳遞,實參和形參就再也沒有瓜葛了,所以,在函數調用過程中,形參的值發生改變並不會影響實參。

作用域:

  • 任何一種編程中,作用域是程序中定義的變量所存在的區域,超過該區域變量就不能被訪問。

C 語言中有三個地方可以聲明變量:

  1. 在函數或塊內部的局部變量
  2. 在所有函數外部的全局變量
  3. 形式參數的函數參數定義中

  • 在某個函數或塊的內部聲明的變量稱為局部變量。它們只能被該函數或該代碼塊內部的語句使用。局部變量在函數外部是不可知的。
  • 全局變量是定義在函數外部,通常是在程序的頂部。
    • 全局變量在整個程序生命周期內都是有效的,在任意的函數內部能訪問全局變量。
    • 全局變量可以被任何函數訪問。
    • 也就是說,全局變量在聲明后整個程序中都是可用的。
  • 具體案例如下:
#include <stdio.h>
// 函數 作用域
int sum(int number_1, int number_2);

int sun_flower = 100; // 聲明全局變量,在程序的任意函數內均可調用
// 不在任何一個函數中的變量,全局變量

int main()
{
	// 局部變量聲明 
	int sun, moon;

	sum(95, 63); // 此處的值,只是為了補充格式,進行函數調用

	printf("main_sun_flower = %d \n", sun_flower);
	return 0;
}

int sum(int number_1, int number_2) {
	int score_sum = number_1 + number_2;
	printf("main_sun_flower = %d \n", sun_flower);
	return score_sum;
}
  • 在程序中,局部變量和全局變量的名稱可以相同,但是在函數內,如果兩個名字相同,會使用局部變量值,全局變量不會被使用

本章練習:函數

第七章 數組

數組是把一系列相同數據類型的變量組合在一起的數據集合。

在這里插入圖片描述

7.1 一維數組

  • 一維數組的定義格式:
數據類型 數組名[數組大小];
  • 案例:
#include <stdio.h>
#define ARRAY_SIZE 5
int main()
{
	// 第一個下標(號)
	// 規定下標從0開始,0代表第一個元素,那第二個:2-1  第三元素:3-1  第四個元素的下標:4-1  N:n-1
	
	// 數據類型 數組名[數組大小];
	unsigned plants[ARRAY_SIZE];
	int age = 100;

	// 給下標為2,也就是第三個元素(下標+1=第幾個元素),賦給他值。
	// 數組名[下標] = 值;
	plants[2] = 99;

	// 數組元素的下標從0開始,規律:第N個元素的下標為N-1,下標 = N-1 
//	printf("plant---%u \n", plants[0]);

	// 遍歷數組
	for (int i = 0; i < 5; i++)
	{
		printf("plant---%u \n", plants[i]);
	}
	return 0;
}

7.2 二維數組

在這里插入圖片描述

#include <stdio.h>

int main()
{
	int score[4][6] = { {1,2,3},{4,5,6},{7,8,9},{52,6,75}};
	printf("score = %d \n", score[0][0]);
	printf("score = %d \n", score[1][2]);

	return 0;
}

  • 數組的表驅動法:一種編程模式,從表里面查找信息而不使用邏輯語句(if、case)。事實上,凡是能通過邏輯語句來選擇的事物,都可以通過查表來選擇。
  • 閏年判斷案例:
#include <stdio.h>
#include <stdbool.h>

int days_of_month(int month, int year);
bool is_leap(int year);

int main()
{
	// 表驅動法
	printf("總共 %d 天! \n", days_of_month(2,2016));

	return 0;
}

int days_of_month(int month,int year) 
{
	int is_leap_res = is_leap(year);

	int day_array[12] = {31,is_leap(year) ? 29 : 28 ,31,30,31,30,31,31,30,31,30,31};

	return day_array[month - 1];
}

// 自定義bool,C語言沒有bool類型
// 計算該年份是否為閏年
/**
* @params year 傳入一個年份,整型數據
* @return bool 如果為真,那么返回true,則為閏年,否則false平年
*/
bool is_leap(int year) {
	if (year % 400 == 0 || (year % 4 == 0 && year % 100 != 0))
	{
		return true;
	}
	else {
		return false;
	}
}

7.3 enum(枚舉)

  • 枚舉是 C 語言中的一種基本數據類型,它可以讓數據更簡潔,更易讀。
  • 枚舉語法定義格式為:
enum 枚舉名 {枚舉元素1,枚舉元素2,……};
#include <stdio.h>
#define PI 3.14
// 宏定義

int main()
{
	// 定義枚舉
	enum DAY
	{
		MON = 1, TUE = 2, WED = 3, TUH = 4, FRI = 5, SAT = 6, SUN = 7
	};

	// 使用枚舉
	enum DAY d = FRI;
	printf("%d", d);

	return 0;
}

本章練習:數組

第八章 指針

8.1 什么是指針?

指針就是存儲了另一個數據的內存地址的一種數據類型即指針中的數據就是另一個數據的內存地址

  • 指針就是內存地址,指針變量是用來存放內存地址的變量。
  • 就像其他變量或常量一樣,您必須在使用指針存儲其他變量地址之前,對其進行聲明。指針變量聲明的一般形式為:
type *var-name;

/* type 是指針的基類型,它必須是一個有效的 C 數據類型,var-name 是指針變量的名稱。
用來聲明指針的星號 * 與乘法中使用的星號是相同的。
但是,在這個語句中,星號是用來指定一個變量是指針。*/

int*    ip;    /* 一個整型的指針 */
double* dp;    /* 一個 double 型的指針 */
float*  fp;    /* 一個浮點型的指針 */
char*   ch;    /* 一個字符型的指針 */
  • 案例:
#include <stdio.h>

int main()
{
	int sun_flower = 100;
	int* p = &sun_flower;
	printf("sun_flower = %p \n", &sun_flower);
	// p輸出地址,p指針
	printf("p = %p \n", p);

	printf("拿到sun_flower的真實數據 : %d \n", sun_flower);
	printf("通過指針拿到sun_flower的值 : %d \n", *p);
	return 0;
}
  • 舉個例子,做個比較:
int a; // 定義一個變量a,用於保存一個int類型。

int* b; // 定義一個指針變量b,用於保存一個地址,這個地址所保存的數據應該是int類型。
  • 是變量就應該可以賦值,指針變量也一樣。但一般不會給指針直接賦值一個數值,而是將其他變量的地址賦值給指針,或者其他指針的值賦值給這個指針。
  • 繼續上面的例子:
b = &a; // 把變量a的地址賦值給b。“&”操作是取變量的地址。
  • 繼續舉例:
int* c; // 我們又定義一個指針c

c = b; // 將b的值賦值給c,上面已經知道b是指針,它的值是a的地址,那么現在c的值和b一樣,也是個a的地址。
  • 完整代碼:
#include <stdio.h>

int main()
{
	int a=10; // 定義一個變量a,用於保存一個int類型。
	int *b; // 定義一個指針變量b,用於保存一個地址,這個地址所保存的數據應該是int類型。

	b = &a;  // 把變量a的地址賦值給b。“&”操作是取變量的地址。

	int *c; // 我們又定義一個指針c
	c = b; // 將b的值賦值給c,上面已經知道b是指針,它的值是a的地址,那么現在c的值和b一樣,也是個a的地址。

	printf("b=%d,c=%d \n", b,c); // b和c相同嗎?

	return 0;
}
  • 指針變量保存的值,也就是地址是可以變的。

  • 舉個數組初始化的例子:
#include <stdio.h>

int main()
{
	int d[100];
	int *e;

	e = &d[0]; // e保存了數組d的第一個數據的地址

	for (int i = 0; i < 100; i++) {
		*e = 0; // 把該地址中的數據賦值0
		e++; // 地址累加一次,也就是數組中下一個數據的地址
		printf("e=%d \n", e);
	}

	return 0;
}
  • 指針和其他變量一樣,可以運算(一般是加減),也可以重新賦值。

  • 說了這么多,指針有啥用?
  • 比方說,我們有個函數,如下:
int add(int x){
    return (x+1); // 把輸入的值加1並返回結果。
}
  • 好了,應用的時候是這樣的:
{
    int a=1;
    a=add(a); // add函數返回的是a+1
	// 現在 a等於2
}
  • 完整代碼:
#include <stdio.h>
int add(int x);

int main()
{
	int a = 1;
	a = add(a); // add函數返回的是a+1
	// 現在a等於2
	printf("a=%d \n", a);
	return 0;
}

int add(int x) {
	return (x + 1); // 把輸入的值加1並返回結果。
}
  • 很簡單吧,就是把a都累加一次。

  • 用指針怎么寫:
void add(int *y){ // 給入的是一個int指針,是一個地址。
    *y = *y + 1; // *是指"引用",這個地址所保存的變量
	// 這條語句的意思就是,把這個地址里的值加1,然后放回這個地址。
}
  • 把這個函數用起來:
{
    int a=1;
	add(&a); // 把a的地址傳到函數里
	// add函數,就是把a的值加1,再放回到變量a里。
	// 現在a等於2
}
  • 完整代碼:
#include <stdio.h>
int add(int x);

int main()
{
	int a = 1;
	a = add(a); // add函數返回的是a+1
	// 現在a等於2
	printf("a=%d \n", a);
	return 0;
}

int add(int x) {
	return (x + 1); // 把輸入的值加1並返回結果。
}
  • 自行測試如下:
#include <stdio.h>
// %p指的是內存地址類型(打印出的是16進制數據)
int main()
{
	int a = 1;
	int* p = &a;
	printf("%p \n", &a);  //①
	printf("%p \n", p);   //②
	printf("%p \n", &p);   //③
	return 0;
}

8.2 C 中的 NULL 指針

  • 賦為 NULL 值的指針被稱為指針。
#include <stdio.h>

int main()
{
	int* ptr = NULL; //空指針
	printf("*ptr = %p \n", ptr);

	return 0;
}
  • 野指針
#include <stdio.h>

int main()
{
	int* ptr = NULL; //空指針
	printf("*ptr = %p \n", ptr);

	int* p; // 野指針
	// 此處程序會隨機分配地址,防止空指針產生異常!
	// 無意義指向
	printf("p = %d \n", *p);
	return 0;
}

8.3 指針數組與字符串

  • 字符串實際上是使用 null 字符 \0 終止的一維字符數組。
  • 一個以 null 結尾的字符串,包含了組成字符串的字符。
char site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'}; // 聲明和初始化創建了一個 RUNOOB 字符串
  • 依據數組初始化規則,您可以把上面的語句寫成以下語句:
char site[] = "RUNOOB";
  • 案例:
#include <stdio.h>

int main()
{
	// 指針數組
	int* p_arry[3] = { NULL,NULL,NULL };

	// 可存儲多個地址

//	char greeting[6] = { 'H','e','l','l','o','\0' };
	char greeting[] = "Hello";
	for (int i = 0; i < 6; i++)
	{
		printf("%c", greeting[i]);
	}

	return 0;
}

8.4 多級指針

  • 多級指針就是指針的指針的指針。
  • 指向指針的指針是一種多級間接尋址的形式,或者說是一個指針鏈。
  • 通常,一個指針包含一個變量的地址。當我們定義一個指向指針的指針時,第一個指針包含了第二個指針的地址,第二個指針指向包含實際值的位置。
  • 一個指向指針的指針變量必須如下聲明,即在變量名前放置兩個星號。例如,下面聲明了一個指向 int 類型指針的指針:
int** var;
  • 案例:
#include <stdio.h>

int main()
{
	// 多級指針
	int number = 100;
	int* p_one = &number; // 獲取 number 的地址

	int** p_two = &p_one; // 使用運算符 & 獲取 p_one 的地址

	printf("number = %d \n", number);
	printf("p_one = %p \n", p_one);
	printf("*p_one = %d \n", *p_one);
	printf("p_two = %p \n", p_two);
	printf("**p_two = %d \n", **p_two);

	return 0;
}

8.5 指針作為函數返回值

  • C語言允許函數的返回值是一個指針(地址),我們將這樣的函數稱為指針函數。
  • 案例: 定義了一個函數 strlong(),用來返回兩個字符串中較長的一個。
#include <stdio.h>
#include <string.h>

// 返回長度最長的字符串
char *strlong(char *str1, char *str2) {
	printf("str1 address = %p \n", &str1);
	printf("str2 address = %p \n", &str2);
	if (strlen(str1) >= strlen(str2)) {
		return str1;
	}
	else {
		return str2;
	}
}
int main() {
	char str1[30], str2[30], *str;
	gets(str1);
	gets(str2);
	str = strlong(str1, str2);
	printf("str address = %p \n", &str);
	printf("Longer string: %s\n", str);
	return 0;
}

第九章 結構體和共用體

9.1 結構體

  • 類型定義(Typedef)
    • 在 C 語言中,可以使用typedef用來定義數據類型取新的名字。
    • typedef 語句的一般形式是:
typedef 已定義的類型 新的類型;

// 例如
typedef int INTEGER; // 指定用 INTEGER 代表 int 類型
typedef float REAL; // 指定用 REAL 代表 float 類型
  • 案例
#include <stdio.h>

int main()
{
	typedef int MYINT;	// 自定義int類型MYINT
	MYINT a = 0;
	printf("%d \n", a);

	typedef char MYCHAR;	// 自定義char類型MYCHAR
	MYCHAR ch = 'a';
	printf("%c\n", ch);
	return 0;
}
  • 結構體的定義。
    • 先看個例子:
#include <stdio.h>

int main()
{
	struct { int x; int y;}point; // 結構體變量
	point.x = 10;
	point.y = 11;
	printf("%d %d\n", point.x,point.y);
	return 0;
}
  • 給上面的結構體類型換名。
#include <stdio.h>

int main()
{
	typedef struct { int x; int y;} Point; // 結構體變量
	Point point;	// 從Point替換為point
	point.x = 10;
	point.y = 11;
	printf("%d %d\n", point.x,point.y);
	return 0;
}
  • 為了在所有的函數中使用,將結構體定義在main函數外面。
#include <stdio.h>
typedef struct // 結構體變量
{ 
	int x; 
	int y; 
} Point;

int main()
{
	Point point;	// 調用結構體
	point.x = 10;
	point.y = 11;
	printf("%d %d\n", point.x,point.y);
	return 0;
}
  • 另一種中定義格式:
#include <stdio.h>
struct Point	// 結構體變量
{ 
	int x; 
	int y; 
};

int main()
{
	struct Point point;	 // 調用結構體
	point.x = 10;
	point.y = 11;
	printf("%d %d\n", point.x,point.y);
	return 0;
}
  • 指向結構體的指針
#include <stdio.h>
typedef struct 
{ 
	int x; 
	int y; 
}Point;

int main()
{
	Point point;
	Point* p;
	p = &point;
	p->x = 10;
	p->y = 11;
	printf("%d %d\n", p->x,p->y);
	return 0;
}
  • 自引用結構指針
    • 在結構體內部定義自引用類型的指針。
#include <stdio.h>
typedef struct Point
{ 
	int x; 
	int y; 
	struct Point* next;	// 自引用類型的指針
}Point;

int main()
{
	Point p1,p2,p3,p4,p5;
	Point* p;
	p1.x = 1; p1.y = 7;
	p2.x = 4; p2.y = 3;
	p3.x = 2; p3.y = 4;
	p4.x = 3; p4.y = 2;
	p5.x = 1; p5.y = 6;

	p1.next = &p2;
	p2.next = &p3;
	p3.next = &p4;
	p4.next = &p5;
	p5.next = NULL;

	for(p=&p1;p!=NULL;p=p->next)
		printf("(%d, %d)\n", p->x,p->y);

	return 0;
}
  • 結構體定義的基本格式:
// 格式①
typedef struct // 結構體變量
{ 
	int x; 
	int y; 
} Point;

// 格式②
struct Point	// 結構體變量
{ 
	int x; 
	int y; 
};

// 格式③
typedef struct Point
{ 
	int x; 
	int y; 
	struct Point* next;	// 自引用類型的指針
}Point;
// 格式③在C/C++中均可使用

9.2 共用體

  • 在 C 語言中,允許幾種不同類型的變量存放到同一段內存單元中。即覆蓋技術:幾個變量互相覆蓋。
  • 這種幾個不同的變量共同占用一段內存的結構,被稱為共用體類型結構,簡稱共用體
  • 一般定義形式為:
union 共用體名 
{ 
    數據類型 成員名 1; 
    數據類型 成員名 2; 
    ...... 
    數據類型 成員名 n; 
}變量名表列; 
  • 必須先定義共用體變量,才能在后續的程序中引用它。不能直接引用共用體變量,而只能引用共用體變量中的成員。
  • 引用方法如下:
共用體變量名.成員名  
  • 案例
#include<stdio.h>
union DATA
{
	int i;
	float f;
	char str[10];
};
int main()
{
	union DATA d;
	d.i = 10;
	d.f = 2.3;
	strcpy(d.str, "C 語言");
	printf("d.i:%d \n", d.i);
	printf("d.f:%f \n", d.f);
	printf("d.str:%s \n", d.str);
	return 0;
}

本章練習:結構體

第十章 文件

此處參考於:C語言文件

  • 對於文件的操作分為三個步驟:
    • 第一步:打開文件;
    • 第二步:讀寫文件;
    • 第三步:關閉文件。

10.1 文件與文件指針

  • 文件(file):存儲在外部介質上的數據集合
  • 編寫C語言程序,主要通過鍵盤輸入給變量賦值,通過顯示器顯示輸出數據。如果我們想讀取磁盤已有的數據,需要讀取磁盤文件,同時也可以將數據保存到文件中,進行永久存儲。
  • 文件的3種分類方法如下:
    • ①按數據存放形式分為 ASCII 文件(文本文件)和二進制文件;
    • ②按存取方式分為順序方式存取和隨機方式存取;
    • ③按處理方式分為緩沖文件和非緩沖文作。

  • 文件類型指針
    • 每個被使用的文件都會在內存中開辟一個相應的文件信息區,用來存放文件的相關信息(比如文件的名字,文件讀寫方式等),這些信息都保存在一個結構體變量中,而這個結構體類型是有系統聲明的,不需要我們自己去定義
    • 這個結構體由系統聲明為FILE,包含在頭文件“stdio.h”中。其內部具體文件類型聲明:
struct _iobuf{
	char* _ptr;
	int   _cnt;
	char* _base;
	int   _flag;
	int   _file;
	int   _charbuf;
	int   _bufsiz;
	char* _tmpfname;
};
typedef struct _iobuf FILE;
  • 對於不同C編譯器的FILE的類型包含的內容不完全相同,但是都大同小異。
  • 每當我們通過程序去打開文件的時候,系統會根據文件的情況自動創建一個FILE結構的變量,並填充其中的信息,我們可以不用關注具體細節
  • 在程序中直接利用 FILE 定義變量:

其實對於不同C編譯器的FILE的類型包含的內容不完全相同,但是都大同小異.
每當我們通過程序去打開文件的時候,系統會根據文件的情況自動創建一個FILE結構的變量,並填充其中的信息,我們可以不用關注具體細節.

FILE* pf;	// 創建了一個文件指針變量(FILE*類型)

定義pf是一個指向FILE類型數據的指針變量,可以使pf指向某個文件的文件信息區(是一個結構體變量),通過該文件信息區中的信息就能訪問該文件,其實也就是說,通過文件指針變量能夠找到與它關聯的文件。

定義pf是一個指向FILE類型數據的指針變量,可以使pf指向某個文件的文件信息區(是一個結構體變量),通過該文件信息區中的信息就能訪問該文件,其實也就是說,通過文件指針變量能夠找到與它關聯的文件.

10.2 文件的打開和關閉

  • 打開文件: fopen( ) 函數

    • 創建一個新的文件或者打開一個已有的文件。
  • 函數原型為:

`FILE` `*``fopen``(``const` `char` `*filename, ``const` `char` `*mode);`
  • filename 是字符串,用來命名文件,訪問模式 mode 的值可以是下列值中的一個:
參數 作用
r 以只讀方式打開文件,該文件必須存在。
r+ 以讀/寫方式打開文件,該文件必須存在。
rb+ 以讀/寫方式打開一個二進制文件,只允許讀/寫數據。
rt+ 以讀/寫方式打開一個文本文件,允許讀和寫。
w 打開只寫文件,若文件存在則文件長度清為零,即該文件內容會消失;若文件不存在則創建該文件。
w+ 打開可讀/寫文件,若文件存在則文件長度清為零,即該文件內容會消失;若文件不存在則創建該文件。
a 以附加的方式打開只寫文件。若文件不存在,則會創建該文件;如果文件存在,則寫入的數據會被加到文件尾后,即文件原先的內容會被保留(EOF 符保留)。
a+ 以附加方式打開可讀/寫的文件。若文件不存在,則會創建該文件,如果文件存在,則寫入的數據會被加到文件尾后,即文件原先的內容會被保留(EOF符不保留)。
wb 以只寫方式打開或新建一個二進制文件,只允許寫數據。
wb+ 以讀/寫方式打開或新建一個二進制文件,允許讀和寫。
wt+ 以讀/寫方式打開或新建一個文本文件,允許讀和寫。
at+ 以讀/寫方式打開一個文本文件,允許讀或在文本末追加數據。
ab+ 以讀/寫方式打開一個二進制文件,允許讀或在文件末追加數據。
// 例如:
FILE* fp;
fp=fopen(d:\\whh.txt","r");
  • 注意:
    • 該文件的目錄是絕對路徑,因此這樣寫,如果不寫盤符比如 whh.txt 則表示相對路徑,表示與本程序同目錄下。
    • 路徑中的反斜杠雖然只有一個,但這里打了兩個,原因在於C語言字符串中對反斜杠要當作轉義字符處理,因此要用兩個反斜杠才能表示一個。
    • 一旦以r也就是只讀的方式打開文件,后面則不允許寫數據,否則會出錯,一定要保持一致!

  • 關閉文件:fclose( )函數
    • 斷開程序與文件關聯,切斷IO數據流,釋放文件不在占用。
  • 函數調用方式:fclose(fp);
int fclose( FILE *fp );
  • 如果成功關閉文件,fclose( ) 函數返回零,如果關閉文件時發生錯誤,函數返回 EOF

10.3 讀寫文件

  • 字符 讀/寫 函數 fgetc()和fputc()

    • 向文本讀寫一個字符。
  • fgetc()函數的功能是從指定的文件中讀取一個字符,其調用的格式為:

字符變量 = fgetc (文件指針);

如果在執行fgetc()函數時遇到文件結束符,函數會返回一個文件結束符標志EOF(-1)。

  • fputc()函數的功能是把一個字符寫入指定的文件中,其調用的格式為:
fput(字符,文件指針);
  • 案例:
#include <stdio.h>
#include <stdlib.h>
 
int main()
{
	FILE* fp1, *fp2;
	errno_t err;
	char c;
 
	err = fopen_s(&fp1, "d:\\1.txt", "w");
	if (err != 0) {
		printf("文件打開失敗!\n");
		exit(0);
	}
	c = getchar();
	while (c != '\n') {
		fputc(c, fp1);
		c = getchar();
	}
	fclose(fp1);
 
	err = fopen_s(&fp2, "d:\\1.txt", "r");
	if (err != 0) {
		printf("文件打開失敗!\n");
		exit(0);
	}
	c = fgetc(fp2);
	while (c != EOF) {
		printf("%c", c);
		c = fgetc(fp2);
	}
	printf("\n");
	fclose(fp2);
 
	return 0;
}

  • 字符串讀/寫函數fgets()和fputs()
  • fgets()函數的功能是從指定的文件中讀取一個字符串,其調用的格式為:
fgets(字符數組名,n,文件指針);
  • 其中,n是一個正整數,表示從文件中讀出的字符串不超過n-1個字符。在讀入一個字符串后加上字符串結束標志'\0'。

  • 如果在執行fgets()函數時如果文件內的字符串讀取c完畢,函數會返回0。

  • fputs()函數的功能是把一個字符串寫入指定的文件中,其調用的格式為:

fputs(字符串,文件指針);
  • 其中,字符串可以是字符串常量、字符數組、字符指針變量。
  • 案例:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
 
int main()
{
	FILE* fp1, *fp2;
	errno_t err;
	char s[30];
 
	err = fopen_s(&fp1,"d:\\1.txt", "w");
	if (err != 0) {
		printf("文件打開失敗!\n");
		exit(0);
	}
	gets(s);
	while (strlen(s) > 0) {
		fputs(s, fp1);
		gets(s);
	}
	fclose(fp1);
 
	err = fopen_s(&fp2, "d:\\1.txt", "r");
	if (err != 0) {
		printf("文件打開失敗!\n");
		exit(0);
	}
	while (fgets(s, 11, fp2) != 0) {
		printf("%s", s);
	}
	printf("\n");
	fclose(fp2);
 
	return 0;
}

  • 數據塊讀/寫函數fread()和fwrite()
  • fread()函數的功能是從文件中讀取字節長度為size的n個數據,並存放到buf指向的內存地址中去
    • 函數的返回值為實際讀出的數據項個數。
    • 其調用的格式為:
fread(buf,size,n,文件指針);
  • 比如:
fread(fa,4,5,fp);

// 其意義是從fp所指向的文件中,每次讀4個字節長度(int)送入到fa指向的內存地址中去,連續讀5次。也就是說,讀5個int類型的數據到fa指向的內存中。
  • fread()函數的功能是將buf中存放的size*n個字節的數據輸出到文件指針所指向的文件中去。
    • 函數的返回值為實際寫入的數據項個數。
    • 其調用的格式為:
fwrite(buf,size,n,文件指針);

fread()和fwrite()函數一般適用於二進制文件,它們是按數據塊的大小來處理輸入/輸出的。


格式化讀/寫函數fscanf()和fprintf()

  • 格式化讀/寫函數與標准的格式輸入/輸出函數功能相同,只不過它們的讀/寫對象不是鍵盤和顯示器,而是文件。
  • fscanf()和fprintf()函數只適用於ASCII碼文件的讀/寫。
  • 兩個函數的格式如下:
fscanf(文件指針,格式字符串,輸入列表);
fprintf(文件指針,格式字符串,輸出列表);

fscanf()和fprintf()函數對文件進行讀/寫,使用方便,容易理解。但由於在輸入時需要將ASCII碼轉換為二進制格式,在輸出時又要將二進制格式轉換為字符,花費時間較長,所以在內存與磁盤交換數據頻繁的時候,最好不要用這兩個函數。

10.4 文件的隨機讀寫

在C語言中,打開文件時,文件指針指向文件頭,即文件的起始位置。在讀寫文件時,需要從文件頭開始,每次讀寫完一個數據后,文件指針會自動指向下一個數據的位置。但有時不想從文件頭開始讀取文件,而是讀取文件中某個位置的數據。這時,系統提供了定位到某個數據存儲位置的函數。

  • 文件頭定位函數rewind()
    • rewind()函數用於把文件指針移動到文件首部,其調用的格式為:
rewind(文件指針);
  • 當前讀/寫位置函數ftell()
    • ftell()函數用於確定文件指針的當前讀/寫位置
    • 此函數有返回值,若成功定位,則返回當前位置;否則返回-1。
    • 其調用的格式為:
ftell(文件指針);
  • 隨機定位函數fseek()
    • fseek()函數用於將文件指針移動到某個確定的位置
    • 此函數有返回值,若成功移動,則返回當前位置;否則返回-1。
    • 其調用的格式為:
fseek(文件指針,位移量,起始點);
  • 其中:位移量指從起始點向前移動的字節數,大多數C版本要求該位移量為long型數據;起始點有三種選擇,具體的含義見下表:

起始點取值含義

起始點 表示符號 數字表示
文件首 SEEK_SET 0
當前位置 SEEK_CUR 1
文件尾 SEEK_END 2
  • 例如,將指針位置移動到距離文件開頭100字節處:
fseek(fp,100L,0)

注意:fseek()函數一般用於二進制文件,因為文本文件計算位置往往比較混亂,容易發生錯誤。

  • 文件結束檢測函數feof()
    • feof()函數用於判斷文件是否處於文件結束為止
    • 該函數有返回值,如果文件結束,函數的返回值為1;否則返回值為0。
    • 其調用的格式為:
feof(文件指針);

第十一章 預處理指令

11.1 宏替換

  • C 預處理器不是編譯器的組成部分,但是它是編譯過程中一個單獨的步驟。即,C 預處理器只不過是一個文本替換工具而已,它們會指示編譯器在實際編譯之前完成所需的預處理
  • 通常將C 預處理器(C Preprocessor)簡寫為 CPP。
  • C 語言提供的預處理功能有三種,分別是:宏定義、文件包含和條件編譯
  • 所有的預處理器命令都是以井號(#)開頭。
    • 它必須是第一個非空字符,為了增強可讀性,預處理器指令應從第一列開始。
  • 在 C 語言中,宏分為有參數和無參數兩種。無參宏的宏名后不帶參數,其定義的一般形式為:
#define 標識符 字符串(或值);  

// 例如: 
#define PI 3.1415926
  • 在宏定義中的參數稱為形式參數,在宏調用中的參數稱為實際參數。對於帶參數的宏,在調用中,不僅要宏展開,而且要用實參去代換形參。
  • 帶參宏定義與宏調用的一般形式為:
#define 宏名(形參表) 字符串; 
// 在字符串中含有各個形參

// 宏調用
宏名(實參表);  
  • 例如:
#define M(y) y*y+3*y // 宏定義

......

k=M(5);	// 宏調用
  • 具體案例:
#include <stdio.h>
#define MAX(a,b) (a>b)?a:b	// 帶參數的宏定義
int main()
{
	int x, y, max;
	printf("請輸入兩個整數: ");
	scanf("%d %d", &x, &y);
	max = MAX(x, y);	// 宏調用
	printf("max=%d\n", max);
	return 0;
}

由此,可以知道宏替換相當於實現了一個函數調用的功能,而事實上,與函數調用相比,宏調用更能提高

11.2 文件包含

  • 文件包含命令行的一般形式為:
#include "文件名" 
// 或者 
#include <文件名> 
  • 文件包含命令的功能:在一個源文件中,將另一個文件包含進來。即,另一個文件是該文件的一部分。
  • 包含命令中的文件名可以用雙引號引起來,也可以用尖括號引起來。
  • 文件包含可以嵌套。

C語言頭文件<>和""的區別:

  • 頭文件#include <> :表示引用標准庫頭文件,編譯器會從系統配置的庫環境中去尋找。
  • 頭文件#include "":一般表示用戶自己定義使用的頭文件,編譯器默認會從當前文件夾中尋找,如果找不到,則到系統默認庫環境中去尋找。

11.3 條件編譯

  • 條件編譯:可以按不同的條件去編譯不同的程序部分。
  • 條件編譯的三種形式:
// 第一種形式: 
#ifdef 標識符 
程序段 1 
#else 
程序段 2 
#endif 
// 注:如果標識符已被 #define 命令定義過則對程序段 1 進行編譯;否則對程序段 2 進行編譯。     
// 第二種形式: 
#ifndef 標識符 
程序段 1 #else 
程序段 2 #endif 
// 注:如果標識符未被#define 命令定義過則對程 序段 1 進行編譯,否則對程序段 2 進行編譯。    
// 第三種形式: 
#if 常量表達式 
程序段 1 #else 
程序段 2 #endif 
// 注:如果常量表達式的值為真(非 0),則對程序段 1 進行編譯,否則對程序段 2 進行編譯。 
  • 所有重要的預處理器指令:
指令 描述
#define 定義宏
#include 包含一個源代碼文件
#undef 取消已定義的宏
#ifdef 如果宏已經定義,則返回真
#ifndef 如果宏沒有定義,則返回真
#if 如果給定條件為真,則編譯下面代碼
#else #if 的替代方案
#elif 如果前面的 #if 給定條件不為真,當前條件為真,則編譯下面代碼
#endif 結束一個 #if……#else 條件編譯塊
#error 當遇到標准錯誤時,輸出錯誤消息
#pragma 使用標准化方法,向編譯器發布特殊的命令到編譯器中

本章練習:預處理

🎉🎉🎉全劇終🎉🎉🎉

至此C語言入門就全部結束了。可能會有諸多不足,比如強制類型轉換、排序等未作代碼說明與文字注解。全文僅記錄個人C語言學習過程。另外,筆者還有個專升本備考交流Q群:1055741739。群資源全部免費,歡迎進群交流。但注意:不要發廣告!不要發廣告!不要發廣告!否則飛機票!

但對於計算機高級語言的學習仍不可止步於此。學編程最重要的是:敲代碼。不是去看,而是去敲。重要的事情說三遍:

敲代碼!敲代碼!敲代碼


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM