当前位置: 首页 > ds >正文

我从零开始学习C语言(14)- 基本类型 PART1

今天学习第7章-基本类型,主要内容如下:

7.1 整数类型

这里的整数的整数值就是数学意义上的整数。C语言支持两种本质上(存储形式)不同的数值类型:整数类型(简称整型)和浮点类型(简称浮点型)。整数类型的值可以是整数,而浮点类型的值除了整数还可能有小数部分。

整数类型分为两大类:有符号型整数无符号型整数

有符号整数:整数如果为正数或0,那么最左边的位(符号位)为0;如果是负数,则符号位为1。

因此,最大的16位整数的二进制表示形式是0111 1111 1111 1111,对应的值是32767(2^{15}-1)。最大的32位整数的二进制表示形式是0111 1111 1111 1111 1111 1111 1111 1111,对应的数值是2147483647(2^{31}-1) ;不带符号位的整数(最左边的位也参与数值表示)称为无符号整数。最大的16位无符号整数是65535(2^{16}-1),而最大的32位无符号的整数是4294967295(2^{32}-1)。

注意:为什么这里表示的数最大范围要减1,这是因为表示0占据了一个位置

默认情况下,C语言中的整型变量都是有符号的,也就是说最左位保留为符号位。(int 类型实质上是signed int类型,但是signed可省略不写)。若要告诉编译器变量没有符号位,需要把它声明为unsigned类型。无符号整数主要用于系统编程和底层与机器相关的应用(单片机的C语言变量经常用这个类型)。由于学的这是通用C语言,先回避无符号整数。

C语言的整数类型有不同尺寸。int类型通常为32位,但在老的CPU上可能是16位。有些程序输入的数字很大,用int类型存储不下,所以C语言还提供了长整型(long)。有时,为了节省空间,我们会指示编译器以比正常存储小的空间来存储一些数,称这样的数为短整型(short)

为了使构造的整数类型满足需求,可以指明变量是long类型或short类型,signed类型或unsigned类型,甚至组合起来(unsigned long int)。然而事实上只有下列6种组合可产生不同类型:

short int
unsigned short intint 
unsigned intlong int
unsigned long int

其他组合均是上述某种类型的同义词。(整数类型默认是有符号的,signed可省略),另外说明符的顺序无影响(unsigned short int = short unsigned int)

C语言允许省略int单词来缩写整数类型的名字。如,(unsigned short int = unsigned short),

(long int = long)。

上述6种整数类型的每一种表示的取值范围会根据机器不同而不同,但,有两条所有编译器都必须遵守的规则。首先,C标准要求short int、int、long int中每种类型要覆盖一个确定的最小取值范围。其次,标准要求int类型不能比short短,long int类型不能比int类型短。但是,short int类型的取值范围可能和int类型范围一样,int类型的取值范围也可以和long int一样。

具体可查看<limits.h>头文件

下表表示在16位机上整数类型通常的取值范围,注意short int 和int有相同的取值范围。

类型最小值最大值
short int-3276832767
unsigned short int065535
int-3276832767
unsigned int065535
long int-21474836482147483647
unsigned long int04294967295

另一表说明32位机上整数类型通常的取值范围(主流),这里的int和long int有着相同的取值范围。

类型最小值最大值
short int-3276832767
unsigned short int065535
int-21474836482147483647
unsigned int02147483647
long int-21474836482147483647
unsigned long int04294967295

现在64位CPU已经很流行的。下表给出64位机(特别是UNIX系统)整数类型常见的取值范围。

类型最小值最大值
short int-3276832767
unsigned short int065535
int-21474836482147483647
unsigned int02147483647
long int-92233720368547758089223372036854775807
unsigned long int018446744073709551615

上述三个表格不是C标准强制的,会随着编译器不同而不同。具体可查看<limits.h>头文件,

这个是标准库的一部分,定义表示每种整数类型最大值和最小值的宏。

7.1.1 C99中整数类型

C99额外提供两个标准整数类型:long long int和unsigned long long int。增加的原因有两个,其一是为满足日益增长的对超大型整数的需求,其二是为适应64位运算的新处理器的能力。这两个long long 类型要求至少64位宽。所以long long int类型值范围通常为-2^{63}(-9223372036854775808)到2^{63}-1(9223372036854775807),而unsigned long long int类型值范围通常为0到2^{64}-1(18446744073709551615)。

