1、初始C语言和数据详解
参照书籍:C primer plus第6版
c语言初识
第一个c语言程序
#include<stdio.h>
int main(void)
{
// 你好世界
printf("hello world\n");
getchar();
return 0;
}
指令和头文件
#include<stdio.h>
这是程序的第一行,其作用相当于把stdio.h文件中的所有内容的输入该行所在的位置。include文件提供了一种方便的途径共享许多程序共有的信息
include这行代码是一条C预处理器指令(preprocessor directive),通常C编译器在编译前会对源码做一些准备工作,即预处理(preprocessing)
main函数
c程序一定从main()函数开始执行(目前不考虑例外的情况)。除了main函数,还可以命名其他函数,但是main函数必须是开始的函数
注释
单行注释://
多行注释:/**/
花括号、函数体和块
一般而言,所有的c函数都使用花括号标记函数体的开始和结束
花括号还可以把函数中多条语句合并成为一个单元或块
声明、变量、关键字和赋值
#include<stdio.h>
int main()
{
int num;
num = 10;
return 0;
}
代码中的int num;
就叫声明(declaration)。声明是c语言最重要的特性之一
在该例子中,声明完成了两件事;其一,在函数中有一个名为num的变量(variable);其二,int表明num是一个整型。int是一种数据类型,编译器利用这些信息为num变量分配存储空间
int是c语言中的一个关键字(keyword),关键字是语言定义的单词,不能做其他用途。例如不能用int作为函数名和变量名
num是一个标识符(identifier),也就是一个变量、函数或其他实体的名称。因此声明把特定标识符与计算机内存中的特定位置联系起来,同时也确定了存储在某位置的信息类型或数据类型。
在c语言中,所有变量都必须先声明才能使用
多行声明
如果两个变量拥有相同的数据类型,我们可以在一行申明两个变量,使用逗号隔开
例如:int age, num;
与int age; int num;
是等价的
数据类型
在下面马上会进行解释
变量名
变量名命名规则:
可以使用小写字母、大写字母、数字和下换线(_)来命名。而且,名称的第一个字符必须是字母或下换线,不能是数字
给变量命名时最好使用有意义的变量名或者标识符,例如程序中要存储一个人的姓名信息,我们就可以将变量命名为:name,存储年龄信息可以将变量名命名为:age
关键字和保留标识符
关键字是c语言内部定义好的词,不能用来作为标识符,如变量名。使用关键字作为变量名会被编译器视为语法错误
c语言关键字:
auto | extern | short | while |
---|---|---|---|
break | float | signed | _Alignas |
case | for | sizeof | _Alignof |
char | goto | static | _Atomic |
const | if | struct | _Bool |
continue | inline | switch | _Complex |
default | int | typedef | _Generic |
do | long | union | _Imaginary |
double | register | unsigned | _Noreturn |
else | restrict | void | _Static_assert |
enum | return | volatile | _Thread_local |
赋值
代码中num = 10;
就是赋值表达式。意思是把值10赋给变量num。在执行int num;
申明时,编译器在计算机内存中为变量num预留了空间,然后在执行这行赋值表达式语句时,把值存储在之前预留的位置。而且我们可以给num赋不同的值,这就是num被称为变量的原因。
注意:该赋值表达式语句从右侧把值赋到左侧
printf函数与转义字符
printf("hello world\n");
使用了c语言的一个标准函数printf(),其作用是把双引号中的内容打印到屏幕上
\n的意思是换行,代表了一个换行符,相当于我们在键盘上敲了一下回车(enter)键
换行符是一个转义序列(escape sequence)。转义序列用于代表难以表示或无法输入的字符。每个转义序列都以反斜杠(\)开始:
转义字符:
转义字符分为一般转义字符、八进制转义字符、十六进制转义字符。
一般转义字符:背诵\o、 \n、 \’、 \”、 \\。
八进制转义字符: ‘\141’ 是合法的, 前导的0是不能写的。
十六进制转义字符:’\x6d’ 才是合法的,前导的0不能写,并且x是小写。
转义字符 | 含义 | ASCII码(16/10进制) |
---|---|---|
\o | 空字符(NULL) | 00H/0 |
\n | 换行符(LF) | 0AH/10 |
\r | 回车符(CR) | 0DH/13 |
\t | 水平制表符(HT) | 09H/9 |
\v | 垂直制表符(VT) | 0B/11 |
\a | 响铃(BEL) | 07/7 |
\b | 退格符(BS) | 08H/8 |
\f | 换页符(FF) | 0CH/12 |
\' | 单引号 | 27H/39 |
\" | 双引号 | 22H/34 |
\\ | 反斜杠 | 5CH/92 |
? | 问号字符 | 3F/63 |
\ddd | 任意字符 | 三位八进制 |
\xhh | 任意字符 | 二位十六进制 |
字符常量中使用单引号和反斜杠以及字符常量中使用双引号和反斜杠时,都必须使用转义字符表示,即在这些字符前加上反斜杠。
在C程序中使用转义字符\ d d d或者\ x h h可以方便灵活地表示任意字符。\ d d d为斜杠后面跟三位八进制数,该三位八进制数的值即为对应的八进制A S C I I码值。\ x后面跟两位十六进制数,该两位十六进制数为对应字符的十六进制A S C I I码值。
使用转义字符时需要注意以下问题:
- 转义字符中只能使用小写字母,每个转义字符只能看作一个字符。
- \v垂直制表和\f换页符对屏幕没有任何影响,但会影响打印机执行响应操作。
- 在C程序中,使用不可打印字符时,通常用转义字符表示
注:
1,\v垂直制表和\f换页符对屏幕没有任何影响,但会影响打印机执行响应操作。
2,\n其实应该叫回车换行。换行只是换一行,不改变光标的横坐标;回车只是回到行首,不改变光标的纵坐标。
3,\t光标向前移动四格或八格,可以在编译器里设置
4,\'在字符里(即单引号里)使用。在字符串里(即双引号里)不需要,只要用'即可。
5,\?其实不必要。只要用?就可以了(在windows VC6和tc2中验证)
return语句
return在c语言中是一个中跳转语句,意思是结束当前函数,如果函数要求有返回值只需要在return后面跟上与函数返回值类型一致的数据即可。
例如int main()函数,该函数的返回值是int型,也就是需要一个整数最为返回值,所以return后面跟上0即可:return 0;
如果函数不需要返回值,只写一个return就可以结束函数
数据
变量和常量数据
在程序中,有些数据类型在程序运行前就已经预先设定好了,在程序整个运行过程中都不会发生变化,这些被称为常量(constant)
一些数据在程序运行期间可能会被改变或者赋值,这数据被称为变量(variable)
示例:
#include<stdio.h>
// 求一个圆的周长
int main(void)
{
// 圆的半径,变量
float r;
printf("输入圆的半径:");
// 获取用户输入
scanf("%f", &r);
// 圆的周长公式:2πr,其中这个π就是一个常量
printf("这个园的周长为:%.2f", 2 * 3.14 * r);
// 如果程序一闪而过,可以加个这个函数
getchar();
return 0;
}
在该示例中,r就是一个变量,而3.14是一个常量
常量定义
如果一个常量在程序中经常使用,则可以在程序顶部用#define
声明一个宏常量
如声明一个π:#define PI 3.14
注意:宏常量我们约定俗成使用大写
示例:
#include<stdio.h>
// 声明常量π,并命名为:PI
#define PI 3.14
// 求一个圆的周长
int main(void)
{
// 圆的半径,变量
float r;
printf("输入圆的半径:");
// 获取用户输入
scanf("%f", &r);
// 圆的周长公式:2πr,其中这个π就是一个常量
printf("这个园的周长为:%.2f", 2 * PI * r);
// 如果程序一闪而过,可以加个这个函数
getchar();
return 0;
}
还一种声明常量的方法:const关键字,这个在后面说
位、字节、字
位、字节、字是描计算机数据单元或存储单元的术语,这里主要指存储单元。
-
最小的存储单位是位(bit),可以存储0或1(或者说用于设置开或关)
-
字节(byte)是最常用的计算机存储单位,对于几乎所有的机器,1字节均为8位。既然1位可以表示0或1,那么8位就有256(2的8次方)种可能的0、1组合,通过二进制编码便可以表示0~255的整数或一组字符
-
字(word)是设计计算机时给定的自然存储单位。对于8位的微型计算机(如最初的苹果计算机),1个字长只有8位,也就是1个字节。从那开始计算机字长增长至16位、32位,直到现在的64位计算机,也就是8字节。
计算机的字长越大,其数据转移越快,允许访问的内存也更多
数据类型
整数和浮点数
数据类型分为两大类:整数类型和浮点数类型
整数
与数学概念一样,c语言中整数就是没有小数部分的数
计算机以二进制数字存储整数,例如整数7以二进制写是111,因此在8位字节中存储该数字,需要把前五位都设为0,后三位设为1:00000111
浮点数
在值的后面加上一个小数点就会成为一个浮点数,例如7是整数,而7.0是一个浮点数。
浮点数有多种书写方式,如:7.00、3.14e8、3.14e-9、3.14E8
e计数法,又叫科学计数法:
e表示10的多少次方,3.4e38表示3.4乘以10的38次方;3.4e-38表示3.4乘以0.1的38次方
浮点数在存储时,计算机会把他们分为小数部分和指数部分来表示,而且分开存储这两部分。
7和7.0虽然在数值上相同,但是存储方案不同,把7.0写成0.7e1,这里,0.7是小数部分,1是指数部分。
下面演示了一个存储浮点数的例子,当然,计算机在内部使用二进制和2的幂进行存储而不是10的幂
整数和浮点数的区别
- 整数没有小数部分,浮点数有小数部分
- 浮点数可以表示的范围比整数大
- 对于一些运算符(如两个和大的数相减),浮点数损失的精度更多
- 因为在任何区间内(如1.0到2.0之间)都存在无穷多个实数,所以计算机的浮点数不能表示区间内所有的值。浮点数通常只是实际值的近似值。例如7.0可能会被存储为6.99999 。等会后面会讲更多精度方面的内容
- 过去浮点运算比整数运算慢。不过现在许多CPU都包含浮点处理器,缩小了速度上的差距
数据类型关键字
数据类型关键字 | 含义 |
---|---|
int | 整型 |
long | 长型 |
short | 短型 |
unsigned | 无符号类型 |
char | 字符型 |
float | 单精度浮点型 |
double | 双精度浮点型 |
signed | 有符号型 |
void | 空类型 |
_Bool | 布尔值 |
_Complex | 复数 |
_Imaginary | 虚数 |
数据类型占用字节大小
在Linux系统下
类型/编译器 | 16位编译器 | 32位编译器 | 64位编译器 |
---|---|---|---|
void | 0 | 0 | 0 |
char | 1 | 1 | 1 |
char * | 2 | 4 | 8 |
short int | 2 | 2 | 2 |
int | 2 | 4 | 4 |
float | 4 | 4 | 4 |
double | 8 | 8 | 8 |
long | 4 | 4 | 8 |
long double | 8 | 12 | 16 |
long long | 8 | 8 | 8 |
整数类型
c语言有许多整数类型,先详细讲解int型
int整型
声明变量
可以分别在四条声明中声明各变量,也可以在一行全部声明
int a;
int b;
int c;
int d;
int a,b,c,d;
// 两者等价
初始化变量
初始化变量就是为变量赋一个初始值,初始化可以在声明中完成,如
int a = 1;
int b = 2, c = 3;
int d, e = 5; // 有效,但是不好,会让人误以为d和e都赋值为5,但其实只给e赋值了
打印int值
在printf函数中打印int类型的值要使用转换说明%d
int a = 1;
printf("a的值为:%d", a);
打印十六进制和八进制
计算机中会用特定的前缀表示不同的进制
0x或者0X的前缀表示十六进制
0前缀表示八进制
想要打印八进制和十六进制需要用到的转换说明是%o和%x/%X,想要把前缀也打印,就需要加上标志字符 :#
int a = 100;
printf("100的八进制为:%#o\n", a); // 0144
printf("100的十六进制为:%#x\n", a); // 0x64
其他整型
初学c语言,int类型可以满足大多数程序的整数类型需求
c语言提供了3个关键字来修饰基本整数类型:short、long、unsigned
- short int(或者简写成short):短整型,占用的存储空间可能比int类型少,与 int 类似,是有符号的。常用于较小数值的场合以节约空间,如记录人的年龄
- long int(或简写成long):长整型,占用的存储空间可能比int类型多,与 int 类似,是有符号的。适用于数值较大的场合,如电话号码或身份证号码
- long long ing(或简写成long long):长长整型,占用的存储空间可能比 long int 类型多,是有符号的。适用于数值较大的场合,如电话号码或身份证号码
- unsigned int(或简写unsigned):无符号整型,只能用于非负值场景。这种类型与有符号类型的表示范围不同。例如,unsigned int 的取值范围是:065535;而int类型的取值范围是:-3276832767。用于表示正负号的位现在用来表示另一个二进制值,所以无符号可以表示更大的数,但是不能表示负数。
- unsigned不仅可以修饰 int 还可以修饰short、long、long long 型,与 unsigned int 类似。用于非负数的情况,且数值较大时
- signed表示有符号的意思,int、short、long等数据类型都是有符号的,所以完全用不上signed关键字修饰,但是如果用signed修饰也完全没有问题,可以强调使用有符号类型的意图
使用多种整型的原因
上面在介绍其他整型时,为什么说short类型“可能”比int类型占用的空间小,long类型“可能”比int类型占用的空间大?
因为c语言只规定了short占用的存储空间不能多于int,long占用的空间不能少于int,这样规定是为了适应不同的机器
例如,过去的一台Windows3.x的机器上,int类型和short类型都占16位,long类型占32位。后来Windows和苹果系统都使用16位存储short类型,32位存储int类型和long类型,现在计算机普遍使用64位的存储器,为了存储64位的整数,才引入了long long类型
c标准规定的基本数据类型的最小大小
-
现在最常用的设置是,long long占64位,long占32位,int占16位或者32位(依据计算机的自然字长而定),short占16位。原则上这四中类型代表4种不同的大小,但是在实际使用中,有些类型之间通常由重叠
-
c标准对基本数据类型只规定了允许的最小大小。
-
对于16位机
short和int类型的最小取值范围是[-32768, 32767]
-
对于32位机
long类型的最小取值范围是[-2147483648, 2147483647]
unsigned short 和unsigned int最小取值范围是[0, 65535]
unsigned long最小取值范围是[0, 4294967295]
-
对于64位机
long long最小取值范围是[-9223372036854775808, 9223372036854775807]
unsigned long long最小取值范围是[0, 18446744073709551615]
-
整数溢出
在下面程序分别将有符号和无符号类型整数设置为比取值范围略大
#include<stdio.h>
int main(void)
{
int a = 2147483647;
unsigned int b = 4294967295;
printf("%d, %d, %d \n", a, a + 1, a + 2);
printf("%u, %u, %u \n", b, b + 1, b + 2);
getchar();
return 0;
}
打印结果
2147483647, -2147483648, -2147483647
4294967295, 0, 1
根据结果可以得知,当他达到取值范围内的最大值时,会重新从取值范围的最小值开始计数,编译器不会抛出错误,所以用户在使用整型时要注意该类型的取值范围
long和long long常量
在程序中使用数字常量时,编译器会自动存储成int类型,如果数值超过了int类型的取值范围,编译器就会将其视为long int类型,如果还是不够则视为unsigned long类型,还不够再视为long long或unsigned long long类型(前提时编译器识别这些类型)
但是在一些特殊情况下,我们需要用long类型来存储一个数值较小的常量,我们就可以在在值的末尾加上l 或 L的后缀
例如,把一个十进制常量77存成long类型:77l或者77L;八进制和十六进制也可以,分别加上进制的前缀0 和 0x即可:020L和0x10L
类似的,long long类型也可以使用ll 或LL 来表示long long类型的值,如4LL,89ll
也可以再加上u后缀表示无符号,如:8ull,3ULL,9LLU
示例
// 后缀使用大写小写都可以,不过推荐使用大写L,因为小写l与数字1有点相似
// 十进制的long类型
#define A 8L
// 十进制的long long类型
#define B 8LL
// 十进制的unsigned long long类型
#define C 8uLL
// 十进制的long long类型
#define D 4ll
// 八进制的long long类型
#define E 04ll
// 十六进制的long long类型
#define F 0x4ll
打印short、long、long long和unsigned类型
-
unsigned int类型可以简写成unsigned,所以打印unsigned int使用%u转换说明
-
打印long 类型使用%ld转换说明。如果系统中int和long的大小相同,使用%d就行,但是如果移植到其他系统(int和long大小不同)中可能会无法正常工作。
- 在x和o前可以使用 l 前缀,%lx表示用十六进制的格式打印long类型
- %lo表示用八进制的格式打印long格式,所以 l 是一个长度修饰符(“长度修饰符”在内置函数中的printf函数那一节有详细讲解)
-
打印long long类型用%lld转换说明,具体与long类似,ll 是一个长度修饰符 ,d和x、o等是转换说明符
-
打印unsigned long类型和unsigned long long类型用 %lu和 %llu 转换说明,其中 u 是转换说明符,l 和 ll 是长度修饰符
-
打印short类型要使用 h 长度修饰符,%hd转换说明表示以十进制打印short类型,%hx和%ho表示以十六进制和八进制打印short类型。
-
转换说明符d和u分别表示有符号和无符号类型。例如%lld表示有符号long long类型,%llu表示无符号long long类型
char类型
char类型存储单个字符,如字母或者标点符号,但是从技术层面看,char是整型,因为char类型实际上存储的是整数。
计算机使用数字编码来处理字符,即用特定的整数表示特定的字符
ASCII编码对照表
ASCII(American Standard Code for Information Interchange,美国信息互换标准代码)是一套基于拉丁字母的字符编码,共收录了 128 个字符,用一个字节就可以存储,它等同于国际标准 ISO/IEC 646。
ASCII 编码于 1967 年第一次发布,最后一次更新是在 1986 年,迄今为止共收录了 128 个字符,包含了基本的拉丁字母(英文字母)、阿拉伯数字(也就是 1234567890)、标点符号(,.!等)、特殊符号(@#$%^&等)以及一些具有控制功能的字符(往往不会显示出来)。
ASCII 编码是美国人给自己设计的,他们并没有考虑欧洲那些扩展的拉丁字母,也没有考虑韩语和日语,我大中华几万个汉字更是不可能被重视。计算机也是美国人发明的,起初使用的就是 ASCII 码,只能显示英文字符。各个国家为了让本国公民也能正常使用计算机,开始效仿 ASCII 开发自己的字符编码,例如 ISO/IEC 8859(欧洲字符集)、shift_Jis(日语字符集)、GBK(中文字符集)等
ASCII 编码中第 0~31 个字符(开头的 32 个字符)以及第 127 个字符(最后一个字符)都是不可见的(无法显示),但是它们都具有一些特殊功能,所以称为控制字符( Control Character)或者功能码(Function Code),这些字符的使用在这一章的printf与转义字符有说,也可以再笔记“C相关表”中查看
二进制 | 十进制 | 十六进制 | 字符/缩写 | 解释 |
---|---|---|---|---|
00000000 | 0 | 00 | NUL (NULL) | 空字符 |
00000001 | 1 | 01 | SOH (Start Of Headling) | 标题开始 |
00000010 | 2 | 02 | STX (Start Of Text) | 正文开始 |
00000011 | 3 | 03 | ETX (End Of Text) | 正文结束 |
00000100 | 4 | 04 | EOT (End Of Transmission) | 传输结束 |
00000101 | 5 | 05 | ENQ (Enquiry) | 请求 |
00000110 | 6 | 06 | ACK (Acknowledge) | 回应/响应/收到通知 |
00000111 | 7 | 07 | BEL (Bell) | 响铃 |
00001000 | 8 | 08 | BS (Backspace) | 退格 |
00001001 | 9 | 09 | HT (Horizontal Tab) | 水平制表符 |
00001010 | 10 | 0A | LF/NL(Line Feed/New Line) | 换行键 |
00001011 | 11 | 0B | VT (Vertical Tab) | 垂直制表符 |
00001100 | 12 | 0C | FF/NP (Form Feed/New Page) | 换页键 |
00001101 | 13 | 0D | CR (Carriage Return) | 回车键 |
00001110 | 14 | 0E | SO (Shift Out) | 不用切换 |
00001111 | 15 | 0F | SI (Shift In) | 启用切换 |
00010000 | 16 | 10 | DLE (Data Link Escape) | 数据链路转义 |
00010001 | 17 | 11 | DC1/XON (Device Control 1/Transmission On) | 设备控制1/传输开始 |
00010010 | 18 | 12 | DC2 (Device Control 2) | 设备控制2 |
00010011 | 19 | 13 | DC3/XOFF (Device Control 3/Transmission Off) | 设备控制3/传输中断 |
00010100 | 20 | 14 | DC4 (Device Control 4) | 设备控制4 |
00010101 | 21 | 15 | NAK (Negative Acknowledge) | 无响应/非正常响应/拒绝接收 |
00010110 | 22 | 16 | SYN (Synchronous Idle) | 同步空闲 |
00010111 | 23 | 17 | ETB (End of Transmission Block) | 传输块结束/块传输终止 |
00011000 | 24 | 18 | CAN (Cancel) | 取消 |
00011001 | 25 | 19 | EM (End of Medium) | 已到介质末端/介质存储已满/介质中断 |
00011010 | 26 | 1A | SUB (Substitute) | 替补/替换 |
00011011 | 27 | 1B | ESC (Escape) | 逃离/取消 |
00011100 | 28 | 1C | FS (File Separator) | 文件分割符 |
00011101 | 29 | 1D | GS (Group Separator) | 组分隔符/分组符 |
00011110 | 30 | 1E | RS (Record Separator) | 记录分离符 |
00011111 | 31 | 1F | US (Unit Separator) | 单元分隔符 |
00100000 | 32 | 20 | (Space) | 空格 |
00100001 | 33 | 21 | ! | |
00100010 | 34 | 22 | " | |
00100011 | 35 | 23 | # | |
00100100 | 36 | 24 | $ | |
00100101 | 37 | 25 | % | |
00100110 | 38 | 26 | & | |
00100111 | 39 | 27 | ' | |
00101000 | 40 | 28 | ( | |
00101001 | 41 | 29 | ) | |
00101010 | 42 | 2A | * | |
00101011 | 43 | 2B | + | |
00101100 | 44 | 2C | , | |
00101101 | 45 | 2D | - | |
00101110 | 46 | 2E | . | |
00101111 | 47 | 2F | / | |
00110000 | 48 | 30 | 0 | |
00110001 | 49 | 31 | 1 | |
00110010 | 50 | 32 | 2 | |
00110011 | 51 | 33 | 3 | |
00110100 | 52 | 34 | 4 | |
00110101 | 53 | 35 | 5 | |
00110110 | 54 | 36 | 6 | |
00110111 | 55 | 37 | 7 | |
00111000 | 56 | 38 | 8 | |
00111001 | 57 | 39 | 9 | |
00111010 | 58 | 3A | : | |
00111011 | 59 | 3B | ; | |
00111100 | 60 | 3C | < | |
00111101 | 61 | 3D | = | |
00111110 | 62 | 3E | > | |
00111111 | 63 | 3F | ? | |
01000000 | 64 | 40 | @ | |
01000001 | 65 | 41 | A | |
01000010 | 66 | 42 | B | |
01000011 | 67 | 43 | C | |
01000100 | 68 | 44 | D | |
01000101 | 69 | 45 | E | |
01000110 | 70 | 46 | F | |
01000111 | 71 | 47 | G | |
01001000 | 72 | 48 | H | |
01001001 | 73 | 49 | I | |
01001010 | 74 | 4A | J | |
01001011 | 75 | 4B | K | |
01001100 | 76 | 4C | L | |
01001101 | 77 | 4D | M | |
01001110 | 78 | 4E | N | |
01001111 | 79 | 4F | O | |
01010000 | 80 | 50 | P | |
01010001 | 81 | 51 | Q | |
01010010 | 82 | 52 | R | |
01010011 | 83 | 53 | S | |
01010100 | 84 | 54 | T | |
01010101 | 85 | 55 | U | |
01010110 | 86 | 56 | V | |
01010111 | 87 | 57 | W | |
01011000 | 88 | 58 | X | |
01011001 | 89 | 59 | Y | |
01011010 | 90 | 5A | Z | |
01011011 | 91 | 5B | [ | |
01011100 | 92 | 5C | \ | |
01011101 | 93 | 5D | ] | |
01011110 | 94 | 5E | ^ | |
01011111 | 95 | 5F | _ | |
01100000 | 96 | 60 | ` | |
01100001 | 97 | 61 | a | |
01100010 | 98 | 62 | b | |
01100011 | 99 | 63 | c | |
01100100 | 100 | 64 | d | |
01100101 | 101 | 65 | e | |
01100110 | 102 | 66 | f | |
01100111 | 103 | 67 | g | |
01101000 | 104 | 68 | h | |
01101001 | 105 | 69 | i | |
01101010 | 106 | 6A | j | |
01101011 | 107 | 6B | k | |
01101100 | 108 | 6C | l | |
01101101 | 109 | 6D | m | |
01101110 | 110 | 6E | n | |
01101111 | 111 | 6F | o | |
01110000 | 112 | 70 | p | |
01110001 | 113 | 71 | q | |
01110010 | 114 | 72 | r | |
01110011 | 115 | 73 | s | |
01110100 | 116 | 74 | t | |
01110101 | 117 | 75 | u | |
01110110 | 118 | 76 | v | |
01110111 | 119 | 77 | w | |
01111000 | 120 | 78 | x | |
01111001 | 121 | 79 | y | |
01111010 | 122 | 7A | z | |
01111011 | 123 | 7B | { | |
01111100 | 124 | 7C | | | |
01111101 | 125 | 7D | } | |
01111110 | 126 | 7E | ~ | |
01111111 | 127 | 7F | DEL (Delete) | 删除 |
声明
char类型变量的声明与其他整型变量的声明方式一样
示例
char c1;
char c2;
char c3, c4;
初始化和赋值
赋的值要用单引号引起来,不能使用双引号,如果使用双引号会被认为是一个字符串,而不是一个字符
示例
char c1 = 'A';
char c2 = 'B', c3 = 'C';
其实还可以使用ASCII码赋值,例如我想要给c赋值为A,找到字符A在ASCII码中对应的整数:65,再把65赋值给c,打印出来的结果是字符 “A”
示例
#include<stdio.h>
int main(void)
{
char c = 65;
printf("%c", c); // A
getchar();
return 0;
}
但是这样是一种不好的编程风格,如果系统不是使用的ASCII码就不能得到我们想要的结果了,所以直接赋值 'A' 是最稳妥的办法
打印字符
使用%d可以打印该字符对应的ASCII码
使用%c打印该字符本身
示例
#include<stdio.h>
int main(void)
{
char c = 'A';
printf("%d \n", c); // 65
printf("%c \n", c); // A
getchar();
return 0;
}
有符号和无符号
有些编译器把char视为有符号类型,这意味着char可表示的范围是[-128127]。而有些编译器会吧char视为无符号类型,此时char可表示的范围是[0255]。
可以查询相应的编译器手册,确定正在使用的编译器如何实现char类型。或者查询limite.h头文件。在后面会详细介绍头文件
也可以使用unsigned和signed关键字来修饰char类型,unsigned char表示无符号类型,signed char表示有符号类型。这在处理小整数时很有用。如果只用char处理字符,那么char前面就无需使用任何修饰符
_Bool类型
用于表示布尔值,即逻辑true和false。
在c语言中1表示true,0表示false,所以bool类型也是一种整型。但原则上他仅占用1位的存储空间,因为对于0和1而言,1位的存储空间就足够了
可移植类型:stdint.h和inttypes.h
C语言提供了许多有用的整数类型。但是,某些类型名在不同系统中的功能不一样。例如int类型有的计算机表示16位整数,有的计算机又表示32位整数
C99新增了两个头文件stdint.h和inttypes.h,以确保C语言的类型在各系统中的功能相同。
stdint.h 定义了一些固定宽度的整数类型别名,主要有下面三类。
- 宽度完全确定的整数
intN_t
,比如int32_t
。 - 宽度不小于某个大小的整数
int_leastN_t
,比如int_least8_t
。 - 宽度不小于某个大小、并且处理速度尽可能快的整数
int_fastN_t
,比如int_fast64_t
。
上面所有类型都是有符号的,类型名前面可以加一个前缀u
,表示无符号类型,比如uint16_t
。
inttypes.h 里面,为 stdint.h 定义的四类整数类型,提供了printf()
和scanf()
的占位符。
- 固定宽度整数类型,比如 int8_t。
- 最小宽度整数类型,比如 int_least8_t。
- 最快最小宽度整数类型,比如 int_fast8_t。
- 最大宽度整数类型,比如 intmax_t。
printf()
的占位符采用PRI + 原始占位符 + 类型关键字/宽度
的形式构成。举例来说,原始占位符为%d
,则对应的占位符如下。
- PRIdn (固定宽度类型)
- PRIdLEASTn (最小宽度类型)
- PRIdFASTn (最快最小宽度类型)
- PRIdMAX (最大宽度类型)
上面占位符中的n
,可以用8、16、32、64代入。
这两个文件的详细使用可在标准库函数一章中看到
浮点数类型
C语言中的浮点数类型有:float、double、long double
计数法示例
一般计数法 | 科学计数法 | 指数计数法 |
---|---|---|
1000000000 | 1.0*10^9 | 1.0 |
123000 | 1.23*10^5 | 1.23e5 |
322.56 | 3.2256*10^2 | 3.2256e5 |
0.000056 | 5.6*10^-5 | 5.6e-5 |
浮点数存储形式
一个浮点数的存储分为3个部分,分别是符号位
,阶码
和尾数
-
float
占用 32 位,包括1位符号位、 8 位指数位(阶码)和 23 位尾数位。 ` -
double
占用 64 位,包括1位符号位、 11 位指数位(阶码)和 52 位尾数位。
对于一个 64 位浮点数,我们可以用下面的这张示意图来表示它的各个部分的长度及顺序。其中一个等号=
表示一个二进制位,|
表示隐含的边界。
|=|===========|===================================================|
|s|-exponent--|--------------------mantissa-----------------------|
上面的图示中,s
(sign)为符号位,占 1 bit,用来表示整个double
的正负性;中间部分exponent
是指数部分,即阶码,占 11 bit;最后的也是最长的一部分mantissa
,尾数,占 52 位,它的长度直接影响力浮点数的精度。
下面是这三个部分的具体细节
- 符号位跟整数一样,符号位取 0 表示无符号,为正数;取 1 表示有符号,为负数。
- 指数部分采用的是移码表示法并且采用的是余1023码,也就是说,指数部分的 11 位没有符号位,在计算时,要先将这 11 位看作一个正整数,然后减掉 1023 之后才得真正的指数。
- 关于尾数部分,有一点需特别注意,尾数部分的值总是
1.M
,而1.M
中的1
是被隐藏了的。为什么呢?这样做有什么意义? 我们知道,一个十进制数采用科学计数法表示的话,形式是 d*10^e 其中 d∈[1.0,10) 。类推一下,就可以知道,一个d 进制的科学计数法,小数的整数部分的取值范围是 [1.0,d) ,所以在二进制中,小数部分的取值范围是 [1.0,2) ,这个范围内的实数,整数部分都是 1 ,所以这个1
是大家共有的,于是就没有存储的必要性了
例如:存储一个double类型的小数:3.625
将其转换为二进制数:11.101
使用二进制的科学计数法表示:1.1101*2^(1)
所以3.625存储在内存中时
符号为正=0;指数部分=1023+1;尾数部分=110100000...(补齐52位)
|0|10000000000|1101000000000000000000000000000000000000000000000000|
+1 * 1.8125 * 2^(1)
其他示例
-0.05 =
|1|01111111010|1001100110011001100110011001100110011001100110011010|
-1 * 1.6 * 2^(-5)
+0.05 =
|0|01111111010|1001100110011001100110011001100110011001100110011010|
+1 * 1.6 * 2^(-5)
-0.5 =
|1|01111111110|0000000000000000000000000000000000000000000000000000|
-1 * 1 * 2^(-1)
+0.5 =
|0|01111111110|0000000000000000000000000000000000000000000000000000|
+1 * 1 * 2^(-1)
-1 =
|1|01111111111|0000000000000000000000000000000000000000000000000000|
-1 * 1 * 2^(0)
+1 =
|0|01111111111|0000000000000000000000000000000000000000000000000000|
+1 * 1 * 2^(0)
-2 =
|1|10000000000|0000000000000000000000000000000000000000000000000000|
-1 * 1 * 2^(1)
+2 =
|0|10000000000|0000000000000000000000000000000000000000000000000000|
+1 * 1 * 2^(1)
-9 =
|1|10000000010|0010000000000000000000000000000000000000000000000000|
-1 * 1.125 * 2^(3)
+9 =
|0|10000000010|0010000000000000000000000000000000000000000000000000|
+1 * 1.125 * 2^(3)
-10 =
|1|10000000010|0100000000000000000000000000000000000000000000000000|
-1 * 1.25 * 2^(3)
+10 =
|0|10000000010|0100000000000000000000000000000000000000000000000000|
+1 * 1.25 * 2^(3)
浮点数范围
IEEE754浮点数的表示方法。C语言里对float类型数据的表示范围为-3.4*10^38~+3.4*10^38
。double为-1.7*10^-308~1.7*10^308
,long double为-1.2*10^-4932~1.2*10^4932.
类型 | 比特(位)数 | 有效数字 | 数值范围 |
---|---|---|---|
float | 32 | 6~7 | ``-3.4*1038~+3.4*1038` |
double | 64 | 15~16 | -1.7*10^-308~1.7*10^308 |
long double | 128/ | 18~19 | -1.2*10^-4932~1.2*10^4932 |
定义浮点数变量及常量
变量
float a = 3.2e-3;
double b;
long double c;
常量
3.141592
.2
4e16
.8E-5
100.
注意:
默认情况下,编译器假定浮点型常量是 double 类型的精度。
例如:
float a = 0.4 * 0.2;
通常,4.0和2.0被储存为 64位的 double类型,使用双精度进行乘法运算,然后将乘积截断成 float类型的宽度。这样做虽然计算精度更高,但是会减慢程序的运行速度。
在浮点数后面加上f或F后缀可覆盖默认设置,编译器会将浮点型常量看作 float 类型,如
float a = 0.4f * 0.2F;
使用l或L后缀使得数字成为long double类型,如54.31和4.32L。
注意,建议使用L后缀,因为字母l和数字1很容易混淆。
没有后缀的浮点型常量是double类型。
复数和虚数类型
C99标准支持复数类型和虚数类型,但是有所保。一些独立实现,如嵌入式处理器的实现,就不需要使用复数和虚数(VCR芯片就不需要复数)。
一般而言,虚数类型都是可选项。C11标准把整个复数软件包都作为可选项。
简而言之,C语言有3种复数类型:float _Complex、double _complex和long double _Complex.例如,float _complex类型的变量应包含两个float 类型的值,分别表示复数的实部和虚部。
C语言的3种虚数类型是float _Imaginary、double _Imaginary和long double _Imaginary。
如果包含 complex.h头文件,便可用complex代替_Complex,用imaginary代替 _Imaginary,还可以用 I 代替-1的平方根。
类型大小
使用sizeof运算符查看
sizeof是C语言的内置运算符,以字节为单位给出指定类型的大小
C99和C11提供了%zd转换说明符匹配sizeof的返回值。不支持C99或C11的编译器可以用%d或%ud代替%zd
int main()
{
char a;
// sizeof内可以写变量名
printf("char:%zd\n", sizeof(a));
// sizeof内可以写常量
printf("float:%zd\n", sizeof(2.1f));
// sizeof内可以直接写数据类型名
printf("int:%zd\n", sizeof(int));
// 可以用%d代替%zd
printf("double:%d\n", sizeof(double));
return 0;
}
/* 运行结果如下
char:1
float:4
int:4
double:8
*/
使用头文件limits.h和float.h查看
C头文件 1imits.h和 float.h分别提供了与整数类型和浮点类型大小限制相关的详细信息。
下述的明示常量就是宏定义
limits.h
float.h
可以使用文本编辑器打开并查看系统使用的 flcat.h头文件。表中所列都与float类型相关。把明示常量名中的FLT分别替换成DBL和LDBL,即可分别表示double和long double 类型对应的明示常量
使用示例
#include <stdio.h>
#include <limits.h>
#include <float.h>
int main()
{
printf("cahr类型所占空间:%d\n", CHAR_BIT);
printf("char类型最大值:%d\n", CHAR_MAX);
printf("char类型最小值:%d\n", CHAR_MIN);
printf("int类型最大值:%d\n", INT_MAX);
printf("int类型最小值:%d\n", INT_MIN);
printf("long long类型最大值%lld\n", LLONG_MAX);
printf("long long类型最小值%lld\n", LLONG_MIN);
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 一个费力不讨好的项目,让我损失了近一半的绩效!
· 清华大学推出第四讲使用 DeepSeek + DeepResearch 让科研像聊天一样简单!
· 实操Deepseek接入个人知识库
· CSnakes vs Python.NET:高效嵌入与灵活互通的跨语言方案对比
· Plotly.NET 一个为 .NET 打造的强大开源交互式图表库