個人全棧之路-C語言基礎之數據存儲

個人全棧之路-C語言基礎之數據存儲

2.1 計算機的計算單位

2.1.1 容量單位

在購買電腦時,一般會選擇高配置的內存、硬盤。例如最新款(2019)15寸的MacBookPro已經能夠選配32G內存和4T的固態硬盤,而這裏的32G和4T就是經常使用的容量單位。html

在物理層面,咱們使用高低電平來記錄信息,一般使用高電平表示1,低電平表示0,所以在計算機底層只能認識0,1兩種狀態。而0,1可以表示的內容太少,迫切須要更大的容量表示方法,所以誕生了字節(Byte),千字節(KB),兆字節(MB),吉字節(GB),太字節(TB),拍字節(PB),艾字節(EB),它們之間的換算關係以下所示linux

1EB=1024PB
1PB=1024TB
1TB=1024GB
1GB=1024MB
1MB=1024KB
1KB=1024B
1Byte=8bit

位(bit)是最小的計算機容量單位,一般用於門電路。
而字節(Byte)、千字節(KB)、兆字節(MB)表示網絡傳輸,文件大小,是最基本的容量計量單位。
吉字節(GB)一般用於表示計算機內存、磁盤的容量單位
太字節(TB),拍字節(PB)一般是用於表示雲盤、移動硬盤的容量單位
艾字節(EB)一般是用於表示數據中心的容量單位web

如今一般筆記本的內存一般是8G,16G,32G,64G等等,而運行在筆記本之上的操做系統廣泛都是64位的,由於32位系統只能使用4G內存,下面是4G的內存換算算法

4G=2^2 * 2^10 * 2^10 * 2^10 =4*1024*1024*1024=2^32

在購買內存或者買移動硬盤時,一般使用的存儲單位就是GB或者是TB,
可是在買4T的移動硬盤時,實際的可用容量卻只有3T多,由於計算機的存儲單位是以2的10次方(即1024)換算,而硬盤廠商們是以1000爲換算單位。編程

4T的硬盤換算成位以下所示windows

4T=4*1024GB*1024MB*1024KB*1024B*8bit

而硬盤廠商的實際容量數組

4T=1000*1000*1000*1000*8

所以實際的可用容量是緩存

4*1000*1000*1000*1000/1024/1024/1024/10243.63T

而在一些互聯網巨頭(例如國內的BAT,國外的亞馬遜、蘋果、微軟、谷歌,臉書)公司中,可能使用到比TB更大的海量數據,也就是PB或者EB,它們的換算單位以下所示。服務器

1PB=1024TB
1EB=1024PB

2.1.2 速度單位

  • 網絡速度
    網絡經常使用的單位是Mbps
    而網絡帶寬提供商(例如長城寬帶)聲稱的百兆帶寬其實是100Mbit/s,可是100M光纖測試的峯值速度只會有12.5MB/s,它們之間的換算是100Mbit/s=(100/8)MB/s=12.5MB/s。微信

  • CPU速度
    CPU的速度通常是由CPU的時鐘頻率所體現的,而時鐘頻率的單位是赫茲(Hz),目前主流的CPU時鐘頻率通常都在2GHz以上,而赫茲(Hz)其實就是秒分之一,也就是每秒鐘的週期性變更重複次數的計量。
    GHz即十億赫茲(10^9Hz),2GHz就是二十億赫茲,也就是說2GHz的CPU每秒能夠變化20億次。

1Khz=1000hz
1Mhz=1000khz
1Ghz=1000Mhz

2.2 計算機底層爲何只能識別二進制

咱們目前主要使用的計算機都是大規模集成電路,是採用大規模和超大規模的集成電路做爲邏輯元件的。集成電路按其功能、結構的不一樣,能夠分爲模擬集成電路、數字集成電路和數/模混合集成電路三大類。而咱們的計算機主要是採用數字集成電路搭建的。邏輯門是數字邏輯電路的基本單元。常見的邏輯門包括「與」門,「或」門,「非」門,「異或」等等。經過邏輯門能夠組合使用實現更爲複雜的邏輯運算和數值運算。邏輯門能夠經過控制高、低電平,從而實現邏輯運算。電源電壓大小的波動對其沒有影響,溫度和工藝誤差對其工做的可靠性影響也比模擬電路小得多,因此相對穩定。由於數字計算機是由邏輯門組成,而邏輯電路最基礎的狀態就是兩個——開和關。因此,數字電路是以二進制邏輯代數爲數學基礎。二進制的基本運算規則簡單,運算操做方便,這樣一來有利於簡化計算機內部結構,提升運算速度。可是在平常開發中,一般都會使用八進制和十六進制,由於八進制和十六進制相對於二進制表示數據更加簡潔,並且一個八進制表示三個二進制,一個十六進制表示四個二進制。例如1024使用二進制表示爲0b100 0000 0000,使用八進制表示爲02000,使用十六進制表示爲0x400。

2.3 進制

2.3.1 進制概述

進制的定義:進制是一種計數方式,也稱爲進位計數法或者位值計數法,使用有限數字符號表示無限的數值,使用的數字符號的數目稱爲這種進位制的基數或者底數,例如十進制就是由0-9十個數字組成。在計算機內存中,都是以二進制的補碼形式來存儲數據的,生活中以十進制方式計算的數據居多,例如帳戶餘額,開發人員的薪水等等。計算的內存地址、MAC地址等等一般都是使用十六進制表示的,Linux系統的權限系統採用八進制的數據表示的。相同進制類型數據進行運算時會遵照加法:逢R進1;減法:借1當R,其中R就表示進制。

計算機經常使用進制的組成、示例和使用場景:

進制名稱 組成 數值示例 應用場景
二進制 0,1 1010 計算機底層數據存儲
八進制 0-7之間的8個整數 010 linux權限系統
十進制 0-9之間的10個整數 12 整數
十六進制 0-9,a-f之間的10個整數加上6個字母 12f 數據的內存地址

2.3.2 十進制轉換二進制、八進制、十六進制

十進制轉換二進制、八進制、十六進制能夠採用短除法,即待轉換的十進制數除以指定的進制(例如2,8,16),直到商數爲0,求餘數。

十進制101轉換爲二進制的計算過程

重複除以2 商數 餘數
101/2 50 1
50/2 25 0
25/2 12 1
12/2 6 0
6/2 3 0
3/2 1 1
1/2 0 1

而後將餘數的結果從下到上串聯起來的結果:1100101,即十進制的101轉換爲二進制的結果爲1100101

十進制的237轉換爲二進制

重複除以2 商數 餘數
237/2 118 1
118/2 59 0
59/2 29 1
29/2 14 1
14/2 7 0
7/2 3 1
3/2 1 1
1/2 0 1

而後將餘數的結果從下到上串聯起來的結果:11101101,即十進制的237轉換爲二進制的結果爲11101101。

2.3.3 二進制、八進制、十六進制轉十進制

首先明確不一樣進制的值是如何計算的,這裏以十進制和二進制爲例子,闡述它們的計算過程。

十進制整數1024

1024=1*10^3+2*10^1+4*10^0=1000+20+4=1024

二進制整數 10000000000

10000000000 =1*2^10=1024

二進制、八進制、十六進制整數轉十進制整數是使用按權展開法計算的,這裏以二進制數據01100101爲例子。從右往左開始數,若是二進制位爲1,則依次用1*2^n,n從0開始。

二進制整數01100101 轉換爲十進制整數的計算過程

01100101=126+1*25+1*22+1*20=64+32+4+1=101

八進制整數0127轉換爲十進制整數的計算過程

0127=1*8^2+2*8^1+7=87

十六進制整數0x12f轉換爲十進制整數的計算過程

0x12f=1*16^2+2*16^1+f*16^0=256+32+15=303

2.3.4 二進制轉八進制、十六進制

二進制轉八進制是按照從右往左,每3位二進制對應1位八進制,由於2的3次方等於8

二進制整數11001100轉八進制計算過程

11 001 100 =0314

二進制轉十六進制是按照從右往左,每4位二進制對應1位十六進制,由於2的4次方等於16。

二進制整數1100 1110轉十六進制計算過程

1100 1110 =0xce

2.3.5 八進制、十六進制轉二進制

八進制轉二進制是按照從右往左,每1位八進制對應3位二進制。

八進制整數0127轉二進制整數計算過程

0127=001 010 111

十六進制轉二進制是按照從右往左,每1位十六進制對應4位二進制。

