If a program manipulates a large amount of data, it does so in a small number of ways.[1]
碼字不易,對你有幫助 點贊:thumbsup:/轉(zhuǎn)發(fā):arrow_right_hook:/關(guān)注 :eyes: ** 支持一下作者
微信搜公眾號:不會編程的程序圓**
看更多干貨,獲取第一時間更新
想看更好的文章排版,可以閱讀原文:
【C語言入門到精通】04 數(shù)據(jù)類型
思維導(dǎo)圖
寫在前面
如果只是寫個人學(xué)習(xí)總結(jié)的博客很容易,簡單寫一些感悟然后貼上代碼走人就可以了,甚至不用校審。但是我命名本系列為【C語言必知必會】幫助你從入門到精通 C語言,那勢必要“事無巨細(xì)”一些:既要考慮到?jīng)]有基礎(chǔ)的初學(xué)者,又不能止于基礎(chǔ)。所以本教程適合各類人群學(xué)習(xí),只要你看就一定會有幫助。
本教程是本人純手打并排版,校審由我與我的搭檔湯圓君一起完成的。你們看這一篇文章我要寫好幾個小時。如果文章對你有幫助,請不要“白嫖”。支持一下作者,作者會發(fā)更多干貨好文。
特別鳴謝:湯圓君(公眾號:【Cc聽香水榭】 長期更新高質(zhì)量英語教學(xué))關(guān)注她表示對她工作的認(rèn)可吧!
》 此符號表示該內(nèi)容以后的章節(jié)會講解,此章節(jié)內(nèi)不要求理解。
目錄
[toc]
一 概述
關(guān)鍵字
C語言的數(shù)據(jù)類型關(guān)鍵字
| 最初 K&R 給出的關(guān)鍵字 | C90標(biāo)準(zhǔn)添加的關(guān)鍵字 | C99標(biāo)準(zhǔn)添加的關(guān)鍵字 |
|---|---|---|
| int | signed | _Bool (布爾型) |
| short | void | _Complex(復(fù)數(shù)) |
| long | _Imaginary(虛數(shù)) | |
| unsigned | ||
| char | ||
| float | ||
| double |
通過這些關(guān)鍵字創(chuàng)建的類型,按計算機(jī)的存儲方式可分為兩大基本類型:整數(shù)類型 和 浮點數(shù)類型
位,字節(jié)和字
位,字節(jié)和字
位(bit): 最小的存儲單元,也稱比特位??梢源鎯?0 或 1(或者說,位用于存儲“開”或“關(guān)”)
字節(jié)(byte): 1 byte = 8 bit 既然 1 位可以表示 0 或 1,那么 1 字節(jié)就有 256 (2^8)種 0/1 組合,通過二進(jìn)制編碼(僅用 0/1 便表示數(shù)字),便可表示 0 ~ 255 的整數(shù)或一組字符。(以后會詳細(xì)講解)
字(word): 是設(shè)計計算機(jī)時給定的自然存儲單位。對于 8 位 的微型計算機(jī)(如:最初的蘋果機(jī)),1 字長 只有 8 位,從那以后,個人計算機(jī)的字長增至 16 位,32位,直至目前的 64位。計算機(jī)字長越大,其數(shù)據(jù)轉(zhuǎn)移越快,允許訪問的內(nèi)存越多。
整數(shù)
整數(shù) 7 以二進(jìn)制形式表示是:111 ,用一個字節(jié)存儲可表示為:
浮點數(shù)
浮點數(shù)相比我們都不陌生,本節(jié)后面還會做更詳細(xì)的介紹。現(xiàn)在我們介紹一種浮點數(shù)的表示方法:e記數(shù)法。
如 3.16E+007 表示 3.16 * 10^7(3.16乘以10的七次方)。007 表示 10^7;+ 表示 10 的指數(shù) 7 為正數(shù)。
其中,E 可以寫成 e;表示正次數(shù)時,+ 號可以省略;007也可以省略為7。即:3.16e7。
浮點數(shù)和整數(shù)的存儲方案是不同的。計算機(jī)將浮點數(shù)分成小數(shù)部分和指數(shù)部分來表示,而且分開存儲這兩部分。因此,雖然 7.0 和 7 在數(shù)值上相同,但它們的存儲方式不同。下圖演示了一個存儲浮點數(shù)的例子。后面我們會做更詳細(xì)的解釋
整數(shù)與浮點數(shù)的區(qū)別:
- 整數(shù)沒有小數(shù)部分,浮點數(shù)有小數(shù)部分
- 浮點數(shù)可以表示的范圍比整數(shù)大
- 對于一些算術(shù)運(yùn)算(如,兩個很大的數(shù)相減),浮點數(shù)損失的精度更多
- 因為在任何區(qū)間內(nèi)都存在無窮多個實數(shù),所以計算機(jī)的浮點數(shù)不能表示區(qū)間內(nèi)的所有值。浮點數(shù)通常只是實際值的近似值。(例如,7.0 可能被存儲為浮點值 6.99999)
- 過去,浮點數(shù)運(yùn)算比整數(shù)運(yùn)算慢。不過現(xiàn)在許多CPU都包含了浮點數(shù)處理器,縮小了速度上的差距。
二 整數(shù)類型
有符號整數(shù)和無符號整數(shù)
有符號整數(shù)如果為零或正數(shù),那么最左邊的位(符號位,只表示符號,不表示數(shù)值)為 0 ;如果為負(fù)數(shù),則符號位為 1。如:最大的 16 位整數(shù)(2個字節(jié))的二進(jìn)制表示形式是 01111111 11111111,對應(yīng)的數(shù)值是 32767(即:2^15 - 1)
無符號整數(shù) 不帶符號位(最左邊的位是數(shù)值的一部分)。因此,最大的 16 位整數(shù)的二進(jìn)制表示形式是:11111111 11111111(即:2^16 - 1)
默認(rèn)情況下,C語言中的整型變量都是有符號的,也就是說最左位保留符號位。若要告訴編譯器變量沒有符號位,需要把他聲明成 unsigned 類型。
整數(shù)的類型
short int
unsigned short int
int
unsigned int
long int
unsigned long int
整數(shù)的類型歸根結(jié)底只有這 6 種,其他組合都是上述某種類型的同義詞。
例如:long signed int 與 long int 是一樣的;unsigned short int 與 short unsigned int 是一樣的
C語言允許通過省略單詞 int 來縮寫整數(shù)類型的名稱。
例如:unsigned short int 可以縮寫為 unsigned short ;而 long int 可以縮寫為 long。
C程序員經(jīng)常省略 int 。
6 種 整數(shù)類型每一種所表示的取值范圍都會根據(jù)機(jī)器的不同而不同,但是有兩條所有編譯器都必須遵守的原則。
- C 標(biāo)準(zhǔn)要求 short,int,long 中的每一種類型都要覆蓋一個確定的最小取值范圍(后面會詳細(xì)講解)
- int 類型不能比 short 類型短,long 類型不能比 int 類型短
這也就是說:short 的大小可以與 int 相等;int 的大小可以與 long 相等
16位,32位,64位機(jī)器的整數(shù)類型都各有些不同,我們常用的是 32 位機(jī)器(嚴(yán)格來說是編譯器,我的電腦是 64 位,但是VS2019用的最多的是 32位模式),我們就以 32 位機(jī)器為例
32位機(jī)器整數(shù)類型
| 類型 | 最小值 | 最大值 |
|---|---|---|
| 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位機(jī)器上,int 與 long的大小是一樣的,都是 4 個字節(jié)。
16位機(jī)器上,int 與 short 大小是一樣的,都是 2 個字節(jié)。
64位機(jī)器上,與 32 位機(jī)器不同的是,long 是 8 個字節(jié)。
但是,上述所說的規(guī)律并不是 C標(biāo)準(zhǔn)規(guī)定的,會隨著編譯器的不同而不同??梢詸z查頭文件<limits.h>,來查看每種整數(shù)類型的最大值和最小值。(下面給出我的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 中的整數(shù)類型
C99 提供了兩個額外的整數(shù)類型:long long int 和 unsigned long long int
整數(shù)常量
整數(shù)常量:在程序中以文本形式出現(xiàn)的數(shù),而不是讀,或計算出來的數(shù)。
C語言允許用 十進(jìn)制(基數(shù)為 10),八進(jìn)制(基數(shù)為 8),十六進(jìn)制(基數(shù)為 16)的形式書寫整數(shù)常量
8 進(jìn)制 與 16 進(jìn)制
8 進(jìn)制數(shù)是用數(shù)字 0 ~ 7 書寫的。八進(jìn)制的每一位表示一個 8 的冪(這就如同 10 進(jìn)制每一位表示 10 的冪一樣)。因此,八進(jìn)制數(shù) 237 表示成 10 進(jìn)制數(shù)就是 2 * 8^2 + 3 * 8^1 + 7 * 8^0 = 128 + 24 + 7 = 159
16 進(jìn)制數(shù)使用數(shù)字 0 ~ 9 加上字符 A ~ F 書寫的,其中字符 A ~ F 表示 10 ~ 15 的數(shù)。16進(jìn)制數(shù)每一位表示一個 16 的冪,16進(jìn)制數(shù) 1AF 的十進(jìn)制數(shù)值是 1 x 16^2 + 10 * 16^1 + 15 * 16^0 = 256 + 160 + 15 = 431
如果上面的描述你還是沒有懂,可以參考下圖:
[外鏈圖片轉(zhuǎn)存失敗,源站可能有防盜鏈機(jī)制,建議將圖片保存下來直接上傳(img-ziyXqpwQ-1585358279189)(C:\Users\1\Desktop\素材\27.png)]
-
十進(jìn)制常量包含 0 ~ 9 的數(shù)字,但是不能以 0 開頭
15 255 32767
-
八進(jìn)制常量包含 0 ~ 7 的數(shù)字,必須要以 0 開頭
017 0377 077777
-
十六進(jìn)制常量包含 0 ~ 9 的數(shù)字 和 A ~ F 的字母,總是以 0x 開頭
0xf 0xff 0x7fff
十六進(jìn)制常量中的字母可以是大寫也可以是小寫
#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
八進(jìn)制與十六進(jìn)制只是書寫數(shù)的方式,他們不會對數(shù)的實際存儲方式產(chǎn)生影響(整數(shù)都是以二進(jìn)制形式存儲的)。任何時候都可以從一種書寫方式切換的另一種,甚至可以混合使用:10 + 015 + 0x20 = 55 。八進(jìn)制和十六進(jìn)制更適合底層程序的編寫(以后會詳細(xì)講到)。
十進(jìn)制整數(shù)常量的類型通常是 int ,但如果常量過大,就用 long int 類型,如果還不夠用,編譯器會用 unsigned long int 做最后嘗試。
八進(jìn)制和十六進(jìn)制常量編譯器會依次嘗試:int,unsigned int,long int 和 unsigned long int 類型,知道找到能表示該常量的類型。
為了強(qiáng)制編譯器把常量作為長整數(shù)來處理,只需要在后面加上一個字母L(或l,字母l比較像數(shù)字1所以建議大寫):
15L 0377L 0x7ffffL
為了指明是無符號常量,可以在常量后面加上字母U(或u):
15U 0377U 0x7ffffU
L 與 U 可以結(jié)合使用:0xffffffffLU(L 與 U 的書寫順序無所謂)
C99 中的整數(shù)常量
在 C99 中,以 LL 或 ll (字母大小寫要一致)結(jié)尾的整數(shù)常量是 long long int 類型。在 LL 或 ll 前面或后面加上 U(u)表示 unsigned long long int 類型。
C99 與 C89 在確定整數(shù)常量類型規(guī)則上有些不同。
對于沒有后綴的十進(jìn)制常量,其類型是 int ,long int,long long int 中能表示該值的 最小類型。
對于八進(jìn)制和十六進(jìn)制常量,可能的類型順序為:int,unsigned int,long int,unsigned long int,long long int,unsigned long long int。
常量后面任何后綴都會改變可能的類型列表。
整數(shù)溢出
對整數(shù)執(zhí)行算數(shù)運(yùn)算時,其結(jié)果可能太大而無法表示。例如,對兩個 int 值進(jìn)行算數(shù)運(yùn)算時,其結(jié)果必須仍然能用 int 來表示;否則(表示結(jié)果所需要的數(shù)位(二進(jìn)制)太多),就會發(fā)生溢出。
有符號整數(shù)的溢出時,程序的行為時未定義的。未定義行為的結(jié)果是不確定的。最有可能的結(jié)果是,僅僅是運(yùn)算出錯,但是程序也有可能崩潰,或者出現(xiàn)其他意想不到的情況。
無符號整數(shù)溢出時,結(jié)果是有定義的:對 2^n 取模,其中 n 是用于存儲結(jié)果的位數(shù)。例如:如果對無符的 16 位數(shù) 65535 加 1,其結(jié)果可以保證為 0 。
請看下面的程序,也許可以幫助你理解。
tobig.c —— 超出系統(tǒng)最大 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 看作是汽車的里程表。當(dāng)達(dá)到他能表示的最大值時,會重新從起點開始。整數(shù) i 也是類似的情況。它們的主要區(qū)別是,在超過最大值時, unsigned int 類型的變量 j 從 0 開始;而 int 型的變量則從 -2147483648 開始。注意,當(dāng) i 超出(溢出)其相對類型所能表示的最大值時,系統(tǒng)并未通知用戶。因此必須自己注意這類問題。
讀/寫整數(shù)
讀寫無符號整數(shù):
unsigned int a;
-
十進(jìn)制:
scanf("%u", &a);printf("%u", a); -
八進(jìn)制
scanf("%o", &a);printf("%o", a); -
十六進(jìn)制
scanf("%x", &a);printf("%x", a);
讀寫*短整型數(shù):在 d,u,o,x 前加上 h
short b
-
scanf("%hd", &b);printf("%hd", b);
讀寫長整數(shù):在 d,u,o,x 前加上 l
long c
-
scanf("%ld", &c);printf("%ld", c);
讀寫長長整數(shù): 在 d,u,o,x 前加上 ll
long long int d
-
scanf("%lld", &d);printf("%lld", d);
改進(jìn)程序
#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型變量允許的最大值。因此,為了改進(jìn)這個程序,我們可以將 int 型的 a,b,sum 都變?yōu)?long long 型(考慮到 32 位機(jī)器的 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語言提供了三種浮點類型,對應(yīng)著不同的浮點格式:
-
float:單精度浮點數(shù) -
double:雙精度浮點數(shù) -
long double:擴(kuò)展精度浮點數(shù)
通常我們用到的是 double
自己編譯器 的浮點特征(浮點類型的范圍)可以在float.h頭文件內(nèi)查看。下面給出我的 VS2019 的 float 頭文件的部分內(nèi)容。
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標(biāo)準(zhǔn)的(大多數(shù)計算機(jī)都是遵循 IEEE 754標(biāo)準(zhǔn))。
浮點常量
浮點常量可以有多種寫法。例如,下面這些寫法都表示數(shù) 57.0
57.0 57. 57.0e0 5.7e1 5.7e+1 .57e2 570.e-1
浮點常量必須包含小數(shù)點或指數(shù)
默認(rèn)情況下,浮點常量都以雙精度的形式存儲。換句話說,當(dāng) C語言的編譯器在程序中發(fā)現(xiàn)常量 57.0 時,它會安排數(shù)據(jù)以 double 類型變量的格式存儲在內(nèi)存中。
如果只需要單精度,可以在常量末尾加上 F或f(如 57.0F);如果想以 long double 格式存儲,在常量尾加上 L或 l(如 57.0L)
讀/寫浮點數(shù)
float:
%e%f%g-
double:
%lfscanf("%lf", &varible);-
printf("%f", varible);lf格式串 只能在 scanf 中使用;在用 printf 輸出 double 時,格式串可以使用e,f,g
-
long double:
%Lfscanf("%Lf", &varible);printf("%Lf", varible);
四 字符類型
字符類型(字符型):char 。
char 類型的值可以根據(jù)計算機(jī)的不同而不同,因為不同的計算機(jī)可能會有不同的字符集。
字符集:當(dāng)今最常用的字符集是 ASCII (美國信息交換標(biāo)準(zhǔn)碼)字符集。
字符操作
C語言把字符當(dāng)作小整數(shù)進(jìn)行處理。
所有字符都是以二進(jìn)制形式進(jìn)行編碼的。
在標(biāo)準(zhǔn)的 ASCII 碼中,字符的取值范圍是 00000000 ~ 01111111,可以看成是 0 ~ 127 。例如,字符 'A' 的值是 65,'a' 的值是 97,'0' 的值是48,' ' 的值是 32 。
許多字符集都超出了 127,甚至多余 255(unsigned char 類型,二進(jìn)制序列為:1111 1111 )。
C語言中,字符和整數(shù)的關(guān)聯(lián)是很強(qiáng)的,字符常量事實上是 int 類型而非 char 類型。
請看下面的例子,你會更深的理解 字符型與整型的關(guān)聯(lián)(字符集位ASCII)
char ch;
int i;
i = 'a';// i is now 97
ch = 65;//'ch' is now 'A'
ch = ch + 1;//'ch' now is 'B'
因此,字符就有了數(shù)的一些特征。比如可以像數(shù)一樣比較,可以當(dāng)作條件應(yīng)用于 if語句,for循環(huán)。這是一個很便利的事情。
但是,以數(shù)的形式處理字符 可能降低程序的可移植性(不同機(jī)器使用的字符集不同) 和 導(dǎo)致編譯器無法檢查出來的多種編程錯誤('a' + 'b' * 'c' 這類沒有意義的表達(dá)式等)。
有符號字符 和 無符號字符
有符號字符signed char:取值范圍:-128 ~ 127
無符號字符unsigned char: 取值范圍:0 ~ 255
可移植性技巧:不要假設(shè) char 類型默認(rèn)為 signed 或 unsigned 。如果有區(qū)別,用 signed char 和 unsigned char 代替 char 。
算數(shù)類型
整數(shù)類型 和 浮點類型 統(tǒng)稱為 算數(shù)類型。以下為 C89 中對算數(shù)類型的分類
- 整數(shù)類型
- 字符類型(char)
- 有符號整型(signed char, short int, int, long)
- 無符號整型(unsigned char, unsigned short int, unsigned int, unsigned long int)
- 枚舉類型
- 浮點類型(float,double,long double)
轉(zhuǎn)義序列
正如前面我們所看到的那樣,字符常量通常是用單引號擴(kuò)起來的單個字符。然而,一些特殊符號(如換行符)是無法采用上述方法書寫的,因此它們不可見(非打印字符),或者無法從鍵盤輸入。因此,為了使程序可以處理字符集中的每一個字符,C語言提供了一種特殊的表示法——轉(zhuǎn)義序列(escape sequence)。
轉(zhuǎn)義序列有兩種:字符轉(zhuǎn)義序列(character escape)和 數(shù)字轉(zhuǎn)義序列(numeric escape)。
字符轉(zhuǎn)義序列(粗體比較常用,需要注意)
| 名稱 | 轉(zhuǎn)義序列 | 名稱 | 轉(zhuǎn)義序列 |
|---|---|---|---|
| 換行符 | \n | 回退符 | \b |
| 水平制表符 | \t | 垂直制表符 | \v |
| 單引號 | \' | 換頁符 | \f |
| 雙引號 | \" | 問號 | ? |
| 回車符 | \r | 報警(響鈴)符 | \a |
| 反斜杠 | \\ |
數(shù)字轉(zhuǎn)義序列
為了將特殊字符寫成數(shù)字轉(zhuǎn)義序列,首先要在 ASCII 碼表上查找字符的 八進(jìn)制或十六進(jìn)制值。比如某個 ASCII 碼轉(zhuǎn)義字符(十進(jìn)制為 27)八進(jìn)制為 33 ,十六進(jìn)制為 1B。
1.八進(jìn)制轉(zhuǎn)義序列 由字符 \和跟隨其后的一個最多含有三位數(shù)字的八進(jìn)制數(shù)組成(此書必須表示為無符號字符,最大值的八進(jìn)制為 377)。例如,可以將轉(zhuǎn)義字符寫成 \33或 \033。和八進(jìn)制常量不同,轉(zhuǎn)義序列的八進(jìn)制數(shù)不一定要用 0 開頭
2.十六進(jìn)制轉(zhuǎn)義序列 由 \x 和跟隨其后的一個十六進(jìn)制數(shù)組成。(標(biāo)準(zhǔn)C對十六進(jìn)制數(shù)的位數(shù)沒有限制,但必須表示為無符號字符,所以最大值為 FF。)若采用這種方法,可以把轉(zhuǎn)義字符寫成 \x1b 或 \x1B 的形式。字符 x必須小寫,但是十六進(jìn)制數(shù)字不限大小寫。
作為字符常量使用時,轉(zhuǎn)義序列必須用一對單引號括起來 。例如,可以將轉(zhuǎn)義字符寫為 \033或 \x1B 這種形式。轉(zhuǎn)移序列可能有些隱晦,所以采用 #define 的方式給他們命名是一種不錯的主意:如果你不懂,可以標(biāo)記下來,然后跳過
#define ESC '\33' // ASCII escape character
轉(zhuǎn)移序列也可以嵌在字符串中使用。
請打印出下面一行的內(nèi)容:
Gramps sez,"a \ is a backslash."
printf("Gramps sez, \" a \\ is a backslash.\"\n");
數(shù)字轉(zhuǎn)義序列嵌入字符串,
printf("Hello!\007\n");// \007 打印警報
printf("\x48\x45\x4C\x4C\x4F\n");//HELLO
關(guān)于轉(zhuǎn)義序列
-
上面的例子中,為何沒有用單引號將轉(zhuǎn)義序列括起來?
無論是普通字符還是轉(zhuǎn)義字符,只要是雙引號擴(kuò)起來的字符集合,就無需再用單引號括起來。雙引號中的字符集合叫做字符串(:arrow_forward:后面會講)
-
何時使用 ASCII碼?何時使用轉(zhuǎn)義序列?
如果要在轉(zhuǎn)義序列(比如,'\f')和 ASCII中('\014')之間選擇,請選擇前者('\f')。這樣的寫法不僅好記,而且可移植性更高。'\f'在不使用 ASCII 碼的系統(tǒng)中,仍然有效。
-
如果要使用 ASCII 碼,為何要寫成 '\032' 而不是 032?
首先,'\032'能清晰的表達(dá)程序員使用字符編碼的意圖。其次,這樣的序列可以嵌入 C 的字符串中。比如上面的例題。
轉(zhuǎn)義序列示例
#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 讀/寫字符
轉(zhuǎn)換說明 %c 允許 scanf 函數(shù)和 printf 函數(shù)對單個字符進(jìn)行 讀/寫 操作:
char ch;
scanf("%c", &ch);
printf("%c", ch);
讀入字符前,scanf 函數(shù)不會跳過空白字符。我們不妨做以下測試:
程序如下,我們輸入“ a”(空格 + 字母 a)
#include<stdio.h>
int main(void) {
char ch;
scanf("%c", &ch);
printf("%c", ch);
return 0;
}
我們發(fā)現(xiàn) printf 函數(shù)沒有輸出任何東西,其實是只打印了一個空格
現(xiàn)在,我們對 scanf 函數(shù)做一點小改動:
scanf(" %c", &ch);//在轉(zhuǎn)換說明 %c 前加一個空格
再次運(yùn)行程序,這時不管我們在字母 a 前輸入多少空格,printf 函數(shù)都會打印出字母 a
scanf格式串中的空白表示“跳過零個或多個空白字符”
以下內(nèi)容不要求初學(xué)者理解
我們可以用 scanf 函數(shù)來檢測輸入行的結(jié)尾:檢查讀入的字符是否為換行符(如果是,則表示當(dāng)前行結(jié)尾)。例如,下面的循環(huán)將讀入并且忽略當(dāng)前輸入行剩下的所有字符:
do{
scanf("%c", &ch);
}while(ch != '\n');
下次調(diào)用 scanf 函數(shù)時,將讀入下一個輸入行中的第一個字符。
用 getchar 和 putchar 讀/寫字符
putchar 函數(shù)用于寫單個字符:
putchar(ch);
每次調(diào)用 getchar函數(shù)時,它都會讀入一個字符并將其返回。為了保存這個字符,必須使用賦值操作符將其存儲到變量中。
ch = getchar();// reads a character and stores it in ch
事實上,getchar 函數(shù)返回的是一個 int 類型的值而不是 char 類型的值(原因在后面會講解)。因此,如果一個變量用于存儲 getchar 函數(shù)讀取的字符,其類型設(shè)置為 int 而不是 char 也理所當(dāng)然。和 scanf 函數(shù)一樣 getchar 函數(shù)也不會跳過空白字符。
執(zhí)行程序時,getchar 與 putchar 比 scanf 和 printf 更加高效。原因如下:
- 這兩個函數(shù)比 scanf函數(shù) 和 printf函數(shù) 簡單的多。 因為 scanf 和 printf 是設(shè)計用來按不同的格式讀/寫多種不同類型的數(shù)的。
- 為了額外提升速度,通常 getchar函數(shù)和 putchar函數(shù)是作為宏(:arrow_forward:后面會講)來實現(xiàn)的。
以下內(nèi)容不要求初者理解
getchar 另一個優(yōu)勢是:返回的是讀入的字符。
對于上面用 scanf 跳過當(dāng)前輸入行的程序,我們可以用 getchar 來改寫
do{
ch = getchar();
}while(ch != '\n');
我們可以讓程序更為精簡:
while((ch = getchar()) != '\n')
;
慣用法:
while(getchar() != '\n')
;
getchar 還可以跳過不定數(shù)量空格字符:
慣用法:
while(getchar() == ' ') //skips blanks
;
當(dāng)循環(huán)終止時,變量 ch 的值為 getchar 遇到的第一個非空白字符。
注意
如果一個程序中混合使用 scanf 和 getchar ,請小心。請看下面的程序,這個程序會發(fā)生什么?
printf("Enter an integer: ");
scanf("%d", &i);
printf("Enter an command: ");
command = getchar();
輸入 i 后,scanf 函數(shù)會留下沒有消耗掉的任意字符,包括(但不限于)換行符。getchar 函數(shù)隨后將取回第一個剩余的字符(這個程序中是換行符),這不是我們所希望的結(jié)果。
程序:確定消息的長度
為了說明字符的讀取方式,下面編寫一個程序來計算消息的長度。用戶輸入消息后,程序顯示長度:
Enter a message: Hello World!
Your message was 12 character(s) long.
消息的長度包含 空格和標(biāo)點符號,但是不包含結(jié)尾的換行符。
length.c
#include<stdio.h>
int main() {
int ch = 0;//定義變量時,如果這個變量沒有值初始化,可以將其初始化為 0 。
int count = 0;
printf("Enter a message: ");//這里可以將你要輸入的信息一次性輸入完,getchar 會負(fù)責(zé)一個一個的去取
ch = getchar();
while (ch != '\n') {
count++;//這個語句的意思就是 count = count + 1(將count加1后的值再賦值給count,實現(xiàn)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 想顯示長度,如果每次測完都要重新運(yùn)行程序就太麻煩了。請你改寫程序,滿足小黃這一要求。自己嘗試編寫。
五 類型定義
類型定義(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 型變量。
類型定義的優(yōu)點
類型定義使得程序更容易理解(選擇有意義的類型名)。例如,假設(shè) cash_in 和 cash_out 用于存儲美元數(shù)量。
typedef float Dollars
隨后可以這樣定義 cash_in 和 cash_out:
Dollars cash_in,cash_out;
上面的寫法比這樣寫更有意義:
float cash_in,cash_out;
類型定義還可以使程序更容易修改 如果稍后覺得 Dollars 實際應(yīng)該該外 double 類型的,
typedef double Dollars
如果沒有定義Dollars ,則需要找到所有用 float 定義美金數(shù)量的地方,這顯然不是一件容易的工作(對大型程序而言)。
類型定義的可移植性
類型定義時編寫可移植性程序的重要工具。程序從一臺計算機(jī)移動到另一臺計算機(jī)可能引發(fā)的問題就是不同計算機(jī)上的類型取值范圍可能不同。例如,如果 int i = 100000 這在 32 位機(jī)器上是沒有問題的,但是在 16位機(jī)器上就會出錯。
這時,在 32 位機(jī)器上我們可以這樣定義:
typedef int Quantity;
Quantity a;
把程序轉(zhuǎn)到 16 位機(jī)器上:
typedef long Quantity;
當(dāng)然只這么做是不夠的,Quantity 定義的變化可能影響類型變量的使用方式。至少我們需要改變 printf 和 scanf 中的格式串(%d 改為 %ld)。
六 sizeof 運(yùn)算符
表達(dá)式(而非函數(shù))sizeof(類型)的值是一個無符號整型,表示存儲屬于 類型名 的值所需要的字節(jié)數(shù)
在自己的計算機(jī)上敲一下下面的代碼,看看你的機(jī)器上每個數(shù)據(jù)類型 sizeof 求出來的值,順便復(fù)習(xí)一下本節(jié)的數(shù)劇類型
#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 這個格式呢?
因為在我的機(jī)器上 sizeof 的值是 unsigned int 類型,每個機(jī)器可能不一樣。
但是C99 和 C11 標(biāo)準(zhǔn)為 sizeof 包括 strlen (:arrow_forward:) 的返回類型添加了 zd轉(zhuǎn)換說明(z 表示 size_t類型)。對于早期的C,sizeof 和 strlen 的返回類型通常是 unsigned 或 unsigned long。
通常情況下,sizeof運(yùn)算符也可以用于常量,變量,和表達(dá)式。
#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應(yīng)用于表達(dá)式時可以省略括號。例如,可以用 sizeof i代替 sizeof(i) ;但是由于運(yùn)算符優(yōu)先級的問題,圓括號有時候還是需要的。編譯器會將 sizeof i + j解釋為 sizeof(i) + j。這是因為 sizeof 作為一元運(yùn)算符 的優(yōu)先級高于 二元運(yùn)算符 + 。為了避免出現(xiàn)這種問題,建議還是保留圓括號。
參考資料:《C Primer Plus》《C語言程序設(shè)計:現(xiàn)代方法》
本文GitHub已收錄,所有教學(xué)和練習(xí)代碼都會上傳上去。
https://github.com/hairrrrr/C-CrashCourse
如果對你有幫助,請點一個 star:star: 呦~ ? 感謝!:love_letter:
以上就是本次的內(nèi)容。
如果文章有錯誤歡迎指正和補(bǔ)充,感謝!
最后,如果你還有什么問題或者想知道到的,可以在評論區(qū)告訴我呦,我可以在后面的文章加上你們的真知灼見:eye:。
關(guān)注我,看更多干貨!
我是程序圓,我們下次再見。:fallen_leaf:
-
如果一個程序用于處理大量數(shù)據(jù),它就沒幾種選擇了 —— 《epigrams-on-programming》。 ?