Learning C Primer Plus | Chapter3

数据与C

位(bit)是计算机最小的存储单位。它可以容纳两个值(0或1)之一,是计算机存储的基本单位。
字节(byte)是常见的计算机存储单位,一般一个字节由八个位组成,可包含256种0、1的组合。
字(word)取决于计算机本身的位数,现代操作系统中一般为32位机和64位机。

变量与常量数据

变量的值可以在程序执行过程中改变与指定,而常量不可以。

数据类型与关键字

K&R关键字 C90关键字 C99关键字
int signed _Bool
long void _Complex
short _Imaginary
unsigned
char
float
double

int关键字是基本的整数类型,long short unsigned signed用于提供基础类型的变种。
char 关键字用于表示字母及符号等字符,也可以表示小的整数。(char本质是按int存储的)
float doublelong double表示带小数点的数。(float本质是按double存储的)
_Bool类型表示布尔值truefalse_Complex_Imaginary 分别表示复数和虚数。
这些类型按照存储方式分为两个系列,即整数(integer)类型和浮点数(floating-point)类型。

整数类型和浮点类型

对于人,整数和浮点数的区别在于它们的书写。
对于计算机,区别在于它们的存储方式。

两种数之间的区别

  • 整数没有小数部分,浮点数可以有小数部分。
  • 浮点数可以表示比整数范围大得多的数。
  • 对于算术运算(比如两个很大的数相减),使用浮点数会损失更多精度。
  • 因为在任何区间内,都存在无穷多个实数所以无法表示所以值,往往只能是实际值的近似。
  • 浮点运算通常比整数运算慢。

整数

整数(integer)就是没有小数部分的数。


Learning C Primer Plus | Chapter3_第1张图片
整数存储方式

浮点数

浮点数(floating-point)差不多是可以和数学中的实数(real number)概念相对应。实数包含了整数之间的那些数。


Learning C Primer Plus | Chapter3_第2张图片
浮点数的存储方式

C数据类型

int类型

int类型是有符号整数,它的值可以是正、负的整数或是0,其取值依赖于计算机系统。
ISO/ANSI C规定 int 类型的最小范围是-32768到32767,系统可以使用一个指示正负号的特定位来表示有符号整数。

声明与初始化

int erns;
int hogs.cows,goats = 14; // 这种情况容易造成误解可读性差
cows = 112;

变量的声明可以多个,也可以分别声明每个变量(可读性好)。
变量的初始化(initialize)就是为变量赋一个初始值,可以在声明时进行初始化,也可以在随后赋值如第三行。

Learning C Primer Plus | Chapter3_第3张图片
定义和初始化变量

打印整数:

int one = 1;
int two = 2;
printf("The number is %d and %d\n",one,two);

注意:int类型打印时使用 %d格式说明符(format specifier),注意格式说明符的书目和要显示的值数目一定要相同,要显示的值都必须对应自己的格式说明符。

八进制与十六进制

计算机中使用二进制存储,所以使用八进制和十六进制可以更方便表示与计算机相关的值。
在C语言中,使用前缀0X或者0x表示使用十六进制值,前缀0(零)表示使用八进制值。
打印八进制和十六进制数

/* 以十进制、八进制和十六进制输出100 */
#include
int main(void)
{
  int x =100;
  printf("dec = %d; octal = %o; hex = %x\n",x, x, x);
  printf("dec = %#d; octal = %#o; hex = %#x\n",x, x, x);
  return 0;
}
// 对应的输出为:
dec = 100; octal = 144; hex = 64
dec = 100; octal = 0144; hex = 0x64

可以看出,使用%o%x可以打印出十六进制数,加上#可以打印出前缀。

  • signed关键字,可以和任何有符号类型一起使用,基本默认使用,使用它可以使数据类型更加明确。
  • unsigned int类型,用于非负值的场合,在十六位机子中取值范围为0到65535。
  • short int类型,可能占用比int类型更少的存储空间。
  • long int类型,可能占用比int类型更多的存储空间。
  • long long int类型,在C99中引入,占用比long类型更多的存储空间
  • 在C90中引入了unsigned long intunsigned short int类型。C99又增加unsigned long long int类型。