C99把short int、int、long int和long long int类型(以及signed char类型)称为标准有符号整型,而把unsigned short int、unsigned int、unsigned long int和unsigned long long int类型(以及unsigned char类型和_Bool类型)称为标准无符号整型

除了标准的整数类型外,C99标准还允许在具体实现时定义扩展的整数类型(包括有符号和无符号)。例如编译器可提供有符号和无符号的128位整数类型。

有符号128位整数‌:使用__int128类型表示

无符号128位整数‌:使用unsigned __int128类型表示

使用时需通过#ifdef __SIZEOF_INT128__等宏检测编译器支持性

这些扩展类型通常用于需要极大整数范围的场景(如密码学、大数计算),但需注意其并非C99标准强制要求,而是编译器特定的实现。

7.1.2 整数常量

现在来看常量——程序中以文本形式出现的数字,而非读、写或计算出来的数。C语言允许用十进制(基数为10)、八进制(基础为8)和十六进制(基数为16)形式书写整数常量。

八进制数由数字0~7书写。八进制数的每一位表示一个8的幂。因此,八进制的数字237表示为十进制为:

2\times 8^{2}+3\times 8^{1}+7\times 8^{0}=128+24+7=159

十六进制数由数字0~9加上字母A~F书写,其中字母A~F表示10~15的数。十六进制数的每一位表示一个16的幂,十六进制数1AF的十进制数值为:

1\times 16^{2}+10\times 16^{1}+15\times 16^{0}=256+160+15=431

十进制:常量包含0~9中的数字,但是一定不能以0开头:

15         155        32767

八进制:常量包含0~7中的数字,而且必须以0开头:

017        0377        077777

十六进制:常量包含0~9中的数字和a~f中的字母,而且总是以0x开头:

0xf        0xff        0x7fff

十六进制常量中字母既可以是大写字母,也可以是小写字母。

0xff        0xfF        0xFf        0xFF  

注意:八进制、十六进制只是书写数的方式,其并不会对数的实际存储方式产生影响。(事实上整数都是以二进制形式存储的,与表示形式无关)它们还能混合用,10 + 015 + 0x20的值为55(十进制)。八进制和十六进制更适合底层程序的编写。

十进制:整数常量的类型通常为int,但如果常量的值大得无法存储在int型中,就用long int类型。若出现long int类型不够用的罕见情况,编译器会用unsigned long int作最后尝试。确定八进制、十六进制常量的规则略有不同:编译器会依次尝试int、unsigend int、long int和unsigned long int类型,直至找到能表示该常量的类型。

为了强制编译器把常量作为长整数来处理,只需在后边加上字母L或(小写l):

15L         0337L        0x7fffL

为了指明是无符号常量,可以在常量后边加上字母U或(小写u):

15U        0337U        0x7fffU

L和U可结合使用,表明常量既是长整型又是无符号的:

0xffffffffUL。(字母L、U顺序大小写无所谓)

7.1.3 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。常量后任何后缀都可能会改变类型的列表。例如:以U或u结尾的常量类型一定是unsigned int、unsigned long int和unsigned long long int中的一种,以L(或l)结尾的十进制常量类型一定是long int或long long int中的一种。如果常量的数值过大以至于不能用标准整数类型表示,则可以使用扩展的整数类型。

7.1.4 整数溢出

对整数执行算术运算时,其结果有可能因为太大而无法表示。例如,对两个int值进行算术运算时,结果必须仍然能用int类型来表示;否则(表示结果所需的数位太多)就会发生溢出。

整数溢出时的行为要根据操作数是有符号型还是无符号型来确定。

有符号整数运算中发生溢出时,程序的行为是未定义的。回顾4.4节的介绍可知,未定义行为的结果是不确定的。最可能的情况是,仅仅是运算的结果出错了,但程序也有可能崩溃,或出现其他意想不到的状况。

无符号整数运算过程中发生溢出时,结果是有定义的:正确答案对2^{n}取模,其中n是用于存储结果的位数。例如,如果对无符号的16位数65 535加1,其结果可以保证为0。

7.1.5 读/写整数

假设有一个程序因为其中一个int 变量发生了“溢出”而无法工作。我们的第一反应是把变量的类型从int变为long int 。但仅仅这样做是不够的,我们还必须检查数据类型的改变对程序其他部分的影响,尤其是需要检查该变量是否用在printf函数或scanf函数的调用中。如果用了,需要改变调用中的格式串,因为%d只适用于int类型。

读写无符号整数短整数长整数需要一些新的转换说明符

① 读写无符号整数时,使用字母u、o或者x代替转换说明中的d。