十六進制整數0x12f轉換爲二進制整數計算過程

0x12f=0001 0010 1111

2.4 數值在計算機的存取方式

2.4.1 數值在計算機的存儲方式

數據在內存中有三種表現方式:原碼、反碼和補碼
可是數據在內存中都是以二進制的補碼存儲,在理解補碼以前首先得理解原碼以及反碼。

原碼就是一個數據自己的二進制表現形式,而數值能夠分爲有符號和無符號兩種。
有符號數其二進制表示最高位(最左邊)是符號位,1表示負數,0表示正數。

  • 例如無符號數15的原碼使用一個字節表示爲0000 1111
  • 例若有符號15的原碼使用一個字節表示爲0000 1111
  • 例若有符號-15的原碼使用一個字節表示爲1000 1111

反碼
無符號數的反碼等於原碼
例如無符號數15的反碼等於原碼,即無符號數15的反碼用一個字節表示爲0000 1111

有符號數正數的反碼也等於原碼
例若有符號正數15的反碼等於原碼,即有符號數15的反碼用一個字節表示爲0000 1111

有符號負數的反碼是原碼最高位不變,其餘位取反
例若有符號負數-15的反碼是原碼最高位不變,其餘位取反用一個字節表示爲1111 0000

補碼
無符號數的補碼等於反碼
例如無符號數15的補碼等於反碼,即無符號數15的補碼使用一個字節表示爲0000 1111

有符號正數的補碼等於反碼
例若有符號正數15的補碼等於反碼,即有符號正數15的補碼使用一個字節表示爲0000 1111

有符號負數的補碼等於反碼加1
例若有符號負數-15的補碼等於反碼1111 0000加1,即有符號負數15的補碼使用一個字節表示爲11111 0001

數值 是否有符號 原碼 反碼 補碼
15 0000 1111 0000 1111 0000 1111
15 0000 1111 0000 1111 0000 1111
-15 1000 1111 1111 0000 1111 0001

無符號數以及有符號正數,計算機存儲的是原碼,由於原碼等於反碼
有符號負數計算機存儲的是補碼,補碼等於原碼取反加1,原碼等於補碼取反加1

2.4.2 數值在計算機的獲取方式

在使用printf函數輸出不一樣格式的數據時

若是是有符號,可使用 %d, %ld,%lld,%f,%lf獲取,由於int,long,long long ,float和double默認就是有符號類型的。

在解析有符號時,首先看內存中的二進制數據最高位是否爲1;

若是是1,表示該數字是某個負數的補碼,printf輸出時還須要轉換爲原碼,由於程序操做的是原碼,此時須要根據符號位不變,其餘位取反,末位加1。
若是是0,表示該數字是某個正數的補碼,因爲正數的補碼等於原碼,此時按照補碼輸出便可。

若是是無符號,可使用%u,%lu,%llu,%o,%x獲取數據。因爲無符號數不須要考慮負數,所以printf()輸出數據時按照內存存放的數據原樣輸出。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* printf格式化輸出數據 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/31 */
int main(int argc, char* argv[]) {

    /* 聲明佔據一個字節的變量number,並賦值爲-15 有符號負數,內存存儲的是補碼 -15的原碼是1000 1111 -15的反碼是1111 0000 -15的補碼是1111 0001 所以內存中存放的是 1111 0001 */
    char number = -15;
    
    /* 有符號輸出 1111 0001 首先看最高位,最高位是1,表示該數是一個負數,須要轉換成原碼輸出 補碼 1111 0001 轉換爲原碼前首先轉換爲反碼 1000 1110 而後再加1 即1000 1111 按照十進制輸出結果就是-15 由於最高位是1,表示該數字是負數,所以輸出結果是-15 */
    printf("-15的有符號輸出結果是 %d \n",number); //%d按照十進制解析,所以輸出結果是-15


    /* 無符號輸出時不考慮符號位 由於 -15在內存中是使用補碼存儲,即1111 0001 不考慮符號位輸出,結果是241,由於1111 0001 轉換爲二進制的結果是241 %u按照無符號整數解析,佔據四個字節,可是number只佔1個字節 可是printf輸出時編譯器會自動添加24個1, 所以使用按位與去掉編譯器自動添加的24個1 number&0x000000ff */

    printf("無符號輸出-15的結果是%u\n",number&0x000000ff);

    system("pause");
    return 0;
}

若是以十六進制給某個變量賦值,十六進制不區分正負,內存原樣存儲,不須要考慮原碼、反碼和補碼的換算。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 十六進制賦值輸出 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/31 */
int main(int argc, char* argv[]) {

    /* 當給一個變量採用十六進制的值賦值時,內存原樣存儲,由於十六進制不區分正負 0x9b在內存中的存儲 1001 1011 */
    char number = 0x9b;
    /* 有符號獲取 0x9b 首先將0x9b轉換爲二進制1001 1011,最高位是1,表示爲number的補碼 這裏須要將1001 1011轉換爲原碼 首先將1001 1011轉換爲反碼 1001 1011 最高位不變,其餘位取反 11100100 獲得反碼後加1,最後的結果是 11100101 一個字節的 1110 0101 最高位是負數 1100101轉換爲10進制的結果是101 所以最終打印輸出number =%d 的結果是-101 */
    printf("16進制0x9b按照有符號輸出的結果是 %d \n",number);


    /* 無符號數輸出時直接輸出1001 1011轉換爲十進制的輸出結果便可 1001 1011轉換爲十進制的結果是155 因爲 */

    printf("16進制0x9b按照無符號輸出的結果是 %u \n",number&0x000000ff);
    // 1001 1011 
    printf("number = %x \n",number);
    system("pause");
    return 0;
}

2.5 常量

2.5.1 常量概述

任何基本類型都有變量和常量兩種類型。
常量是其值在運行期間不能修改,例如小數3.14,字符'a'都是常量,常量之因此不能被修改,是由於常量是在文字常量區。內存在存儲數據時,考慮到數據不一樣的用途和特色,把內存條分爲各類區域:棧區、堆區、代碼區、文字常量區、全局區。每一個區域的數據類型都有各自的特色。

2.5.2 平常開發中經常使用的常量

在平常開發中經常使用的常量有字符常量、短整型常量、長整型常量、單精度浮點型常量以及雙精度浮點型常量。

結合C語言提供的printf函數以及格式替換符實現格式化輸出常量的值

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 常量 常量一旦定義後不能被修改(賦值) @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //平常開發中經常使用的常量
    //%c 用於輸出一個字符,這裏輸出字符a
    printf("char c= %c \n",'a');
    //%hd用於輸出短整型
    printf("short s= %hd \n",10);
    //%d用於輸出整型
    printf("int i= %d \n",100);
    //%ld用於輸出長整型
    printf("long l= %ld \n",10000);
    //%lld用於輸出長長整型
    printf("long ll=%lld \n",100000000000000000);
    //%f用於輸出單精度浮點型
    printf("float f= %f",3.14f);
    //%ld用於輸出雙精度浮點型
    printf("double d= %lf",3.14);
    //常量一旦定義後不能被修改(賦值)
    //10 = 20;

    
    system("pause");
    return 0;
}

2.5.3 const常量

C語言中除了字面量常量外,還可使用const修飾變量,使其成爲不能直接修改,即常量,可是能夠經過獲取變量地址來間接修改。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* const常量 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    const int number = 12;
    //這裏會發生編譯錯誤,由於const修飾的變量是常量,常量一旦定義後沒法再次被賦值
    //number = 18;

    //const雖然沒法直接修改,可是能夠經過間接的方式依然能夠修改
    const long id = 10001;
    printf("常量 id的內存地址是%p \n", &id);

    printf("常量id修改以前的值是%d \n", id);

    //經過間接的方式修改id的值
    //(int*) 類型轉換爲非 常量地址
    //* 根據地址取出內容
    *(int*)(&id) = 10002;
    printf("常量id修改以後的值是%d \n", id);

    system("pause");
    return 0;
}

2.5.4 define常量

C語言中除了使用const定義常量之外還可使用#define來定義真正意義的常量,由於在C語言層面沒法直接或者間接修改其值,由於C語言只能操做內存,不能操做寄存器,而#define定義的常量是在寄存器內部產生的。

在使用#define定義常量的語法爲 #define CONST_NAME CONST_VALUE,須要注意的是使用#define定義常量時不須要使用分號。