C仅保证short类型不会比int类型长,long类型不会比int类型短。
目前的一般情况是,long long类型为64位,long类型为32位,short类型为16位,int类型为16位或32位(依机器自然字大小而定)。

类型 最小取值范围
short和int -32767~32767
unsigned short和unsigned int 0~65535
long -2147483647~ 2147483647
unsigned long 0~4294967295
long long -9223372036854775807 ~9223372036854775807
unsigned long long 0~18446744073709551615

如何选取数据类型

  • 优先考虑unsigned类型,用于计数比较自然,不需要负数所以能容纳更大的值。
  • int类型无法表示一个数而long类型可以时,使用long类型。然而long会减慢计算,当确实需要32位整数时再使用。
  • 同上,如果需要64位整数时,使用long long类型,当int为32位系统时,如果需要16位整数,使用short类型可以节省空间。

整数溢出

// 超出系统上的最大int值
#include
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

由上可知,整数溢出时会回到初始值,就好像时钟一样走完了一圈回到一开始的地方(就像循环单链表)。
具体原理可以参考下面整数类型的存储方式一图
无符号数当达到最大值时再加1的话会往前进位直到最高位时溢出(无法存储到最高位前面的数据),导致可存储的位数都为0,结果也为0。
而有符号整数达到最大值时再加1的话会变成它的最大负数(也就是正数的负值加一,因为正数有一个给了零)。

编译器如何确定常量类型大小

一般像2345这样的数字时,它会以int存储,当使用1000000 这样的数字时,编译器会视为long int类型,当大于long类型时 会视为 unsigned long,如果仍然不够C会视为long long或者unsigned long long
八进制和十六进制常量通常视为int类型。如果值过大会按上面的顺序依次试用。
一般操作系统会选择较小的类型存储,如果希望把小的常量作为long类型对待,可以使用lL,这个同样适用于八进制与十六进制。
与之类似,在支持long long的系统中,可以使用ll或者LL后缀标识long long类型值,如3LL
uU后缀用于标识 unsigned long long类型值,比如5ull10LLU等等。

打印short、long、long long和unsigned类型数

类型 说明符
unsigned int %u
long %ld(十进制) %lx(十六进制) %lo(八进制)
short %hd(十进制) %hx(十六进制) %ho(八进制)
long long %lld(有符号) %llu(无符号)

注意:h 和 l 都可以与 u 结合使用标识无符号类型
常量后缀支持大小写而类型说明符只能使用小写。
printf 函数根据类型说明符读取该类型大小的内存区,并根据类型说明符解析该内存区内的数据并打印出来。
int 类型是计算机处理起来最方便有效的整数类型,所以在 short 和 int 长度不相同的系统中,使用 int 类型值进行参数传递会更快。(书中41页 print2.c程序)
如果类型说明符与类型不匹配,大的类型可以显示小的类型,如果小的类型显示大的就会对数据截断显示出截断部分的数据。

使用字符:char类型

char类型的技术实现是整数类型,存储的是整数,计算机使用一种数字编码,用特定的整数表示特定字符。(比如ASCII码)

声明及初始化

char response;
char itable,latan;
char grade = 'A'; // 与char grade = 65 是相同的, 不过这样可读性差。
itable = 'T'      // 只要是在char类型范围内的整数就允许赋值。

A作为数值65存储在32位内存单元内,而赋值给grade则把66存储在一个8位单元中。
使用单引号表示一个字符,如果是双引号的字符串需要存放在char数组内。

非打印字符

非打印字符例如一些动作描述:退格、换行或者让终端响铃。
C提供了三种方法来表示:

  1. 使用 ASCII 码,例如蜂鸣器的 ASCII 值为7,可以这样写:char beep = 7;
  2. 使用特殊的符号序列,即转义序列(Escape Sequence),使用转义序列易于记忆且可移植性好。
