日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

黑马程序员C语言基础(第五天)运算符与表达式、程序流程结构、数组和字符串、函数

發布時間:2025/3/20 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 黑马程序员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、判斷時最好變量放常量放左邊(防止少寫一個等號導致難以找出錯誤所在)

#include <stdio.h>int main() {int a = 1;int b = 2;if (a > b){printf("%d\n", a);}return 0; }

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 = 10

for語句

#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結構。
  • 當它出現在循環語句中,作用是跳出當前內循環語句,執行后面的代碼。
  • 當它出現在嵌套循環語句中,跳出最近的內循環語句,執行后面的代碼。
#include <stdio.h>int main() {int i = 0;while (1){i++;printf("i = %d\n", i);if (i == 10){break; //跳出while循環}}int flag = 0;int m = 0;int n = 0;for (m = 0; m < 10; m++){for (n = 0; n < 10; n++){if (n == 5){flag = 1;break; //跳出for (n = 0; n < 10; n++)}}if (flag == 1){break; //跳出for (m = 0; m < 10; m++)}}return 0; }

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; }

數組和字符串

概述

在程序設計中,為了方便處理數據把具有相同類型的若干變量按有序形式組織起來——稱為數組。

數組就是在內存中連續的相同類型的變量空間。同一個數組所有的成員都是相同的數據類型,同時所有的成員在內存中的地址是連續的。

數組屬于構造數據類型:

  • 一個數組可以分解為多個數組元素:這些數組元素可以是基本數據類型或構造類型。
int a[10]; struct Stu boy[10];
  • 按數組元素類型的不同,數組可分為:數值數組、字符數組、指針數組、結構數組等類別。
int a[10]; char s[10]; char *p[10];

通常情況下,數組元素下標的個數也稱為維數,根據維數的不同,可將數組分為一維數組、二維數組、三維數組、四維數組等。通常情況下,我們將二維及以上的數組稱為多維數組。

一維數組

一維數組的定義和使用

  • 數組名字符合標識符的書寫規定(數字、英文字母、下劃線)
  • 數組名不能與其它變量名相同,同一作用域內是唯一的
  • 方括號[]中常量表達式表示數組元素的個數


    可以使用常量定義數組大小: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 1
3) 冒泡法排序(每次把大的數字冒泡到右邊)
#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]行,并且每行有四個元素,也是依次存放的。

  • 二維數組在概念上是二維的:其下標在兩個方向上變化,對其訪問一般需要兩個下標。

  • 在內存中并不存在二維數組,二維數組實際的硬件存儲器是連續編址的,也就是說內存中只有一維數組,即放完一行之后順次放入第二行,和一維數組存放方式是一樣的。

#include <stdio.h>int main() {//定義了一個二維數組,名字叫a//由3個一維數組組成,這個一維數組是int [4]//這3個一維數組的數組名分別為a[0],a[1],a[2]int a[3][4];a[0][0] = 0;//……a[2][3] = 12;//給數組每個元素賦值int i = 0;int j = 0;int num = 0;for (i = 0; i < 3; i++){for (j = 0; j < 4; j++){a[i][j] = num++;}}//遍歷數組,并輸出每個成員的值for (i = 0; i < 3; i++){for (j = 0; j < 4; j++){printf("%d, ", a[i][j]);}printf("\n");}return 0; }

結果:

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要素:

  • 頭文件:包含指定的頭文件
  • 函數名字:函數名字必須和頭文件聲明的名字一樣
  • 功能:需要知道此函數能干嘛后才調用
  • 參數:參數類型要匹配
  • 返回值:根據需要接收返回值
#include <time.h> time_t time(time_t *t); 功能:獲取當前系統時間 參數:常設置為NULL 返回值:當前系統時間, time_t 相當于long類型,單位為毫秒#include <stdlib.h> void srand(unsigned int seed); 功能:用來設置rand()產生隨機數時的隨機種子 參數:如果每次seed相等,rand()產生隨機數相等 返回值:無#include <stdlib.h> int rand(void); 功能:返回一個隨機數值 參數:無 返回值:隨機數 #include <stdio.h> #include <time.h> #include <stdlib.h>int main() {time_t tm = time(NULL);//得到系統時間printf("%ld\n", sizeof(tm));//8srand((unsigned int)tm);//隨機種子只需要設置一次即可int r = rand();printf("r = %d\n", r);return 0; }