#define _CRT_SECURE_NO_WARNINGS
//使用 #define定義常量
#define MY_NAME "Tony"
#define MY_GENDER "Boy"
#include <stdio.h>
#include <stdlib.h>
/* #define常量 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //使用常量
    printf("my name is %s and my gender is %s \n",MY_NAME,MY_GENDER);
    system("pause");
    return 0;
}

使用#define定義常量時建議使用有意義的常量名,使得別人在使用常量時能明確知道其含義,常量亦能夠在多個方法中使用,若是須要修改常量,只須要修改一次即可以實現批量修改,效率高並且準確。

define常量的應用案例:實現代碼混淆

首先定義頭文件define.h,其內容爲

/* 使用define實現代碼混淆 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
#include <stdio.h>
#include <stdlib.h>
#define _ void
#define __ main()
#define ___ {
#define ____ system("notepad");
#define _____ system("pause");
#define ______ }

而後在源文件define.c中使用#include包含define.h並引用define.h頭文件中定義的常量值

#include "define.h"

/* 使用define實現代碼混淆 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
_ __ ___ ____ _____ ______

而後在Visual Studio 2019中運行該程序,程序可以調用Windows的記事本程序。

2.6 變量

2.6.1 變量的概述

變量就是能夠變化的量,在同一時刻,內存中的變量只能存儲一份值。若是對變量進行修改,新值會覆蓋舊值。

這裏能夠經過一段程序結合Visual Studio 2019的調試功能理解變量的本質。

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

/* 變量在內存中的存儲 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    int num = 20;
    //查看num的內存地址
    printf("整數變量num的地址是%p\n", &num);
    printf("整數變量num = %d\n", num);
    num = 30;
    printf("修改以後整數變量num的值是%d\n", num);
    system("pause");
    return 0;

}

首先經過printf的格式替換符%p獲取變量的內存地址,而後經過Visual Studio 2019的調試->窗口->內存菜單查看內存,並在地址欄中輸入整數變量num的地址,查看變量值。
Visual Studio 2019中查看內存的值默認是一字節,不帶符號顯示,能夠經過鼠標右鍵設置成四字節、帶符號顯示。
Visual Studio 2019 調試變量
而後逐語句執行,並觀察變量的值。
查看變量的值

生活中隨處可見變量,例如股市的漲跌,遊戲人物的武力值都是不斷變化的。變量在使用前必需要先定義即聲明並賦初始值,不然會出現編譯錯誤。定義變量時系統會針對變量的類型開闢指定的內存空間。

2.6.2 變量的定義

變量的定義表示讓變量在系統中存在,系統給變量開闢內存空間,變量定義的格式是 類型名+類型名,例如 int number,number就是變量名,類型爲int,意味着編譯器會針對number變量開闢4個字節的內存空間。
變量名的本質就是空間內容的別名,操做變量就是操做變量表明的那塊內存空間。
變量使用前必須賦值完成初始化,即變量定義時就給變量賦值,例如int age=0;,不然會發生編譯錯誤:error C4700: 使用了未初始化的局部變量

在定義變量時,變量名還要遵照如下的命名規則。

  • 變量名由字母、數字、下劃線組成。
  • 變量名不能以數字開頭。
  • 變量名不能是關鍵字,關鍵字是被C語言賦予了特殊的含義,可是能夠包含關鍵字,Visual Studio 2019中藍色的都是關鍵字。
#include <stdio.h>
#include <stdlib.h>

/* 變量的聲明賦值及其命名規範 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //合法的標識符
    int number;
    //見名知意
    int age;
    char ch;
    double db;
    //變量名不能是關鍵字
    //int void;
    //變量名不能以數字開頭
    //int 1num;
    /****************************************編譯器特性*******************************/
    //VC支持中文變量,GCC不支持中文命名
    int 年齡 = 29;
    printf("年齡 =%d\n", 年齡);
    //在老版(C++11以前)的編譯器中,變量聲明必須放在函數調用以前
    /****************************************編譯器特性*******************************/
    //聲明多個變量
    int one, two, three;
    system("pause");

    return 0;
}

定義變量時賦值叫變量的初始化,而定義完成之後再賦值不叫初始化,只是單純的賦值。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 變量的定義 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //變量定義的語法 變量類型 變量名
    int number;
    //變量使用前必須定義並初始化,初始化即定義時給變量賦值,不然會發生編譯錯誤
    //printf("number = %d",number);

    //非法的變量名 會形成程序編譯錯誤
    // 不能以數字開頭
    //int 1number;
    //只能由字母、數字、下劃線組成,這裏包含了空格
    // int num ber;

    //定義時賦值叫變量的初始化
    int age = 26;

    //變量定義
    int val;
    //定義完成後再賦值就不叫初始化,只是單純的賦值
    val = 12;


    system("pause");
    return 0;
}

2.6.3 變量的聲明

變量的聲明表示告訴編譯器該變量已經存在,此處經過編譯,可是不會再開闢內存空間。
若是變量定義在使用前面,編譯器能夠自動識別變量聲明,由於編譯器在編譯時是從上到下逐語句編譯。

若是變量的定義再也不使用的前面,可使用extern關鍵字顯示聲明變量,在聲明時不用賦值,不然會引起變量「重定義,屢次初始化的編譯錯誤」。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 變量的聲明 變量的聲明包含顯示聲明和非顯示聲明 通常狀況下都是非顯示聲明,即變量的定義在使用前面。編譯器編譯時是從上到下逐語句編譯的,當變量的定義在使用前面,編譯器可以自動識別變量的聲明 若是變量的定義在使用的後面,那麼此時須要使用extern來顯示聲明變量,在聲明時不用賦值,不然會引起變量「重定義,屢次初始化的編譯錯誤 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
//顯示聲明變量,不用賦值,告訴編譯器在使用到該變量的地方編譯經過
extern int age;

//extern int age=300; // error C2374: 「age」: 重定義;屢次初始化
int main(int argc, char* argv[]) {
    //編譯器從上到下逐行編譯,
    int number=10;
    //變量的定義在使用以前,此處會自動識別變量的聲明
    printf("number = %d \n",number);

    printf("age = %d",age);
    system("pause");
    return 0;
}
//變量的定義在使用以後
int age = 26;

可是若是最終變量沒有定義在使用以後,程序運行時仍是會出現異常,例如這裏若是註釋int age = 26;就會出現 沒法解析的外部符號 age。

平常開發中一般都是先定義再使用變量,並且一般都須要在定義時初始化即賦值,若是使用了一個沒有賦值的變量,程序會發生編譯錯誤。