序列 意义
\a 警报(ASCII C)
\b 退格 (使活动位置在当前行上退回一个空格)
\f 走纸 (将活动位置移到下一页开始处)
\n 换行 (将活动位置移到下一行开始处)
\r 回车 (将活动位置移到当前行的开始处)
\t 水平制表符
\v 垂直制表符
\ 反斜杠(\)
' 单引号 (')
" 双引号 (")
? 问号 (?)
\0oo 八进制值(o表示一个八进制数字)
\xhh 十六进制值(h表示一个十六进制数字)

给一个字符变量赋值时,转义序列必须使用单引号:char nerf = '\n';,在双引号中的字符集合则无需单引号。

  1. C90开始提供了使用十六进制形式表示字符常量,例如Ctrl+P字符的十六进制ASCII码为10,表示为\x10\X010
    当使用数值编码时使用\032而不是032,更能表现使用字符编码的意图且方便嵌入到字符串中。

打印字符

#include
int main(void)
{
  char ch;
  printf(" Please enter a character.\n");
  scanf("%c",&ch);
  printf(" The code for %c is %d.\n", ch, ch);
  return 0;
}
/* 程序运行如下:
Please enter a character.
C
The code for C is 67.
*/

printf()说明符决定数据的显示方式而不是决定数据的存储方式。

有符号还是无符号

根据C90标准,C允许在关键字char前使用signedunsigned。无论默认的char类型是什么,signed char就是有符号类型,而unsigned char则是无符号类型。这对于使用字符类型处理小整数十分有用。如处理字符,则只须使用不带修饰词的标准char类型。

_Bool类型

是由C99引入的,用于标识布尔值,即逻辑值true(真)与false(假)。因为C用值 1 表示 true 用值 0 表示 false,所以_Bool类型实际上是用一位来存储的整数类型。

可移植的类型: inttypes.h

C99提供了几组可选的名字合集,使用时要包含inttypes.h头文件。
“确切长度类型” 例如int16_t表示16位有符号整数 、uint32_t表示32位无符号整数。
“最小长度类型” 例如int_least8_t是可以容纳8位有符号数的那些类型中长度最小的一个的别名。
“最快最小长度类型”(fastest minimum width type)如int_fast8_t定义为系统中对8位有符号数而言计算最快的整数类型的别名。
“最大的整数类型”例如intmax_t定义为最大的无符号整数类型。

详细其他的内容参考书中P614 参考资料6 “扩展的整数类型”。

如何打印

inttypes.h提供了打印需要的宏定义。

#include
#include
int main(void)
{
  int16_t me16;
  me16 = 4593;
  printf(" Use a \" macro \" from inttypes.h: ");
  printf(" me16 = %" PRId16 "\n" ,me16);
  return 0;
}

必须在C99之后才提供该支持。

floatdoublelong double类型

类型 最小位数
float 6
double 10
long double 只保证至少同double一样

声明浮点变量

float noah,jonah;
double trouble;
float planck = 6.63e-34;
long double gnp;

浮点常量

一般的常量如-1.56E+122.87e-3,可以省略正号,可以没有小数点(2E5)或指数部分(19.28),当不能都没有。
默认情况下,编译器将浮点常量当作double类型,如 some = 4.0 * 2.0其中 4.0 和 2.0 被存储为double类型,通常使用64位存储。乘积使用双精度结果被截成正常的 float 长度。这能保证精度但会减慢程序执行。
C可以使用 f 或 F 后缀使编译器把浮点常量当 float 类型,比如 2.3f9.11E9F
l 和 L 后缀使一个数字成为 long double类型,比如 54.31 和 4.32e4L。
C99增加了一种十六进制格式。这种格式使用0x0X前缀,接着是十六进制数字,然后是字母 p 或 P,最后是二的指数。如 0xa.1fp10 a 是10, .1f 表示1/16 加上 15/256,p10 表示二的十次方。十进制为10364.0