u说明符,该数将以十进制形式读写o表示八进制形式,而x表示十六进制形式

#include <stdio.h>int main(void)
{unsigned int u;printf("请输入十进制数字:");scanf("%u", &u); //以10进制读取uprintf("%u\n", u); //以10进制输出uprintf("请输入八进制数字:");scanf("%o", &u); //以8进制读取uprintf("%o\n", u); //以8进制输出uprintf("请输入十六进制数字:");scanf("%x", &u); //以16进制读取uprintf("%x\n", u); //以16进制输出ureturn 0;
}

在读写短整数时,在d、o、u或x前面加上字母h

#include <stdio.h>int main(void)
{short s;scanf("%hd", &s); printf("%hd\n", s); return 0;
}

在读写长整数时,在d、o、u或x前面加上字母l

#include <stdio.h>int main(void)
{long l;scanf("%ld", &l); printf("%ld\n", l); return 0;
}

(C99)在读写长长整数时,在d、o、u或x前面加上字母ll

#include <stdio.h>int main(void)
{long long ll;scanf("%lld", &ll); printf("%lld\n", ll); return 0;
}

程序013 数列求和(改进版)

6.1节编写了一个程序对用户输入的整数数列求和。该程序的一个问题就是所求出的和(或其中某个输入数)可能会超出int型变量允许的最大值。如果程序运行在整数长度为16位的机器上,可能会发生下面的情况:

This program sums a series of integers.
Enter integers (0 to terminate):10000 20000 30000 0
The sum is:-5536

求和的结果应该为60 000,但这个值不在int型变量表示的范围内,所以出现了溢出。当有符号整数发生溢出时,结果是未定义的,在本例中我们得到了一个毫无意义的结果。为了改进这个程序,可以把变量改换成long 型。

sum2.c

/* Sums a series of numbers (using long int variables) */
#include <stdio.h>
int main(void)
{long n, sum = 0;printf("This program sums a series of integers.\n");printf("Enter integers (0 to terminate): ");scanf("%ld", &n);while (n != 0) {sum += n;scanf("%ld", &n);}printf("The sum is: %ld\n", sum);return 0;
}

这种改变非常简单:将n 和sum 声明为long 型变量而不是int 型变量,然后把scanf 和printf 函数中的转换说明由%d 改为%ld。

7.2 浮点类型

整数类型并不适用于所有应用。有些时候需要变量能存储带小数点的数,或者能存储极大数或极小数。这类数可以用浮点(因小数点是“浮动的”而得名)格式进行存储。C语言提供了3种浮点类型,对应三种不同的浮点格式。

float:单精度浮点数

double:双精度浮点数

long double:扩展精度浮点数

当精度要求不严格时(例如,计算带一位小数的温度),float 类型是很适合的类型。double 提供更高的精度,对绝大多数程序来说都够用了。long double 支持极高精度的要求,很少会用到。

C标准没有说明float、double 和long double 类型提供的精度到底是多少,因为不同的计算机可以用不同方法存储浮点数。大多数现代计算机都遵循IEEE 754标准(即IEC60559)的规范,所以这里也用它作为一个示例。

IEEE浮点标准

由IEEE开发的IEEE标准提供了两种主要的浮点数格式:单精度(32位)和双精度(64位)。数值以科学计数法的形式存储,每一个数都由三部分组成:符号、指数和小数。指数部分的位数说明了数值的可能大小程度,而小数部分的位数说明了精度。单精度格式中,指数长度为8位,而小数部分占了23位。因此,单精度数可以表示的最大值大约是3.4\times 10^{38},其中精度是6个十进制数字。

IEEE标准还描述了另外两种格式:单扩展精度和双扩展精度。标准没有指明这些格式中的位数,但要求单扩展精度类型至少为43位,而双扩展精度类型至少为79位。

下表给出了根据IEEE标准实现时浮点类型的特征。(表中给出了规范化的最小正值,非规范化的数)可以更小。)long double 类型没有显示在此表中,因为它的长度随着机器的不同而变化,而最常见的大小是80位和128位。

类型最小正值最大值精度
float1.17549\times 10^{-38}3.40282\times 10^{38}6个数字
double2.22507\times 10^{-308}1.79769\times 10^{308}15个数字

在不遵循IEEE标准的计算机上,上表是无效的。事实上,在一些机器上,float 可以有和double 相同的数值集合,或者double 可以有和long double 相同的数值集合。可以在头<float.h>中找到定义浮点类型特征的宏。

