主頁 > 軟體設計 > (??ヮ?)?【精品C語言整理】?(?ヮ??)女盆友纏著你讓你教她寫代碼怎么辦?安排,三萬字博文帶你走遍C語言,從此不再害怕編程

(??ヮ?)?【精品C語言整理】?(?ヮ??)女盆友纏著你讓你教她寫代碼怎么辦?安排,三萬字博文帶你走遍C語言,從此不再害怕編程

2021-08-09 08:40:23 軟體設計

大家好,我🏁🏁Aaron, 對于學習編程的 hxd
們都有一個困擾,女朋友想了解你在干嘛卻完全不懂代碼怎么辦,本文整理了C語言入門到進階全知識點的概述以及基本用法,從此讓你和女朋友一起幸福快樂的敲代碼,該專欄后期會出C語言知識點詳解和進階和各種經典例題喔~感興趣的朋友們可以訂閱下來慢慢學習!!

? ω ? ? ω ?

 👍點贊👍 +  👀關注👀  +  ?收藏?    

正文開始(老爺點個收藏慢慢看~)

文章目錄

  • 前言
  • 一、什么是C語言
  • 二、第一個C語言程式
  • 三、資料結構
  • 四、 變數,常量
    • 4.1 變數的定義方法
    • 4.2 變數的分類
    • 4.3 變數的使用
    • 4.4變數的作用域和生命周期
    • 4.5 常量
  • 五、 字串 + 轉義字符
    • 1.字串
    • 2.轉義字符
  • 六、注釋
  • 七、選擇(分支)陳述句
  • 八、 回圈陳述句
  • 九、 函式
  • 十、 陣列
    • 10.1 陣列定義
    • 10.2 陣列的下標
    • 10.3 陣列的使用
  • 十一、 運算子
  • 十二、 常用關鍵字
    • 12.1 關鍵字typedef
    • 12.2 關鍵字 static
  • 十三、 #define 定義常量和宏
  • 十四、 指標
    • 14.1 記憶體
    • 4.2 指標變數的大小
  • 十五、結構體
  • 總結


前言


本文旨在完整介紹C語言所學內從,是C語言專欄中的第一版,有興趣完整的,系統的學習C語言知識的讀者可以訂閱本專欄,博主將會努力整理,博主準備的C語言專欄總共有三版,本專欄會介紹到很多計算機底層知識,建議收藏,慢慢學習嗷!

本文目標:
基本了解C語言的基礎知識,對C語言有一個大概的認識
每個知識點就是簡單認識,不做詳細講解,專欄后期都會細講

全文重點

  • 什么是C語言
  • 第一個C語言程式
  • 資料型別
  • 變數、常量
  • 字串 + 轉義字符 + 注釋
  • 選擇陳述句
  • 回圈陳述句
  • 函式
  • 陣列
  • 運算子
  • 常見關鍵字
  • define 定義常量和宏
  • 指標
  • 結構體

正文開始

一、什么是C語言

C語言是一門通用計算機編程語言,廣泛應用于底層開發,C語言的設計目標是提供一種能以簡易的方式編譯、處理低級存盤器、產生少量的機器碼以及不需要任何運行環境支持便能運行的編程語言,

盡管C語言提供了許多低級處理的功能,但任然保持著良好的跨平臺的特性,以一個標準規格寫出的C語言程式可在許多電腦平臺上進行編譯,甚至包含一些嵌入式處理器(單片機或稱MCU)以及超級電腦等作業平臺

二十世紀八十年代,為了比年各開發廠商用的C語言語法差異,由美國國家標準局為C語言制定了一套完整的美國國家標準語法名稱為ANSI C,作為C語言最初的標準,目前2011年12月8日,國際標準化組織(ISO)和國際電工委員會(IEC)發布的C11標準是C語言的第三個官方標準,也是C語言最新的標準,該標準更好的支持了漢字函式名和漢字識別符號,一定程度上實作了漢字編程

C語言是一門面向程序的計算機編程語言,與C++,Java等面向物件的編程語言有所不同

其編譯器主要有Clang、GCC、WIN - TC、MSVC、Turbo C等,

接下來介紹其中我們必須要了解和掌握的!

什么是C語言呢?

C語言就是一種計算機語言

我們如何理解計算機語言?
其實將計算機語言和我們日常的語言類比起來就很好理解了

  • 計算機語言 - 人和計算機交流的語言(C/C++/Java/Python…)
  • 語言 - 人和人交流的語言(漢語、英語、日語)

C語言廣泛運用于底層開發

如圖:

這里的應用軟體的開發被稱為頂層開發
作業系統、驅動、硬體的開發則被稱為底層開發

計算機

任何一門沒有國際標準的語言,都不能被稱為一門好的語言,而C語言,恰好有!!

C標準:
C語言出現 --> C語言標準:
K & R
C89 --> C90 --> C98 --> C99 --> C11
二進制 --> 二進制代碼 --> 助記符(匯編語言) --> B語言 --> C語言
二進制:用0/1表示
在早期,用一個二進制代碼表示一種演算法,如:
00001011 - 加法 - ADD


編程工具:編譯器

  • VC6.0 - 98年的軟體(過于古老,不建議使用)

推薦使用:

  • VS2013 / VS2019 - 企業中常用的編譯器

IDE - 集成開發環境,呼叫的是底層的MSVC編譯器

  • Linux - gcc

  • Linux - clang

  • DEV - C++ - 學校推薦的!!!不建議使用,已經十幾年不再更新了

二、第一個C語言程式

寫一個C語言代碼步驟:

  1. 工具(編譯器) - VS2019

如果有碼友使用的是VS2013版本的編譯器,可能會遇到程式運行結果一閃而過的情況

解決辦法1:
設定專案屬性 --> 聯結器 --> 系統 --> 子系統 --> 控制臺

解決辦法2:
使用系統控制命令

system(“pause”);

# include <stdio.h>
# include <stdlib.h>

int main()
{
    printf("hehe\n");
    //system是一個庫函式
    //是用來執行系統命令的
    //pause - 暫定
    system("pause");
    
    return 0;
}

system是一個庫函式,是用來執行系統命令的,需要呼叫

#include - - 頭檔案
pause - 暫停控制臺的作用


  1. 寫代碼
    1. 創建專案
    2. 創建源程式
    3. test.c - 源檔案
      test.h - header頭檔案

    4. 寫出main函式
#include <stdio.h>
//stdio.h 是一個頭檔案的名稱
//std - 標準,i - input,o - output
//標準輸入輸出頭檔案

int main()  //  int - 整型
{
    //實際運行的代碼
    //列印hello world
    //列印函式 - printf - 庫函式,庫函式的使用得參考頭檔案的
    printf("hello world\n");
    return 0;
}

printf - 列印函式 - 庫函式,庫函式的使用得參考頭檔案

標準輸入輸出
stdio.h 是一個頭檔案的名稱
std - 標準, i - input, o - output

  1. 運行代碼
    1. 快捷鍵:

      ctrl +F5 / ctrl + Fn + F5

    2. 按鍵

這里補充一點關于鍵盤上 Fn 鍵的知識


補充
Fn - 輔助功能鍵
F1 - F12本來是在變成中被賦予了相應的功能
但為了迎合大眾小白用戶的使用體驗,有些鍵盤中賦給了F1 - F12鍵新的功能,如果想要使用這些鍵原本的功能,則需要按住 Fn + F1 - F12即可使用他們原本的功能
但這知識針對某些機器或者鍵盤所具有的功能輔助鍵,并不是所有的都需要這樣操作
而且有的鍵盤上的 Fn 鍵是可以關閉的,可根據自身硬體采取相應措施
據博主目前測驗,大部分筆記本電腦是會有 Fn 鍵的,而臺式機基本都是應鍵盤而異,有的有,有的沒有,有的像博主這樣的,可以關閉,很方便,美滋滋,



C程式寫法
寫C語言代碼時,雖然 VS2019 對 C99 標準并不是很支持,但我們現在敲代碼基本采用 C99 的新寫法為主,接下來展示C99和C89兩種主函式的寫法

# include <stdio.h>
//按F10

//main 函式是C語言程式的入口
//代碼是從main函式的第一行開始執行的
//main函式有且只有一個

//C99 中引入的寫法
int main()
{
    printf("hello world\n");
    printf("hello world\n");
    printf("hello world\n");
    printf("hello world\n");
    printf("hello world\n");
    
    return 0;
}
// 早期的寫法
void main()
{
    
}

main 函式是C語言程式的入口
代碼是從main函式的第一行開始執行的
main 函式有且只有一個
并且 main 函式可以放在程式的任意位置


三、資料結構

C語言是計算機語言
使用計算機語言寫代碼 - 寫程式
程式是用來干什么的? - 程式是用來解決我們日常生活中的問題的
解決我們生活中的問題,比如:

購物 - - 網上購物 - 網上商城 - 淘寶 - 京東
模擬生活中的購物,然后在網路上解決購物問題

寫程式就是利用各種各樣的演算法來解決如同上述這樣的問題,其實我們的網購就是利用了很多高級的演算法和程式拼接在一起實作的

既然要解決日常問題,那我們在生活中的資料是各式各樣的,如果C語言想要描述這些資料,就需要存在與之相匹配的資料型別