#include <stdio.h>
#include <stdlib.h>
/* 變量在使用前必須初始化賦值,不然會出現C4700錯誤-> "使用了未初始化的局部變量" @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main() {
    //聲明一個整數變量並未初始化
    int num;
    //編譯錯誤錯誤 C4700 使用了未初始化的局部變量「num」
    printf("num =%d\n", num);
    system("pause");
    return 0;
}

在定義變量時能夠針對變量的類型賦對應的初始值,例如整數賦值爲0,浮點數賦值爲0.0。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 變量賦初始值 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    //變量初始化時若是類型爲整數,推薦初始化爲0
    int number = 0;
    double dbl = 0.0;
    char c = '\u00';
    

    system("pause");
    return 0;
}

2.6.4 變量的使用

變量的使用表示對變量的讀寫操做,所謂讀就是獲取變量的值,例如使用printf()輸出變量的值,寫就是賦值以及各類變量的運算,C語言中使用"="表示賦值,賦值是將右邊的值賦值給左邊的變量,也就是操做內存空間,除此之外後續還會學習各類運算符,例如算術運算符、邏輯運算符、關係運算符、三元運算符、位運算符等等。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 變量的操做:讀&寫 打印輸出就是讀操做 賦值操做就是寫操做 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //變量賦值 就是變量的寫操做
    int age = 26;
    //printf函數就是對變量的讀操做
    printf("age = %d\n ",age);
    //=是將右邊的值賦值給左邊的變量
    int my_age = age;
    //這裏也會打印輸出my_age = 26
    printf("my_age = %d\n ", my_age);
    system("pause");
    return 0;
}

變量除了能夠賦值之外還能夠進行算術運算

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 變量的操做:算術運算 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    int left = 10;
    int right = 20;
    int result = left + right;
    printf("%d + %d = %d \n",left,right, result);
    result = right - left;
    printf("%d - %d = %d \n", right, left, result);
    system("pause");
    return 0;
}

須要注意的是變量全部的運算都是經過CPU來完成的。

#include <stdio.h>
#include <stdlib.h>
/* 變量運算的原理 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //只能給變量賦值
    int a = 1;
    int b = 2;
    //分配四個字節的內存
    int c;
    printf("變量a的地址是%p\t,變量b的地址是%p\t,變量c的地址是%p\n", &a, &b, &c);
    //數據的運算是在CPU的寄存器完成的
    c = a + b;
    c = b - a;
    //對數據的操做是由CPU完成的,所以這裏不能直接複製
    //a + 1 = 4;
    printf("c=%d\n", c);
    system("pause");

    return 0;

}

使用匯編語言實現變量的賦值以及變量的運算

#include <stdio.h>
#include <stdlib.h>
/* 使用匯編語言實現變量的賦值以及運算來理解數據的運算是在CPU內部的寄存器完成的 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main() {

    //申請四個字節的內存
    int a;
    printf("整數變量a的地址是%p\n", &a);


    //變量的賦值都是經過CPU的寄存器來完成的
    //這裏藉助彙編語言實現將10賦值給變量a
    _asm {

        mov eax, 10
        mov a, eax
    }

    printf("整數變量a的值等於%d\n", a);

    _asm {

        //把變量a的值賦值給寄存器eax
        mov eax, a
        //將eax的值加5
        add eax, 5
        //把eax的值賦值給a
        mov a, eax
    }
    printf("變量a加5以後的結果是%d\n", a);
    system("pause");

    return 0;
}

在運行程序時。能夠經過調試查看寄存器EAX的變化,證實數據的運算都是經過CPU來完成的,而後將運算的結果存儲在寄存器中。
寄存器EAX

2.6.5 變量的內存機制

當聲明變量時,編譯器會使用變量表維護變量的信息,包含變量名、變量類型以及變量的內存地址。而內存中經過變量地址關聯變量值。若是在程序中使用了未聲明的變量,則會發生編譯錯誤。

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

/* 編譯器和內存對變量的處理 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    //聲明3個整數變量
    int a, b, c;
    //這裏會發生編譯錯誤 不能使用未聲明的變量
    //printf(" %d\n", d);
    system("pause");
    return 0;
}

2.6.6 變量交換

變量的交換,能夠經過採用中間變量,算術(加減法或者乘除法)運算、異或運算
三種方式實現,其應用場景主要在使用在排序算法中,每種實現變量交換方法的時空複雜度有不一樣的考量。

經過中間變量實現變量交換

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 變量交換的三種方式 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/09/03 */
int main(int argc, char* argv[]) {
    int left = 5;
    int right = 10;
    printf("使用臨時變量實現變量交換交換以前\t left=%d \t right=%d\n", left, right);
    int middle = left;
    left = right;
    right = middle;
    printf("使用臨時變量實現變量交換交換以後\t left=%d \t right=%d\n", left, right);
    system("pause");
    return 0;
}

經過算術運算實現變量交換

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 變量的交換:經過算術運算實現 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/09/03 */
int main(int argc, char* argv[]) {
    int left = 5;
    int right = 10;
    printf("使用算術運算實現變量交換交換以前\t left=%d \t right=%d\n", left, right);
    left = left + right; // 加號變成乘號
    right = left - right;//減號變成除號
    left = left - right; //減號變成除號
    printf("使用算術運算實現變量交換交換以後\t left=%d \t right=%d\n", left, right);

    system("pause");
    return 0;
}

使用異或運算實現變量的交換

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 變量的交換:使用異或運算實現變量交換 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/09/03 */
int main(int argc, char* argv[]) {
    int left = 5;
    int right = 10;
    printf("使用異或運算實現變量交換交換以前\t left=%d \t right=%d\n", left, right);
    left = left ^ right;
    right = left ^ right;
    left = left ^ right;
    printf("使用異或運算實現變量交換交換以後\t left=%d \t right=%d\n", left, right);
    system("pause");
    return 0;
}

2.7 讀取鍵盤的輸入

以前在定義整數變量並初始化值時只能寫死一個值,整數變量建議初始化時爲0,這樣能夠避免許多沒必要要的錯誤出現。
爲了讓程序變得更加靈活,這裏引入C語言標準庫函數scanf()函數實現基於終端的人機交互。固然平常應用(例如淘寶、京東)都是基於UI界面實現人機交互,可是底層處理的邏輯是同樣的。

scanf()函數能夠從鍵盤中讀取用戶輸入的整數、小數、字符等等,該函數的參數須要傳遞數據格式和變量地址兩個參數,數據格式指的就是用戶輸入的數據類型,例如整數、小數等等,變量地址就是&變量名,&表示地址符號,變量名只能表明空間的內容。
scanf()函數是阻塞式的,當用戶輸入數據以後,數據會存儲到標準輸入緩存區中,而後scanf()函數負責從標準緩衝區中拿指定格式的數據,若是用戶不輸入數據,那麼程序會一直阻塞在那裏。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* scanf函數獲取鍵盤的輸入實現終端不帶界面交互 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    //變量初始化時若是類型爲整數,推薦初始化爲0
    int age = 0;
    printf("請輸入你的年齡\n(按回車鍵結束)");
    //當用戶輸入數據並回車以後先到達標準輸入緩衝區
    //scanf函數負責從標準緩衝區拿整數,此函數是阻塞的,即等到用戶輸入數據纔會往下執行
    //%d表示提取鍵盤輸入的數字
    //變量名僅僅表明變量的內容,若是想要獲取變量的起始地址,須要使用&變量名
    scanf("%d",&age);

    printf("你的年齡是%d\n",age);

    system("pause");
    return 0;
}

2.8 數據類型

2.8.1 數據類型概述

數據類型就是給數據分類,其目的就是合理的利用內存空間,提升存儲效率。
類型是抽象的概念,類型有大小,可是沒有空間,系統不會給類型分配空間,可是會給類型定義的變量分配空間,例如定義變量 int age =28;時系統會給age變量分配四個字節的空間。
不一樣的數據類型佔據不一樣的內存大小,其存儲數據的極限也不同、可以執行的運算也是不相同的。
C語言中基本數據類型有整型、浮點型、字符型,布爾型。其餘的類型都是由基本數據類型封裝而來的。

其中整數按照不一樣的字節大小有short,int,long,long long(C99支持)。
浮點數按照精度不一樣有float,double,其中float表示單精度浮點型,double表示雙精度浮點型。
字符只有char表示,用於存儲單個字符。
布爾使用boolean表示,C語言中的0表示false,非0表示true。

2.8.2 sizeof()關鍵字