在C99中,浮点类型分为两种:一种是实浮点类型 ,包括float、double 和long double 类型;另一种是C99新增的复数类型(包括float _Complex 、double _Complex 和long double _Complex )。

7.2.1 浮点常量

浮点常量可以有许多种书写方式。例如,下面这些常量全都是表示数57.0的有效方式:


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

浮点常量必须包含小数点或指数;其中,指数指明了对前面的数进行缩放所需的10的幂次。如果有指数,需要在指数数值前放置字母E(或e )。可选符号+ 或- 可以出现在字母E (或e )的后边。

默认情况下,浮点常量都以双精度数的形式存储。换句话说,当C语言编译器在程序中发现常量57.0时,a它会安排数据以double 类型变量的格式存储在内存中。这条规则通常不会引发任何问题,因为在需要时double 类型的值可以自动转化为float 类型值。

在某些极个别的情况下,可能会需要强制编译器以float 或long double 格式存储浮点常量。为了表明只需要单精度,可以在常量的末尾处加上字母F或f(如57.0F);而为了说明常量必须以long double 格式存储,可以在常量的末尾处加上字母L或l (如57.0L)

7.2.2 读/写浮点数

前面已讨论过,转换说明符%e 、%f 和%g 用于读写单精度浮点数。读写double 和long double 类型的值所需的转换说明符略有不同。

读取 double 类型的值时,在e 、f 或g 前放置字母l

double d;
scanf("%lf", &d);

注意 : 只能在scanf 函数格式串中使用l ,不能在printf函数格式串中使用。在printf 函数格式串中,转换e 、f 和g可以用来写float 类型或double 类型的值。( C99允许printf 函数调用中使用%le 、%lf 和%lg ,不过字母l 不起作用。)

读写long double 类型的值时,在e 、f 或g 前放置字母L

long double ld;
scanf("%Lf", &ld);
printf("%Lf", ld);

7.3 字符类型

目前还没有讨论的唯一基本类型是 char 类型,即字符类型(也称字符型)。char 类型的值可以根据计算机的不同而不同,因为不同的机器可能会有不同的字符集。

字符集

当今最常用的字符集是ASCII(美国信息交换标准码)字符集,它用7位代码表示128个字符。在ASCII码中,数字0~9用0110000~0111001码来表示,大写字母A~Z用1000001~1011010码表示。ASCII码常被扩展用于表示256个字符,相应的字符集Latin-1包含西欧和许多非洲语言中的字符。

我们国内自己的字符集有GB-2312和GB-18030。国际通用的有UTF-8等。

char 类型的变量可以用任意单字符赋值:

char ch;
ch = 'a'; /* lower-case a */
ch = 'A'; /* upper-case A */
ch = '0'; /* zero */
ch = ' '; /* space */

注意,字符常量需要用单引号括起来,而不是双引号

7.3.1 字符操作

在C语言中字符的操作非常简单,因为存在这样一个事实:C语言把字符当作小整数进行处理 。毕竟所有字符都是以二进制的形式进行编码的,而且无需花费太多的想象就可以将这些二进制代码看成整数。例如,在ASCII码中,字符的取值范围是0000000~1111111,可以看成是0~ 127的整数。字符'a' 的值为97,'A' 的值为65,'0' 的值为48,而' ' (空格)的值为32。在C中,字符和整数之间的关联是非常强的字符常量事实上是int 类型而不是char 类型(这是一个非常有趣的现象,但对我们并无影响)。

当计算中出现字符时,C语言只是使用它对应的整数值。思考下面这个例子,假设采用ASCII字符集:

char ch;
int i;
i = 'a'; /* i is now 97 */
ch = 65; /* ch is now 'A' */
ch = ch + 1; /* ch is now 'B' */
ch++; /* ch is now 'C' */

可以像比较数那样对字符进行比较。下面的if 语句测试ch 中是否含有小写字母,如果有,那么它会把ch 转化为相应的大写字母

if ('a' <= ch && ch <= 'z')ch = ch - 'a' + 'A';

诸如'a'<= ch 这样的比较使用的是字符所对应的整数值,这些数值依据使用的字符集有所不同,所以程序使用< 、<= 、> 和>= 来进行字符比较可能不易移植

字符拥有和数相同的属性,这一事实会带来一些好处。例如,可以让for 语句中的控制变量遍历所有的大写字母:

for (ch = 'A'; ch <= 'Z'; ch++)...