打印浮点数

说明符 输出类型
%f 小数形式
%e或%E 指数计数法
%a或%A 十六进制格式浮点数

long double 类型需要加上 L 如 %Lf%Le%La
floatdouble使用同样的类型符是因为 printf() 在参数传递时,C自动将 float 类型的参数转化成 double 类型。

浮动值的上溢和下溢

假设计算的数大到计算机不能表达的数时,会发生上溢(overflow)。printf()函数显示此值为infinfinity(或其他个含义的名称)。
假设计算的数已经是能表示的最小数字,将此数除以 2。通常计算机只好将尾数部分的位进行右移,空出首位二进制数,并丢弃最后一位二进制。以十进制为例,把一个包含四位有效数字的数字的数 0.1234E-10 除以 10,将得到结果 0.0123E-10 ,损失了一位有效数字。此过程称为下溢(underflow)。
还有一个特殊的浮动值 NaN (Not-a-Number)例如 asin(); 输入参数不能大于 1 否则函数返回 NaN 值。

复数和虚数类型

C99支持 complex.h 头文件。
_Complex的三种:float _Complexdouble _Complexlong double _Complex
_Imaginary的三种:float _Imaginarydouble _Imaginarylong double _Imaginary

各种类型所占大小

C99提供了 %zd 说明符来打印数据大小。
// todo:程序代码。

注意的内容

C对待类型不匹配现象更宽容。

int apples = 3;      // 正确
int oranges = 3.0; // 不好的形式

C会自动对该值进行类型转换以便和变量类型相匹配,但是可能会丢失一部分数据。

int cost = 12.99;            // 把一个 int 变量初始化为一个 double 值
float pi = 3.1415926536;// 把一个 float 变量初始化为一个 double 值

第一个会丢弃小数部分不进行四舍五入,第二个会损失部分精度,因为 float类型只能保证前 6 位是精确的。

有的变量命名规则,其中变量名可以表达它的类型。例如: 使用 i_ 前缀表示 int 变量,使用 us_ 表达 unsigned short 变量。

#include
int main(void)
{
  int f = 4;
  int g = 5;
  float h = 5.0f;
  printf("%d\n", f, g);  //  参数太多
  printf("%d %d\n", f); // 参数太少
  printf("%d %f\n", h, g);  // 值的类型不对应
  return 0;
}

当参数过多或过少时,它的值会随平台不同而不同。
使用 %d 显示 float 值不会把该 float 值转换为近似的 int 值,而是显示垃圾值。与之类似,使用 %f 显示 int 值也不会把 int值转换为浮点值。

转义序列的应用

#include
int main(void)
{   
    float salary;
    printf("\aEnter your desired monthly salary: ");
    printf(" $_______\b\b\b\b\b\b\b");
    scanf("%f",&salary);
    printf("\n\t$%.2f a mouth is $%.2f a year.",salary, salary * 12.0);
    printf("\rGee!\n");
    return 0;
}
/*
输入:2000.00
Enter your desired monthly salary:  $2000.00

Gee!    $2000.00 a mouth is $24000.00 a year.
*/

演示了退格(\b)、制表符(\t)和回车符(\r)的工作方式。

刷新输出

pintf()语句将输出传递给一个被称为缓冲区(buffer)的中介存储区域。缓冲区中的内容再不断地传递给屏幕。
标准 C 规定在以下几种情况下将缓冲区传给屏幕

  • 缓冲区满的时候
  • 遇到换行符的时候
  • 需要输入的时候(scanf)
    将缓冲区传输给屏幕或文件称为刷新缓冲区(flushing the buffer)。

早期的C语言版本遇到 scanf()不会强迫缓冲区刷新,为防止此情况可以使用换行符刷新缓冲区。

你可能感兴趣的:(Learning C Primer Plus | Chapter3)