比如:
50 - 是整數 使用 int 整型型別
55.5 - 是小數 使用 float / double 浮點數型別

下面把C語言中的資料型別一一羅列:

char                //字符資料型別
short               //短整型
int                 //整型
long                //長整型
long long           //更長的整型
float               //單精度浮點型
double              //雙精度浮點型

我們在此可以思考一下:C語言中有沒有字串型別呢?

以下為C語言中資料型別的分類整理

字符型別
char
整型
short [int]
int
long [int]
long long [int]
浮點型
float - 單精度浮點型
double - 雙精度浮點型

小數為什么在C語言中被稱為浮點數呢?

123.45
12.345 * 10
1.2345 * 100
如上所示
因為小數點可以浮動,所以叫做浮點型

那么每種資料型別的所占記憶體空間大小是多少呢?
我們可以通過寫代碼來驗證和計算
這里就要用到我們的 sizeof 運算子,一個專門用來計算占記憶體空間大小的運算子

代碼如下:

#include <stdio.h>
int main()
{
    //printf - %d - 列印有符號數
    //printf("%d\n", 100);
    printf("%d\n", sizeof(char));
    printf("%d\n", sizeof(short));
    printf("%d\n", sizeof(int));
    printf("%d\n", sizeof(long));
    printf("%d\n", sizeof(long long));
    printf("%d\n", sizeof(float));
    printf("%d\n", sizeof(double));
    return 0;
}

這里注意, 如果 sizeof 的運算元是變數,括號是可以省去的,但如果 sizeof 的運算元是資料型別,這個括號不可以省略,否則會報出語法錯誤,無法通過編譯的!!!

運行結果:
運行結果
控制視窗列印了這些數字,那么他們的單位又是什么呢?

接下來就要介紹計算機中的單位了
計算機中的單位總共有如下這么幾種:
bit byte kb mb gb tb pb
而我們的計算機能夠識別的是二進制(0/1) --> 我們規定一個位元位只能存放一個二進制位,得出如下單位換算:
1byte = 8bit
1kb = 1024byte
1mb = 1024kb
1gb = 1024mb
1tb = 1024gb
1pb = 1024tb

sizeof 計算的單位是byte - 位元組

C語言標準規定:
sizeof (long) >= sizeof (int)
sizeof (long long) >= sizeof (long)
在64位機器上:sizeof (long) = = 8
在32位機器上:sizeof (long) = = 4

所以 long 型別不一定比 int 型別大
而 long long 型別也不一定比 long 型別大
這都是要取決于代碼處在多少位的機器上決定的,

那么為什么會出現這個多的型別呢?

其實很簡單

舉個例子:
錢的數目不同:可以有100元 10000元 10000000元 100億
年齡大小可以不同:可以有 10 歲,20 歲,100歲
不同大小的值存放在不同的型別中,可以避免浪費記憶體
比如用一個 long long 型別的變數去存放一個數字 20 ,實在是有些鋪張浪費了
又或者如果用 char 存放 100 億,那也存不下呀,所以我們需要有靈活的資料型別存放不同的資料

代碼展示:

#include <stdio.h>
int main()
{
    int age += 20;//age 在記憶體中分配4個位元組的空間
    char ch = 'w';//ch 在記憶體中分配1個位元組的空間
    short = age;
    //型別 變數名   值
    int salary = 10000;
}

注意:存在這么多的型別,其實是為了更加豐富的表達生活中的各種值

四、 變數,常量

生活中有些值是不變的(比如:圓周率,性別(?),身份證號碼,血型等等),
有些值是可變的(比如:年齡,體重,薪資),
不變的值,C語言中用常量的概念來表示,變的值C語言中用變數來表示,

4.1 變數的定義方法

定義一個變數,有一個固定的寫法:

變數型別 + 變數名 + 賦初值
如:int a = 10;

代碼展示:

#include <stdio.h>
int main()
{
    int age = 20;
    float weight = 55.8;
    return 0;
}

4.2 變數的分類

變數分為兩類:

  1. 全域變數
  2. 區域變數

下面用代碼的形式給大家演示,方便理解

#include <stdio.h>
//全域變數 - 在大括號外部定義的變數就是全域變數
int a = 100;

//區域變數就是在main函式中定義的變數 - 對不對? - error

void test()
{
    int b = 10;
}

int main()
{
    //區域標量 - 在大括號(代碼塊)內部定義的變數就是區域變數
    int a = 10;
    printf("a = %d\n", a);//區域變數和全域變數名字沖突(一樣)的情況下,區域優先
    return 0;
}

總結:

上面的區域變數global變數的定義其實是沒有什么問題的!
區域變數和全域變數名字沖突(一樣)的情況下,區域優先

全域變數 - 在大括號外部定義的變數就是全域變數
區域標量 - 在大括號(代碼塊)內部定義的變數就是區域變數

4.3 變數的使用

下面用代碼的形式給大家展示變數在創建之后是如何使用的:

int main()
{
    int num1 = 0;
    int num2 = 0;
    int sum = 0;
    
    //printf()是輸出函式 - 庫函式
    //scanf()是輸入函式 - 庫函式
    
    //scanf函式要正常使用,就得在scanf所在的源檔案的第一行加上#define _CRT_SECURE_NO_WARNINGS 1
    scanf("%d %d", &num1, &num2);
    
    sum = num1 + num2;
    
    printf("sum = %d\n", sum);
    
    return 0;
}

大家在使用編譯器寫代碼的時候,難免會經常遇到一個問題:
總用如下所示的安全警告
請添加圖片描述
這就是函式不安全問題

解決方法1:在當前代碼第一行加上
請添加圖片描述

_CRT_SECURE_NO_WARNINGS

如果每次寫代碼都要復制一次,未免太過于繁瑣,所以給出了一勞永逸的方法:

找到VS安裝路徑 --> 找到newc++file.cpp檔案 --> 編輯檔案,在檔案中加上上面這句話
之后每次使用會自動生成

解決方法2:我們可以看到警告后面會提示我們使用 scanf_s 函式,但博主這里建議大家使用第一種方法,因為 scanf_s 是編譯器里的函式,C語言各種版本中沒有這種函式,所以以后如果我們將代碼放在其他編譯器上運行,就會跑不起來,是我們的代碼不具有可移植性

其他不安全的函式:strcat / strcpy

那么為什么會出現不安全的現象呢?

給出不安全示范代碼如下:

//%d - 整型
//%s - 字串

int main()
{
    char arr[5] = {0};//陣列
    scanf("%s", arr);
    //scanf_s不是C語言的,是VS編譯器提供的
    //如果你的代碼中使用了scanf_s函式,你的代碼可能在其他的編譯器上無法編譯
    printf("%s\n", arr);
    
    return 0;
}

運行結果:
運行結果
如上述代碼和運行結果可知,我們在定義陣列的時候,只創建了大小為5的陣列,而我們在輸入的時候,如果輸入的字符個數大于5,則會導致程式崩潰,這也就是函式為什么會不安全的原因

但是就算使用上述方法,也只不過是讓編譯器不再警告,也就是忽略警告的作用,讓我們的代碼可知正常跑過去,但我們還是要人為的控制輸入字符的個數,不要讓輸入的字符過多,導致陣列越界,程式崩潰,

4.4變數的作用域和生命周期

1)變數的作用域:

作用域(scope):程式實際概念,通常來說,一段程式中所用到的名字并不總是有效 / 可用的,而限定這個名字的可用性代碼回傳就是這個名字的作用域,

  1. 區域變數的作用域就是區域變數所在的區域范圍
  2. 全域變數的作用域是整個工程

下面看兩個代碼解釋:

代碼1:

#include <stdio.h>
void test()
{
    int b = 100;
}

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

這段代碼的結果無法列印 a, b
因為 a 的作用域只能在自己的代碼塊內
b 只能在 test 函式內部使用

代碼2

#include <stdio.h>

int g_val = 100;

void test()
{
    printf("g_val = %d\n", g_val);
}

int main()
{
    printf("g_val = %d\n", g_val);
    test();
    return 0;
}

可以列印兩個 g_val = 100,因為 g_val 是定義在全域范圍內的,在整個工程中都可以使用

生命周期:

變數的生命周期值指的是變數的創建到變數的銷毀之間的一個時間段

  1. 區域變數的生命周期是:進入作用域(區域范圍)生命周期開始,出作用域(區域范圍)生命周期結束,
  2. 全域變數的生命周期是:整個程式(main函式)的生命周期
#include <stdio.h>
int main()
{
    {
        int a = 100;
        printf("1: a = %d\n", a);
    }
    printf("2: a = %d\n", a);
    
    //程式的生命周期和main函式的生命周期是一樣的
    //全域變數的生命周期和程式,main函式的生命周期是一樣的
    
    return 0;
}

列印結果為 1:a = 100
區域變數 a 在進入代碼塊時創建,出這個代碼塊之后就會銷毀,遇到第二個 printf 函式時已經不存在 a 這個變數了

全域變數演示:
test1.c

int g_val = 2000;

test2.c

#include <stdio.h>

//宣告外部變數
extern int g_val;  //  --  extern 關鍵字是宣告關鍵字,在做宣告時使用