另一方面,以数的方式处理字符可能会导致编译器无法检查出来的多种编程错误,还可能会导致我们编写出诸如'a' * 'b' / 'c' 这类无意义的表达式。此外,这样做也可能会妨碍程序的可移植性,因为程序可能会基于一些对字符集的假设。(例如,上述for 循环假设从字母A到字母Z的代码都是连续的。)

7.3.2 有符号字符和无符号字符

既然C语言允许把字符作为整数来使用,那么char 类型应该像整数类型一样也存在有符号型和无符号型两种。通常有符号字符的取值范围是-128~127,而无符号字符的取值范围则是0~255。

char是1个字节,有8位,所以符号字符的取值范围是-128~127(-2^{7}\sim 2^{7}-1),无符号字符的取值范围0~255(0\sim 2^{8})

C语言标准没有说明普通char 类型数据是有符号型还是无符号型,有些编译器把它们当作有符号型来处理,有些编译器则将它们当作无符号型来处理。(甚至还有一些编译器允许程序员通过编译器选项来选择把char 类型当成有符号型还是无符号型。)

大多数时候,人们并不真的关心char 类型是有符号型还是无符号型。但是,我们偶尔确实需要注意,特别是当使用字符型变量存储一个小值整数的时候。基于上述原因, 标准C允许使用单词signed和unsigned 来修饰char 类型

signed char sch;
unsigned char uch;

可移植性技巧 不要假设char 类型默认为signed 或unsigned 。如果有区别,用signed char 或unsigned char 代替char 。

由于字符和整数之间有密切关系,C89采用术语整值类型来(统称)包含整数类型和字符类型。枚举类型也属于整值类型。

C99不使用术语“整值类型”,而是扩展了整数类型的含义使其包含字符类型和枚举类型。C99中的_Bool 型(5.2节)是无符号整数类型

7.3.3 算术类型

整数类型和浮点类型统称为算术类型。下面对C89中的算术类型进行了总结分类。

整值类型:
字符数型(char );
有符号整型(signed char 、short int 、int 、long int );
无符号整型(unsigned char 、unsigned short int、unsigned int 、unsigned long int );
枚举类型。
浮点类型(float 、double 、long double )。

C99的算术类型具有更复杂的层次。

整数类型:
字符类型(char );
有符号整型, 包括标准的(signed char 、short int、int 、long int 、long long int )和扩展的;
无符号整型,包括标准的(unsigned char 、unsignedshort int 、unsigned int 、unsigned long int、unsigned long long int 、_Bool )和扩展的;
枚举类型。
浮点类型:
实数浮点类型(float 、double 、long double );
复数类型(float_Complex 、double_Complex 、long double_Complex )。

今天就更新到这里,明天继续学习下半部分和做习题,感谢您的阅读。

http://www.xdnf.cn/news/18746.html

相关文章:

  • vscode 中自己使用的 launch.json 设置
  • 5.7 生成环境使用cdn加载element ui
  • ASCOMP PDF Conversa:高效精准的PDF转换工具
  • pcie实现虚拟串口
  • 人工智能之数学基础:离散随机变量和连续随机变量
  • Java中如何实现对象的拷贝
  • RHCSA--命令(一)
  • 我是如何写作的?
  • Manus AI 与多语言手写识别技术文章大纲
  • 单例模式与线程池
  • 【Vue✨】Vue 中的 diff 算法详解
  • 云原生概述
  • git的工作使用中实际经验
  • 【码蹄杯】2025年本科组省赛第一场
  • 【Linux系统】命名管道与共享内存
  • 硬件笔记(27)---- 恒流源电路原理
  • [Redis进阶]---------持久化
  • 如何查看MySQL 的执行计划?
  • Spring Boot 3为何强制要求Java 17?
  • JavaScript 性能优化实战技术文章大纲
  • Games 101 第四讲 Transformation Cont(视图变换和投影变换)
  • 深入剖析结构体内存对齐
  • 边缘计算服务器EMI滤波器 故障分析与解决思路
  • 【LeetCode 热题 100】300. 最长递增子序列——(解法一)记忆化搜索
  • C++ 20: Concepts 与Requires
  • 链表-23.合并K个升序链表-力扣(LeetCode)
  • Qt从qmake迁移到cmake的记录
  • Spring Boot 整合网易163邮箱发送邮件实现找回密码功能
  • PHP - 线程安全 - 疑问与答案
  • PyQt6 进阶篇:构建现代化、功能强大的桌面应用