C語言提供了提供了sizeof()關鍵字來獲取類型佔據的內存空間。
sizeof()中能夠傳類型名或者變量名。傳遞變量名其實是求變量類型的大小

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/* sizeof關鍵字的使用 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    printf("char 佔據的字節數量是%d\n",sizeof(char));
    printf("short 佔據的字節數量是%d\n",sizeof(short));
    printf("int 佔據的字節數量是%d\n",sizeof(int));
    printf("long 佔據的字節數量是%d\n",sizeof(long));
    printf("long long 佔據的字節數量是%d\n",sizeof(long long));
    printf("float 佔據的字節數量是%d\n",sizeof(float));
    printf("double 佔據的字節數量是%d\n",sizeof(double));
    printf("bool 佔據的字節數量是%d\n",sizeof(bool));
    system("pause");
    return 0;
}

須要注意的是C語言相同的數據類型在不一樣的操做系統環境下佔據的空間是不同的。
在Visual Studio 2019中,C程序默認是以32位運行的。
x86
32位sizeof()關鍵字運算結果
32位sizeof()關鍵字運算
若是想要切換到64位下運行,只須要將X86換成x64便可
x64
64位sizeof()關鍵字運算
在Windows平臺下long類型不管是在32位仍是64位都是佔據四個字節,而Linux(Ubuntu18.04)則是佔據8個字節。

Ubuntu18.04下sizeof()關鍵字測試

//
// Created by guanglei on 8/28/19.
//
#include <stdio.h>
#include <stdlib.h>
int main(){

    printf("char 佔據的字節數量是%d\n",sizeof(char));
    printf("short 佔據的字節數量是%d\n",sizeof(short));
    printf("int 佔據的字節數量是%d\n",sizeof(int));
    printf("long 佔據的字節數量是%d\n",sizeof(long));
    printf("long long 佔據的字節數量是%d\n",sizeof(long long));
    printf("float 佔據的字節數量是%d\n",sizeof(float));
    printf("double 佔據的字節數量是%d\n",sizeof(double));
    printf("_Bool 佔據的字節數量是%d\n",sizeof(_Bool));
    return 0;
}

Ubuntu18.04 sizeof()關鍵字運算結果
Ubuntu18.04 sizeof()關鍵字運算結果

2.8.3 數據類型的有符號和無符號

有符號表示數據有正負之分,而無符號則沒有正負之分,即全是正數(大於等於0數)。

有符號數最高位(左邊第一位)是符號位,若是是0表示該數據是整數,1表示該數據爲負數。
以2個字節的數據 1爲例子,其二進制表示爲0000 0000 0000 0001由於是正數,所以最高位是0,-1的二進制表示爲1000 0000 0000 0001,由於是負數,所以最高位是1。
無符號數即沒有符號位,全是數據位,仍是以2個字節的數據1爲例子,其二進制表示爲0000 0000 0000 0001

2.8.3.1 有符號和無符號常量(整型和長整型)

整數常量默認是有符號的整數,若是想要變成無符號數,須要在正數後面加上U後綴。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 有符號數(整型,長整型)和無符號數(整型,長整型) 常量 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //整型常量10默認是有符號的整數,若是想要變成無符號數,須要在10後面加上U,即10U
    printf("%d \n",10);
    printf("%u \n", 10U);

    //長整型常量的有符號和無符號

    //有符號長整型
    printf("%ld \n",10L);
    //無符號長整型
    printf("%lu \n", 10U);

    system("pause");
    return 0;
}

2.8.3.2 有符號和無符號變量

C語言中的變量默認就是有符號的,使用signed修飾,例如signed int,可是一般直接使用int聲明變量,而無符號的變量使用unsigned修飾,例如unsigned int,這裏的unsigned不能省略。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 有符號和無符號變量 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    //整數默認就是有符號的,即默認是使用signed
    signed int number = 12;
    printf("number = %d ",number);

    // 無符號必須加上unsigned
    unsigned int value = 12;
    printf("value = %u ", value);


    system("pause");
    return 0;
}

2.8.4 數據類型的極限

數據類型都有其存儲範圍(即存儲的最大值和最小值),C語言中的limits.h和float.h頭文件中分別定義了整數和浮點數的極限。在使用數據類型時,切勿超過其極限,不然會形成程序異常。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
#include <float.h>

/* 數據類型的極限 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    printf("int能存儲的最大值是%d\tint能存儲的最小值是%d,佔據的字節數量是%d\n", INT_MAX, INT_MIN, sizeof(int));
    printf("double能存儲的最大值是%e\t double能存儲的最小值是%e,double佔據的字節數量是%d\n \n", DBL_MAX, DBL_MIN,sizeof(double));

    printf("unsigned char 表示的最大值是%d\n",UCHAR_MAX);
    //無符號的char存儲的最大值是255 這裏超過了最大範圍,輸出的結果不是預期的256,,並且0
    unsigned char num = 255+1;
    printf("num = %d\n",num);

    system("pause");

    return 0;
}

2.8.5 數據類型與printf

在使用prinf函數打印變量的值時,若是沒有正確使用格式轉換符,獲得一個非預期的結果,由於printf函數不會進行類型轉換的操做,只會按照傳遞的格式轉換數據後輸出到終端上。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/* printf處理數據類型 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]){

    int number = 10;
    
    printf("number = %f \n",number);
    printf("number = %f \n", (float)number);


    float flt = 10.8;
    //printf不會進行數據類型轉換
    printf("flt = %d \n",flt);
    //使用強制類型轉換實現輸出正確的結果
    printf("flt = %d \n", (int)flt);


    return 0;
}

2.8.6 數據在內存中的存儲

在手機、PC上,數據在內存中的存儲是低位在低字節,高位在高字節。
而在Unix大型服務器上爲了加速尋址,數據在內存中的存儲是低位在高字節, 高位在低字節。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/* 數據在內存中的存儲 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    int number = 10;
    printf("number的內存地址是%p\n", &number);
    printf("number = %d",number);


    return 0;
}

數據在內存中的存儲
數據在內存中的存儲

2.9 整型

2.9.1 整數常量

C語言整數常量可使用u後綴表示位無符號整數,使用l後綴表示long類型的整數,使用ll後綴表示爲long long類型的整數

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 整數常量的三種類型 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/09/03 */
int main(int argc, char* argv[]) {
    printf("有符號整數常量%d\n",100);
    printf("無符號整型常量%u\n",100u);
    printf("有符號長整型常量%ld\n",2147483647L);
    printf("有符號長長整型常量%lld\n",9223372036854775807LL);
    system("pause");
    return 0;
}

同時C語言中的整型常量支持八進制、十進制和十六進制三種進制類型,不支持二進制。

  • 八進制由0-7之間的八個整數組成,八進制的常量值是以0開頭。
  • 十進制由0-9之間的十個整數組成。
  • 十六進制由0-9,a-f之間的十個整數加上6個字母組成。

printf()函數針對整數的三種進制類型提供了對應的輸出格式,其中八進制輸出使用%o表示,十進制使用%d表示,十六進制使用%f表示,#表示輸出進制的完整格式,例如八進制會在最左邊填充一個0,十六進制會在最左邊填充0x。

不一樣進制的輸出不會改變數據原來的值,底層依然是以二進制存儲,只是輸出的表現形式變了。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* printf輸出整數的三個進製表現方式 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    int number = 0;
    printf("請輸入一個整數\n");
    scanf("%d",&number);
    //不一樣進制的輸出不會修改number的值,只是數據的表現形式變化了
    // %o表示按照八進制解析,#表示輸出完整進制格式,即以0開頭
    printf("%d按照八進制輸出的結果是%#o\n",number,number);
    printf("%d按照十進制進制輸出的結果是%d\n",number, number);
    // %x表示按照十六進制解析,#表示輸出完整進制格式,即以0x開頭
    printf("%d按照十六進制輸出的結果是%#x\n",number, number);
    system("pause");
    return 0;
}

2.9.2 整數的極限

整數按照佔據不一樣的字節大小能夠分爲short,int,long和long long 四種類型,它們默認是有符號(signed)類型用於存儲正負數,而對應的無符號類型(unsigned)則用來存儲非負數的整數。
整數的極限定義以#define的方式在<limits.h>頭文件中定義,Visual Studio 2019中能夠選中一個極限值常量,而後使用快捷鍵F12轉到定義,直接查看常量值的表示範圍。

