黑马程序员C语言基础(第五天)运算符与表达式、程序流程结构、数组和字符串、函数
https://www.bilibili.com/video/BV15W411K7k6?p=93&spm_id_from=pageDriver
黑馬程序員C語言基礎(第五天)運算符與表達式、程序流程結構、數組和字符串、函數
文章目錄
- 運算符與表達式
- 常用運算符分類
- 算術運算符
- 兩個數相除,要想得到小數,分子分母必須有一個是小數,否則結果只會取整(可以把其中一個數乘以1.0)
- 前自增和后自增區別
- 賦值運算符
- 比較運算符
- 邏輯運算符
- 邏輯優化示例
- 運算符優先級
- 類型轉換
- 隱式類型轉換
- 強制類型轉換
- 程序流程結構
- 概述
- 選擇結構
- if語句
- if…else語句
- if…else if…else語句
- 三目運算符
- switch語句(電梯模型)
- 循環結構
- while語句
- 調試
- QT的調試
- visual studio的調試
- do…while語句
- for語句
- 死循環
- 嵌套循環
- 跳轉語句break、continue、goto
- break語句
- continue語句
- goto語句(無條件跳轉,盡量少用)(注意:有時候去了就回不來了,但是有時候也可讓代碼精妙絕倫)
- 數組和字符串
- 概述
- 一維數組
- 一維數組的定義和使用
- 一維數組的初始化(初始化:定義的時候同時賦值)
- 數組名(數組名地址是數組首元素地址)
- 示例1:數組名是一個地址的常量,代表數組中`首元素`的地址
- 示例2:求數組的長度,同時遍歷每個數組元素(c語言沒有len函數,用數組大小除以每個數組元素大小得到數組長度 `int n = sizeof(a)/sizeof(a[0])`)
- 強化訓練
- 1) 一維數組的最值
- 2) 一維數組的逆置(如果數組元素個數為奇數,會多交換一次)
- 3) 冒泡法排序(每次把大的數字冒泡到右邊)
- 二維數組
- 二維數組的定義和使用
- 二維數組的初始化
- 示例,數組初始化自動推導(第一維度:`sizeof(a)/sizeof(a[0])` 第二維度:`sizeof(a[0])/sizeof(a[0][0])`)
- 數組名
- 強化訓練
- 多維數組(了解)
- 字符數組`char a[]`與字符串 `char a[, , , , , ... , '\0']`
- 字符數組與字符串區別
- 字符串的初始化
- 字符串的輸入輸出
- 強化訓練:字符串追加(重要)
- 函數的調用:產生隨機數(用系統時間做種子產生隨機數 `time_t time(NULL) rand() srand()`)
- 字符串處理函數
- 1) gets() (從鍵盤獲取輸入字符【可含空格】,遇換行符結束)(被拋棄了)
- 2) fgets()(可指定字符數獲取鍵盤輸入字符,不夠的話會取到換行符,超出字符的話只截取指定字符數)
- 3) puts()(輸出字符串,能自動換行,但不能像printf那樣用%格式化其他數據類型再輸出)
- 4) fputs()(也是輸出內容,但不止輸出到屏幕,也能指定輸出到某個文件)
- 5) strlen()(計算字符串長度,但遇到`\0`會結束,感覺還不如用`sizeof`呢)
- 6) strcpy(拷貝字符串,遇到結束符`\0`將停止拷貝,不能指定拷貝字符串長度)
- 7) strncpy()(拷貝字符串,遇到結束符`\0`將停止拷貝,能指定拷貝字符串長度)
- 8) strcat()(字符串追加)
- 9) strncat()(字符串追加,n個字符)
- 10) strcmp()(按ascii碼逐個比較字符串中的字符)
- 11) strncmp()(按ascii碼逐個比較字符串中的前n個字符)
- 12) sprintf()(將格式化字符串輸出到指定數組,返回值是實際格式化字符個數,失敗返回 -1)(組包)
- 13) sscanf()(從字符串中以指定格式提取字符,提取字符串%s默認以空格分割,以逗號分割不行,提取數字%d可以以逗號分割)(拆包)
- 14) strchr()(字符類型查詢)(int c 指字符)
- 15) strstr()(字符串類型查詢)(char * 指字符串)
- 16) strtok()(字符串切割,一個一個返回,要用循環一個一個獲取)(會破壞原來字符串,需要復制原字符串后使用)
- 17) atoi() atof() atol()(將字符串轉換成整型、浮點、長整型)
- 函數
- 概述
- 函數分類
- 函數的作用
- 函數的定義
- 函數定義格式
- 函數名字、形參、函數體、返回值
- 1) 函數名
- 2) 形參列表
- 3) 函數體
- 4) 返回值
- 函數的調用
- 函數執行流程
- 1)進入main()函數
- 2)調用print_test()函數:
- 3)print_test()函數執行完( 這里打印一句話 ),main()才會繼續往下執行,執行到return 0, 程序執行完畢。
- 函數的形參和實參
- 無參函數調用
- 有參函數調用
- 函數返回值 return
- 函數的聲明
- 函數定義和聲明的區別:
- main函數與exit函數
- 多文件(分文件)編程
- 分文件編程
- 防止頭文件重復包含
- 怎么看預處理后的文件(`gcc -E main.c -o main.i`)
運算符與表達式
常用運算符分類
運算符類型 作用 算術運算符 用于處理四則運算 賦值運算符 用于將表達式的值賦給變量 比較運算符 用于表達式的比較,并返回一個真值或假值 邏輯運算符 用于根據表達式的值返回真值或假值 位運算符 用于處理數據的位運算 sizeof運算符 用于求字節數長度算術運算符
運算符 術語 示例 結果 + 正號 +3 3 - 負號 -3 -3 + 加 10 + 5 15 - 減 10 - 5 5 * 乘 10 * 5 50 / 除 10 / 5 2 % 取模(取余) 10 % 3 1 ++ 前自增 a=2; b=++a; a=3; b=3; ++ 后自增 a=2; b=a++; a=3; b=2; -- 前自減 a=2; b=--a; a=1; b=1; -- 后自減 a=2; b=a--; a=1; b=2;兩個數相除,要想得到小數,分子分母必須有一個是小數,否則結果只會取整(可以把其中一個數乘以1.0)
示例:
#include <stdio.h>int main() {int a = 5;int b = 2;double c;double d;c= a/b;d = a*1.0/b;printf("%lf\n", c);//2.000000printf("%lf\n", d);//2.500000return 0; }前自增和后自增區別
b = a++ 是先把 a 的值賦給b,再自身+1
#include <stdio.h>int main() {int a;int b;//后置++a = 1;b = 0;b = a++;printf("%d %d\n", a, b);//2 1//前置++a = 1;b = 0;b = ++a;printf("%d %d\n", a ,b);//2 2//其他a = 1;b = 0;a++;//++a也一樣的b = a;printf("%d %d\n", a ,b);//2 2return 0; }賦值運算符
運算符 術語 示例 結果 = 賦值 a=2; b=3; a=2; b=3; += 加等于 a=0; a+=2; a=2; -= 減等于 a=5; a-=3; a=2; *= 乘等于 a=2; a*=2; a=4; /= 除等于 a=4; a/=2; a=2; %= 模等于 a=3; a%2; a=1;比較運算符
C 語言的比較運算中, “真”用數字“1”來表示, “假”用數字“0”來表示。
運算符 術語 示例 結果 == 相等于 4 == 3 0 != 不等于 4 != 3 1 < 小于 4 < 3 0 > 大于 4 > 3 1 <= 小于等于 4 <= 3 0 >= 大于等于 4 >= 1 1邏輯運算符
邏輯運算中,只要不為0都為真
運算符 術語 示例 結果
! 非 !a 如果a為假,則!a為真;
如果a為真,則!a為假。
&& 與 a && b 如果a和b都為真,則結果為真,否則為假。
|| 或 a || b 如果a和b有一個為真,則結果為真,二者都為假時,結果為假。
邏輯優化示例
//Dontla #include <stdio.h>int main() {int c = 0;1 || (c = 250);printf("%d\n", c);//0c = 0;0 || (c = 250);printf("%d\n", c);//250return 0; }運算符優先級
注意特殊情況:
b = a++; 跟 b = (a++);等價,不會因為括號而影響結果
類型轉換
數據有不同的類型,不同類型數據之間進行混合運算時必然涉及到類型的轉換問題。
轉換的方法有兩種:
- 自動轉換(隱式轉換):遵循一定的規則,由編譯系統自動完成。
- 強制類型轉換:把表達式的運算結果強制轉換成所需的數據類型。
類型轉換的原則:占用內存字節數少(值域小)的類型,向占用內存字節數多(值域大)的類型轉換,以保證精度不降低。
隱式類型轉換
示例1:
#include <stdio.h>int main() {int num = 5;printf("s1=%d\n", num / 2);//s1=2printf("s2=%lf\n", num / 2.0);//s2=2.500000return 0; }示例2:
#include <stdio.h>int main() {double a;int b = 10;a = b;printf("%lf\n", a);//10.000000return 0; }強制類型轉換
強制類型轉換指的是使用強制類型轉換運算符,將一個變量或表達式轉化成所需的類型,其基本語法格式如下所示:
(類型說明符) (表達式)示例1:
#include <stdio.h>int main() {float x = 0;int i = 0;x = 3.6f;i = x; //x為實型, i為整型,直接賦值會有警告i = (int)x; //使用強制類型轉換printf("x=%f, i=%d\n", x, i);//x=3.600000, i=3return 0; }示例2:
#include <stdio.h>int main() {double a;a = (double)1/2;printf("%lf\n", a);//0.500000printf("%lu\n", sizeof(int));//4printf("%u\n", (unsigned int)sizeof(int));//4int b = 12;printf("%lf\n", (double)b);//12.000000return 0; }發現同樣的代碼在不同編譯器運行結果還不一樣
linux上:
windows QT上:
程序流程結構
概述
C語言支持最基本的三種程序運行結構:順序結構、選擇結構、循環結構。
- 順序結構:程序按順序執行,不發生跳轉。
- 選擇結構:依據是否滿足條件,有選擇的執行相應功能。
- 循環結構:依據條件是否滿足,循環多次執行某段代碼。
選擇結構
if語句
注意:
1、果if 語句不加{},則只有后面第一句代碼屬于if 語句
2、判斷時最好變量放常量放左邊(防止少寫一個等號導致難以找出錯誤所在)
https://www.bilibili.com/video/BV1jW411K7v2/?spm_id_from=autoNext
if…else語句
#include <stdio.h> int main() {int a;int b;printf("請輸入a的值:");scanf("%d", &a);printf("請輸入b的值:");scanf("%d", &b);if (a > b){printf("%d\n", a);}else{printf("%d\n", b);}return 0; }結果:
if…else if…else語句
#include <stdio.h>int main() {unsigned int a;scanf("%u", &a);if (a < 10){printf("個位\n");}else if (a < 100){printf("十位\n");}else if (a < 1000){printf("百位\n");}else{printf("很大\n");}return 0; }結果:
三目運算符
#include <stdio.h>int main() {int a = 10;int b = 20;int c;if (a > b){c = a;}else{c = b;}printf("c1 = %d\n", c);//c1 = 20a = 1;b = 2;c = ( a > b ? a : b );printf("c2 = %d\n", c);//c2 = 2return 0; }switch語句(電梯模型)
#include <stdio.h>int main() {char c;c = getchar();switch (c) //參數只能是整型變量{case '1':printf("OK\n");break;//switch遇到break就中斷了case '2':printf("not OK\n");break;default://如果上面的條件都不滿足,那么執行defaultprintf("are u ok?\n");}return 0; }結果:
循環結構
while語句
#include <stdio.h>int main() {int a = 20;while (a > 10){scanf("%d", &a);printf("a = %d\n", a);}return 0; }結果:
調試
QT的調試
qt creator創建cmake構建的程序,無法啟動調試(點左下角運行不出結果 No executable specified.)
visual studio的調試
跟qt調試基本上是一樣的操作,F9打斷點,F5啟動調試,F10逐步調試,F11進入函數
do…while語句
#include <stdio.h>int main() {int a = 1;do{a++;printf("a = %d\n", a);} while (a < 10);return 0; }結果:
a = 2 a = 3 a = 4 a = 5 a = 6 a = 7 a = 8 a = 9 a = 10for語句
#include <stdio.h>int main() {int i;int sum = 0;for (i = 0; i <= 100; i++){sum += i;}printf("sum = %d\n", sum);//sum = 5050return 0; }其實也可以這樣:
死循環
嵌套循環
循環語句之間可以相互嵌套:
#include <stdio.h>int main() {int num = 0;int i, j, k;for (i = 0; i < 10; i++){for (j = 0; j < 10; j++){for (k = 0; k < 10; k++){printf("hello world\n");num++;}}}printf("num = %d\n", num);return 0; }結果:
跳轉語句break、continue、goto
break語句
在switch條件語句和循環語句中都可以使用break語句:
- 當它出現在switch條件語句中時,作用是終止某個case并跳出switch結構。
- 當它出現在循環語句中,作用是跳出當前內循環語句,執行后面的代碼。
- 當它出現在嵌套循環語句中,跳出最近的內循環語句,執行后面的代碼。
continue語句
在循環語句中,如果希望立即終止本次循環,并執行下一次循環,此時就需要使用continue語句。
示例:求0-100所有奇數的和
#include<stdio.h>int main() {int sum = 0; //定義變量sumfor (int i = 1; i <= 100; i++){if (i % 2 == 0) //如果i是一個偶數,執行if語句中的代碼{continue; //結束本次循環}sum += i; //實現sum和i的累加}printf("sum = %d\n", sum);//sum = 2500return 0; }goto語句(無條件跳轉,盡量少用)(注意:有時候去了就回不來了,但是有時候也可讓代碼精妙絕倫)
#include <stdio.h>int main() {goto End; //無條件跳轉到End的標識printf("aaaaaaaaa\n");End:printf("bbbbbbbb\n");return 0; }數組和字符串
概述
在程序設計中,為了方便處理數據把具有相同類型的若干變量按有序形式組織起來——稱為數組。
數組就是在內存中連續的相同類型的變量空間。同一個數組所有的成員都是相同的數據類型,同時所有的成員在內存中的地址是連續的。
數組屬于構造數據類型:
- 一個數組可以分解為多個數組元素:這些數組元素可以是基本數據類型或構造類型。
- 按數組元素類型的不同,數組可分為:數值數組、字符數組、指針數組、結構數組等類別。
通常情況下,數組元素下標的個數也稱為維數,根據維數的不同,可將數組分為一維數組、二維數組、三維數組、四維數組等。通常情況下,我們將二維及以上的數組稱為多維數組。
一維數組
一維數組的定義和使用
- 數組名字符合標識符的書寫規定(數字、英文字母、下劃線)
- 數組名不能與其它變量名相同,同一作用域內是唯一的
- 方括號[]中常量表達式表示數組元素的個數
可以使用常量定義數組大小:define SIZE 10
示例: 用宏定義常量創建一個數組,并給數組元素賦值
#include <stdio.h> #define SIZE 10int main() {int a[SIZE];int i;for (i = 0; i <10; i ++){a[i] = i;printf("a[%d] = %d\n", i, a[i]); }printf("%d\n", i);return 0; } int a[3]表示數組a有3個元素 其下標從0開始計算,因此3個元素分別為a[0],a[1],a[2]定義數組時[]內最好是常量,使用數組時[]內即可是常量,也可以是變量
示例2:
#include <stdio.h>int main() {int a[10];//定義了一個數組,名字叫a,有10個成員,每個成員都是int類型//a[0]…… a[9],沒有a[10]//沒有a這個變量,a是數組的名字,但不是變量名,它是常量a[0] = 0;//……a[9] = 9;int i = 0;for (i = 0; i < 10; i++){a[i] = i; //給數組賦值}//遍歷數組,并輸出每個成員的值for (i = 0; i < 10; i++){printf("%d ", a[i]);}printf("\n");return 0; }結果:
0 1 2 3 4 5 6 7 8 9注意:數組下標越界錯誤在某些編譯器上是不能及時發現的
一維數組的初始化(初始化:定義的時候同時賦值)
在定義數組的同時進行賦值,稱為初始化。全局數組若不初始化,編譯器將其初始化為零。局部數組若不初始化,內容為隨機值。
int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//定義一個數組,同時初始化所有成員變量 int a[10] = { 1, 2, 3 };//初始化前三個成員,后面所有元素都設置為0 int a[10] = { 0 };//所有的成員都設置為0//[]中不定義元素個數,定義時必須初始化 int a[] = { 1, 2, 3, 4, 5 };//定義了一個數組,有5個成員示例:初始化為隨機數
#include <stdio.h>int main() {int a[10];for (int i =0; i<10; i ++){printf("%d\n", a[i]); }return 0; }結果:
-1553645624 32604 369074640 21996 0 0 369074304 21996 -559922368 32765數組名(數組名地址是數組首元素地址)
示例1:數組名是一個地址的常量,代表數組中首元素的地址
#include <stdio.h>int main() {int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};printf("%p\n", a);printf("%p\n", &a);printf("%p\n", &a[0]);printf("%p\n", &a[1]);return 0; }示例2:求數組的長度,同時遍歷每個數組元素(c語言沒有len函數,用數組大小除以每個數組元素大小得到數組長度 int n = sizeof(a)/sizeof(a[0]))
#include <stdio.h>int main() {int a[10] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };//定義一個數組,同時初始化所有成員變量printf("a = %p\n", a);printf("&a[0] = %p\n", &a[0]);int n = sizeof(a); //數組占用內存的大小,10個int類型,10 * 4 = 40int n0 = sizeof(a[0]);//數組第0個元素占用內存大小,第0個元素為int,4int i = 0;for (i = 0; i < sizeof(a) / sizeof(a[0]); i++){printf("%d ", a[i]);}printf("\n");return 0; }強化訓練
1) 一維數組的最值
#include <stdio.h>int main() {int a[] = { 1, -2, 3,- 4, 5, -6, 7, -8, -9, 10 };//定義一個數組,同時初始化所有成員變量int i = 0;int max = a[0];for (i = 0; i < sizeof(a) / sizeof(a[0]); i++){if (a[i] > max){max = a[i];}}printf("數組中最大值為:%d\n", max);//數組中最大值為:10return 0; }2) 一維數組的逆置(如果數組元素個數為奇數,會多交換一次)
#include <stdio.h>int main() {int a[] = { 1, -2, 3,- 4, 5, -6, 7, -8, -9, 10 };//定義一個數組,同時初始化所有成員變量int i = 0;int j = sizeof(a) / sizeof(a[0]) -1;int tmp;while (i < j){tmp = a[i];a[i] = a[j];a[j] = tmp;i++;j--;}for (i = 0; i < sizeof(a) / sizeof(a[0]); i++){printf("%d ", a[i]);}printf("\n");return 0; }結果:
10 -9 -8 7 -6 5 -4 3 -2 13) 冒泡法排序(每次把大的數字冒泡到右邊)
#include <stdio.h>int main() {int a[] = { 1, -2, 3,- 4, 5, -6, 7, -8, -9, 10 };//定義一個數組,同時初始化所有成員變量int i = 0;int j = 0;int n = sizeof(a) / sizeof(a[0]);int tmp;//1、流程//2、試數for (i = 0; i < n-1; i++){for (j = 0; j < n - i -1 ; j++)//內循環的目的是比較相鄰的元素,把大的放到后面{if (a[j] > a[j + 1]){tmp = a[j];a[j] = a[j+1];a[j+1] = tmp;}}}for (i = 0; i < n; i++){printf("%d ", a[i]);}printf("\n");return 0; }結果:
-9 -8 -6 -4 -2 1 3 5 7 10二維數組
二維數組的定義和使用
二維數組定義的一般形式是:
類型說明符 數組名[常量表達式1][常量表達式2]其中常量表達式1表示第一維下標的長度,常量表達式2 表示第二維下標的長度。
int a[3][4];-
命名規則同一維數組
-
定義了一個三行四列的數組,數組名為a其元素類型為整型,該數組的元素個數為3×4個,即:
二維數組a是按行進行存放的,先存放a[0]行,再存放a[1]行、a[2]行,并且每行有四個元素,也是依次存放的。 -
二維數組在概念上是二維的:其下標在兩個方向上變化,對其訪問一般需要兩個下標。
-
在內存中并不存在二維數組,二維數組實際的硬件存儲器是連續編址的,也就是說內存中只有一維數組,即放完一行之后順次放入第二行,和一維數組存放方式是一樣的。
結果:
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,二維數組的初始化
//分段賦值 int a[3][4] = {{ 1, 2, 3, 4 },{ 5, 6, 7, 8, },{ 9, 10, 11, 12 }}; int a[3][4] = { { 1, 2, 3, 4 },{ 5, 6, 7, 8, },{ 9, 10, 11, 12 } };//連續賦值 int a[3][4] = { 1, 2, 3, 4 , 5, 6, 7, 8, 9, 10, 11, 12 };//可以只給部分元素賦初值,未初始化則為0 int a[3][4] = { 1, 2, 3, 4 };//所有的成員都設置為0 int a[3][4] = {0};//[]中不定義元素個數,定義時必須初始化(不能直接:int a[][4];,會報錯) int a[][4] = { 1, 2, 3, 4, 5, 6, 7, 8};示例,數組初始化自動推導(第一維度:sizeof(a)/sizeof(a[0]) 第二維度:sizeof(a[0])/sizeof(a[0][0]))
#include <stdio.h>int main() {int a[][4] = {1,2,3,4,5,6,7,8};int n = sizeof(a)/sizeof(a[0][0]);printf("%d\n", n);//8printf("%ld\n", sizeof(a)/sizeof(a[0]));//2printf("%ld\n", sizeof(a[0])/sizeof(a[0][0]));//4for(int i =0; i < sizeof(a)/sizeof(a[0]); i++){for(int j =0; j < sizeof(a[0])/sizeof(a[0][0]); j++){printf("%d ", a[i][j]); } }printf("\n");return 0; }結果:
數組名
數組名是一個地址的常量,代表數組中首元素的地址。
#include <stdio.h>int main() {//定義了一個二維數組,名字叫a//二維數組是本質上還是一維數組,此一維數組有3個元素 //每個元素又是一個一維數組int[4]int a[3][4] = { 1, 2, 3, 4 , 5, 6, 7, 8, 9, 10, 11, 12 };//數組名為數組首元素地址,二維數組的第0個元素為一維數組//第0個一維數組的數組名為a[0]printf("a = %p\n", a);//a = 0x7ffe7750a0a0printf("a[0] = %p\n", a[0]);//a[0] = 0x7ffe7750a0a0//測二維數組所占內存空間,有3個一維數組,每個一維數組的空間為4*4//sizeof(a) = 3 * 4 * 4 = 48printf("sizeof(a) = %ld\n", sizeof(a));//sizeof(a) = 48//測第0個元素所占內存空間,a[0]為第0個一維數組int[4]的數組名,4*4=16printf("sizeof(a[0]) = %ld\n", sizeof(a[0]) );//sizeof(a[0]) = 16//測第0行0列元素所占內存空間,第0行0列元素為一個int類型,4字節printf("sizeof(a[0][0]) = %ld\n", sizeof(a[0][0]));//sizeof(a[0][0]) = 4//求二維數組行數printf("i = %ld\n", sizeof(a) / sizeof(a[0]));//i = 3// 求二維數組列數printf("j = %ld\n", sizeof(a[0]) / sizeof(a[0][0]));//j = 4//求二維數組行*列總數printf("n = %ld\n", sizeof(a) / sizeof(a[0][0]));//n = 12return 0; }強化訓練
#include <stdio.h>int main() {//二維數組: 五行、三列//行代表人: 老大到老五//列代表科目:語、數、外float a[5][3] = { { 80, 75, 56 }, { 59, 65, 71 }, { 59, 63, 70 }, { 85, 45, 90 }, { 76, 77, 45 } };int i, j, person_low[3] = { 0 };float s = 0, lesson_aver[3] = { 0 };for (i = 0; i < 3; i++){for (j = 0; j < 5; j++){s = s + a[j][i];if (a[j][i] < 60){person_low[i]++;}}lesson_aver[i] = s / 5;s = 0;}printf("各科的平均成績:\n");for (i = 0; i < 3; i++){printf("%.2f\n", lesson_aver[i]);}printf("各科不及格的人數:\n");for (i = 0; i < 3; i++){printf("%d\n", person_low[i]);}return 0; }結果:
多維數組(了解)
多維數組的定義與二維數組類似,其語法格式具體如下:
數組類型修飾符 數組名 [n1][n2]…[nn]; int a[3][4][5];定義了一個三維數組,數組的名字是a,數組的長度為3,每個數組的元素又是一個二維數組,這個二維數組的長度是4,并且這個二維數組中的每個元素又是一個一維數組,這個一維數組的長度是5,元素類型是int。
#include <stdio.h>int main() {//int a[3][4][5] ;//定義了一個三維數組,有3個二維數組int[4][5]int a[3][4][5] = { { { 1, 2, 3, 4, 5 }, { 6, 7, 8, 9, 10 }, { 0 }, { 0 } }, { { 0 }, { 0 }, { 0 }, { 0 } }, { { 0 }, { 0 }, { 0 }, { 0 } } };int i, j, k;for (i = 0; i < 3; i++){for (j = 0; j < 4; j++){for (k = 0; k < 5; k++){//添加訪問元素代碼printf("%d, ", a[i][j][k]);}printf("\n");}}return 0; }字符數組char a[]與字符串 char a[, , , , , ... , '\0']
字符數組與字符串區別
- C語言中沒有字符串這種數據類型,可以通過char的數組來替代;
- 字符串一定是一個char的數組,但char的數組未必是字符串;
- 數字0(和字符‘\0’等價)結尾的char數組就是一個字符串,但如果char數組沒有以數字0結尾,那么就不是一個字符串,只是普通字符數組,所以字符串是一種特殊的char的數組。
用%s打印字符數組時,會給它一個字符數組名地址,然后系統就一個字符一個字符打印,遇到 '\0'就結束,如果沒有結束字符,后面打印就會出現一堆亂碼(\0就是數字0)
#include <stdio.h>int main() {char c1[] = { 'c', ' ', 'p', 'r', 'o', 'g' }; //普通字符數組printf("c1 = %s\n", c1); //c1 = c progU? //亂碼,因為沒有’\0’結束符//以‘\0’(‘\0’就是數字0)結尾的字符數組是字符串char c2[] = { 'c', ' ', 'p', 'r', 'o', 'g', '\0'}; printf("c2 = %s\n", c2);//c2 = c prog//字符串處理以‘\0’(數字0)作為結束符,后面的'h', 'l', 'l', 'e', 'o'不會輸出char c3[] = { 'c', ' ', 'p', 'r', 'o', 'g', '\0', 'h', 'l', 'l', 'e', 'o', '\0'};printf("c3 = %s\n", c3);//c3 = c progreturn 0; }注意事項:
{‘a’, ‘b’, ‘c’, '\0'} 或 {‘a’, ‘b’, ‘c’, 0}相當于 “abc”
字符串的初始化
#include <stdio.h>// C語言沒有字符串類型,通過字符數組模擬 // C語言字符串,以字符‘\0’結束, 就是數字0 int main() {//不指定長度, 沒有0結束符,有多少個元素就有多長char buf[] = { 'a', 'b', 'c' };printf("buf = %s\n", buf);//abc?XP //亂碼(有的編譯器不會出現亂碼,但不代表能這樣用)//指定長度,后面沒有賦值的元素,自動補0char buf2[100] = { 'a', 'b', 'c' };printf("buf2 = %s\n", buf2);//buf2 = abc//所有元素賦值為0char buf3[100] = { 0 };//char buf4[2] = { '1', '2', '3' };//數組越界char buf5[50] = { '1', 'a', 'b', '0', '7' };printf("buf5 = %s\n", buf5);//buf5 = 1ab07char buf6[50] = { '1', 'a', 'b', 0, '7' };printf("buf6 = %s\n", buf6);//buf6 = 1abchar buf7[50] = { '1', 'a', 'b', '\0', '7' };printf("buf7 = %s\n", buf7);//buf7 = 1ab//使用字符串初始化,編譯器自動在后面補0,常用char buf8[] = "agjdslgjlsdjg";//'\0'后面最好不要連著數字,有可能幾個數字連起來剛好是一個轉義字符//'\ddd'八進制字義字符,'\xdd'十六進制轉義字符// \012相當于\nchar str[] = "\012abc";printf("str == %s\n", str);return 0; }最后一個結果:
字符串的輸入輸出
由于字符串采用了’\0’標志,字符串的輸入輸出將變得簡單方便。
#include <stdio.h>int main() {char str[100];printf("input string1 : \n");scanf("%s", str);//scanf(“%s”,str)默認以空格分隔printf("output:%s\n", str);return 0; }強化訓練:字符串追加(重要)
#include <stdio.h>int main() {char str1[] = "abcdef";char str2[] = "123456";char dst[13];int i = 0;while (str1[i] != 0){dst[i] = str1[i];i++;}int j = 0;while (str2[j] != 0){dst[i + j] = str2[j];j++;}dst[i + j] = 0; //字符串結束符(也可寫成'\0')printf("dst = %s\n", dst);printf("\n");printf("%ld\n", sizeof(dst));printf("%ld\n", sizeof(dst[0]));printf("\n");for(int k = 0; k < sizeof(dst)/sizeof(dst[0]); k++){printf("%c\n", dst[k]); printf("%d\n", dst[k]); printf("\n");}return 0; }結果:
dst = abcdef12345613 1a 97b 98c 99d 100e 101f 1021 492 503 514 525 536 540注意:結束符就是數字零
函數的調用:產生隨機數(用系統時間做種子產生隨機數 time_t time(NULL) rand() srand())
當調用函數時,需要關心5要素:
- 頭文件:包含指定的頭文件
- 函數名字:函數名字必須和頭文件聲明的名字一樣
- 功能:需要知道此函數能干嘛后才調用
- 參數:參數類型要匹配
- 返回值:根據需要接收返回值
字符串處理函數
1) gets() (從鍵盤獲取輸入字符【可含空格】,遇換行符結束)(被拋棄了)
#include <stdio.h> char *gets(char *s); 功能:從標準輸入讀入字符,并保存到s指定的內存空間,直到出現換行符或讀到文件結尾為止。 參數:s:字符串首地址 返回值:成功:讀入的字符串失敗:NULLgets(str)與scanf(“%s”,str)的區別:
- gets(str)允許輸入的字符串含有空格
- scanf(“%s”,str)不允許含有空格
注意:由于scanf()和gets()無法知道字符串s大小,必須遇到換行符或讀到文件結尾為止才接收輸入,因此容易導致字符數組越界(緩沖區溢出)的情況。
#include <stdio.h> #include <time.h> #include <stdlib.h>int main() {char str[100];printf("請輸入str: ");gets(str);printf("str = %s\n", str);return 0; }結果:
2) fgets()(可指定字符數獲取鍵盤輸入字符,不夠的話會取到換行符,超出字符的話只截取指定字符數)
#include <stdio.h> char *fgets(char *s, int size, FILE *stream); 功能:從stream指定的文件內讀入字符,保存到s所指定的內存空間,直到出現換行字符、讀到文件結尾或是已讀了size - 1個字符為止,最后會自動加上字符 '\0' 作為字符串結束。 參數:s:字符串size:指定最大讀取字符串的長度(size - 1)stream:文件指針,如果讀鍵盤輸入的字符串,固定寫為stdin 返回值:成功:成功讀取的字符串讀到文件尾或出錯: NULLfgets()在讀取一個用戶通過鍵盤輸入的字符串的時候,同時把用戶輸入的回車也做為字符串的一部分。通過scanf和gets輸入一個字符串的時候,不包含結尾的“\n”,但通過fgets結尾多了“\n”。fgets()函數是安全的,不存在緩沖區溢出的問題。
#include <stdio.h> #include <time.h> #include <stdlib.h>int main() {char str[10];printf("%ld\n", sizeof(str));//10,只會輸出9個字符,留一個放結束符0printf("請輸入str: ");fgets(str, sizeof(str), stdin);printf("str = \"%s\"\n", str); }3) puts()(輸出字符串,能自動換行,但不能像printf那樣用%格式化其他數據類型再輸出)
#include <stdio.h> int puts(const char *s); 功能:標準設備輸出s字符串,在輸出完成后自動輸出一個'\n'。 參數:s:字符串首地址 返回值:成功:非負數失敗:-1 #include <stdio.h>int main() {printf("hello world\n");puts("hello world");return 0; }4) fputs()(也是輸出內容,但不止輸出到屏幕,也能指定輸出到某個文件)
#include <stdio.h> int fputs(const char * str, FILE * stream); 功能:將str所指定的字符串寫入到stream指定的文件中, 字符串結束符 '\0' 不寫入文件。 參數:str:字符串stream:文件指針,如果把字符串輸出到屏幕,固定寫為stdout 返回值:成功:0失敗:-1fputs()是puts()的文件操作版本,但fputs()不會自動輸出一個’\n’。
#include <stdio.h>int main() { printf("hello world\n");puts("hello world");fputs("hello world\n", stdout); }結果:
5) strlen()(計算字符串長度,但遇到\0會結束,感覺還不如用sizeof呢)
#include <string.h> size_t strlen(const char *s); 功能:計算指定指定字符串s的長度,不包含字符串結束符‘\0’ 參數: s:字符串首地址 返回值:字符串s的長度,size_t為unsigned int類型 #include <stdio.h> #include <string.h>int main() {char str[] = "abcdefg";printf("%ld\n", sizeof(str));//8int n = strlen(str);printf("n = %d\n", n);//n = 7char str2[] = "\0abcdefg"; printf("%ld\n", sizeof(str2));//9printf("n = %ld\n", strlen(str2));//n = 0 }6) strcpy(拷貝字符串,遇到結束符\0將停止拷貝,不能指定拷貝字符串長度)
#include <string.h> char *strcpy(char *dest, const char *src); 功能:把src所指向的字符串復制到dest所指向的空間中,'\0'也會拷貝過去 參數:dest:目的字符串首地址src:源字符首地址 返回值:成功:返回dest字符串的首地址失敗:NULL注意:如果參數dest所指的內存空間不夠大,可能會造成緩沖溢出的錯誤情況。(在visual studio中會表現出來)
#include <stdio.h> #include <string.h>int main() {char src[] = "abcdefg\0dgf3";char dst[20];strcpy(dst, src);printf("%s\n", dst);//abcdefgprintf("%d %c\n", dst[6],dst[6]);//103 gprintf("%d %c\n", dst[7],dst[7]);//0printf("%d %c\n", dst[8],dst[8]);//-64(不知道為什么一直是這個結果,結束符后面不都是隨機的嗎?)printf("%d %c\n", dst[9],dst[9]);//隨機printf("%d %c\n", dst[10],dst[10]);//隨機return 0; }結果:
7) strncpy()(拷貝字符串,遇到結束符\0將停止拷貝,能指定拷貝字符串長度)
#include <string.h> char *strncpy(char *dest, const char *src, size_t n); 功能:把src指向字符串的前n個字符復制到dest所指向的空間中,是否拷貝結束符看指定的長度是否包含'\0'。 參數:dest:目的字符串首地址src:源字符首地址n:指定需要拷貝字符串個數 返回值:成功:返回dest字符串的首地址失敗:NULL #include <stdio.h> #include <string.h>int main() {char src[] = "abcdefg\0";char dst[20];char dst2[20];strncpy(dst, src, strlen(src)+1);strncpy(dst2, src, sizeof(src));printf("%s\n", dst);printf("%s\n", dst2);return 0; }8) strcat()(字符串追加)
#include <string.h> char *strcat(char *dest, const char *src); 功能:將src字符串連接到dest的尾部,‘\0’也會追加過去 參數:dest:目的字符串首地址src:源字符首地址 返回值:成功:返回dest字符串的首地址失敗:NULL#include <stdio.h> #include <string.h>int main() {char str[20] = "123";char *src = "hello world";printf("%s\n", strcat(str, src));return 0; }結果:
123hello world9) strncat()(字符串追加,n個字符)
#include <string.h> char *strncat(char *dest, const char *src, size_t n); 功能:將src字符串前n個字符連接到dest的尾部,‘\0’也會追加過去 參數:dest:目的字符串首地址src:源字符首地址n:指定需要追加字符串個數 返回值:成功:返回dest字符串的首地址失敗:NULL#include <stdio.h> #include <string.h>int main() {char str[20] = "123";char *src = "hello world";printf("%s\n", strncat(str, src, 7));return 0; } 123hello w10) strcmp()(按ascii碼逐個比較字符串中的字符)
#include <string.h> int strcmp(const char *s1, const char *s2); 功能:比較 s1 和 s2 的大小,比較的是字符ASCII碼大小。 參數:s1:字符串1首地址s2:字符串2首地址 返回值:相等:0大于:>0小于:<0 #include <stdio.h> #include <string.h>int main() {char *str1 = "hello world";char *str2 = "hello mike";if (strcmp(str1, str2) == 0){printf("str1==str2\n");}else if (strcmp(str1, str2) > 0){printf("str1>str2\n");} else{printf("str1<str2\n");}return 0; }結果:
str1>str211) strncmp()(按ascii碼逐個比較字符串中的前n個字符)
#include <string.h> int strncmp(const char *s1, const char *s2, size_t n); 功能:比較 s1 和 s2 前n個字符的大小,比較的是字符ASCII碼大小。 參數:s1:字符串1首地址s2:字符串2首地址n:指定比較字符串的數量 返回值:相等:0大于: > 0小于: < 0 #include <stdio.h> #include <string.h>int main() {char *str1 = "hello world";char *str2 = "hello mike";int i = strncmp(str1, str2, 7);if (i == 0){printf("str1==str2\n");}else if (i > 0){printf("str1>str2\n");}else{printf("str1<str2\n");}return 0; }結果:
str1>str212) sprintf()(將格式化字符串輸出到指定數組,返回值是實際格式化字符個數,失敗返回 -1)(組包)
#include <stdio.h> int sprintf(char *str, const char *format, ...); 功能:根據參數format字符串來轉換并格式化數據,然后將結果輸出到str指定的空間中,直到出現字符串結束符 '\0' 為止。 參數:str:字符串首地址format:字符串格式,用法和printf()一樣 返回值:成功:實際格式化的字符個數失敗: - 1 #include <stdio.h> #include <string.h>int main() {char dst[100] = { 0 };int a = 10;char src[] = "hello world";printf("a = %d, src = %s", a, src);printf("\n");int len = sprintf(dst, "a = %d, src = %s", a, src);printf("dst = \"%s\"\n", dst);printf("len = %d\n", len);return 0; }結果:
a = 10, src = hello world dst = "a = 10, src = hello world" len = 2513) sscanf()(從字符串中以指定格式提取字符,提取字符串%s默認以空格分割,以逗號分割不行,提取數字%d可以以逗號分割)(拆包)
#include <stdio.h> int sscanf(const char *str, const char *format, ...); 功能:從str指定的字符串讀取數據,并根據參數format字符串來轉換并格式化數據。 參數:str:指定的字符串首地址format:字符串格式,用法和scanf()一樣 返回值:成功:參數數目,成功轉換的值的個數失敗: - 1 #include <stdio.h> #include <string.h>int main() {char src[] = "a=10, b=20";int a;int b;sscanf(src, "a=%d, b=%d", &a, &b);printf("a:%d, b:%d\n", a, b);char src2[] = "sdf dom mike";char a1[10];char b1[10];char c1[10];sscanf(src2, "%s %s %s", a1, b1, c1);//不能取地址&printf("%s %s %s\n", a1, b1, c1 );return 0; }結果:
a:10, b:20 sdf dom mike14) strchr()(字符類型查詢)(int c 指字符)
#include <string.h> char *strchr(const char *s, int c); 功能:在字符串s中查找字母c出現的位置 參數:s:字符串首地址c:匹配字母(字符) 返回值:成功:返回第一次出現的c地址失敗:NULL #include <stdio.h> #include <string.h>int main() {char src[] = "ddda123abcd";char *p = strchr(src, 'a');printf("p = %s\n", p);//p = a123abcdreturn 0; }15) strstr()(字符串類型查詢)(char * 指字符串)
#include <string.h> char *strstr(const char *haystack, const char *needle); 功能:在字符串haystack中查找字符串needle出現的位置 參數:haystack:源字符串首地址needle:匹配字符串首地址 返回值:成功:返回第一次出現的needle地址失敗:NULL #include <stdio.h> #include <string.h>int main() {char src[] = "ddddabcd123abcd333abcd";char *p = strstr(src, "abcd");printf("p = %s\n", p);//p = abcd123abcd333abcdreturn 0; }16) strtok()(字符串切割,一個一個返回,要用循環一個一個獲取)(會破壞原來字符串,需要復制原字符串后使用)
#include <string.h> char *strtok(char *str, const char *delim); 功能:來將字符串分割成一個個片段。當strtok()在參數s的字符串中發現參數delim中包含的分割字符時, 則會將該字符改為\0 字符,當連續出現多個時只替換第一個為\0。 參數:str:指向欲分割的字符串delim:為分割字符串中包含的所有字符 返回值:成功:分割后字符串首地址失敗:NULL- 在第一次調用時:strtok()必需給予參數s字符串
- 往后的調用則將參數s設置成NULL,每次調用成功則返回指向被分割出片段的指針
結果:
adc fvcv ebcy hghbdfg casdert17) atoi() atof() atol()(將字符串轉換成整型、浮點、長整型)
#include <stdlib.h> int atoi(const char *nptr); 功能:atoi()會掃描nptr字符串,跳過前面的空格字符,直到遇到數字或正負號才開始做轉換,而遇到非數字或字符串結束符('\0')才結束轉換,并將結果返回返回值。 參數:nptr:待轉換的字符串 返回值:成功轉換后整數類似的函數有:
- atof():把一個小數形式的字符串轉化為一個浮點數。
- atol():將一個字符串轉化為long類型
結果:
函數
概述
函數分類
C 程序是由函數組成的,我們寫的代碼都是由主函數 main()開始執行的。函數是 C 程序的基本模塊,是用于完成特定任務的程序代碼單元。
從函數定義的角度看,函數可分為系統函數和用戶定義函數兩種:
- 系統函數,即庫函數:這是由編譯系統提供的,用戶不必自己定義這些函數,可以直接使用它們,如我們常用的打印函數printf()。
- 用戶定義函數:用以解決用戶的專門需要。
函數的作用
- 函數的使用可以省去重復代碼的編寫,降低代碼重復率
- 函數可以讓程序更加模塊化,從而有利于程序的閱讀,修改和完善
假如我們編寫一個實現以下功能的程序:讀入一行數字;對數字進行排序;找到它們的平均值;打印出一個柱狀圖。如果我們把這些操作直接寫在main()里,這樣可能會給用戶感覺代碼會有點凌亂。但,假如我們使用函數,這樣可以讓程序更加清晰、模塊化:
#include <stdio.h>int main() {float list[50];// 這里只是舉例,函數還沒有實現readlist(list, 50);sort(list, 50);average(list, 50);bargraph(list, 50);return 0; }這里我們可以這么理解,程序就像公司,公司是由部門組成的,這個部門就類似于C程序的函數。默認情況下,公司就是一個大部門( 只有一個部門的情況下 ),相當于C程序的main()函數。如果公司比較小( 程序比較小 ),因為任務少而簡單,一個部門即可( main()函數 )勝任。但是,如果這個公司很大( 大型應用程序 ),任務多而雜,如果只是一個部門管理( 相當于沒有部門,沒有分工 ),我們可想而知,公司管理、運營起來會有多混亂,不是說這樣不可以運營,只是這樣不完美而已,如果根據公司要求分成一個個部門( 根據功能封裝一個一個函數 ),招聘由行政部門負責,研發由技術部門負責等,這樣就可以分工明確,結構清晰,方便管理,各部門之間還可以相互協調。
函數的定義
函數定義格式
函數定義的一般形式:
返回類型 函數名(形式參數列表) {數據定義部分;執行語句部分; }函數名字、形參、函數體、返回值
1) 函數名
理論上是可以隨意起名字,最好起的名字見名知意,應該讓用戶看到這個函數名字就知道這個函數的功能。注意,函數名的后面有個圓換號(),代表這個為函數,不是普通的變量名。
2) 形參列表
在定義函數時指定的形參,在未出現函數調用時,它們并不占內存中的存儲單元,因此稱它們是形式參數或虛擬參數,簡稱形參,表示它們并不是實際存在的數據,所以,形參里的變量不能賦值。
void max(int a = 10, int b = 20) // error, 形參不能賦值 { }在定義函數時指定的形參,必須是,類型+變量的形式:
//1: right, 類型+變量 void max(int a, int b) { }//2: error, 只有類型,沒有變量 void max(int, int) { }//3: error, 只有變量,沒有類型 int a, int b; void max(a, b) { }在定義函數時指定的形參,可有可無,根據函數的需要來設計,如果沒有形參,圓括號內容為空,或寫一個void關鍵字:
// 沒形參, 圓括號內容為空 void max() { }// 沒形參, 圓括號內容為void關鍵字 void max(void) { }3) 函數體
花括號{ }里的內容即為函數體的內容,這里為函數功能實現的過程,這和以前的寫代碼沒太大區別,以前我們把代碼寫在main()函數里,現在只是把這些寫到別的函數里。
4) 返回值
函數的返回值是通過函數中的return語句獲得的,return后面的值也可以是一個表達式。
a)盡量保證return語句中表達式的值和函數返回類型是同一類型。
b)如果函數返回的類型和return語句中表達式的值不一致,則以函數返回類型為準,即函數返回類型決定返回值的類型。對數值型數據,可以自動進行類型轉換。
double max() // 函數的返回值為double類型 {int a = 10;return a;// 返回值a為int類型,它會轉為double類型再返回 }注意:如果函數返回的類型和return語句中表達式的值不一致,而它又無法自動進行類型轉換,程序則會報錯。
c)return語句的另一個作用為中斷return所在的執行函數,類似于break中斷循環、switch語句一樣。
int max() {return 1;// 執行到,函數已經被中斷,所以下面的return 2無法被執行到return 2;// 沒有執行 }d)如果函數帶返回值,return后面必須跟著一個值,如果函數沒有返回值,函數名字的前面必須寫一個void關鍵字,這時候,我們寫代碼時也可以通過return中斷函數(也可以不用),只是這時,return后面不帶內容( 分號“;”除外)。
void max()// 最好要有void關鍵字 {return; // 中斷函數,這個可有可無 }函數的調用
定義函數后,我們需要調用此函數才能執行到這個函數里的代碼段。這和main()函數不一樣,main()為編譯器設定好自動調用的主函數,無需人為調用,我們都是在main()函數里調用別的函數,一個 C 程序里有且只有一個main()函數。
函數執行流程
#include <stdio.h>void print_test() {printf("this is for test\n"); }int main() {print_test(); // print_test函數的調用return 0; }1)進入main()函數
2)調用print_test()函數:
a.它會在main()函數的前面尋找有沒有一個名字叫“print_test”的函數定義;
b.如果找到,接著檢查函數的參數,這里調用函數時沒有傳參,函數定義也沒有形參,參數類型匹配;
c.開始執行print_test()函數,這時候,main()函數里面的執行會阻塞( 停 )在print_test()這一行代碼,等待print_test()函數的執行。
3)print_test()函數執行完( 這里打印一句話 ),main()才會繼續往下執行,執行到return 0, 程序執行完畢。
函數的形參和實參
- 形參出現在函數定義中,在整個函數體內都可以使用,離開該函數則不能使用。
- 實參出現在主調函數中,進入被調函數后,實參也不能使用。
- 實參變量對形參變量的數據傳遞是“值傳遞”,即單向傳遞,只由實參傳給形參,而不能由形參傳回來給實參。
- 在調用函數時,編譯系統臨時給形參分配存儲單元。調用結束后,形參單元被釋放。
- 實參單元與形參單元是不同的單元。調用結束后,形參單元被釋放,函數調用結束返回主調函數后則不能再使用該形參變量。實參單元仍保留并維持原值。因此,在執行一個被調用函數時,形參的值如果發生改變,并不會改變主調函數中實參的值。
無參函數調用
如果是調用無參函數,則不能加上“實參”,但括號不能省略。
// 函數的定義 void test() { }int main() {// 函數的調用test(); // right, 圓括號()不能省略test(250); // error, 函數定義時沒有參數return 0; }有參函數調用
a)如果實參表列包含多個實參,則各參數間用逗號隔開。
// 函數的定義 void test(int a, int b) { }int main() {int p = 10, q = 20;test(p, q); // 函數的調用return 0; }b)實參與形參的個數應相等,類型應匹配(相同或賦值兼容)。實參與形參按順序對應,一對一地傳遞數據。
c)實參可以是常量、變量或表達式,無論實參是何種類型的量,在進行函數調用時,它們都必須具有確定的值,以便把這些值傳送給形參。所以,這里的變量是在圓括號( )外面定義好、賦好值的變量。
函數返回值 return
a)如果函數定義沒有返回值,函數調用時不能寫void關鍵字,調用函數時也不能接收函數的返回值。
// 函數的定義 void test() { }int main() {// 函數的調用test(); // rightvoid test(); // error, void關鍵字只能出現在定義,不可能出現在調用的地方int a = test(); // error, 函數定義根本就沒有返回值return 0; }b)如果函數定義有返回值,這個返回值我們根據用戶需要可用可不用,但是,假如我們需要使用這個函數返回值,我們需要定義一個匹配類型的變量來接收。
// 函數的定義, 返回值為int類型 int test() { }int main() {// 函數的調用int a = test(); // right, a為int類型(visual studio會報錯,說函數必須返回一個值)int b;b = test(); // right, 和上面等級char *p = test(); // 雖然調用成功沒有意義, p為char *, 函數返回值為int, 類型不匹配// error, 必須定義一個匹配類型的變量來接收返回值// int只是類型,沒有定義變量int = test(); return 0; }函數的聲明
如果使用用戶自己定義的函數,而該函數與調用它的函數(即主調函數)不在同一文件中,或者函數定義的位置在主調函數之后,則必須在調用此函數之前對被調用的函數作聲明。
所謂函數聲明,就是在函數尚在未定義的情況下,事先將該函數的有關信息通知編譯系統,相當于告訴編譯器,函數在后面定義,以便使編譯能正常進行。
注意:一個函數只能被定義一次,但可以聲明多次。
1、聲明時可在前面加extern關鍵字,加不加沒有區別
2、聲明不一定必須要放在main函數前,在使用前一句代碼聲明也是可以的(聲明直接把函數定義的第一行代碼后面加分號即可)
3、聲明函數里的形參變量名可以跟定義的形參變量名不一樣(甚至形參變量名都可以不寫)
#include <stdio.h>extern int max(int, int); // 函數的聲明,分號不能省略 // int max(int, int); // 另一種方式int main() {int a = 10, b = 25, num_max = 0;int max(int gg, int sffd);//可在使用前聲明,形參變量名隨意num_max = max(a, b); // 函數的調用printf("num_max = %d\n", num_max);return 0; }// 函數的定義 int max(int x, int y) {return x > y ? x : y; }結果:
num_max = 25函數定義和聲明的區別:
1)定義是指對函數功能的確立,包括指定函數名、函數類型、形參及其類型、函數體等,它是一個完整的、獨立的函數單位。
2)聲明的作用則是把函數的名字、函數類型以及形參的個數、類型和順序(注意,不包括函數體)通知編譯系統,以便在對包含函數調用的語句進行編譯時,據此對其進行對照檢查(例如函數名是否正確,實參與形參的類型和個數是否一致)。
main函數與exit函數
在main函數中調用exit和return結果是一樣的,但在子函數中調用return只是代表子函數終止了,在子函數中調用exit,那么程序終止。
#include <stdio.h> #include <stdlib.h>void fun() {printf("fun\n");//return;exit(0);//exit什么數字這里結果都一樣,具體不同數字含義學了系統編程再講 }int main() {fun();printf("here\n");while (1);return 0; }結果:
多文件(分文件)編程
分文件編程
- 把函數聲明放在頭文件xxx.h中,在主函數中包含相應頭文件
- 在頭文件對應的xxx.c中實現xxx.h聲明的函數
示例:
main.c
my_add.c
#include <stdio.h> void my_add(int a, int b) {printf("%d\n", a + b); }my_add.h
void my_add(int, int);運行指令gcc *.c -o test編譯并執行:
注意:
1、同個項目的多個.c文件中,不能出現同名函數(static靜態函數除外)
2、.c文件是用來定義函數的,.h頭文件是用來聲明函數的
防止頭文件重復包含
當一個項目比較大時,往往都是分文件,這時候有可能不小心把同一個頭文件 include 多次,或者頭文件嵌套包含。
a.h 中包含 b.h :
#include "b.h"b.h 中包含 a.h:
#include "a.h"main.c 中使用其中頭文件:
#include "a.h"int main() {return 0; }編譯上面的例子,會出現如下錯誤:
為了避免同一個文件被include多次,C/C++中有兩種方式,一種是 #ifndef 方式,一種是 #pragma once 方式。
方法一:
關于下劃線,說是一種編程風格,用不用,用幾個,都不影響(意思是如果文件名為somefile.h,你可以寫成SOMEFILE_H,也可以_SOMEFILE_H_,也可以_SOMEFILE_H等等)
其實,貌似只要上面下面相同,寫啥都無所謂,都能實現功能:
參考文章1:#ifndef詳解
參考文章2:#ifndef STDIO_H 中的下劃線含義分別是什么哦
方法二:
將這個放在.h頭文件最上面
怎么看預處理后的文件(gcc -E main.c -o main.i)
gcc -E main.c -o main.i總結
以上是生活随笔為你收集整理的黑马程序员C语言基础(第五天)运算符与表达式、程序流程结构、数组和字符串、函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: ubuntu终端按ctrl+s就卡住怎么
- 下一篇: scanf_s写入错误怎么办?(字符串需