【C語言入門到精通】04 數據類型

【C語言入門到精通】04 數據類型

If a program manipulates a large amount of data, it does so in a small number of ways.1

碼字不易,對你有幫助 **點贊👍/轉發↪️/關注 👀 ** 支持一下作者
微信搜公衆號:不會編程的程序圓
看更多幹貨,獲取第一時間更新

🗺思維導圖


✉️寫在前面


如果只是寫個人學習總結的博客很容易,簡單寫一些感悟然後貼上代碼走人就可以了,甚至不用校審。但是我命名本系列爲【C語言必知必會】幫助你從入門到精通 C語言,那勢必要“事無鉅細”一些:既要考慮到沒有基礎的初學者,又不能止於基礎。所以本教程適合各類人羣學習,只要你看就一定會有幫助。

本教程是本人純手打併排版,校審由我與我的搭檔湯圓君一起完成的。你們看這一篇文章我要寫好幾個小時。如果文章對你有幫助,請不要“白嫖”。支持一下作者,作者會發更多幹貨好文。

特別鳴謝:湯圓君(公衆號:【Cc聽香水榭】 長期更新高質量英語教學)關注她表示對她工作的認可吧!

▶️ 此符號表示該內容以後的章節會講解,此章節內不要求理解。

🌐目錄


🍌概述


關鍵字

C語言的數據類型關鍵字

最初 K&R 給出的關鍵字 C90標準添加的關鍵字 C99標準添加的關鍵字
int signed _Bool (布爾型)
short void _Complex(複數)
long _Imaginary(虛數)
unsigned
char
float
double

通過這些關鍵字創建的類型,按計算機的存儲方式可分爲兩大基本類型:整數類型浮點數類型

位,字節和字

位,字節和字

位(bit): 最小的存儲單元,也稱比特位。可以存儲 0 或 1(或者說,位用於存儲“開”或“關”)