轉到定義
轉到定義

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
/* 整數的極限 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int int_limits_main(int argc, char* argv[]) {
    //16位(嵌入式系統) int和short是等價的
    printf("short能存儲的最大值是%d\tshort能存儲的最小值是%d,佔據的字節數量是%d\n", SHRT_MAX, SHRT_MIN, sizeof(short));
    printf("unsigned short能存儲的最大值是%d\n", USHRT_MAX);
    //32位和64位系統 int和long是等價的
    printf("int能存儲的最大值是%d\tint能存儲的最小值是%d,佔據的字節數量是%d\n", INT_MAX, INT_MIN, sizeof(int));
    printf("unsigned int能存儲的最大值是%d\n", UINT_MAX);
    //無符號的整數 最小值都是0 即不能表示負數
    printf("long能存儲的最大值是%d\tlong能存儲的最小值是%d,佔據的字節數量是%d\n", LONG_MAX, LONG_MIN, sizeof(long));
    printf("long long能存儲的最大值是%lld\tlong long能存儲的最小值是%lld,佔據的字節數量是%d\n", LLONG_MAX, LLONG_MIN, sizeof(long long));
    printf("unsigned long long 能存儲的最大值是%llu\n", ULLONG_MAX);
    system("pause");
    return 0;
}

在使用整數參與運算時,須要考慮到數據範圍對應的極限,不然會發生錯誤的結果

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 整數的越界 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //爲了保證結果運算正確,必須在極限範圍以內
    unsigned short int  shortnum = 65536;
    printf("無符號short int所能存儲的最大值是%d\n", USHRT_MAX);
    printf("shortnum=%d", shortnum); //結果爲0 由於chnum所能表示的最大值爲65535,這裏發生了越界,結果錯誤
    system("pause");
    return 0;
}

若是想要存儲身份證號等超大類型的整數數據,可使用無符號的long long類型來存儲

#include <stdio.h>
#include <stdlib.h>
/* long long 的應用場景 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main() {

    unsigned long long mobilePhone = 18601767221;
    printf("mobilePhone=%llu\n", mobilePhone);
    unsigned long long qq = 1079351401;
    printf(" qq = %llu", qq);
    system("pause");
    return 0;
}

2.9.3 跨平臺的整型

爲了解決不一樣平臺,相同的類型佔據的大小不一致的問題,C語言標準委員會在C99標準中提出了跨平臺的整數,在<stdint.h>頭文件中定義,意味着一樣的類型在不一樣的系統下的大小是一致的。
例如int64_t在全部實現C99標準的編譯器下佔據的都是8個字節,int32_t在全部實現C99標準的編譯器下佔據的都是4個字節。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>
/* 跨平臺的整數 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    long val = 100;
    printf("windows下long佔據的字節數量是%d\n", sizeof(val));

    //在不一樣的平臺下佔據都是32字節
    int32_t int_32_MAX_VALUE = INT32_MAX;
    printf("sizeof(int_32_MAX_VALUE ) = %d\n", sizeof(int_32_MAX_VALUE));
    printf("int_32_MAX_VALUE = %d\n", int_32_MAX_VALUE);


    //在不一樣的平臺下佔據都是64字節
    int64_t int_64_MAX_VALUE = INT64_MAX;
    printf("sizeof(int_64_MAX_VALUE ) = %d\n", sizeof(int_64_MAX_VALUE));
    printf("int_64_MAX_VALUE = %lld\n", int_64_MAX_VALUE);
    system("pause");
    return 0;
}

三角形面積計算:給定三角形的三條邊,使用math.h頭文件中的sqrt函數實現三角形的面積計算。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/* 根據給出的邊長求面積 使用math.h文件中提供的開平方根函數 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    int a = 6;
    int b = 8;
    int c = 10;
    int p = (a + b + c) / 2;
    //sqrt返回float,這裏使用賦值運算完成了類型轉換
    int s = sqrt(p * (p - a) * (p - b) * (p - c));
    printf("三角形的面積是%d\n", s);
    system("pause");
    return 0;
}

2.10 浮點型

2.10.1 浮點型常量

浮點型即生活中使用的小數類型(例如3.14),例如帳戶的餘額,銀行的存款利率等等都是浮點型。
C語言中按照精度的不一樣分別使用float,double和long double表示,默認浮點類型是double,float佔據四個字節,double佔據8個字節,long double大於等於8個字節,Windows 32位和64位系統long double都是8個字節,Ubuntu18.04系統下long double是佔據16個字節。

浮點數的常量可使用十進制的小數和科學計數法表示,科學計數法能夠存儲特大或者特小的數字

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 浮點常量 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //輸出結果顯示3.14佔據8個字節,由於浮點數默認是double類型
    printf("浮點數3.14佔據的字節數量是%d\n",sizeof(3.14));
    //若是以f結尾的就是float類型
    printf("浮點數3.14f佔據的字節數量是%d\n",sizeof(3.14f));



    //十進制
    float flt = 12.0f; //小數後面加f表示float類型
    double dbl = 12.0; //小數默認是double類型
    //科學計數法
    double db1 = 0.12e3;
    //e以前必須有數字,指數必須爲整數
    double db2 = 12000.124e5; //e5表示10的5次方
    //%f默認輸出小數點後六位
    printf("flt = %f \n", flt);
    printf("db1 = %f \t db2 = %f\n", db1, db2);
    system("pause");
    return 0;
}

2.10.2 浮點數變量

在初始化浮點數變量時,默認值建議爲0.0或者0.0f,賦值時變量的值和變量的類型保持一致。
printf()函數輸出float類型的變量使用格式符%f,輸出double類型的變量使用%lf。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 浮點數變量 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //在賦值時儘可能保證賦值號(=)左右兩邊類型一致,若是將表示範圍大的值賦值給表示範圍小的變量,可能會形成數據溢出,
    float flt = 3.14f;
    printf("flt = %f \n",flt);
    flt = 0.0f;
    printf("flt = %f \n", flt);

    double dbl = 5.67;
    //printf()默認輸出小數後6位數
    printf("dbl = %lf \n", dbl);
    //若是隻要輸出小數點後2位數,可使用格式符 %.2lf 實現
    printf("dbl = %.2lf \n",dbl);

    //請輸入一個浮點數
    printf("請輸入一個浮點數\n");
    scanf("%lf",&dbl);
    printf("你輸入的浮點數是%.2lf \n",dbl);


    system("pause");
    return 0;
}

2.10.3 浮點型極限

C語言在limits.h的頭文件中使用常量定義了float和double以及long double的極限值,咱們可使用sizeof()關鍵字求出float,double和long double的字節數量以及使用常量
FLT_MAX,FLT_MIN求出float表示的最大值和最小值以及DBL_MAX,DBL_MIN求出double所能表示的最大值和最小值。

在windows上double和long double是等價的,可是在Linux(例如Ubuntu 18.04上)long double是佔據16個字節,這也就意味着long double的極限比double更大。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <float.h>
/* 浮點數佔據的內存數量和取值範圍 在32位和64位Windows上double 和long double是等價的 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    printf("float佔據的字節數量是%d\n", sizeof(float));
    printf("float能存儲的最大值是%e\t float能存儲的最小值是%e \n", FLT_MAX, FLT_MIN);
    printf("\n");
    printf("double佔據的字節數量是%d \n ",sizeof(double));
    printf("double能存儲的最大值是%e\t double能存儲的最小值是%e \n", DBL_MAX, DBL_MIN);
    printf("\n");
    printf("long double佔據的字節數量是%d\n", sizeof(long double));
    printf("long double 能存儲的最大值是%e\t long ouble能存儲的最小值是%e \n", LDBL_MAX, LDBL_MIN);


    system("pause");
    return 0;
}

2.10.4 浮點數在內存中的存儲

首先明確一點,不管是整型、浮點型仍是字符等等數據類型在計算機底層都是以二進制的方式存儲的。

浮點數在內存中的存儲和整數不一樣,由於整數均可以轉換爲一一對應的二進制數據。
而浮點數的存儲是由符號位(sign)+指數位(exponent)+小數位(fraction)組成。
其中float是由1位符號位+8位指數+23位小數組成,
而double是由1位符號位+11位指數位+52位小數位組成。

int和float一樣佔據四個字節的內存,可是float所能表示的最大值比int大得多,其根本緣由是浮點數在內存中是以指數的方式存儲。
咱們都知道在內存中,一個float類型的實數變量是佔據32位,即32個二進制的0或者1組成

四字節浮點數  最左邊的第一位是最高位

0000 0000 0000 0000 0000 0000 0000 0000

從低位依次到高位叫第0位和第31位,這32位能夠由三部分組成:

符號位:第31位數表示符號位,若是爲0表示整數,若是爲1表示負數
指數:第23位到第30位,這8個二進制表示該實數轉化爲規格化的二進制實數後的指數與127(127即所謂的偏移量)之和所謂階碼,規格化的二進制實數只能在-127-127之間。
小數位:第0位到第22位,最多能夠表示23位二進制小數,不然超過了就會產生偏差。

2.10.5 浮點數相等性判斷

float佔據四個字節,提供的有效位是6-7位,而double佔據八個字節,提供的有效位數是15-16位,若是在使用float或者double表示實數時超過有效數字,若拿來進行關係運算(例如等於)的話,會獲得一個錯誤的結果。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 浮點數的相等性判斷 若是實數超過有效範圍,使用==判斷會出錯 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    float flt1 = 1.00000000001;
    float flt2 = 1.00000000000000000001;
    //由於float的有效數字是6-7位 這裏超出有效數字 計算不許確
    printf(" flt1 == flt2 ? %d\n", (flt1 == flt2)); // 輸出結果1表示相等 0則表示不相等

    //double精確的有效位數是15-16位,這裏也超出了有效數字,計算不夠正確 
    double db1 = 1.00000000000000000000000000000001;
    double db2 = 1.000000000000000000000000000000000000000000000000000000000000000000000000000000001;

    printf(" db1 == db2 ? %d\n", (db1 == db2)); // 輸出結果1表示相等 0則表示不相等
    system("pause");
    return 0;
}

中美GDP計算:給出當前美國和中國的GDP以及增加率,使用math.h的pow函數實現計算出中國GDP超過美國GDP的年份

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
/* 給出當前中國、美國的GDP以及增加率,計算中國超過美國的年份 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //當前中美GDP
    double ch_current_gdp = 14.6;
    double us_current_gdp = 20.5;

    //當前中美GDP的增加率
    double ch_rate = 1.06;
    double us_rate = 1.04;

    double ch_gdp=0.0;
    double us_gdp=0.0;
    int year=2018;
    for (int i = 1; i <= 100; i++) {
        //使用pow函數計算中美每一年增加後的GDP
        ch_gdp = ch_current_gdp * pow(ch_rate, i);
        us_gdp = us_current_gdp * pow(us_rate, i);
        year++;
        printf("%d年中國的GDP是%f\n", year, ch_gdp);
        printf("%d年美國的GDP是%f\n", year, us_gdp);

        if (ch_gdp > us_gdp) {
            printf("在%d年,中國的GDP超越了美國的GDP", year);
            break;
        }

    }

    system("pause");
    return 0;
}

2.11 字符型

2.11.1 字符型常量

在平常開發應用中,字符是最經常使用的數據類型。由於生活中的許多數據都是經過字符表示,而不是數字表示,字符能表示更多的含義,最典型的就是網絡協議,例如超文本傳輸協議HTTP協議。

C語言中字符使用一對單引號('')表示,注意單引號只能做用域一個字符。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 字符常量(英文) @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //輸出英文字符
    printf("輸出英文字符%c\n",'a');
    printf("char佔據的字節數量是%d\n",sizeof(char));
    system("pause");
    return 0;
}

C語言中的char只佔用1個字節,所以不能存儲中文,若是想要存儲中文,須要使用wchar_t表示,而後還要進行本地化的設置

#define _CRT_SECURE_NO_WARNINGS
#include <locale.h> //引入本地化的頭文件
#include <stdio.h>
#include <stdlib.h>
/* 中文字符 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {


    //設置本地化
    setlocale(LC_ALL, "chs");
    wchar_t ch = L'劉'; //使用wprintf()函數輸出中文 wprintf(L"ch = %c \n",ch); system("pause"); return 0; }

除了使用prinf函數結合%c輸出字符之外,C語言還提供了putchar()函數來輸出字符

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 輸出字符的兩種方式 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    char c = 'A';
    putchar(c);
    printf("\n");
    printf("c = %c \n",c);
    system("pause");
    return 0;
}

2.11.2 字符的本質

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 字符變量 字符變量的本質存儲的是字符的ASC||碼值 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    //聲明並初始化字符變量ch,初始化值爲z
    char ch = 'z';
    printf("char ch = %c \n", ch);
    printf("字符變量%c對應的整數是%d \n",ch,ch);
    printf("char佔據的字節大小是 %u \n",sizeof(ch));
    system("pause");
    return 0;
}

因爲計算機最底層只能識別二進制的數據,可是字符不是二進制的數據。
若是將字符和特定的數值一一對應起來,這張對應表就是ASC||表。
若是字符變量ch存儲的字符是'z',那麼實際上存儲的是字符z對應的ASC||值即整數122。即字符變量存儲的本質就是存儲的字符對應的ASC||值

平常開發中最經常使用的字符就是大小寫字母以及數字字符
咱們能夠藉助printf()函數的格式%c和%d實現輸出字符對應的ASC||值。
數字字符'0'表示的整數是48,小寫字符'a'表示的整數是97,大寫字符'A'表示的整數時65。數字0表示空字符。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 經常使用的字符對應的ASC||表 輸出字符使用%c,輸出ASC||值使用%d @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    char ch = '0';
    printf("字符%c對應的整數是%d\n", ch, ch);
    ch = 'a';
    printf("字符%c對應的整數是%d\n", ch, ch);
    ch = 'A';
    printf("字符%c對應的整數是%d\n", ch, ch);

    int number = 97;
    printf("數字%d對應的字符是%c\n", number, number);
    number = 65;
    printf("數字%d對應的字符是%c\n", number, number);
    number = 48;
    printf("數字%d對應的字符是%c\n", number, number);


    system("pause");
    return 0;
}

既然字符變量的本質存儲的ASC||值,即整數。所以字符也能夠參與加減運算。
因爲ASC||碼規定了小寫字母''a' 表示97,而後一次遞增,小寫字母'z'表示122,而大寫字母'A'表示65,而後依次遞增,大寫字母'Z'表示90。所以根據這個規律能夠經過加減運算實現大小寫字母轉換。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 字符的大小寫轉換 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    
    //小寫轉大寫 
    char lower_ch = 'a';
    // 0x20就是十進制的32
    char upper_ch = lower_ch - 0x20;

    printf("小寫字母%c轉換爲大寫字母的結果是%c \n",lower_ch,upper_ch);


    //大寫轉小寫
    upper_ch = 'A';
    lower_ch = upper_ch + 32;
    printf("大寫字母%c轉換爲小寫字母的結果是%c \n", upper_ch, lower_ch);


    system("pause");
    return 0;
}

2.11.3 轉義字符

一些特殊的符號沒法直接顯示時,咱們使用\特殊字符來表示。
例如\a表示發聲,沒法在終端上直接顯示。
在平常開發中還會使用到各類經常使用的轉義字符,例如\t實現tab的效果。
轉義字符\n實現換行的效果,轉義字符\實現路徑轉義。
%%輸出百分號。
轉義字符還可使用八進制和十六進制表示,用於表示字符對應的ASC||碼值。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 轉義字符 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    //聲音沒法輸出在屏幕上顯示,所以這裏使用轉字符\a實現發聲
    printf("%c",'\a');
    //平常開發中經常使用的轉移字符 \n實現換行
    printf("hello \n");
    printf("world \n");
    // \t 實現tab效果
    printf("C\t語\t言\t核\t心\t編\t程\n");
    // \r 實現光標移到行首 因爲光標移到行首,最終只會輸出C語言核心編程
    printf("CPrimerPlus%cC語言核心編程\n",'\r');
    //路徑轉義 \\表示路徑轉義 
    //使用system函數使用電腦上的微信,調用成功的前提條件是電腦上安裝了微信
    system("\"C:\\Program Files (x86)\\Tencent\\WeChat\\WeChat.exe\"");
    // %%實現輸出百分號
    printf("合格率爲%%%d\n",90);

    //八進制轉義和十六進制轉義
    //八進制的62轉換爲十進制是50,50表示字符2
    char ch = '\62';
    printf("八進制的轉義字符62轉換爲字符的結果是%c \n",ch);//2
    //十六進制的62轉換爲十進制是98,98表示的字符是b
    ch = '\x62';
    printf("十六進制的轉義字符62轉換爲字符的結果是%c\n", ch); //b

    system("pause");
    return 0;
}

2.12 字符串

字符串用於表示字符序列,也就是一串使用" "包含起來的內容。
C語言中的字符串以\0結尾,這也就是意味着即便雙引號""中什麼都沒有也會佔據一個字節,而中文字符串中的每一個字符一樣會佔據兩個字節

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* C語言字符串以\0結尾 空字符串也會佔據一個字節 中文佔據兩個字節 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    //字符串是以/0結尾,這裏字符串A佔據2個字節
    printf("字符串A佔據的字節數量是%d\n", sizeof("A"));
    //空字符串也是以\0結尾,所以這裏佔據一個字節
    printf("\"\"佔據的字節數量爲%d\n", sizeof("")); //以\0結尾

    //字符串單箇中文佔據兩個字節
    printf("字符串劉光磊佔據的字節數量是%d\n", sizeof("劉光磊")); //每一箇中文佔據兩個字節,而後以\0結尾 所以是7個
    system("pause");
    return 0;
}

使用算術運算實現字符串加密解密

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
/* 字符串加密解密簡單實現 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    //使用字符數組初始化一個字符串 calc
    char str[5] = { 'c','a','l','c','\0' };

    //調用計算器
    system(str);

    printf("加密以前str = %s\n", str);


    //循環 將字符+1 由於字符的本質就是ASC||碼 所以能夠看成整數來使用
    for (int i = 0; i < 4; i++) {

        str[i] += 1;
    }

    printf("加密以後str = %s\n", str);


    //逆運算解密
    for (int i = 0; i < 4; i++) {

        str[i] -= 1;
    }
    printf("解密以後str = %s\n", str);
    
    system("pause");
    return 0;
}

2.13 布爾類型

bool類型只有兩個值,即true和fasle,它們在內存中分別使用1和0表示,這樣一個字節即可以存儲bool類型的變量。
在C程序中使用bool類型的變量,須要引入頭文件stdbool.h,後續的各類邏輯、關係運算以及選擇結構if/else和while循環會大量使用bool類型的變量。

#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
/* bool類型的使用 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    bool flag = true;
    //佔據的字節數量爲1
    printf("bool佔據的字節數量是%d\n", sizeof(flag));
    //成立的結果爲1
    printf("bool = %d\n", flag);

    flag = false;
    //不成立的結果爲0
    printf("bool = %d\n", flag);


    //bool在if/else結構和關係運算的使用
    int age = 20;
    bool isAdult = age > 18;
    if (isAdult==1) {
        printf("你成年了\n");
    }
    else {
        printf("你沒有成年\n");
    }
    
    system("pause");
    return 0;
}

2.14 數據類型轉換

2.14.1 數據類型自動轉換

當多種數據類型(整數、浮點數、字符)同時參與運算時,會發生自動類型轉換,容量小的數據類型的變量與容量大的大數據類型的變量作運算時,結果自動提高爲容量大的數據類型,防止運算時超過極限值,防止運算結果的精度丟失。此時容量大小指的是,數據類型表示數的範圍大小,而不是佔用內存大小,好比float容量大於long的容量。

自動類型轉換的規則以下

char,short->int->long->float->double->long double

其中對應的有符號類型還會自動轉換爲無符號類型,char和char運算,short和short運算也會轉換爲int。

char和int,double的自動類型轉換

#include <stdio.h>
#include <stdlib.h>
/* 自動類型轉換 在進行算術運算時,會發生自動類型轉換 表示範圍小的值自動轉換爲表示範圍大的變量,保存精度 char->short>int->long->float->double->long double @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int type_convert_auto_main(int argc, char* argv[]) {

    char ch = 'A';
    printf("1.0佔據的字節數量是%d\n", sizeof(1.0));
    //ch+1自動轉換爲int類型,所以佔據四個字節
    printf("字符變量ch+1的字節數量是%d\n", sizeof(ch + 1));
    printf("字符變量ch+1.0的字節數量是%d\n", sizeof(ch + 1.0));
    getchar();

    return 0;
}

int和unsigned int的自動類型轉換

#include <stdio.h>
#include <stdlib.h>
/* 自動類型轉換 有符號類型int轉換爲無符號類型int @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    int number1 = -10;
    unsigned int number2 = 5;

    //有符號數和無符號數參與運算時,先將有符號數轉換爲無符號數

    printf("number1按照無符號輸出結果爲%u\n",number1);
    int result = number1 + number2;

    //使用三目運算和算術運算輸出表達式number1+number2大於0仍是小於0
    //由於number1按照無符號的結果是4294967286 所以結果是大於0
    number1 + number2 > 0 ? printf("number1 + number2 > 0 \n"):printf("number1 + number2 < 0 \n");
    printf("按照有符號整數輸出 result = %d \n", result);
    printf("按照無符號整數輸出 result = %u \n", result);

    system("pause");
    return 0;

}

char、short的自動類型轉換:因爲char,short佔據的內存空間太小,編譯器規定,只要是char,short參與運算,都會自動轉換爲int類型。

#include <stdio.h>
#include <stdlib.h>
/* char,short的自動類型轉換 char和short參與運算都會轉換爲int @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    
    char x = 10;
    int target = x + x;

    short s = 20;
    int short2 = s + s;
    int result = x + s;
    system("pause");
    return 0;

}

2.14.2 數據類型強制轉換

強制類型轉換就是在待轉換的表達式的左邊使用強轉符(目標類型)實現

#include <stdio.h>
#include <stdlib.h>
/* 強制類型轉換 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    float fl = 10.8;
    float flt = 10.3;
    int num = (int)fl + flt; //20.3 先把fl強制轉換爲int類型,而後再和flt相加,再轉換爲int,由於賦值會進行自動類型轉換
    printf("num =%d\n", num);
    num = (int)(fl + flt);//21 先把fl和flt相加後,強制轉換爲int
    printf("num =%d\n", num);
    getchar();
    return 0;

}

強制類型轉換隻是臨時改變變量的值

#include <stdio.h>
#include <stdlib.h>
/* 強制類型轉換的特性 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main() {
    float flt = 3.14f;
    int number = 0;
    //強制類型轉換隻是臨時改變變量flt的值,其本質是讀取flt的值,而後強制轉換爲int類型的值
    number = (float)flt;
    //number =3
    printf("number = %d", number);
    // flt=3.14
    printf("flt = %f", flt);

    getchar();

    return 0;
}

在使用printf函數打印輸出變量值時,不會進行自動類型轉換,若是想要獲取預期的結果,須要使用強制類型轉換實現。

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

/* printf()函數與強制類型轉換 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main() {
    //由於printf函數不會進行類型轉換,因此這裏獲得一個錯誤的結果
    printf("%d\n", 12.1);
    //12.1爲浮點類型,這裏使用強制類型轉換實現轉換爲整數
    printf("%d\n", (int)12.1);
    printf("%f\n", 10); //整數按照浮點數解析,獲得的結果就是0.000000
    printf("%f\n", (float)10); //強制類型轉換
    getchar();
    return 0;
}

強制類型轉換案例:實現對小數點後三位實現四捨五入

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

/* 實現對小數點後三位實現四捨五入 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {
    printf("請輸入四捨五入的三位小數\n");
    double input = 0.0;
    scanf("%lf", &input);
    double val = 1.235;
    //1.234*100=123.4 123.4+0.5=123 123/100.0=1.23

    // 1.235*100=123.5 123.5+0.5=124 124/100=1.24
    // 1.24>1.235
    // 1.24-1.235=0.05
    //1.235+0.05=1.24
    double result = (int)(input * 100 + 0.5) / 100.0;
    printf("result =%.2f", result);
    getchar();
    return 0;

}

強制類型轉換案例:帳戶餘額的分大於4分就偷錢

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


/* 實現偷錢程序 若是帳戶餘額的分大於等於4分就不偷錢,小於等於3分就偷走 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int main(int argc, char* argv[]) {

    printf("請輸入你的帳戶餘額\n");
    double balance = 0.0;
    scanf("%lf", &balance);

    // 12.34*10=123.4 123.4+0.6=124 124/10.0=12.4 12.4>12.34
    double rest = (int)((balance * 10) + 0.6) / 10.0;
    printf("rest = %f", rest);
    if (rest < balance) {
        //
        printf("能夠偷錢%.2f元", balance - rest);
    }

    getchar();

    return 0;
}

2.14.2 數據類型轉換的原理

當在進行數據類型轉換時,若是該數據是有符號的,在進行數據類型轉換時按照符號位數來填充,若是是無符號則按照0來填充。

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

/* 數據類型轉換的內存原理 有符號 低字節轉高字節按照符號位數填充 無符號 低字節轉高字節按照0填充 @author liuguanglei ittimelinedotnet@gmail.com @website ittimeline.net @version 2019/08/28 */
int convert_main(int argc, char* argv[]) {
    //正數按照0填充
    char ch = 1; // 二進制表示 0000 0001
    int num = 1; // 二進制表示 0000 0000 0000 0000 0000 0000 0000 0001


    //負數按照1填充
 // 二進制表示 原碼 1000 0001 反碼 1111 1110 補碼 1111 1111 ->ff
    ch = -1;
    // 二進制表示 原碼 1000 0000 0000 0000 0000 0000 0000 00001
    // 反碼 1111 1111 1111 1111 1111 1111 1111 1110
    // 補碼 1111 1111 1111 1111 1111 1111 1111 1111 -> ffffffff
    num = ch;


    unsigned char data = 255 + 1; // 二進制補碼 1 0000 0000 可是char只能佔據8位,所以這裏會截取8位即0000 0000,結果位0
    printf("unsigned char data的地址是%p", &data);
    printf("data = %d", data);


    unsigned int u_num = -1; //賦值錯誤,能編譯不意味着結果正確
                            // 1000 0000 0000 0000 0000 0000 0000 0000 0001
                            // 1111 1111 1111 1111 1111 1111 1111 1111 1110
                            // 1111 1111 1111 1111 1111 1111 1111 1111 1111 無符號解析結果爲2的32次方即4294967295
    for (int i = 0; i < u_num; i++) {
        system("mspaint");
    }
    getchar();

    return 0;
}
相關文章
相關標籤/搜索