int main()
{
    printf("g_val = %d\n", g_val);  // 2000
    
    return 0;
}

以上代碼說明:全域變數可以在不同的源檔案內起效用,但需要在使用之前提前宣告(使用 extern 關鍵字)

4.5 常量

C語言中的常量和變數的定義的形式有所差異

C語言中的常量分為以下幾種:

  • 字面常量
  • const 修飾的常變數
  • #define 定義的識別符號常量
  • 列舉常量

下面 一 一 介紹

  1. 字面常量

就是類似于普通數字一樣的常量,無法改變

下面用代碼演示:

#include <stdio.h>
int main()
{
    "abcdef";
    3.14;
    100;
     
    return 0;
}
  1. const 修飾的常變數

一個變數在被 const 修飾之后就會具有了常屬性,但本質上還是一個變數
在C99版本之前,被const 修飾的變數不能放在陣列中使用作為陣列的大小
但是在C99版本之后,引入了變長陣列的概念,可以將被 const 修飾的變數放在陣列定義大小的方括號( [ ] )內使用

#include <stdio.h>
int main()
{
    const int num = 10;
    num = 20;//對常變數進行賦值, num 的值無法被修改
    printf("%d\n", num);  // 結果依然是10
    
    const int n = 10;// n 雖然被 const 修飾,但本質上還是一個變數,不能用來被指定為陣列的大小(C99版本之前)
    int arr[n] = {0};
    //在C99標準之前,陣列的大小只能是常量
    //VS2019對C99支持的不是很好啊
    //不支持變長陣列,所以會報錯
    
    return 0;
}

在C99標準之前,陣列的大小只能是常量,但是在C99版本之后,陣列的大小可以用變數來表示,即為變長陣列,
但是博主使用的編譯器是: VS2019 ,而VS2019對C99標準支持的并不是很好啊,不支持變長陣列,所以會報錯,所以我們還是要對自己使用的編譯器稍作了解,再結合不同的C語言標準版本去學習,效果才能最優!!!

  1. #define 定義的識別符號常量

#define 定義的識別符號常量其實就是給定一個符號,讓他代表你在代碼里想要表示的值,代碼在編譯程序中,會將所有的符號替換成給定的值

下面用代碼給大家演示 #define 定義識別符號常量的效果和用法:

#include <stdio.h>

#define MAX 100    // 注意用 #define 定義的時候,陳述句后面沒有分號(;)

int main()
{
    printf("%d\n", MAX);
    MAX = 200; // err, 不能改變
    int arr[MAX] = {0};// 是可以的
    
    return 0;
}

我們要注意在使用 #define 定義的時候,陳述句后面沒有分號(;),并且在習慣上用 #define 定義的識別符號,我們都用大寫字母表示,
因為用 #define 定義的識別符號常量其本質上還是一個常量,代碼在編譯的程序中只是將其替換,所以在定義陣列大小的程序中是可以使用被 #define 定義的表示符常量的,而且并不是變長陣列,

  1. 列舉常量

我們在生活中會遇到很多可以 一 一列舉的常量,這些常量在C語言中就被稱為列舉常量,

比如:

性別:男(MALE),女(FEMALE),保密(SECRET)
三原色:R,G,B

下面用代碼演示列舉常量的用法:

#include <stdio.h>

//enum 列舉關鍵字
enum Sex
{
    //列舉變數
    MALE = 5, // 定義時賦初值是可以的
    FEMALE,
    SECRET
};  // -- 列舉常量的定義需要在代碼塊末端加上分號(;)

int main()
{
    enum Sex s = FEMALE;
    //MALE = 5;  err 不能改,列舉是常量但可以在定義時賦初值
    printf("%d\n", MALE);
    printf("%d\n", FEMALE);
    printf("%d\n", SECRET);
    
    return 0;
}

注意使用列舉關鍵字定義列舉常量時,代碼塊后面的分號不能少!!
我們在默認狀況下列印 MALE, FEMALE, SECRET 時,列印的是 0, 1, 2,這是編譯器默認的,而且其本質就是個常量,所以我們后期無法對其進行修改或者賦值,如果想要讓列舉常量具有我們想讓它有的值,只能用過在定義時就對其進行賦初值,賦成我們想要的值!

五、 字串 + 轉義字符

1.字串

我們都知道資料型別有:char int long …
那么有沒有字串型別呢?
答案是C語言中沒有字串型別

C語言中字串的表示形式:

“hello world!\n”

這種由雙引號(Double Quote)引起來的一串字符稱為字串字面值(String Literal),或者簡稱字串

ps:字串的結束標志是一個 ‘\0’ ,在計算字串長度的時候 \0 是結束標志,不算作字串的內容

字串用法代碼演示:

#include <stdio.h>

int main()
{
    //"abcdef";
    //字串可以存放在字符陣列中
    char arr1[] = "abc";// 在字串的末尾隱藏了一個\0,這個\0是字串的結束標志
    char arr2[] = {'a', 'b', 'c'};
    printf("arr1 = %s\n", arr1);
    printf("arr2 = %s\n", arr2);
    
    return 0;
}

列印結果為:
列印結果
我們可以通過試驗發現,如果在 arr2 后面加上一個 ‘\0’,則列印的結果會相同
原因是 arr2 中如果沒有 \0 判斷字串結束標志,那么就會繼續訪問記憶體中后面的內容,而這些內容都是編譯器預先放進的隨機值,當遇到記憶體中的 \0 (或者 0) 時,就停下來,

為解釋 \0 的作用,下面再舉一個例子,方便大家掌握,
代碼:

#include <stdio.h>
#include <string.h>

int main()
{
    char arr1[] = "abc";
    cahr arr2[] = {'a'. 'b', 'c'};
    
    //庫函式 - strlen - string lenth
    //string.h
    int len1 = strlen(arr1);
    int len2 = strlen(arr2);
    
    printf("len1 = %d\n", len1);
    printf("len2 = %d\n", len2);
    
    return 0;
}

列印結果:
運行結果
首先 strlen 是一個計算字串長度的庫函式,在遇到 \0 后就停下來,計算遇到 \0 之前訪問過得字符個數,len1 是 3很好理解,但是 len2 為什么是15呢?其實他的值不是永遠都是 15 的,而是一個大于等于 3 的隨機值
因為 arr2 中沒有 \0 ,所以 strlen 函式會繼續越界訪問記憶體中的其他后面的內容,直到訪問到記憶體中存放的 0 (或者 \0),就停下來,計算訪問過的字符的個數,也就是字串的長度

len1 :a, b, c, \0 在算到 \0 之前有3個字符,所以 len1 = 3;
len2 :a, b, c, …在 c 后面沒有 \0 ,所以會一直訪問記憶體后面的內容,遇到 \0 之前訪問了 15 個數,是隨機的,

再給出一個代碼:

下面代碼,列印的結果是什么?為什么?
(突出 ‘\0’ 的重要性)

#include <stdio.h>

int main()
{
    char arr1[] = "bit";
    char arr2[] = {'b', 'i', 't'};
    char arr3[] = {'b', 'i', 't''\0'};
    printf("arr1 = %s\n", arr1);
    printf("arr2 = %s\n", arr2);
    printf("arr3 = %s\n", arr3);
    return 0; }

2.轉義字符

剛才一直說到 \0 ,那他到底是個什么東西呢?

其實 \0 就是是一個字符 - 轉義字符 - 轉變原來的意思的字符

給出一個代碼,大家看一下:

代碼:

#include <stdio.h>

int main()
{
    printf("abcdef\n");
    printf("\0");
    printf("---------\n");
    printf("abc\ndef\n");// \n --  換行
    return 0;
}

請大家思考片刻,你們認為這段代碼的運行結果是什么呢?
.
.
.
.

.
.
.
.

運行結果結果:
運行結果
\0 被轉義為結束標志,不再是原來的字符 0,所以不會列印
n 加上 \ 變成 \n ,被轉義為換行,不再是原來的字符 n ,所以在 abc 后面換行再輸出 def

假如我們要在螢屏上列印一個目錄:c:\code\test.c
我們該如何寫代碼?

下面給大家展示轉義字符的效果:
代碼:

#include <stdio.h>
int main()
{
    printf("c:\code\test.c\n");  //  \t - 水平制表符
    return 0;
}

運行結果結果:
運行結果

這樣就不得不提一下轉義字符了,轉義字符顧名思義就是轉變意思,下面看一些轉義字符:

給大家附上一張常用轉義字符表(建議存起來,用到的時候可以查一查,不用特意去背出來):

轉義字符釋義
\ ?在書寫連續多個問號時使用,防止它們被決議成三字母詞
\ ’用于表示字符常量 ’
\ "用于表示一個字串內部的雙引號 "
\ \用于表示一個反斜杠,防止它被解釋成一個轉義序列符
\ a警告字符,蜂鳴
\ b退格符
\ f進紙符
\ n換行
\ r回車
\ t水平制表符
\ v垂直制表符
\ dddddd表示1-3個八進制的數字,如:\130
\ xdddd表示2個十六進制數字,如:\x30