字符串處理函數

1) gets() (從鍵盤獲取輸入字符【可含空格】,遇換行符結束)(被拋棄了)
#include <stdio.h> char *gets(char *s); 功能:從標準輸入讀入字符,并保存到s指定的內存空間,直到出現換行符或讀到文件結尾為止。 參數:s:字符串首地址 返回值:成功:讀入的字符串失敗:NULL

gets(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 返回值:成功:成功讀取的字符串讀到文件尾或出錯: NULL

fgets()在讀取一個用戶通過鍵盤輸入的字符串的時候,同時把用戶輸入的回車也做為字符串的一部分。通過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失敗:-1

fputs()是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_tunsigned 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 world
9) 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 w
10) 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>str2
11) 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>str2
12) 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 = 25
13) 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 mike
14) 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,每次調用成功則返回指向被分割出片段的指針
#include <stdio.h> #include <string.h>int main() {char a[100] = "adc*fvcv*ebcy*hghbdfg*casdert";char temp[100];strcpy(temp, a);char *s = strtok(temp, "*");//將"*"分割的子串取出while (s != NULL){printf("%s\n", s);s = strtok(NULL, "*");}return 0; }

結果:

adc fvcv ebcy hghbdfg casdert
17) atoi() atof() atol()(將字符串轉換成整型、浮點、長整型)
#include <stdlib.h> int atoi(const char *nptr); 功能:atoi()會掃描nptr字符串,跳過前面的空格字符,直到遇到數字或正負號才開始做轉換,而遇到非數字或字符串結束符('\0')才結束轉換,并將結果返回返回值。 參數:nptr:待轉換的字符串 返回值:成功轉換后整數

類似的函數有:

  • atof():把一個小數形式的字符串轉化為一個浮點數。
  • atol():將一個字符串轉化為long類型
#include <stdio.h> #include <string.h> #include <stdlib.h>int main() {char str1[] = " -10ds hjk 55";int num1 = atoi(str1);printf("num1 = %d\n", num1);char str2[] = "0.123";double num2 = atof(str2);printf("num2 = %lf\n", num2);return 0; }

結果:

函數

概述

函數分類

C 程序是由函數組成的,我們寫的代碼都是由主函數 main()開始執行的。函數是 C 程序的基本模塊,是用于完成特定任務的程序代碼單元。

從函數定義的角度看,函數可分為系統函數和用戶定義函數兩種:

  • 系統函數,即庫函數:這是由編譯系統提供的,用戶不必自己定義這些函數,可以直接使用它們,如我們常用的打印函數printf()。
  • 用戶定義函數:用以解決用戶的專門需要。

函數的作用

  • 函數的使用可以省去重復代碼的編寫,降低代碼重復率
// 求兩數的最大值 #include <stdio.h> #include <string.h> #include <stdlib.h>int max(int a, int b) {if (a > b){return a;}else{return b;} }int main() {// 操作1 ……// ……int a1 = 10, b1 = 20, c1 = 0;c1 = max(a1, b1); // 調用max()printf("%d\n", c1);//20// 操作2 ……// ……int a2 = 11, b2 = 21, c2 = 0;c2 = max(a2, b2); // 調用max()printf("%d\n", c2);21// ……return 0; }
  • 函數可以讓程序更加模塊化,從而有利于程序的閱讀,修改和完善

假如我們編寫一個實現以下功能的程序:讀入一行數字;對數字進行排序;找到它們的平均值;打印出一個柱狀圖。如果我們把這些操作直接寫在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語句中表達式的值和函數返回類型是同一類型。

int max() // 函數的返回值為int類型 {int a = 10;return a;// 返回值a為int類型,函數返回類型也是int,匹配 }

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)實參可以是常量、變量或表達式,無論實參是何種類型的量,在進行函數調用時,它們都必須具有確定的值,以便把這些值傳送給形參。所以,這里的變量是在圓括號( )外面定義好、賦好值的變量。

// 函數的定義 void test(int a, int b) { }int main() {// 函數的調用int p = 10, q = 20;test(p, q); // righttest(11, 30 - 10); // righttest(int a, int b); // error, 不應該在圓括號里定義變量return 0; }

函數返回值 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