字節(byte): 1 byte = 8 bit 既然 1 位可以表示 0 或 1,那麼 1 字節就有 256 (2^8)種 0/1 組合,通過二進制編碼(僅用 0/1 便表示數字),便可表示 0 ~ 255 的整數或一組字符。(以後會詳細講解

字(word): 是設計計算機時給定的自然存儲單位。對於 8 位 的微型計算機(如:最初的蘋果機),1 字長 只有 8 位,從那以後,個人計算機的字長增至 16 位,32位,直至目前的 64位。計算機字長越大,其數據轉移越快,允許訪問的內存越多。

整數

整數 7 以二進制形式表示是:111 ,用一個字節存儲可表示爲:

浮點數

浮點數相比我們都不陌生,本節後面還會做更詳細的介紹。現在我們介紹一種浮點數的表示方法:e記數法。

如 3.16E+007 表示 3.16 * 10^7(3.16乘以10的七次方)。007 表示 10^7;+ 表示 10 的指數 7 爲正數。

其中,E 可以寫成 e;表示正次數時,+ 號可以省略;007也可以省略爲7。即:3.16e7。

浮點數和整數的存儲方案是不同的。計算機將浮點數分成小數部分和指數部分來表示,而且分開存儲這兩部分。因此,雖然 7.0 和 7 在數值上相同,但它們的存儲方式不同。下圖演示了一個存儲浮點數的例子。後面我們會做更詳細的解釋

整數與浮點數的區別:

  • 整數沒有小數部分,浮點數有小數部分
  • 浮點數可以表示的範圍比整數大
  • 對於一些算術運算(如,兩個很大的數相減),浮點數損失的精度更多
  • 因爲在任何區間內都存在無窮多個實數,所以計算機的浮點數不能表示區間內的所有值。浮點數通常只是實際值的近似值。(例如,7.0 可能被存儲爲浮點值 6.99999)
  • 過去,浮點數運算比整數運算慢。不過現在許多CPU都包含了浮點數處理器,縮小了速度上的差距。

整數類型


有符號整數和無符號整數

有符號整數如果爲零或正數,那麼最左邊的位(符號位,只表示符號,不表示數值)爲 0 ;如果爲負數,則符號位爲 1。如:最大的 16 位整數(2個字節)的二進制表示形式是 01111111 11111111,對應的數值是 32767(即:2^15 - 1)

無符號整數 不帶符號位(最左邊的位是數值的一部分)。因此,最大的 16 位整數的二進制表示形式是:11111111 11111111(即:2^16 - 1)

默認情況下,C語言中的整型變量都是有符號的,也就是說最左位保留符號位。若要告訴編譯器變量沒有符號位,需要把他聲明成 unsigned 類型。

整數的類型

short int

unsigned short int

int

unsigned int

long int

unsigned long int

整數的類型歸根結底只有這 6 種,其他組合都是上述某種類型的同義詞。

例如:long signed int 與 long int 是一樣的;unsigned short int 與 short unsigned int 是一樣的

C語言允許通過省略單詞 int 來縮寫整數類型的名稱。

例如:unsigned short int 可以縮寫爲 unsigned short ;而 long int 可以縮寫爲 long。

C程序員經常省略 int 。

6 種 整數類型每一種所表示的取值範圍都會根據機器的不同而不同,但是有兩條所有編譯器都必須遵守的原則。

  • C 標準要求 short,int,long 中的每一種類型都要覆蓋一個確定的最小取值範圍(後面會詳細講解
  • int 類型不能比 short 類型短,long 類型不能比 int 類型短

這也就是說:short 的大小可以與 int 相等;int 的大小可以與 long 相等

16位,32位,64位機器的整數類型都各有些不同,我們常用的是 32 位機器(嚴格來說是編譯器,我的電腦是 64 位,但是VS2019用的最多的是 32位模式),我們就以 32 位機器爲例

32位機器整數類型

類型 最小值 最大值
short -32768( - 2^15 ) 32767(2^15 -1 )
unsigned short 0 65535 (2^16 - 1)
int - 2147483648(- 2^31) 2147483647(2^31 - 1)
unsigned int 0 4294967295
long - 2147483648 2147483647
unsigned long 0 4294967295

可以看出,32位機器上,int 與 long的大小是一樣的,都是 4 個字節。

16位機器上,int 與 short 大小是一樣的,都是 2 個字節。

64位機器上,與 32 位機器不同的是,long 是 8 個字節。

但是,上述所說的規律並不是 C標準規定的,會隨着編譯器的不同而不同。可以檢查頭文件<limits.h>,來查看每種整數類型的最大值和最小值。(下面給出我的VS2019的 limits.h 頭文件)

limits.h

#pragma once
#define _INC_LIMITS

#include <vcruntime.h>

_CRT_BEGIN_C_HEADER



#define CHAR_BIT      8         // number of bits in a char 
#define SCHAR_MIN   (-128)      // minimum signed char value
#define SCHAR_MAX     127       // maximum signed char value
#define UCHAR_MAX     0xff      // maximum unsigned char value

#ifndef _CHAR_UNSIGNED
    #define CHAR_MIN    SCHAR_MIN   // mimimum char value
    #define CHAR_MAX    SCHAR_MAX   // maximum char value
#else
    #define CHAR_MIN    0
    #define CHAR_MAX    UCHAR_MAX
#endif

#define MB_LEN_MAX    5             // max. # bytes in multibyte char
#define SHRT_MIN    (-32768)        // minimum (signed) short value
#define SHRT_MAX      32767         // maximum (signed) short value
#define USHRT_MAX     0xffff        // maximum unsigned short value
#define INT_MIN     (-2147483647 - 1) // minimum (signed) int value
#define INT_MAX       2147483647    // maximum (signed) int value
#define UINT_MAX      0xffffffff    // maximum unsigned int value
#define LONG_MIN    (-2147483647L - 1) // minimum (signed) long value
#define LONG_MAX      2147483647L   // maximum (signed) long value
#define ULONG_MAX     0xffffffffUL  // maximum unsigned long value
#define LLONG_MAX     9223372036854775807i64       // maximum signed long long int value
#define LLONG_MIN   (-9223372036854775807i64 - 1)  // minimum signed long long int value
#define ULLONG_MAX    0xffffffffffffffffui64       // maximum unsigned long long int value

#define _I8_MIN     (-127i8 - 1)    // minimum signed 8 bit value
#define _I8_MAX       127i8         // maximum signed 8 bit value
#define _UI8_MAX      0xffui8       // maximum unsigned 8 bit value

#define _I16_MIN    (-32767i16 - 1) // minimum signed 16 bit value
#define _I16_MAX      32767i16      // maximum signed 16 bit value
#define _UI16_MAX     0xffffui16    // maximum unsigned 16 bit value

#define _I32_MIN    (-2147483647i32 - 1) // minimum signed 32 bit value
#define _I32_MAX      2147483647i32 // maximum signed 32 bit value
#define _UI32_MAX     0xffffffffui32 // maximum unsigned 32 bit value

// minimum signed 64 bit value
#define _I64_MIN    (-9223372036854775807i64 - 1)
// maximum signed 64 bit value
#define _I64_MAX      9223372036854775807i64
// maximum unsigned 64 bit value
#define _UI64_MAX     0xffffffffffffffffui64

#if _INTEGRAL_MAX_BITS >= 128
    // minimum signed 128 bit value
    #define _I128_MIN   (-170141183460469231731687303715884105727i128 - 1)
    // maximum signed 128 bit value
    #define _I128_MAX     170141183460469231731687303715884105727i128
    // maximum unsigned 128 bit value
    #define _UI128_MAX    0xffffffffffffffffffffffffffffffffui128
#endif

#ifndef SIZE_MAX
    #ifdef _WIN64
        #define SIZE_MAX _UI64_MAX
    #else
        #define SIZE_MAX UINT_MAX
    #endif
#endif

#if __STDC_WANT_SECURE_LIB__
    #ifndef RSIZE_MAX
        #define RSIZE_MAX (SIZE_MAX >> 1)
    #endif
#endif



_CRT_END_C_HEADER

C99 中的整數類型

C99 提供了兩個額外的整數類型:long long intunsigned long long int

整數常量

整數常量:在程序中以文本形式出現的數,而不是讀,或計算出來的數。

C語言允許用 十進制(基數爲 10),八進制(基數爲 8),十六進制(基數爲 16)的形式書寫整數常量

8 進制 與 16 進制

8 進制數是用數字 0 ~ 7 書寫的。八進制的每一位表示一個 8 的冪(這就如同 10 進制每一位表示 10 的冪一樣)。因此,八進制數 237 表示成 10 進制數就是 2 * 8^2 + 3 * 8^1 + 7 * 8^0 = 128 + 24 + 7 = 159

16 進制數使用數字 0 ~ 9 加上字符 A ~ F 書寫的,其中字符 A ~ F 表示 10 ~ 15 的數。16進制數每一位表示一個 16 的冪,16進制數 1AF 的十進制數值是 1 x 16^2 + 10 * 16^1 + 15 * 16^0 = 256 + 160 + 15 = 431

如果上面的描述你還是沒有懂,可以參考下圖:

[外鏈圖片轉存失敗,源站可能有防盜鏈機制,建議將圖片保存下來直接上傳(img-ziyXqpwQ-1585358279189)(C:\Users\1\Desktop\素材\27.png)]

  • 十進制常量包含 0 ~ 9 的數字,但是不能以 0 開頭

    15 255 32767

  • 八進制常量包含 0 ~ 7 的數字,必須要以 0 開頭

    017 0377 077777

  • 十六進制常量包含 0 ~ 9 的數字 和 A ~ F 的字母,總是以 0x 開頭

    0xf 0xff 0x7fff

    十六進制常量中的字母可以是大寫也可以是小寫

#include<stdio.h>

int main(void) {

	int x = 100;

	printf("decimal = %d	octonary = %o	hexadecimal = %x \n", x, x, x);
	printf("decimal = %d	octonary = %#o	hexadecimal = %#x \n", x, x, x);
	return 0;
}

輸出:

decimal = 100   octonary = 144  hexadecimal = 64
decimal = 100   octonary = 0144 hexadecimal = 0x64

八進制與十六進制只是書寫數的方式,他們不會對數的實際存儲方式產生影響整數都是以二進制形式存儲的)。任何時候都可以從一種書寫方式切換的另一種,甚至可以混合使用:10 + 015 + 0x20 = 55 。八進制和十六進制更適合底層程序的編寫(以後會詳細講到)。

十進制整數常量的類型通常是 int ,但如果常量過大,就用 long int 類型,如果還不夠用,編譯器會用 unsigned long int 做最後嘗試。

八進制和十六進制常量編譯器會依次嘗試:int,unsigned int,long int 和 unsigned long int 類型,知道找到能表示該常量的類型。

爲了強制編譯器把常量作爲長整數來處理,只需要在後面加上一個字母L(或l,字母l比較像數字1所以建議大寫):

15L 0377L 0x7ffffL

爲了指明是無符號常量,可以在常量後面加上字母U(或u):

15U 0377U 0x7ffffU

L 與 U 可以結合使用:0xffffffffLU(L 與 U 的書寫順序無所謂)

C99 中的整數常量

在 C99 中,以 LL 或 ll (字母大小寫要一致)結尾的整數常量是 long long int 類型。在 LL 或 ll 前面或後面加上 U(u)表示 unsigned long long int 類型。

C99 與 C89 在確定整數常量類型規則上有些不同。

對於沒有後綴的十進制常量,其類型是 int ,long int,long long int 中能表示該值的 最小類型。

對於八進制和十六進制常量,可能的類型順序爲:int,unsigned int,long int,unsigned long int,long long int,unsigned long long int。

常量後面任何後綴都會改變可能的類型列表。

整數溢出

對整數執行算數運算時,其結果可能太大而無法表示。例如,對兩個 int 值進行算數運算時,其結果必須仍然能用 int 來表示;否則(表示結果所需要的數位(二進制)太多),就會發生溢出

有符號整數的溢出時,程序的行爲時未定義的。未定義行爲的結果是不確定的。最有可能的結果是,僅僅是運算出錯,但是程序也有可能崩潰,或者出現其他意想不到的情況。

無符號整數溢出時,結果是有定義的:*對 2^n 取模,其中 n 是用於存儲結果的位數。*例如:如果對無符的 16 位數 65535 加 1,其結果可以保證爲 0 。

請看下面的程序,也許可以幫助你理解。

tobig.c —— 超出系統最大 int 值

#include<stdio.h>

int main(void) {

	int i = 2147483647;
	unsigned int j = 4294967295;

	printf("%d	%d	%d\n", i, i + 1, i + 2);
	printf("%u	%u	%u\n", j, j + 1, j + 2);

	return 0;
}

輸出:

2147483647      -2147483648     -2147483647
4294967295      0       1

可以將無符號整型 j 看作是汽車的里程錶。當達到他能表示的最大值時,會重新從起點開始。整數 i 也是類似的情況。它們的主要區別是,在超過最大值時, unsigned int 類型的變量 j 從 0 開始;而 int 型的變量則從 -2147483648 開始。注意,當 i 超出(溢出)其相對類型所能表示的最大值時,系統並未通知用戶。因此必須自己注意這類問題。

讀/寫整數

讀寫無符號整數

unsigned int a;

  • 十進制

    scanf("%u", &a);

    printf("%u", a);

  • 八進制

    scanf("%o", &a);

    printf("%o", a);

  • 十六進制

    scanf("%x", &a);

    printf("%x", a);

讀寫**短整型*數:在 d,u,o,x 前加上 h

short b

  • scanf("%hd", &b);

    printf("%hd", b);

讀寫長整數:在 d,u,o,x 前加上 l

long c

  • scanf("%ld", &c);

    printf("%ld", c);

讀寫長長整數: 在 d,u,o,x 前加上 ll

long long int d

  • scanf("%lld", &d);

    printf("%lld", d);

改進程序
#include<stdio.h>

int main(void){
    
    int a, b, sum;
    
    printf("Enter two integers:\n");
    scanf("%d %d", &a, &b);
    
    sum = a + b;
    
    printf("The sum is %d\n", sum);
    return 0;
}

觀察上述程序,請思考:兩個 int 型的變量的和可能超過 int型變量允許的最大值。因此,爲了改進這個程序,我們可以將 int 型的 a,b,sum 都變爲 long long 型(考慮到 32 位機器的 long 與 int 大小是相同的。)如下:

int main(void) {

	long long a, b, sum;

	printf("Enter two integers:\n");
	scanf("%lld %lld", &a, &b);

	sum = a + b;

	printf("The sum is %lld\n", sum);
	return 0;
}

浮點類型


C語言提供了三種浮點類型,對應着不同的浮點格式:

  • float:單精度浮點數
  • double:雙精度浮點數
  • long double:擴展精度浮點數

通常我們用到的是 double

自己編譯器 的浮點特徵(浮點類型的範圍)可以在float.h頭文件內查看。下面給出我的 VS2019 的 float 頭文件的部分內容。

float.h

#define DBL_DECIMAL_DIG  17                      // # of decimal digits of rounding precision
#define DBL_DIG          15                      // # of decimal digits of precision
#define DBL_EPSILON      2.2204460492503131e-016 // smallest such that 1.0+DBL_EPSILON != 1.0
#define DBL_HAS_SUBNORM  1                       // type does support subnormal numbers
#define DBL_MANT_DIG     53                      // # of bits in mantissa
#define DBL_MAX          1.7976931348623158e+308 // max value
#define DBL_MAX_10_EXP   308                     // max decimal exponent
#define DBL_MAX_EXP      1024                    // max binary exponent
#define DBL_MIN          2.2250738585072014e-308 // min positive value
#define DBL_MIN_10_EXP   (-307)                  // min decimal exponent
#define DBL_MIN_EXP      (-1021)                 // min binary exponent
#define _DBL_RADIX       2                       // exponent radix
#define DBL_TRUE_MIN     4.9406564584124654e-324 // min positive value

#define FLT_DECIMAL_DIG  9                       // # of decimal digits of rounding precision
#define FLT_DIG          6                       // # of decimal digits of precision
#define FLT_EPSILON      1.192092896e-07F        // smallest such that 1.0+FLT_EPSILON != 1.0
#define FLT_HAS_SUBNORM  1                       // type does support subnormal numbers
#define FLT_GUARD        0
#define FLT_MANT_DIG     24                      // # of bits in mantissa
#define FLT_MAX          3.402823466e+38F        // max value
#define FLT_MAX_10_EXP   38                      // max decimal exponent
#define FLT_MAX_EXP      128                     // max binary exponent
#define FLT_MIN          1.175494351e-38F        // min normalized positive value
#define FLT_MIN_10_EXP   (-37)                   // min decimal exponent
#define FLT_MIN_EXP      (-125)                  // min binary exponent
#define FLT_NORMALIZE    0
#define FLT_RADIX        2                       // exponent radix
#define FLT_TRUE_MIN     1.401298464e-45F        // min positive value

如果你的編譯器 float 和 double 的最大值和最小值和我的一樣,說明你的編譯器也是支持 IEEE標準的(大多數計算機都是遵循 IEEE 754標準)。

浮點常量

浮點常量可以有多種寫法。例如,下面這些寫法都表示數 57.0

57.0 57. 57.0e0 5.7e1 5.7e+1 .57e2 570.e-1

浮點常量必須包含小數點或指數

**默認情況下,浮點常量都以雙精度的形式存儲。**換句話說,當 C語言的編譯器在程序中發現常量 57.0 時,它會安排數據以 double 類型變量的格式存儲在內存中。

如果只需要單精度,可以在常量末尾加上 Ff(如 57.0F);如果想以 long double 格式存儲,在常量尾加上 Ll(如 57.0L)

讀/寫浮點數

  • float: %e %f %g

  • double: %lf

    • scanf("%lf", &varible);

    • printf("%f", varible);

      lf格式串 只能在 scanf 中使用;在用 printf 輸出 double 時,格式串可以使用 e,f,g

  • long double: %Lf

    • scanf("%Lf", &varible);
    • printf("%Lf", varible);

字符類型


字符類型(字符型):char

char 類型的值可以根據計算機的不同而不同,因爲不同的計算機可能會有不同的字符集。

字符集:當今最常用的字符集是 ASCII (美國信息交換標準碼)字符集。

字符操作

C語言把字符當作小整數進行處理

所有字符都是以二進制形式進行編碼的。

在標準的 ASCII 碼中,字符的取值範圍是 00000000 ~ 01111111,可以看成是 0 ~ 127 。例如,字符 ‘A’ 的值是 65,‘a’ 的值是 97,‘0’ 的值是48,’ ’ 的值是 32 。

許多字符集都超出了 127,甚至多餘 255(unsigned char 類型,二進制序列爲:1111 1111 )。

C語言中,字符和整數的關聯是很強的,字符常量事實上是 int 類型而非 char 類型

請看下面的例子,你會更深的理解 字符型與整型的關聯(字符集位ASCII)

char ch;
int i;

i = 'a';// i is now 97
ch = 65;//'ch' is now 'A'
ch = ch + 1;//'ch' now is 'B'

因此,字符就有了數的一些特徵。比如可以像數一樣比較,可以當作條件應用於 if語句,for循環。這是一個很便利的事情。

但是,以數的形式處理字符 可能降低程序的可移植性(不同機器使用的字符集不同) 和 導致編譯器無法檢查出來的多種編程錯誤(‘a’ + ‘b’ * ‘c’ 這類沒有意義的表達式等)。

有符號字符 和 無符號字符

有符號字符signed char:取值範圍:-128 ~ 127

無符號字符unsigned char: 取值範圍:0 ~ 255

可移植性技巧:不要假設 char 類型默認爲 signed 或 unsigned 。如果有區別,用 signed char 和 unsigned char 代替 char 。

算數類型

整數類型浮點類型 統稱爲 算數類型。以下爲 C89 中對算數類型的分類

  • 整數類型
    • 字符類型(char)
    • 有符號整型(signed char, short int, int, long)
    • 無符號整型(unsigned char, unsigned short int, unsigned int, unsigned long int)
    • 枚舉類型
  • 浮點類型(float,double,long double)

轉義序列

正如前面我們所看到的那樣,字符常量通常是用單引號擴起來的單個字符。然而,一些特殊符號(如換行符)是無法採用上述方法書寫的,因此它們不可見(非打印字符),或者無法從鍵盤輸入。因此,爲了使程序可以處理字符集中的每一個字符,C語言提供了一種特殊的表示法——轉義序列(escape sequence)。

轉義序列有兩種:字符轉義序列(character escape)和 數字轉義序列(numeric escape)。

字符轉義序列(粗體比較常用,需要注意)

名稱 轉義序列 名稱 轉義序列
換行符 \n 回退符 \b
水平製表符 \t 垂直製表符 \v
單引號 \’ 換頁符 \f
雙引號 \" 問號 ?
回車符 \r 報警(響鈴)符 \a
反斜槓 \\
數字轉義序列

爲了將特殊字符寫成數字轉義序列,首先要在 ASCII 碼錶上查找字符的 八進制或十六進制值。比如某個 ASCII 碼轉義字符(十進制爲 27)八進制爲 33 ,十六進制爲 1B。

  • 八進制轉義序列由字符 \和跟隨其後的一個最多含有三位數字的八進制數組成(此書必須表示爲無符號字符,最大值的八進制爲 377)。例如,可以將轉義字符寫成 \33\033。和八進制常量不同,轉義序列的八進制數不一定要用 0 開頭
  • 十六進制轉義序列\x 和跟隨其後的一個十六進制數組成。(標準C對十六進制數的位數沒有限制,但必須表示爲無符號字符,所以最大值爲 FF。)若採用這種方法,可以把轉義字符寫成 \x1b\x1B 的形式。字符 x必須小寫,但是十六進制數字不限大小寫。

作爲字符常量使用時,轉義序列必須用一對單引號括起來。例如,可以將轉義字符寫爲 \033\x1B 這種形式。轉移序列可能有些隱晦,所以採用 #define 的方式給他們命名是一種不錯的主意:如果你不懂,可以標記下來,然後跳過

#define ESC '\33' // ASCII escape character

轉移序列也可以嵌在字符串中使用。

請打印出下面一行的內容:

Gramps	sez,"a \ is a backslash."
printf("Gramps sez, \" a \\ is a backslash.\"\n");

數字轉義序列嵌入字符串,

printf("Hello!\007\n");// \007 打印警報
printf("\x48\x45\x4C\x4C\x4F\n");//HELLO

關於轉義序列

  • 上面的例子中,爲何沒有用單引號將轉義序列括起來?

    無論是普通字符還是轉義字符,只要是雙引號擴起來的字符集合,就無需再用單引號括起來。雙引號中的字符集合叫做字符串(▶️後面會講)

  • 何時使用 ASCII碼?何時使用轉義序列?

    如果要在轉義序列(比如,’\f’)和 ASCII中(’\014’)之間選擇,請選擇前者(’\f’)。這樣的寫法不僅好記,而且可移植性更高。’\f’在不使用 ASCII 碼的系統中,仍然有效。

  • 如果要使用 ASCII 碼,爲何要寫成 ‘\032’ 而不是 032?

    首先,’\032’能清晰的表達程序員使用字符編碼的意圖。其次,這樣的序列可以嵌入 C 的字符串中。比如上面的例題。

轉義序列示例
#include<stdio.h>

int main(void) {

	float salary;

	printf("\aEnter your desired monthly salary:");
	printf("$_____\b\b\b\b\b");//5 個退格符
	scanf("%f", &salary);
	printf("\t%.2f amonth is $%.2f a year", salary, 12 * salary);
	printf("\rGee!");//回到行首
	return 0;
}

嘗試思考一下這個程序會輸出什麼。

用 scanf 和 printf 讀/寫字符

轉換說明 %c 允許 scanf 函數和 printf 函數對單個字符進行 讀/寫 操作:

char ch;
scanf("%c", &ch);
printf("%c", ch);

讀入字符前,scanf 函數不會跳過空白字符。我們不妨做以下測試:

程序如下,我們輸入“ a”(空格 + 字母 a)

#include<stdio.h>

int main(void) {

	char ch;

	scanf("%c", &ch);
	printf("%c", ch);

	return 0;
}

我們發現 printf 函數沒有輸出任何東西,其實是隻打印了一個空格

現在,我們對 scanf 函數做一點小改動:

scanf(" %c", &ch);//在轉換說明 %c 前加一個空格

再次運行程序,這時不管我們在字母 a 前輸入多少空格,printf 函數都會打印出字母 a

scanf格式串中的空白表示“跳過零個或多個空白字符”

以下內容不要求初學者理解

我們可以用 scanf 函數來檢測輸入行的結尾:檢查讀入的字符是否爲換行符(如果是,則表示當前行結尾)。例如,下面的循環將讀入並且忽略當前輸入行剩下的所有字符:

do{
    scanf("%c", &ch);
}while(ch != '\n');

下次調用 scanf 函數時,將讀入下一個輸入行中的第一個字符。

用 getchar 和 putchar 讀/寫字符

putchar 函數用於寫單個字符:

putchar(ch);

每次調用 getchar函數時,它都會讀入一個字符並將其返回。爲了保存這個字符,必須使用賦值操作符將其存儲到變量中。

ch = getchar();// reads a character and stores it in ch

事實上,getchar 函數返回的是一個 int 類型的值而不是 char 類型的值(原因在後面會講解)。因此,如果一個變量用於存儲 getchar 函數讀取的字符,其類型設置爲 int 而不是 char 也理所當然。和 scanf 函數一樣 getchar 函數也不會跳過空白字符

執行程序時,getchar 與 putchar 比 scanf 和 printf 更加高效。原因如下:

  1. 這兩個函數比 scanf函數 和 printf函數 簡單的多。 因爲 scanf 和 printf 是設計用來按不同的格式讀/寫多種不同類型的數的。
  2. 爲了額外提升速度,通常 getchar函數和 putchar函數是作爲宏(▶️後面會講)來實現的。

以下內容不要求初者理解

getchar 另一個優勢是:返回的是讀入的字符

對於上面用 scanf 跳過當前輸入行的程序,我們可以用 getchar 來改寫

do{
    ch = getchar();
}while(ch != '\n');

我們可以讓程序更爲精簡:

while((ch = getchar()) != '\n')
    ;

慣用法

while(getchar() != '\n')
    ;

getchar 還可以跳過不定數量空格字符:

慣用法

while(getchar() == ' ') //skips blanks
    ;

當循環終止時,變量 ch 的值爲 getchar 遇到的第一個非空白字符。


⚠️

如果一個程序中混合使用 scanf 和 getchar ,請小心。請看下面的程序,這個程序會發生什麼?

printf("Enter an integer: ");
scanf("%d", &i);
printf("Enter an command: ");
command = getchar();

輸入 i 後,scanf 函數會留下沒有消耗掉的任意字符,包括(但不限於)換行符。getchar 函數隨後將取回第一個剩餘的字符(這個程序中是換行符),這不是我們所希望的結果。


程序:確定消息的長度

爲了說明字符的讀取方式,下面編寫一個程序來計算消息的長度。用戶輸入消息後,程序顯示長度:

Enter a message: Hello World!

Your message was 12 character(s) long.

消息的長度包含 空格和標點符號,但是不包含結尾的換行符。

length.c

#include<stdio.h>

int main() {

	int ch = 0;//定義變量時,如果這個變量沒有值初始化,可以將其初始化爲 0 。
	int count = 0;

	printf("Enter a message: ");//這裏可以將你要輸入的信息一次性輸入完,getchar 會負責一個一個的去取
	ch = getchar();

	while (ch != '\n') {
		count++;//這個語句的意思就是 count = count + 1(將count加1後的值再賦值給count,實現count增加1)
		ch = getchar();
	}

	printf("Your message was %d character(s) long\n", count);

	return 0;
}

簡化一下:

length2.c

#include<stdio.h>

int main() {

	int count = 0;

	printf("Enter a message: ");

	while (getchar() != '\n')
		count++;

	printf("Your message was %d character(s) long\n", count);

	return 0;
}

如果小黃有多條 message 想顯示長度,如果每次測完都要重新運行程序就太麻煩了。請你改寫程序,滿足小黃這一要求。自己嘗試編寫。

類型定義


類型定義(type definition)

#include<stdio.h>
typedef int int32;

int main(void) {
	int32 a;
	scanf("%d", &a);
	printf("%d", a);
	return 0;
}

編譯器會把 int32 類型看作 int 類型,因此 a 就是一個普通的 int 型變量。

類型定義的優點

類型定義使得程序更容易理解(選擇有意義的類型名)。例如,假設 cash_in 和 cash_out 用於存儲美元數量。

typedef float Dollars 

隨後可以這樣定義 cash_in 和 cash_out:

Dollars cash_in,cash_out;

上面的寫法比這樣寫更有意義:

float cash_in,cash_out;

類型定義還可以使程序更容易修改 如果稍後覺得 Dollars 實際應該該外 double 類型的,

typedef double Dollars 

如果沒有定義Dollars ,則需要找到所有用 float 定義美金數量的地方,這顯然不是一件容易的工作(對大型程序而言)。

類型定義的可移植性

類型定義時編寫可移植性程序的重要工具。程序從一臺計算機移動到另一臺計算機可能引發的問題就是不同計算機上的類型取值範圍可能不同。例如,如果 int i = 100000 這在 32 位機器上是沒有問題的,但是在 16位機器上就會出錯。

這時,在 32 位機器上我們可以這樣定義:

typedef int Quantity;
Quantity a;

把程序轉到 16 位機器上:

typedef long Quantity;

當然只這麼做是不夠的,Quantity 定義的變化可能影響類型變量的使用方式。至少我們需要改變 printf 和 scanf 中的格式串(%d 改爲 %ld)。

sizeof 運算符

表達式(而非函數)sizeof(類型)的值是一個無符號整型,表示存儲屬於 類型名 的值所需要的字節數

在自己的計算機上敲一下下面的代碼,看看你的機器上每個數據類型 sizeof 求出來的值,順便複習一下本節的數劇類型

#include<stdio.h>

int main(void) {
	printf("sizeof(signed char)        = %u byte \n", sizeof(signed char));
	printf("sizeof(unsigned char)      = %u byte \n", sizeof(unsigned char));
	printf("\n");
	printf("sizeof(short)              = %u byte \n", sizeof(short));
	printf("sizeof(unsigned short)     = %u byte \n", sizeof(unsigned short));
	printf("\n");
	printf("sizeof(int)                = %u byte \n", sizeof(int));
	printf("sizeof(unsigned int)       = %u byte \n", sizeof(unsigned int));
	printf("\n");
	printf("sizeof(long)               = %u byte \n", sizeof(long));
	printf("sizeof(unsigned long)      = %u byte \n", sizeof(unsigned long));
	printf("\n");
	printf("sizeof(long long)          = %u byte \n", sizeof(long long));
	printf("sizeof(unsigned long long) = %u byte \n", sizeof(unsigned long long));
	printf("\n");
	printf("sizeof(float)              = %u byte\n", sizeof(float));
	printf("sizeof(double)             = %u byte\n", sizeof(double));
	printf("sizeof(long double)        = %u byte\n", sizeof(long double));

	return 0;
}

爲什麼要用 %u 這個格式呢?

因爲在我的機器上 sizeof 的值是 unsigned int 類型,每個機器可能不一樣。

但是C99 和 C11 標準爲 sizeof 包括 strlen (▶️) 的返回類型添加了 zd轉換說明(z 表示 size_t類型)。對於早期的C,sizeof 和 strlen 的返回類型通常是 unsignedunsigned long

通常情況下,sizeof運算符也可以用於常量,變量,和表達式。

#include<stdio.h>

int main(void) {
	
	short a = 3;
	int b = 1, c = 2;

	printf("sizeof(1.)        = %u byte \n", sizeof(1.));
	printf("sizeof(1)         = %u byte \n", sizeof(1));
	printf("sizeof(a)      	  = %u byte \n", sizeof(a));
	printf("sizeof(a + b)     = %u byte \n", sizeof(a + b));
	printf("sizeof(b + c)     = %u byte \n", sizeof(b + c));


	return 0;
}
//輸出
sizeof(1.)        = 8 byte
sizeof(1)         = 4 byte
sizeof(a)         = 2 byte
sizeof(a + b)     = 4 byte
sizeof(b + c)     = 4 byte

sizeof(類型)不同的是, sizeof應用於表達式時可以省略括號。例如,可以用 sizeof i代替 sizeof(i) ;但是由於運算符優先級的問題,圓括號有時候還是需要的。編譯器會將 sizeof i + j解釋爲 sizeof(i) + j。這是因爲 sizeof 作爲一元運算符 的優先級高於 二元運算符 + 。爲了避免出現這種問題,建議還是保留圓括號。

參考資料:《C Primer Plus》《C語言程序設計:現代方法》


本文GitHub已收錄,所有教學和練習代碼都會上傳上去。

https://github.com/hairrrrr/C-CrashCourse

如果對你有幫助,請點一個 star⭐️ 呦~ ​ 感謝!💌

以上就是本次的內容。

如果文章有錯誤歡迎指正和補充,感謝!

最後,如果你還有什麼問題或者想知道到的,可以在評論區告訴我呦,我可以在後面的文章加上你們的真知灼見👁。

關注我,看更多幹貨!

我是程序圓,我們下次再見。🍂


  1. 如果一個程序用於處理大量數據,它就沒幾種選擇了 —— 《epigrams-on-programming》。 ↩︎

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章