下面簡單解釋幾個比較難懂的轉義字符:

  • 三字母詞 - ??) --> ]
    ??( --> [

    在問號前加上 \ 讓它變成普通的 ?,不再構成三字母詞
    現在大部分編譯器上都無法演示三字母詞了,用的太少!!!,導致很多編譯器直接就不支持這種寫法了,我們在這里作為了解即可

  • 列印單引號、雙引號

代碼演示:

#include <stdio.h>
int main()
{
    // %c - 字符格式
    // %s - 字串
    printf("%c\n", '\'');
    printf("%s\n", "\"");
    
    return 0;
}

輸出結果為一個單引號 ’ 和一個雙引號 "
加上 \ 讓其變成普通的單引號或者雙引號,不再是和另一個單引號或者雙引號結合構成的效果


ASCII編碼

我們知道在鍵盤上有很多的字符: a % & & 9 0 i o 但是計算機只能識別二進制,所以給所有的字符一個編號,這些編號就叫做ASCII編碼

#include <stdio.h>
int main()
{
    //十進制數字由0-9
    //十六進制數字:0-9 a b c d e f
    printf("%c\n", '\162');
    printf("%c\n", '\071');//57
    printf("%c\n", '\x5a');
    return 0;
}

第一行的運行結果為一個字符 r
分析:
\162 是轉義字符\ddd 的形式,表示的是一個三位的八進制數字,八進制數字 162 轉化為十進制數字對應的是 114,在ASCII碼中對應的字符是 r

這里給大家放上一張博主珍藏多年的 ASCII 碼值表,建議保存,不用背,遇到了就查一查!!!

ASCII表

簡單介紹一下幾個0:
0 數字0
\0 是一個轉義字符 - ASCII值是0
‘0’ 字符0 - ASCII碼值是48

下面看一道曾經某公司的筆試題:

代碼:

#include <stdio.h>
#include <string.h>

int main()
{
    printf("%d\n", strlen("abcdef"));  // 6
    printf("%d\n", strlen("abc\0def"));  // 3
    printf("%d\n", strlen("c:\test\328\test.c"));  // 14
    //識別轉義字符
    // \t, \32
    
    return 0;
}

這里特別注意:
第二行,遇到 \0 之后結束運行,后面的字符直接不進行計算了
第三行,\32 是一個轉義八進制數字,8是一個單獨的字符
雖然我們 \ddd 表示轉義三個八進制數字,但我們應該了解,二進制數字是 0/1,八進制數字是 0-7, 十進制數字是 0-9 ,十六進制數字是0 - 9 + a b c d e f,也就是0 - 15,所以這里的 \328,8明顯不是八進制數字,所以我們應該把 \32 看做一個字符,把 8 看做一個字符

六、注釋

注釋的用處:

  1. 代碼中有不需要的代碼可以直接洗掉,也可以直接注釋掉
  2. 代碼中有些代碼比較難懂,可以加一下注釋文字
#include <stdio.h>
int main()
{
    //printf("hehe\n");
    //此時運行不會出現任何結果
    
    int arr[10] = {0}; // 創建一個整型陣列,10個元素
    return 0;
}

下面用代碼演示C++和C語言兩種不同風格的代碼注釋

  • C++注釋風格(只能注釋一行)
    // 這里是注釋
    
  • C語言注釋風格(可以注釋多行)
/*
         這里是注釋
*/

C語言的這種注釋風格不支持嵌套使用,一般不建議,并且現在大多數編譯器在撰寫C代碼時是支持C++風格注釋的,所以我們在平常使用的時候可以多使用C++注釋風格

完整代碼給大家展示一下注釋的用法:

代碼:

#include <stdio.h>
int Add(int x, int y) {
    return x+y; }
/*C語言風格注釋
int Sub(int x, int y)
{
    return x-y;
}
*/
int main()
{
    //C++注釋風格
    //int a = 10;
    //呼叫Add函式,完成加法
    printf("%d\n", Add(1, 2));
    return 0; }

七、選擇(分支)陳述句

如果你好好學習,校招時拿一個好offer,走上人生巔峰,迎娶白富美!!!! 如果你不好好學習,畢業等于失業,只能回家賣紅薯🤢

這,就是選擇!

選擇陳述句(分支陳述句)流程圖:

選擇(分支)陳述句
選擇陳述句又叫分支陳述句

一般有以下兩種選擇(分支)陳述句

  • if else
  • switch

這里用代碼演示 if else 陳述句用法:

#include <stdio.h>

int main()
{
    int num = 0;
    //1 0
    printf("進入學校\n");
    printf("你要好好學習嗎?(選擇1/0):\n");
    scanf("%d", &num);
    if (1 == num)   //  -- 寫成 1 == num ,避免少些一個等號導致程式出bug
    {
        printf("堅持,拿到好offer\n");
    }
    else
    {
        printf("放棄,回家賣紅薯\n");
    }
        
    return 0;
}

注意: 博主在 if 陳述句后邊的判斷陳述句中寫成了 1 == num ,而不是 num == 1,這樣寫的目的是為了防止在寫代碼的時候,不小心漏掉一個等號,導致運行結果錯誤,程式產生 bug,這樣寫的好處是,如果少寫了一個等號,會導致編譯錯誤,程式無法正常編譯,更不可能產生 bug,

八、 回圈陳述句

有些事我們必須一直做,比如老師日復一日的講課,學生日復一日的學習,我們日復一日的敲代碼😎

回圈陳述句流程圖:
回圈陳述句
C語言中如何實作回圈呢?:

  • for陳述句 - (專欄下期講)
  • while陳述句 - 演示
  • do … while陳述句 - (專欄下期講)

下面給出 while 回圈的代碼實體:

代碼:

#include <stdio.h>

//while 回圈的實體
int main()
{
    int line = 0;
    printf("進入學校\n");
    
    while (line <= 20000)
    {
        printf("我要努力敲代碼!:%d\n", line);
        line++;
    }
    if (line > 20000)
    {
        printf("拿到好offer,迎娶白富美!!\n");
    }
    
    return 0;
}

九、 函式

我們從小學習數學,就一定會學到函式:
數學里的函式:
f (x) = 2 * x + 1

那么變成中的函式又是怎么一回事呢?
來吧,展示!
編程中的函式:

代碼:

#include <stdio.h>

int Add(int x, int y)
{
    int z = 0;
    z = x + y;
    return z;
}

int main()
{
    int num1 = 0;
    int num2 = 0;
    int sum = 0;
    scanf("%d %d", &num1, &num2);
    //求和
    //sum = num1 + num2;
    sum = Add(num1, num2);
    printf("%d\n", sum)
    return 0;
}

在主函式中呼叫函式,在呼叫之前定義或者宣告被呼叫函式,在函式呼叫中傳入實參,在函式中定義相同型別形參接收實參,被呼叫函式回傳值型別應當與函式型別保持一值,被呼叫函式回傳值應當被相同型別變數接收,形成函式呼叫鏈,

其中函式呼叫涉及函式堆疊幀的內容,如果碼友們有需要可以在評論區留言,博主去搜集整理!

函式的特點就是簡化代碼,代碼復用,不同分工

十、 陣列

想要存盤 1- 10 的數字,應該怎么辦?
C語言中給了陣列的定義:一組相同型別元素的集合

10.1 陣列定義

陣列中元素型別 + 陣列名 + 【陣列大小】 + 初始化內容

int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };

10.2 陣列的下標

C語言規定的每個元素都有一個下標,下標是從0開始的,
陣列可以通過訪問下標來訪問陣列內容,

如果陣列10各元素,下標范圍是0-9

int arr[10] = {1,2,3,4,5,6,7,8,9,10};

陣列下標演示表:

int arr[10]12345678910
下標0123456789

10.3 陣列的使用

陣列使用代碼演示:

#include <stdio.h>

int main()
{
    //陣列
    // 0-9
    //arr[10] - 表示下標為10的元素
    int arr[10] = {1,2,3,4,5};//不完全初始化,剩下默認初始化為0
    int arr[10] = {1,2,3,4,5,6,7,8,9,10};
    printf("%d\n", arr[4]);
    int i = 0;
    //計算陣列的元素個數
    //sizeof(arr)  計算的是陣列的總大小,單位是位元組
    //sizeof(arr[0])  計算的是陣列的第一個元素的大小  單位是位元組  -  4
    int sz = sizeof(arr) / sizeof(arr[0]);
    while (i = 0; i < 10; i++)
    {
        printf("%d ", arr[i]);// arr[i] 不是陣列,是陣列元素,i是變數
        i++;
    }
    return 0;
}

計算陣列大小的方式:sizeof (arr) / sizeof (arr[0])
讀者可能有的疑惑博主都在注釋中做了解釋,如果有其他問題,可以在下方評論區留言!

十一、 運算子

進行簡單的介紹:

算數運算子

+ - * / %

用法:

代碼演示:

#include <stdio.h>

int main()
{
    int a = 7 / 2;
    int b = 7 % 2; // 取模、余
    printf("%d\n", a); // 3
    printf("%d\n", b); // 1
    float f = 7 / 2.0;
    printf("%f\n", f); // 3.5
    
    return 0;
}

這里我們需要注意的是,整型除以整型的結果還是整型,這一點是永遠不會變的,所以哪怕我們定義的變數是 float 或者是 double 型別的,7 / 2 的結果還是 3 ,如果想要得到正確的值,就必須將其中一個整型改為浮點型,比如:7.0 / 2 或者 7 / 2.0 ,這樣的結果才會算出 3.5 ,請大家注意!

移位運算子

<< >>

用法:

代碼演示:

#include <stdio.h>

int main()
{
    int a = 12; // a是整型,4個位元組,32個bit
    //00000000000000000000000000001100
    int b = a << 1; // 左移運算子
    // 移動的是二進制位
    printf("%d\n", b); // 24
    
    return 0;
}

這里給大家解釋一下:
a = 12的二進制序列如下:
00000000000000000000000000001100
右移一位
00000000000000000000000000011000
結果就為24

位運算子

& :按(2進制)位與
| :按(2進制)位或
^ :按(2進制)位異或

用法:

代碼演示:

#include <stdio.h>

int main()
{
    int a = 3;
    int b = 5;
    //00000000000000000000000000000011
    //00000000000000000000000000000101
    
    //00000000000000000000000000000001   與
    //00000000000000000000000000000111   或
    //00000000000000000000000000000110   異或
    
    int c = a & b;  // 按位與 - 對應的二進制位有0則為0,兩個同時為1,才是1
    int d = a | b;  // 按位或 - 對應的二進制位有1則為1,兩個用時為0,才是0
    int e = a ^ b;  // 按位異或 - 相同為0,相異為1
    
    printf("%d\n", c); // 1
    printf("%d\n", d); // 7
    printf("%d\n", e);
    
    return 0;
}

賦值運算子:

= , += , -= , *= , /= , &= , |= , ^= , >>= , <<=

使用:

代碼演示:

#include <stdio.h>

int main()
{
    int a = 10;// 初始化
    float score = 0.0;
    a = 45; // 賦值
    score = 95.5; // 直接寫出來的浮點數默認是double型別
    //95.5f - 指定為float型別
    
    a = a + 5; //1
    a += 5;    //2 復合賦值符
    
    a *= 5;
    a /= 5;
    
    return 0;
}

單目運算子

將單目運算子之前,先解釋一下雙目運算子,對比起來看,就好理解了,

雙目運算子

int main()
{
    3 + 5;//  + 運算子
    //3 和 5 是運算元
    //3是左運算子
    //5是右運算子
    //+ 是雙目運算子
    return 0;
}

對于一個運算子而言,有兩個運算元,就叫做雙目運算子,
那么單目運算子,也就是對于一個運算子而言,只有一個運算元,那么這個運算子就被稱為單目運算子

下面給出一張表,介紹常用的單目運算子:

單目運算子
邏輯反操作
-負值
+正值
&取地址
sizeof運算元的型別長度(以位元組為單位)
~對一個數的二進制按位取反
- -前置、后置- -
++前置、后置++
*間接訪問運算子(解參考運算子)
(型別)強制型別轉換

單目運算子 - 只有一個運算元的運算子

下面簡單介紹幾個單目運算子的用法:

  • 邏輯反操作: !

顧名思義,就是把真的變成假的,假的變成真的,就叫做邏輯反操作

代碼演示:

#include <stdio.h>

int main()
{
    int flag = 1;
    //C語言是如何表示真偽的?
    //0就是假, 非0就是真
    
    //flag為真就列印hehe
    if (flag)
    {
        printf("hrhr\n");
    }
    if (!flag)
    {
        printf("haha\n");
    }
    
    int a = !flag;
    printf("%d\n", a); // 非0默認列印1
    
    return 0;
}

表示真偽,即為邏輯操作
但這里可能會有小伙伴陷入誤區,C語言規定,0為假,并不是1才是真,所有的非0都為真,只不過我們在編譯器上列印邏輯真的時候,編譯器默認列印1而已,并不是只有1為真!!!

  • 正負運算子:+ -

這個跟生活中或者數學里的正負是一樣的,不過多介紹,直接上代碼:

代碼演示:

#include <stdio.h>

int main()
{
    int a = -10;
    // a = -a;
    
    int b = +a;
    printf("%d\n", b);
    
    return 0}

這個概念比較簡單易懂,尤其是正號(+)在寫代碼時用的很少,這里的是正負號,而不是加減號,可不敢輕易弄混了!!

  • sizeof 運算子

需要注意:
sizeof 是運算子, 不是函式
運算子也叫作運算子

用法:用于計算所占記憶體空間的大小的一個運算子

代碼展示:

#include <stdio.h>
int main()
{
    int a = 10;
    int arr[10] = {0};
    int sz = sizeof(arr) / sizeof (arr[0]); 
    printf("%d\n", sizeof(int)); // 4
    printf("%d\n", sezeof(a)); // 4
    printf("%d\n", sizeof a); // 4
    printf("%d\n", sizeof(arr)); // 40
    printf("%d\n", sizeof(arr[0])); // 4
    printf("%d\n", sz); // 10 
    
    return 0;
}

可以看出,sizeof 計算變數所占空間大小時,可以寫成 sizeof a ,把括號給省略了,但我們可不敢隨便亂寫,在計算 int 或者 long 這樣的資料型別所占空間大小的時候,可不能輕易把括號給省略了;
使用 sizeof 還有一點需要注意,就是計算字串所占空間大小的時候,雖然我們看不見字串末尾隱藏的0,并且有strlen函式計算的時候也算不到這個\0,但它在記憶體中可是真實存在的,所以 sizeof 計算時也是會把 \0 所占記憶體空間的大小給算進去的!!

  • 按(2進制)位取反運算子: ~

用法:
如果一個整數存盤在記憶體中的二進制序列是:
00010101001010101000101010100010

那么它的按位取反則是:
11101010110101010111010101011101

講到按位取反運算子就不得不介紹原反補碼的知識:

原反補

計算機在記憶體中存放的也是二進制
而整數在記憶體中存放的是二進制的補碼
整數的二進制怎么表示呢?
整數的二進制表示方式有 3 種:
原碼 - 直接按照數字的正負轉換成二進制就是原碼
反碼 - 原始碼的符號位不變,其他按位取反,就是反碼
補碼 - 反碼 + 1,就是補碼

規定:
對于有符號整數,二進制最高位是符號位
最高位是0,表示正數
最高位是1,表示負數
無符號數的原碼、反碼、補碼相同
有符號數(負數)的原碼、反碼、補碼要通過計算獲得

下面看按位取反運算子的用法:

代碼演示:

#include <stdio.h>

int main()
{
    //int a = 0; // 32bit
    //00000000000000000000000000000000 - 原碼
    //00000000000000000000000000000000 - 反碼
    //00000000000000000000000000000000 - 補碼
    //int b = -1;
    //10000000000000000000000000000001 - 原碼
    //11111111111111111111111111111110 - 反碼
    //11111111111111111111111111111111 - 補碼
    
    int a = 0;
    //00000000000000000000000000000000 - 原碼
    //11111111111111111111111111111111 - 取反后的結果 - 補碼
    //11111111111111111111111111111110 - 反碼
    //10000000000000000000000000000001 - 原碼
    printf("~a = %d\n", ~a); // -1
    
    return 0;
}

我們的頭腦要始終保持清晰,要知道,無符號整數其實是沒有原碼、反碼、補碼的
而有符號數在記憶體中存盤的都是二進制的補碼,要知道它代碼多大的數,一定要通過計算得出

  • ++ - - 運算子

相信大家都有過跟博主一樣悲慘的遭遇,就是遇到了這兩個"可怕"的運算子,首先給大家展示一下博主在大一在學習C語言時遇到的期末考試題

#include <stdio.h>

int main()
{
    int a = 1;
    int b = (++a) + (++a) + (++a);
    
    printf("b = %d\n", b);
    
    return 0;
}

對于這樣的代碼,我只想用一個字來形容他,搓!!!!

其實這是一個錯誤的代碼,千萬不能寫出這樣的程式,也不要想這去研究這個代碼,想著就是要理解它,算出他的值,有興趣的碼友們可以復制這段代碼去不同編譯器上跑一下試試,答案肯定是五花八門的,同一個代碼在不同編譯器下跑出來居然是不同的結果,這樣的代碼還有什么研究的價值嗎?所以,希望大家在學習的時候遇到這樣的代碼直接跳過,千萬不要想著去弄懂,這個代碼本身就是錯誤的代碼!!!!

接下來看看真正我們會用到的知識點:

代碼演示:

#include <stdio.h>

int main()
{
    // ++ --
    int a = 10;
    int b = a++; // 后置++, 先使用,再++
    int c = ++a; ///前置++,先++,再使用
    
    printf("a = %d\n", a); // 12
    printf("b = %d\n", b); // 10
    printf("c = %d\n", c); // 12
    
    return 0;
}

我們唯一要會的就是前置++, - -和后置++,- -,所以這兩個運算子其實還是很好學的,
大家可不要被錯誤的代碼給帶偏了喔!
ps(小聲說:那些教我們這種代碼的大學老師真的會編程嗎??)

  • (型別),強制型別轉換

用法:其實原理很簡單,就相當于是亡羊補牢,發現自己的資料很所給的變數的資料型別不符合,編譯器會報錯,自己又懶得改或者特殊原因無法修改,加個強制型別轉換就ok了,不過這里要提示一點:可不敢隨便胡亂使用,畢竟是強制型別轉換,一下搞不好就很容易丟失精度的!!

代碼演示:

#include <stdio.h>

int main()
{
    int a = 3.14; // double -- > int
    int b = (int)3.14;// 將double強制型別轉換為int
    printf("a = %d\n", a);
    printf("b = %d\n", b);
    
    return 0;
}

這個運算子我們平時用的也很少,因為寫代碼一般要自己注意變數使用的規范,所以了解即可,

關系運算子

>
<
>=
<=
!=   // 用于測驗:“不相等”
==   // 用于測驗:“相等”

要注意將 == 和 = 兩個運算子區分開

== :判斷左右是否相等
!=:判斷左右是否不相等
= : 一個等號是賦值運算子,將右值賦給左值

用法:

代碼演示:

#include <stdio.h>

int main()
{
    int a = (3 > 5);
    printf("%d\n", a); // 0
    if (3 > 5)
    {
        printf("hehe\n"); // 不列印
    }
    
    return 0;
}

邏輯運算子

&&(邏輯與) | | (邏輯或)
他們的名字叫做邏輯運算子,也就是與和或,要和按位與和按位或區分開!
一個 & – > 叫做按(二進制)位與 或者取地址運算子,
兩個 & – > && – > 叫做邏輯與
一個 | – > 叫做按(二進制)位或
兩個 | – > || – > 叫做邏輯或

這邊舉個簡單例子:
比如
法定結婚年齡為:
男:22
女:20
那么想要結婚就必須兩個都滿足,就要用到與邏輯

用法:

代碼演示:

# include <stdio.h>
int main()
{
    int a = 25;
    int b = 19;
    // && 邏輯與 - 并且
    if ((a >= 22) && (b >= 20))
    {
        
    }
    // || 邏輯或 - 或者
    if ((a >= 22) || (b >= 20))
    {
        
    }
    return 0;
}

條件運算子(三目運算子)

格式:
exp1 ? exp2 : exp3
(exp表示運算式)

意思是先判斷運算式1,如果運算式1滿足,則整個運算式的結果為運算式2,如果運算式1不滿足,則整個運算式結果為運算式3
光說理解得比較的抽象,直接上代碼:

代碼演示:

#include <stdio.h>

int main()
{
    int a = 10;
    int b = 20;
    int max = 0;
    //找出最大值
    /*if (a > b)
    {
        max = a;
    }
    else 
    {
        max = b;
    }*/
    //條件運算子
    max = (a > b) ? a : b;
    printf("max = %d\n", max);
    
    return 0;
}

可以看出,要實作找出兩個數最大值,如果用普通的方法需要8行代碼,最少也需要4行代碼,但是如果使用三目運算子,一行就搞定了,你學廢了嗎?

逗號運算式

格式:
exp1, exp2, exp3, … expN
(exp表示運算式)

逗號運算式就是從左到右依次計算,然后將最后一個運算式的結果賦給整個運算式,

用法:

代碼展示:

#include <stdio.h>
int main()
{
    int a = 3;
    int b = 5;
    int c = 6;
    // 逗號運算式 - 從左向右依次計算,整個運算式的結果是最后一個運算式的結果,
    int d = (a -= 2, b = a + c, c = a - b);  // -6
    // a = 1  b = 7  c = -6
    return 0;
}

注意在 int d 這行代碼里,整個運算式結果是最后一個運算式的結果,賦給變數d,但其他的運算也是需要進行的,比如說,這里最后的結果a應該變成了1,b應該變成了7,然后才是c變成了-6,再將-6賦給我們的變數d,

下標參考運算子、函式呼叫運算子和結構體成員

[] , () , . , ->

看用法:
[] : 在陣列中使用,叫做下標參考運算子
() : 在呼叫函式時使用,叫做函式呼叫運算子
. 和 -> 叫做結構體成員,在本文后面介紹結構體時給大家講解

代碼演示:

#include <stdio.h>

// int 表示函式呼叫完成之后回傳一個整數
int Add(int x, int y)
{
    int z = x + y;
    return z;
}

//void 是無的意思
//就是說函式test呼叫完成之后什么都不回傳
void test()
{
    printf("test\n");
}

//main函式最后也會回傳
//main函式也是要被呼叫的
int main()
{
    int arr[10] = {1,2,3,4,5,6};
    arr[4]; // [] - 下標參考運算子
    // [] 的運算元是arr, 4
    
    int a = 10;
    int b = 20;
    // 函式呼叫, ()就是函式呼叫運算子
    // ()的運算元是 Add, a, b
    int sum = Add(a, b);
    printf("sum = %d\n", sum);
    test(); // ()函式呼叫運算子
    //運算元是test
    return 0;
}

代碼中void 是無的意思
就是說函式test呼叫完成之后什么都不回傳

一些可能有問題的知識博主都在注釋中解釋了,有什么問題可以在下方評論區留言哦!
有關于函式堆疊幀的問題篇幅較長,如果有想要看的,可以在評論區留言,博主單獨整理喔~~

十二、 常用關鍵字

32個C語言關鍵字:

auto break case cahr const continue default do double else enum extern float for goto if int long register return short signed sizeof static struct switch typedef union unsigned void volatile while

下面對關鍵字做一下分類,方便大家學習時理解,

auto 關鍵字 - 修飾臨時變數
一般是省略的,給大家看看什么意思:

代碼:

#include <stdio.h>

int main()
{
    auto int a = 10; //auto 
    //臨時變數都是auto的,一般省略
    return 0;
}

我們平常學習一般是見不到這個關鍵字的,通常省略

回圈:break continue do for while
分支(選擇):case switch default else if
型別:char double float int long signed unsigned short
自定義型別:enum - 列舉 struct
extern - 宣告
goto陳述句
register - 暫存器
return - 回傳 - 函式
sizeof - 計算記憶體大小
static - 靜態關鍵字
typedef - 型別重定義
union - 聯合體關鍵字
void - 無,空
volatile - Linux系統部分
const - 修飾變數 - 常變數

關鍵字

  1. 關鍵字不能自己創建
  2. 變數名不能是關鍵字

注:關鍵字,先介紹下面幾個,后期遇到講解,

12.1 關鍵字typedef

typedef 顧名思義是型別定義,這里應該理解為型別重命名

用法:

其實就是在開頭給你想要改名字的型別做一個重命名

代碼演示:

#include <stdio.h>

//將unsigned int 重命名為unit_32,所以unit_32也是一個型別名
typedef unsigned int uint_32;

int main()
{
    //觀察age1和age2,這兩個變數的型別是一樣的
    unsigne int age1 = 10;
    uint_32 age2 = 20;
    
    return 0;
}

上述代碼中兩種寫法其實在 typedef 關鍵字的作用下變成了同一個意思,一般是為了方便使用,但使用時最好加上備注,好讓我們的代碼更具有可讀性,日后別人看我們的代碼或者我們自己再回過頭來看自己的代碼的時候,不至于看不懂,,

12.2 關鍵字 static

在C語言中:
static 是用來修飾變數和函式的

static 關鍵字有三種用法

1. static 修飾區域變數 - 靜態區域變數
使得區域變數出了自己的范圍也不銷毀,其實是改變了區域變數的生命周期,但作用域還是區域的
2. static 修飾全域變數 - 靜態全域變數
全域變數本身是具有外部鏈接屬性的,但是用 static 修飾全域變數,會使得全域變數失去外部鏈接屬性,變成內部鏈接屬性,所以 static 修飾的全域變數嗎,只能在自己的 .c 檔案中使用
3. static 修飾函式 - 靜態函式
函式默認是具有外部鏈接屬性的,但是被 static 修飾之后,會使得函式失去外部鏈接屬性,變成內部鏈接屬性,所以 static 修飾的函式只能在自己所在的 .c 檔案內部使用,不能在其他檔案內部使用

下面給大家一 一羅列static的用法

  • static 修飾區域變數的用法:
#include <stdio.h>

void test()
{
    int a = 1;  // 每次退出銷毀,進入重新創建 a = 1;
    //static修飾區域變數
    static b = 1;  // 出范圍不會銷毀
    a++;
    b++;
    printf("%d ", a); // 2 2 2 2 2 2 2 2 2 2
    printf("%d ", b); // 2 3 4 5 6 7 8 9 10 11
}

int main()
{
    int i = 0;
    while (i < 10)
    {
        test();
        i++;
    }
    return 0;
}

a 的輸出結果為 :2,2,2,2,2,2,2,2,2,2
b 的輸出結果為:2,3,4,5,6,7,8,9,10,11
原因就是static修飾了區域變數,延長了區域變數的生命周期,出了作用域不銷毀,重新進作用域時自然也不會重新創建,所以此時的 a 還是保持上一次的賦值,所以會累加,

  • static 修飾全域變數的用法:

代碼展示:
test1.c

//代碼1
//test1.c
int g_val1 = 50;
static int g_val2 = 100;

test2.c

//代碼2
//test2.c
#include <stdio.h>

//宣告
int g_val1;
int g_val2;

int main()
{
    printf("g_val1 = %d\n", g_val1);
    printf("g_val2 = %d\n", g_val2);
    return 0;
}

以上代碼只會輸出 g_val1 而不會輸出 g_val2,就是其中的道理,在源檔案編譯成目標程式(.obj)之后鏈接的時候,被 static 修飾的全域變數將不具有外部鏈接屬性,無法正常列印 g_val1

  • static 修飾函式的用法:

代碼展示:
test1.c

//代碼1
//test1.c
#include <stdio.h>

void test1()
{
    printf("1: %d\n", g_val);
}

static void test2()
{
    printf("2: %d\n", g_val);
}

test2.c

//代碼2
//test2.c
#include <stdio.h>

int main()
{
    int g_val = 100;
    test1(g_val);
    test2(g_val);
    return 0;
}

以上代碼只會列印 : 1:100, 而不會列印: 2:100, 原因就是 test2 這個函式被 static 修飾,使得其失去了外部鏈接屬性,無法正常鏈接運行

剩余關鍵字會在后續專欄里講解,如果碼友們有需要可以在評論區留言,博主去整理出一期C語言所有關鍵字詳解~

十三、 #define 定義常量和宏

#define 定義識別符號常量:

#define MAX 1000

#define 定義宏

#define ADD(x, y) ((x) + (y))

利用 #define 定義其本質上就是進行替換,甚至不占記憶體!非常方便!!

代碼演示:

//define 定義識別符號常量
#define NUM 1000
#include <stdio.h>
//define 定義宏
#define ADD(X, Y) ((X) + (Y)) // -- 這里注意習慣上字母全大寫,并且無分號(;)  

int Add(int x, int y)
{
    int z = x + y;
    return z;
}

int main()
{
    int a = NUM;
    printf("a = %d\n", a);  //  1000
    // 函式 - 呼叫
    int x = 10;
    int y = 20;
    int sum1 = Add(a, b);
    printf("sum1 = %d\n", sum1);  // 30
    
    // 宏 - 替換
    int m = 10;
    int n = 10;
    int sum2 = ADD(m, n);
    printf("sum2 = %d\n", sum2);  // 20
    
    return 0;
}

輸出結果博主都在注釋中標明了喔~ 又不懂的地方歡迎在評論區留言!!


經過上述介紹,相比大家已經對編程有了一定的了解,接下來給大家介紹我們編輯的代碼是如何實作的

PS 我們在編譯器中編輯的代碼是在源檔案( .c 檔案)中進行的,若干個源檔案在編輯完成之后,首先由預處理程式對預處理指令進行編譯,如將 stdio.h 檔案替換
#include<stdio.h>,接著分別對各個源檔案進行編譯,生成一個個目標程式( .obj檔案)然后將生成的目標程式和函式庫鏈接,生成一個可執行程式( .exe檔案),然后進行程式運行,

這個知識點對我們寫代碼也許幫助不大,但其實能讓我們更好的理解代碼在編譯器中的執行程序,如果練習演算法相當于練習技能,那么學習這些知識應當屬于修煉內功


十四、 指標

14.1 記憶體

記憶體是電腦中特別重要的存盤器,計算機中程式的運行都是在記憶體中進行的,所以為了有效地使用記憶體,就把記憶體劃分為一個個小的記憶體單元,每個記憶體單元的大小是一個位元組,為了能夠有效地訪問到記憶體中的每個單元,就給記憶體單元進行了編號,這些編號就稱為該記憶體單元的地址

下面給出我們常用來理解記憶體的圖:
記憶體條
學習指標之前我們一定要了解一下兩個問題:

  1. 一個小的格子稱為一個記憶體單元 - 多大?
    1byte – 一個位元組
  2. 編號 - 地址 - 地址是怎么產生的呢?
    在計算機中:
    分為32位機器和64位機器
    32位: 32根地址線 – 地址線(0/1)
    00000000000000000000000000000000
    00000000000000000000000000000001
    00000000000000000000000000000010
    00000000000000000000000000000011

    011111111111111111111111111111111111
    10000000000000000000000000000000

    1111111111111111111111111111111111111
    總共有 2 ^ 32 次方個不同的編號

下面給出記憶體條的地址示意圖:
記憶體地址

變數都有地址,取出變數地址如下:

下面代碼介紹取地址運算子的用法:

代碼:

#include <stdio.h>
int main()
{
    int a = 10;
    //列印地址,%p是以地址的形式列印
    printf("%p\n", &a); 
    // 注:&a 的時候,取出的是a所占記憶體中4個位元組中第一個位元組的地址(較小的地址)
    //取出a的記憶體地址
    int* pa = &a; // 數值
    // pa稱為指標
    
    char ch = 'w';
    char* pc = &ch;
    
    //指標變數是一種變數,這個變數是用來存放地址的
    
    return 0;
}

指標變數是一種變數,這個變數是用來存放地址的
&a 的時候,取出的是a所占記憶體中4個位元組中第一個位元組的地址
pa稱為指標變數

那地址如何存盤,需要定義指標變數,

定義指標變數時,要根據地址中存放的元素的資料型別來定我們的指標變數定義為什么型別的

int num = 10;
int *p;//p為一個整形指標變數
p = &num;

指標的使用實體:
代碼:

#include <stdio.h>
int main()
{
   int num = 10;
   int *p = &num;
   *p = 20;
   return 0; 
}

常用輸出控制符:
%s - 字串
%c - 字符
%d - 有符號的整型
%f - 單精度浮點數
%lf - 雙精度浮點數
%p - 地址 - 地址的十六進制表示形式


* : 解參考運算子

指標的用法:
利用解參考運算子可以通過地址改變變數中的元素

代碼演示:

#include <stdio.h>
int main()
{
    int a = 10;
    int* pa = &a;
    *pa = 20; // * 解參考運算子
    // a 被改成了20
    printf("%d\n", a); // 20
    
    return 0;
}

其實 const 可以修飾指標,讓其無法進行解參考操作或者無法存放其他的地址,與 const 所處的位置有關,這些知識點在本專欄后期將指標的地方會詳細介紹,有興趣的小伙伴可以訂閱該專欄喔~

以整型指標舉例,可以推廣到其他型別,如:

代碼演示:

#include <stdio.h>
int main()
{
   char ch = 'w';
   char* pc = &ch;
   *pc = 'q';
   printf("%c\n", ch);
   return 0; 
}

4.2 指標變數的大小

指標變數就是用來存放地址的
地址就是那個編號 - 編號所占記憶體空間大小可能是4byte,也可能是8byte
32位環境下 :指標所占記憶體空間大小是4位元組
64位環境下 :指標所占記憶體空間大小是8位元組

代碼試驗:

#include <stdio.h>

int main()
{
    char ch = 'w';
    char* pc = &ch;
    
    printf("size of pc = %d\n", sizeof(pc));
    
    return 0;
}

輸入結果為 4 或 8
與是什么型別的指標變數無關!!

如果是 32 位機器,每 8 位是一個位元組,則有 4 位元組
如果是 64 位機器,每 8 位是一個位元組,則有 8 位元組

結論:指標大小在32位平臺是4個位元組,64位平臺是8個位元組,

十五、結構體

結構體是C語言中特別重要的知識點,結構體使得C語言有能力描述復雜型別

比如:

人: 名字 + 電話 + 性別 + 年齡 + 地址
書: 書名 + 作者 + 定價 + 出版社 + 書號

復雜物件,不能簡單的用普通的型別直接描述
要通過結構體型別描述
例如:

struct Stu
{
    char name[20];//名字
    int age;      //年齡
    char sex[5];  //性別
    char id[15]//學號
};

結構體的初始化:

代碼:

//列印結構體資訊
struct Stu s = {"張三"20"男""20180101"};
//.為結構成員訪問運算子
printf("name = %s age = %d sex = %s id = %s\n", s.name, s.age, s.sex, s.id);
//->運算子
struct Stu *ps = &s;
printf("name = %s age = %d sex = %s id = %s\n", ps->name, ps->age, ps->sex, ps- >id);

結構體 - 組合型別 - 自己創造的型別 - 自定義型別 - struct

代碼舉例:

#include <stdio.h>

//書的型別
//書名+定價+書號
struct Book
{
    char name[30]; // 書名
    float price; // 定價
    char id[20]; // 書號
};  //  -- 這里的分號不能漏了

void Print (struct Book* pb)
{
    //寫法一:
    printf("書名:%s\n", (*pb).name);
    printf("定價:%f\n", (*pb).price);
    printf("書號:%s\n", (*pb).id);
    //寫法二:
    printf("書名:%s\n", pb -> name);
    printf("定價:%f\n", pb -> price);
    printf("書號:%s\n", pb -> id);
}

int main()
{
    // 結構體變數的定義和初始化
    struct Book b1 = {"C語言程式設計", 55.0f, "C1001020"};
    struct Book b2 = {"資料結構"60.0f"DS001001"};
    struct Book b3 = {"作業系統"71.5f, "OS0001001"};
    printf("書名:%s\n", b1.name);
    printf("定價:%f\n", b1.price);
    printf("書號:%s\n", b1.id);
    
    Print(&b1);
    
    return 0;
}

列印時呼叫成員有兩種寫法:

  1. 結構體變數 .成員名
  2. 指標變數 -> 成員名

結構體型別就先介紹這么多,因為比較復雜,專欄后期也會繼續將,想了解的小伙伴們可以跟進哦!

總結

本文介紹了C語言從入門到進階的知識點內容,每個知識點介紹的其實并不詳細,本專欄下一章會詳細介紹每個分支的具體用法以及專案實戰!!,本文目的旨在讓菜鳥不再害怕學習編程,讓高手重溫經典,更深一步了解編程,以上就是本文所有內容,我是Aaron,感謝您的閱讀,下期再見!!!

最后別忘了一鍵三連哦
點贊👍 + 收藏? + 關注👀!!!

😊😊😊

點贊

轉載請註明出處,本文鏈接:https://www.uj5u.com/ruanti/292550.html

標籤:其他

上一篇:第16屆智能車競賽雙車接力組—直立車經驗語錄

下一篇:長達萬字的git常用指令總結(適合小白及在作業中想要對git基本指令有所了解的人群)建議收藏

標籤雲
其他(157675) Python(38076) JavaScript(25376) Java(17977) C(15215) 區塊鏈(8255) C#(7972) AI(7469) 爪哇(7425) MySQL(7132) html(6777) 基礎類(6313) sql(6102) 熊猫(6058) PHP(5869) 数组(5741) R(5409) Linux(5327) 反应(5209) 腳本語言(PerlPython)(5129) 非技術區(4971) Android(4554) 数据框(4311) css(4259) 节点.js(4032) C語言(3288) json(3245) 列表(3129) 扑(3119) C++語言(3117) 安卓(2998) 打字稿(2995) VBA(2789) Java相關(2746) 疑難問題(2699) 细绳(2522) 單片機工控(2479) iOS(2429) ASP.NET(2402) MongoDB(2323) 麻木的(2285) 正则表达式(2254) 字典(2211) 循环(2198) 迅速(2185) 擅长(2169) 镖(2155) 功能(1967) .NET技术(1958) Web開發(1951) python-3.x(1918) HtmlCss(1915) 弹簧靴(1913) C++(1909) xml(1889) PostgreSQL(1872) .NETCore(1853) 谷歌表格(1846) Unity3D(1843) for循环(1842)

熱門瀏覽
  • 面試突擊第一季,第二季,第三季

    第一季必考 https://www.bilibili.com/video/BV1FE411y79Y?from=search&seid=15921726601957489746 第二季分布式 https://www.bilibili.com/video/BV13f4y127ee/?spm_id_fro ......

    uj5u.com 2020-09-10 05:35:24 more
  • 第三單元作業總結

    1.前言 這應該是本學期最后一次寫作業總結了吧。總體來說,對作業的節奏也差不多掌握了,作業做起來的效率也更高了。雖然和之前的作業一樣,作業中都要用到新的知識,但是相比之前,更加懂得了如何利用工具以及資料。雖然之間卡過殼,但總體而言,這幾次作業還算完成的比較好。 2.作業程序總結 相比前兩個單元,此單 ......

    uj5u.com 2020-09-10 05:35:41 more
  • 北航OO(2020)第四單元博客作業暨課程總結博客

    北航OO(2020)第四單元博客作業暨課程總結博客 本單元作業的架構設計 在本單元中,由于UML圖具有比較清晰的樹形結構,因此我對其中需要進行查詢操作的元素進行了包裝,在樹的父節點中存盤所有孩子的參考。考慮到性能問題,我采用了快取機制,一次查詢后盡可能快取已經遍歷過的資訊,以減少遍歷次數。 本單元我 ......

    uj5u.com 2020-09-10 05:35:48 more
  • BUAA_OO_第四單元

    一、UML決議器設計 ? 先看下題目:第四單元實作一個基于JDK 8帶有效性檢查的UML(Unified Modeling Language)類圖,順序圖,狀態圖分析器 MyUmlInteraction,實際上我們要建立一個有向圖模型,UML中的物件(元素)可能與同級元素連接,也可與低級元素相連形成 ......

    uj5u.com 2020-09-10 05:35:54 more
  • 6.1邏輯運算子

    邏輯運算子 1. && 短路與 運算式1 && 運算式2 01.運算式1為true并且運算式2也為true 整體回傳為true 02.運算式1為false,將不會執行運算式2 整體回傳為false 03.只要有一個運算式為false 整體回傳為false 2. || 短路或 運算式1 || 運算式2 ......

    uj5u.com 2020-09-10 05:35:56 more
  • BUAAOO 第四單元 & 課程總結

    1. 第四單元:StarUml檔案決議 本單元采用了圖模型決議UML。 UML檔案可以抽象為圖、子圖、邊的邏輯結構。 在實作中,圖的節點包括類、介面、屬性,子圖包括狀態圖、順序圖等。 采用了三次遍歷UML元素的方法建圖,第一遍遍歷建點,第二、三次遍歷設定屬性、連邊,實作圖物件的初始化。這里借鑒了一些 ......

    uj5u.com 2020-09-10 05:36:06 more
  • 談談我對C# 多型的理解

    面向物件三要素:封裝、繼承、多型。 封裝和繼承,這兩個比較好理解,但要理解多型的話,可就稍微有點難度了。今天,我們就來講講多型的理解。 我們應該經常會看到面試題目:請談談對多型的理解。 其實呢,多型非常簡單,就一句話:呼叫同一種方法產生了不同的結果。 具體實作方式有三種。 一、多載 多載很簡單。 p ......

    uj5u.com 2020-09-10 05:36:09 more
  • Python 資料驅動工具:DDT

    背景 python 的unittest 沒有自帶資料驅動功能。 所以如果使用unittest,同時又想使用資料驅動,那么就可以使用DDT來完成。 DDT是 “Data-Driven Tests”的縮寫。 資料:http://ddt.readthedocs.io/en/latest/ 使用方法 dd. ......

    uj5u.com 2020-09-10 05:36:13 more
  • Python里面的xlrd模塊詳解

    那我就一下面積個問題對xlrd模塊進行學習一下: 1.什么是xlrd模塊? 2.為什么使用xlrd模塊? 3.怎樣使用xlrd模塊? 1.什么是xlrd模塊? ?python操作excel主要用到xlrd和xlwt這兩個庫,即xlrd是讀excel,xlwt是寫excel的庫。 今天就先來說一下xl ......

    uj5u.com 2020-09-10 05:36:28 more
  • 當我們創建HashMap時,底層到底做了什么?

    jdk1.7中的底層實作程序(底層基于陣列+鏈表) 在我們new HashMap()時,底層創建了默認長度為16的一維陣列Entry[ ] table。當我們呼叫map.put(key1,value1)方法向HashMap里添加資料的時候: 首先,呼叫key1所在類的hashCode()計算key1 ......

    uj5u.com 2020-09-10 05:36:38 more
最新发布
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:20:47 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:20:25 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:20:17 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:20:10 more
  • 【中介者設計模式詳解】C/Java/JS/Go/Python/TS不同語言實作

    * 中介者模式是一種行為型設計模式,它可以用來減少類之間的直接依賴關系,
    * 將物件之間的通信封裝到一個中介者物件中,從而使得各個物件之間的關系更加松散。
    * 在中介者模式中,物件之間不再直接相互互動,而是通過中介者來中轉訊息。 ......

    uj5u.com 2023-04-20 08:19:44 more
  • 露天煤礦現場調研和交流案例分享

    他們集團的資訊化公司及研究院在一個礦區正在做智能礦山的統一平臺的 試點,專案投資大概1億,包括了礦山的各方面的內容,顯示得我們這次交流有點多余。他們2年前開始做智能礦山的規劃,有很多煤礦行業專家的加持,他們的描述是非常完美,但是去年底應該上線的平臺,現在還沒有看到影子。他們確實有很多場景需求,但是被... ......

    uj5u.com 2023-04-20 08:19:07 more
  • 《社區人員管理》實戰案例設計&個人案例分享

    設計是一個讓人夢想成真程序,開始編碼、測驗、除錯之前進行需求分析和架構設計,才能保證關鍵方面都做正確 ......

    uj5u.com 2023-04-20 08:18:57 more
  • 軟體架構生態化-多角色交付的探索實踐

    作為一個技術架構師,不僅僅要緊跟行業技術趨勢,還要結合研發團隊現狀及痛點,探索新的交付方案。在日常中,你是否遇到如下問題 “ 業務需求排期長研發是瓶頸;非研發角色感受不到研發技改提效的變化;引入ISV 團隊又擔心質量和安全,培訓周期長“等等,基于此我們探索了一種新的技術體系及交付方案來解決如上問題。 ......

    uj5u.com 2023-04-20 08:18:49 more
  • 05單件模式

    #經典的單件模式 public class Singleton { private static Singleton uniqueInstance; //一個靜態變數持有Singleton類的唯一實體。 // 其他有用的實體變數寫在這里 //構造器宣告為私有,只有Singleton可以實體化這個類! ......

    uj5u.com 2023-04-19 08:42:51 more
  • 【架構與設計】常見微服務分層架構的區別和落地實踐

    軟體工程的方方面面都遵循一個最基本的道理:沒有銀彈,架構分層模型更是如此,每一種都有各自優缺點,所以請根據不同的業務場景,并遵循簡單、可演進這兩個重要的架構原則選擇合適的架構分層模型即可。 ......

    uj5u.com 2023-04-19 08:42:41 more