//#include <stdio.h> //#include <stdlib.h> #include "my_add.h" //void my_add(int, int);//可以不用指定哪個.c文件而直接找到函數,所以一般項目中不允許存在同名函數 int main() {my_add(1, 2);return 0; }

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 方式。
方法一:

#ifndef __SOMEFILE_H__ #define __SOMEFILE_H__// 聲明語句#endif

關于下劃線,說是一種編程風格,用不用,用幾個,都不影響(意思是如果文件名為somefile.h,你可以寫成SOMEFILE_H,也可以_SOMEFILE_H_,也可以_SOMEFILE_H等等)
其實,貌似只要上面下面相同,寫啥都無所謂,都能實現功能:

參考文章1:#ifndef詳解
參考文章2:#ifndef STDIO_H 中的下劃線含義分別是什么哦

方法二:
將這個放在.h頭文件最上面

#pragma once// 聲明語句

怎么看預處理后的文件(gcc -E main.c -o main.i)

gcc -E main.c -o main.i

總結

以上是生活随笔為你收集整理的黑马程序员C语言基础(第五天)运算符与表达式、程序流程结构、数组和字符串、函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

主站蜘蛛池模板: 国产福利视频一区二区三区 | 日韩黄色网 | 国产丝袜视频在线 | 欧美a网 | 在线亚洲精品 | 东京热加勒比无码少妇 | 成人精品三级av在线看 | 久久中文字幕一区二区 | 手机av免费在线 | 无人在线观看的免费高清视频 | xxxxx黄色片 噜噜噜噜噜色 | 亚洲欧洲自拍偷拍 | 久久作爱视频 | 91亚洲精品在线 | 国产12页 | 91视频青青草 | 日韩激情电影在线 | 午夜视频福利在线观看 | 精品久久久中文字幕 | 久操久操久操 | 欧美无专区 | 精品自拍第一页 | 国产卡一卡二卡三无线乱码新区 | 俺也去综合 | jizz在线播放 | 一级黄色片看看 | 水蜜桃av在线 | 国产精品极品白嫩在线 | 男人天堂影院 | 国产人妖在线播放 | 91蝌蚪91九色 | 亚洲欧美综合精品久久成人 | 女女h百合无遮涩涩漫画软件 | h色视频在线观看 | 黑人精品一区二区三区 | 91午夜精品亚洲一区二区三区 | 天天鲁 | 午夜一二区| 日本一区二区三区免费看 | 国产调教打屁股xxxx网站 | 色资源在线观看 | 精产国品一二三产区m553麻豆 | 国产在线一二 | 日韩熟妇一区二区三区 | 一级在线视频 | 亚洲人成影视 | 日韩中文字幕免费观看 | 亚洲天堂一区二区三区四区 | 亚洲色偷偷色噜噜狠狠99网 | 色老大网站 | 国产成人免费av一区二区午夜 | 国产a久久麻豆入口 | 免费福利av | videosex抽搐痉挛高潮 | 日本视频免费在线 | 美女扒开腿让人桶爽 | 18成人免费观看网站 | 久久综合国产精品 | 亚洲av毛片基地 | 90岁老太婆乱淫 | 日本a区 | 君岛美绪在线 | 久久在线免费 | 日韩成人在线观看 | 成人国产在线观看 | 午夜av毛片 | 成人婷婷| 中文在线a∨在线 | 麻豆社 | 人人妻人人澡人人爽欧美一区双 | 天堂网www.| 麻豆自拍偷拍 | 超碰在线观看免费 | 欧美大喷水吹潮合集在线观看 | 天天干天天插天天操 | 精品人妻一区二区三区四区不卡 | 日本xxxxxwwwww| 51久久| 图片区 小说区 区 亚洲五月 | 精品人人妻人人澡人人爽牛牛 | 午夜爽爽爽 | 91桃色网站 | 日韩视频免费在线 | 无人在线观看的免费高清视频 | www.日本黄色 | 欧美aa在线观看 | 毛片av网址 | 免费黄色片视频 | 欧美第一页在线 | 国产午夜精品久久久久久久久久 | gav成人 | 不卡视频国产 | 亚洲欧美日韩综合一区二区 | 99久久精品国产成人一区二区 | 女人舌吻男人茎视频 | 新超碰在线 | 麻豆视频网站在线观看 | 中国女人内谢69xxxx免费视频 | 精品999久久久一级毛片 |