C语言学习笔记-入门整合篇
Posted jia-huan
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C语言学习笔记-入门整合篇相关的知识,希望对你有一定的参考价值。
(该篇学习内容全部来自于C语言中文网, 本篇内容仅仅是简易学习笔记 , 以自己的理解+网站部分描述结合+个人补充,并不适合编程初学者观看!!! 需要有一定的编程基础)
数据在内存中存储
内存条包含了上亿个电子元器件。这些元器件,实际上就是电路;电路的电压会变化,要么是 0V,要么是 5V,只有这两种电压。5V 是通电,用1来表示,0V 是断电,用0来表示。所以,一个元器件有2种状态,0 或者 1。
一般情况下将8个元器件看做一个单位,即使表示很小的数,例如 1,也需要8个,也就是 00000001。
1个元器件称为1比特(Bit)或1位,8个元器件称为1字节(Byte),那么16个元器件就是2Byte,32个就是4Byte,以此类推:
单位换算:
1Byte = 8 Bit
1KB = 1024Byte
函数
C语言规定,一个程序必须有且只有一个 main 函数。main 被称为主函数,是程序的入口函数,程序运行时从 main 函数开始,直到 main 函数结束(遇到 return 或者执行到函数末尾时,函数才结束)。
#include <stdio.h>
//( ) 表明这是函数定义, 之间的代码是函数要实现的功能。
int main()
puts("C语言中文网");
return 0;
头文件的概念
C语言开发者们编写了很多常用函数,并分门别类的放在了不同的文件,这些文件就称为头文件(header file)。每个头文件中都包含了若干个功能类似的函数,调用某个函数时,要引入对应的头文件,否则编译器找不到函数。
实际上,头文件往往只包含函数的说明,也就是告诉我们函数怎么用,而函数本身保存在其他文件中,在链接时才会找到。对于初学者,可以暂时理解为头文件中包含了若干函数。
引入头文件使用#include命令,并将文件名放在< >中,#include 和 < > 之间可以有空格,也可以没有。
头文件以.h为后缀,而C语言代码文件以.c为后缀,它们都是文本文件,没有本质上的区别,#include 命令的作用也仅仅是将头文件中的文本复制到当前文件,然后和当前文件一起编译。你可以尝试将头文件中的内容复制到当前文件,那样也可以不引入头文件。
.h中代码的语法规则和.c中是一样的,你也可以#include <xxx.c>,这是完全正确的。不过实际开发中没有人会这样做,这样看起来非常不专业,也不规范。
较早的C语言标准库包含了15个头文件,stdio.h 和 stdlib.h 是最常用的两个: stdio 是 standard input
output 的缩写,stdio.h 被称为“标准输入输出文件”,包含的函数大都和输入输出有关,puts() 就是其中之一。 stdlib
是 standard library 的缩写,stdlib.h
被称为“标准库文件”,包含的函数比较杂乱,多是一些通用工具型函数,system() 就是其中之一。
变量和数据类型
变量定义&赋值
int a; // 这个语句的意思是:在内存中找一块区域,命名为 a,用它来存放整数。
a=123; // 把 123 交给了变量 a,这个过程叫做给变量赋值;第一次赋值,也称变量的初始化,或者赋初值。
数据类型(Data Type)
数据是放在内存中的,变量是给这块内存起的名字,有了变量就可以找到并使用这份数据。
诸如数字、文字、符号、图形、音频、视频等数据都是以二进制形式存储在内存中的,它们并没有本质上的区别,那么,00010000 该理解为数字16呢,还是图像中某个像素的颜色呢,还是要发出某个声音呢?如果没有特别指明,我们并不知道。
也就是说,内存中的数据有多种解释方式,使用之前必须要确定;上面的int a;就表明,这份数据是整数,不能理解为像素、声音等。int 有一个专业的称呼,叫做数据类型(Data Type)。
顾名思义,数据类型用来说明数据的类型,确定了数据的解释方式,让计算机和程序员不会产生歧义。
C语言基本的数据类型:
说 明 | 字符型 | 短整型 | 整型 | 长整型 | 单精度浮点型 | 双精度浮点型 | 无类型 |
---|---|---|---|---|---|---|---|
数据类型 | char | short | int | long | float | double | void |
连续定义的多个变量以逗号,分隔,并且要拥有相同的数据类型;变量可以初始化,也可以不初始化。
int a, b, c;
float m = 10.9, n = 20.56;
char p, q = '@';
数据的长度
所谓数据长度(Length),是指数据占用多少个字节。占用的字节越多,能存储的数据就越多,对于数字来说,值就会更大,反之能存储的数据就有限。
多个数据在内存中是连续存储的,彼此之间没有明显的界限,如果不明确指明数据的长度,计算机就不知道何时存取结束。例如我们保存了一个整数1000,它占用4个字节的内存,而读取时却认为它占用3个字节或5个字节,这显然是不正确的。
所以,在定义变量时还要指明数据的长度。而这恰恰是数据类型的另外一个作用。数据类型除了指明数据的解释方式,还指明了数据的长度。因为在C语言中,每一种数据类型所占用的字节数都是固定的,知道了数据类型,也就知道了数据的长度。
整数(short,int,long)
在现代操作系统中,int一般占用 4 个字节(Byte)的内存,共计 32 位(Bit)。
int 是基本的整数类型,short 和 long 是在 int 的基础上进行的扩展,short 可以节省内存,long 可以容纳更大的值。
short a = 10;
short b, c = 99;
long m = 102023;
long n, p = 562131;
这样 a、b、c 只占用 2 个字节的内存,而 m、n、p 可能会占用 8 个字节的内存。
整型的长度
在不同的环境下, 只有 short 的长度是确定的,是两个字节,而 int 和 long 的长度无法确定。
C语言并没有严格规定 short、int、long 的长度,只做了宽泛的限制:
- short 至少占用 2 个字节。
- int 建议为一个机器字长。32 位环境下机器字长为 4 字节,64 位环境下机器字长为 8 字节。
- short 的长度不能大于 int,long 的长度不能小于 int。
总结起来,它们的长度(所占字节数)关系为:
2 ≤ short ≤ int ≤ long
这就意味着,short 并不一定真的“短”,long 也并不一定真的“长”,它们有可能和 int 占用相同的字节数。
举例:
- 在 16 位环境下,short 的长度为 2 个字节,int 也为 2 个字节,long 为 4 个字节。16 位环境多用于单片机和低级嵌入式系统,在PC和服务器上已经见不到了。
- 对于 32 位的 Windows、Linux 和 Mac OS,short 的长度为 2 个字节,int 为 4 个字节,long 也为 4 个字节。PC和服务器上的 32 位系统占有率也在慢慢下降,嵌入式系统使用 32 位越来越多。
在 64 位环境下,不同的操作系统会有不同的结果,如下所示:
操作系统 | short | int | long |
---|---|---|---|
Win64(64位 Windows) | 2 | 4 | 4 |
类Unix系统(包括 Unix、Linux、Mac OS、BSD、Solaris 等) | 2 | 4 | 8 |
目前我们使用较多的PC系统为 Win XP、Win 7、Win 8、Win 10、Mac OS、Linux,在这些系统中,short 和 int 的长度都是固定的,分别为 2 和 4,大家可以放心使用,只有 long 的长度在 Win64 和类 Unix 系统下会有所不同,使用时要注意移植性。
sizeof 操作符
sizeof 用来获取某个数据类型或变量所占用的字节数,需要注意的是,sizeof 是C语言中的操作符,不是函数,所以可以不带( );
如果后面跟的是变量名称,那么可以省略( ),如果跟的是数据类型,就必须带上( )。
short a = 10;
int b = 100;
int short_length = sizeof a;
int int_length = sizeof(b);
int long_length = sizeof(long);
int char_length = sizeof(char);
不同整型的输出
使用不同的格式控制符可以输出不同类型的整数,它们分别是:
- %hd用来输出 short int 类型,hd 是 short decimal 的简写;
- %d用来输出 int 类型,d 是 decimal 的简写;
- %ld用来输出 long int 类型,ld 是 long decimal 的简写。
#include <stdio.h>
int main()
short a = 10;
int b = 100;
long c = 9437;
printf("a=%hd, b=%d, c=%ld\\n", a, b, c);
return 0;
当使用%d输出 short,或者使用%ld输出 short、int 时,不管值有多大,都不会发生错误,因为格式控制符足够容纳这些值。
当使用%hd输出 int、long,或者使用%d输出 long 时,如果要输出的值比较小(就像上面的情况),一般也不会发生错误,如果要输出的值比较大,就很有可能发生错误。
二进制数、八进制数和十六进制数的表示
二进制
二进制由 0 和 1 两个数字组成,使用时必须以0b或0B(不区分大小写)开头
//合法的二进制
int a = 0b101; //换算成十进制为 5
int b = -0b110010; //换算成十进制为 -50
int c = 0B100001; //换算成十进制为 33
//非法的二进制
int m = 101010; //无前缀 0B,相当于十进制
int n = 0B410; //4不是有效的二进制数字
读者请注意,标准的C语言并不支持上面的二进制写法,只是有些编译器自己进行了扩展,才支持二进制数字。换句话说,并不是所有的编译器都支持二进制数字,只有一部分编译器支持,并且跟编译器的版本有关系。
下面是实际测试的结果:
- Visual C++ 6.0 不支持。
- Visual Studio 2015 支持,但是 Visual Studio 2010 不支持;可以认为,高版本的 Visual Studio 支持二进制数字,低版本的 Visual Studio 不支持。
- GCC 4.8.2 支持,但是 GCC 3.4.5 不支持;可以认为,高版本的 GCC 支持二进制数字,低版本的 GCC 不支持。
- LLVM/Clang 支持(内嵌于 Mac OS 下的 Xcode 中)。
八进制
八进制由 0~7 八个数字组成,使用时必须以0开头(注意是数字 0,不是字母 o)
//合法的八进制数
int a = 015; //换算成十进制为 13
int b = -0101; //换算成十进制为 -65
int c = 0177777; //换算成十进制为 65535
//非法的八进制
int m = 256; //无前缀 0,相当于十进制
int n = 03A2; //A不是有效的八进制数字
十六进制
十六进制由数字 0~9、字母 A~F 或 a~f(不区分大小写)组成,使用时必须以0x或0X(不区分大小写)开头
//合法的十六进制
int a = 0X2A; //换算成十进制为 42
int b = -0XA0; //换算成十进制为 -160
int c = 0xffff; //换算成十进制为 65535
//非法的十六进制
int m = 5A; //没有前缀 0X,是一个无效数字
int n = 0X3H; //H不是有效的十六进制数字
二进制数、八进制数和十六进制数的输出
C语言中常用的整数有 short、int 和 long 三种类型,通过 printf 函数,可以将它们以八进制、十进制和十六进制的形式输出。
short | int | long | |
---|---|---|---|
八进制 | %ho | %o | %lo |
十进制 | %hd | %d | %ld |
十六进制 | %hx 或者 %hX | %x 或者 %X | %lx 或者 %lX |
二进制数的输出
部分编译器支持二进制数字的表示,但是却不能使用 printf 函数输出二进制。
通过转换函数可以将其它进制数字转换成二进制数字,并以字符串的形式存储,然后在 printf 函数中使用%s输出即可。
八进制数的输出
八进制数字和十进制数字不区分大小写,所以格式控制符都用小写形式。
十六进制数的输出
十六进制数字的表示用到了英文字母,有大小写之分,要在格式控制符中体现出来:
- %hx、%x 和 %lx 中的x小写,表明以小写字母的形式输出十六进制数;
- %hX、%X 和 %lX 中的X大写,表明以大写字母的形式输出十六进制数。
short a = 0b1010110; //二进制数字
int b = 02713; //八进制数字
long c = 0X1DAB83; //十六进制数字
printf("a=%ho, b=%o, c=%lo\\n", a, b, c); //以八进制形似输出
printf("a=%hd, b=%d, c=%ld\\n", a, b, c); //以十进制形式输出
printf("a=%hx, b=%x, c=%lx\\n", a, b, c); //以十六进制形式输出(字母小写)
printf("a=%hX, b=%X, c=%lX\\n", a, b, c); //以十六进制形式输出(字母大写)
运行结果:
a=126, b=2713, c=7325603
a=86, b=1483, c=1944451
a=56, b=5cb, c=1dab83
a=56, b=5CB, c=1DAB83
一个数字不管以何种进制来表示,都能够以任意进制的形式输出。数字在内存中始终以二进制的形式存储,其它进制的数字在存储前都必须转换为二进制形式;同理,一个数字在输出时要进行逆向的转换,也就是从二进制转换为其他进制。
输出时加上前缀
- 对于八进制数字,它没法和十进制、十六进制区分,因为八进制、十进制和十六进制都包含 0~7 这几个数字。
- 对于十进制数字,它没法和十六进制区分,因为十六进制也包含 0~9 这几个数字。如果十进制数字中还不包含 8 和 9,那么也不能和八进制区分了。
- 对于十六进制数字,如果没有包含 a~f 或者 A~F,那么就无法和十进制区分,如果还不包含 8 和 9,那么也不能和八进制区分了。
区分不同进制数字的一个简单办法就是,在输出时带上特定的前缀。在格式控制符中加上#即可输出前缀,例如 %#x、%#o、%#lX、%#ho 等:
short a = 0b1010110; //二进制数字
int b = 02713; //八进制数字
long c = 0X1DAB83; //十六进制数字
printf("a=%#ho, b=%#o, c=%#lo\\n", a, b, c); //以八进制形似输出
printf("a=%hd, b=%d, c=%ld\\n", a, b, c); //以十进制形式输出
printf("a=%#hx, b=%#x, c=%#lx\\n", a, b, c); //以十六进制形式输出(字母小写)
printf("a=%#hX, b=%#X, c=%#lX\\n", a, b, c); //以十六进制形式输出(字母大写)
运行结果:
a=0126, b=02713, c=07325603
a=86, b=1483, c=1944451
a=0x56, b=0x5cb, c=0x1dab83
a=0X56, b=0X5CB, c=0X1DAB83
C语言中的正负数及其输出
在C语言中short、int、long 都可以带上正负号,如果不带正负号,默认就是正数。
如果将一个数字分为符号和数值两部分,那么不加 unsigned 的数字称为有符号数,能表示正数和负数,加了 unsigned 的数字称为无符号数,只能表示正数。
请读者注意一个小细节,如果是unsigned int类型,那么可以省略 int ,只写 unsigned,例如:
unsigned n = 100; 它等价于: unsigned int n = 100;
对于有符号数,符号也是数字的一部分,也要在内存中体现出来。符号只有正负两种情况,用1位(Bit)就足以表示;C语言规定,把内存的最高位作为符号位;C语言规定,在符号位中,用 0 表示正数,用 1 表示负数。。
对于有符号数,如果只考虑正数,那么各种类型能表示的数值范围(取值范围)就比原来小了一半。但是在很多情况下,我们非常确定某个数字只能是正数,比如班级学生的人数、字符串的长度、内存地址等,这个时候符号位就是多余的了,就不如删掉符号位,把所有的位都用来存储数值,这样能表示的数值范围更大(大一倍)。
如果不希望设置符号位,可以在数据类型前面加上 unsigned 关键字:
unsigned short a = 12;
unsigned int b = 1002;
unsigned long c = 9892320;
无符号数的输出
无符号数可以以八进制、十进制和十六进制的形式输出,它们对应的格式控制符分别为:
unsigned short | unsigned int | unsigned long | |
---|---|---|---|
八进制 | %ho | %o | %lo |
十进制 | %hu | %u | %lu |
十六进制 | %hx 或者 %hX | %x 或者 %X | %lx 或者 %lX |
严格来说,格式控制符和整数的符号是紧密相关的,具体就是:
- %d 以十进制形式输出有符号数;
- %u 以十进制形式输出无符号数;
- %o 以八进制形式输出无符号数;
- %x 以十六进制形式输出无符号数。
C 语言中printf 并不支持八进制和十六进制形式输出有符号数呢
下表全面地总结了不同类型的整数,以不同进制的形式输出时对应的格式控制符(- -表示没有对应的格式控制符):
short | int | long | unsigned short | unsigned int | unsigned long | |
---|---|---|---|---|---|---|
八进制 | - - | - - | - - | %ho | %o | %lo |
十进制 | %hd | %d | %ld | %hu | %u | %lu |
十六进制 | - - | - - | - - | %hx 或者 %hX | %x 或者 %X | %lx 或者 %lX |
- 当以有符号数的形式输出时,printf 会读取数字所占用的内存,并把最高位作为符号位,把剩下的内存作为数值位;
- 当以无符号数的形式输出时,printf 也会读取数字所占用的内存,并把所有的内存都作为数值位对待;
- 对于一个有符号的正数,它的符号位是 0,当按照无符号数的形式读取时,符号位就变成了数值位,但是该位恰好是 0 而不是 1,所以对数值不会产生影响;
- “有符号正数的最高位是 0”这个巧合才使得 %o 和 %x 输出有符号数时不会出错。
- 不管是以 %o、%u、%x 输出有符号数,还是以 %d 输出无符号数,编译器都不会报错,只是对内存的解释不同了;
整数在内存中是如何存储的
存储方式的目的:为了提高加减法的运算效率,硬件电路要设计得尽量简单。
原因:加法和减法是计算机中最基本的运算,计算机时时刻刻都离不开它们,所以它们由硬件直接支持。
存储方案难点:
- 加法和减法是计算机中最基本的运算,计算机时时刻刻都离不开它们,所以它们由硬件直接支持,加法和减法是两种运算,增加了电路设计的复杂度。
- 对于有符号数,内存要区分符号位和数值位,对于人脑来说,很容易辨别,但是对于计算机来说,就要设计专门的电路,这无疑增加了硬件的复杂性,增加了计算的时间。
设计思路:
- 加法和减法可以合并为一种运算,就是加法运算,因为减去一个数相当于加上这个数的相反数。
- 把符号位和数值位等同起来,让它们一起参与运算,不再加以区分,这样硬件电路就变得简单了。
存储方法:
在计算机内存中,整数一律采用补码的形式来存储。这意味着,当读取整数时还要采用逆向的转换,也就是将补码转换为原码。
原码:将一个整数转换成二进制形式,就是其原码。例如short a = 6;,a 的原码就是0000 0000 0000 0110;更改 a 的值a = -18;,此时 a 的原码就是1000 0000 0001 0010。
反码:对于正数,它的反码就是其原码(原码和反码相同);负数的反码是将原码中除符号位以外的所有位(数值位)取反,也就是 0 变成 1,1 变成 0。例如short a = 6;,a 的原码和反码都是0000 0000 0000 0110;更改 a 的值a = -18;,此时 a 的反码是1111 1111 1110 1101。
补码:对于正数,它的补码就是其原码(原码、反码、补码都相同);负数的补码是其反码加 1。例如short a = 6;,a 的原码、反码、补码都是0000 0000 0000 0110;更改 a 的值a = -18;,此时 a 的补码是1111 1111 1110 1110。
C语言整数的取值范围以及数值溢出
有符号数: 以int为例,共32位,最高位为符号位,31位为数值位
无符号数: 以int为例,共32位,无符号位,32位均为数值位
无符号数的取值范围
计算无符号数(unsigned 类型)的取值范围(或者说最大值和最小值)很容易,将内存中的所有位(Bit)都置为 1 就是最大值,都置为 0 就是最小值。
以 unsigned char 类型为例,它的长度是 1,占用 8 位的内存,所有位都置为 1 时,它的值为 28 - 1 =
255,所有位都置为 0 时,它的值很显然为 0。由此可得,unsigned char 类型的取值范围是 0~255。
有符号数的取值范围
char | short | int(4个字节) | long(8个字节) | |
---|---|---|---|---|
最小值 | -2^7 = -128 | -2^15 = -32,768 ≈ -3.2万 | -2^31 = -2,147,483,648 ≈ -21亿 | -2^63 ≈ -9.22×10^18 |
最大值 | 2^7 - 1= 127 | 2^15 - 1 = 32,767 ≈ 3.2万 | 2^31 - 1 = 2,147,483,647 ≈ 21亿 | 2^63 - 1≈ 9.22×10^18 |
数值溢出
#include <stdio.h>
int main()
unsigned int a = 0x100000000;
int b = 0xffffffff;
printf("a=%u, b=%d\\n", a, b);
return 0;
运行结果:
a=0, b=-1
a 定义为无符号int类型,占4个字节,32位,最大值是 0xFFFFFFFF,0x100000000 = 0xFFFFFFFF + 1,溢出1,最高位溢出,只能读到32位0,所以读出来是0
b 定义为有符号数,有效数值位是31位,而0xffffffff,转化为二进制是32位1:1111 1111 … 1111, 所以最高位的 1 会覆盖符号位,数值位只留下 31 个 1,所以 b 的原码为:1111 1111 …… 1111 1111, 这也是 b 在内存中的存储形式。当 printf 读取到 b 时,由于最高位是 1,所以会被判定为负数,要从补码转换为原码:
[1111 1111 …… 1111 1111]补
= [1111 1111 …… 1111 1110]反
= [1000 0000 …… 0000 0001]原
= -1
最终 b 的输出结果为 -1。(关于b在内存中存储为1111 1111 …… 1111 1111这一点,尚不能理解;也无法理解最高位覆盖符号位的描述)
C语言中的小数(float,double)
C语言中常用的小数有两种类型,分别是 float 或 double;float 称为单精度浮点型,double 称为双精度浮点型。
小数的长度是固定的,float 始终占用4个字节,double 始终占用8个字节。
C语言同时支持两种形式的小数(十进制形式&指数形式)。但是在书写时,C语言中的指数形式和数学中的指数形式有所差异:
数学中的指数:7.25×10^2
C语言中的指数:aEn 或 aen
2.1E5 = 2.1×10^5,其中 2.1 是尾数,5 是指数。
3.7E-2 = 3.7×10^-2,其中 3.7 是尾数,-2 是指数
小数的输出
- %f 以十进制形式输出 float 类型;
- %lf 以十进制形式输出 double 类型;
- %e 以指数形式输出 float 类型,输出结果中的 e 小写;
- %E 以指数形式输出 float 类型,输出结果中的 E 大写;
- %le 以指数形式输出 double 类型,输出结果中的 e 小写;
- %lE 以指数形式输出 double 类型,输出结果中的 E 大写。
#include <stdio.h>
#include <stdlib.h>
int main()
float a = 0.302;
float b = 128.101;
double c = 123;
float d = 112.64E3;
double e = 0.7623e-2;
float f = 1.23002398;
printf("a=%e \\nb=%f \\nc=%lf \\nd=%lE \\ne=%lf \\nf=%f\\n", a, b, c, d, e, f);
return 0;
运行结果:
a=3.020000e-01
b=128.100998
c=123.000000
d=1.126400E+05
e=0.007623
f=1.230024
- %f 和 %lf 默认保留六位小数,不足六位以 0 补齐,超过六位按四舍五入截断。
- 将整数赋值给 float 变量时会变成小数。
- 以指数形式输出小数时,输出结果为科学计数法;也就是说,尾数部分的取值为:0 ≤ 尾数 < 10。
- b 的输出结果让人费解,才三位小数,为什么不能精确输出,而是输出一个近似值呢?这和小数在内存中的存储形式有关,很多简单的小数压根不能精确存储,所以也就不能精确输出。
小数还有一种更加智能的输出方式,就是使用%g。%g 会对比小数的十进制形式和指数形式,以最短的方式来输出小数,让输出结果更加简练。所谓最短,就是输出结果占用最少的字符。
#include <stdio.h>
#include <stdlib.h>
int main()
float a = 0.00001;
float b = 30000000;
float c = 12.84;
float d = 1.229338455;
printf("a=%g \\nb=%g \\nc=%g \\nd=%g\\n", a, b, c, d);
return 0;
运行结果:
a=1e-05
b=3e+07
c=12.84
d=1.22934
- %g 默认最多保留六位有效数字,包括整数部分和小数部分;%f 和 %e 默认保留六位小数,只包括小数部分。
- %g 不会在最后强加 0 来凑够有效数字的位数,而 %f 和 %e 会在最后强加 0 来凑够小数部分的位数。
- %g 和 %lg 分别用来输出 float 类型和 double 类型,并且当以指数形式输出时,e小写。
- %G 和 %lG 也分别用来输出 float 类型和 double 类型,只是当以指数形式输出时,E大写。
数字的后缀
- 在整数后面紧跟 l 或者 L(不区分大小写)表明该数字是 long 类型;
- 在小数后面紧跟 f 或者 F(不区分大小写)表明该数字是 float 类型。
小数和整数相互赋值
- 将一个整数赋值给小数类型,在小数点后面加 0 就可以,加几个都无所谓。
- 将一个小数赋值给整数类型,就得把小数部分丢掉,只能取整数部分,这会改变数字本来的值。注意是直接丢掉小数部分,而不是按照四舍五入取近似值。
小数在内存中是如何存储的
小数在内存中是以浮点数的形式存储的。浮点数是数字(或者说数值)在内存中的一种存储格式,它和定点数是相对的。
C语言标准规定,小数在内存中以科学计数法的形式来存储,具体形式为:
flt = (-1)sign × mantissa × baseexponent
在C语言中使用英文字符
字符类型由单引号’ '包围,字符串由双引号" "包围。
//字符正确的写法
char a = '1';
char b = '$';
char c = 'X';
char d = ' '; // 空格也是一个字符
//错误的写法
char x = '中'; //char 类型不能包含 ASCII 编码之外的字符
char y = 'A'; //A 是一个全角字符
char z = "t"; //字符类型应该由单引号包围
// 字符串
char str1[] = "http://c.biancheng.net";
char *str2 = "C语言中文网";
在C语言中使用中文字符
非ASCII 编码字符存储
使用宽字符的编码方式。常见的宽字符编码有 UTF-16 和 UTF-32,它们都是基于 Unicode 字符集的,能够支持全球的语言文化。
C语言推出了一种新的类型,叫做 wchar_t。wchar_t 的长度由编译器决定:
- 在微软编译器下,它的长度是 2,等价于 unsigned short;
- 在GCC、LLVM/Clang 下,它的长度是 4,等价于 unsigned int。
单独的字符由单引号’ ‘包围,例如’B’、’@’、‘9’等;但是,这样的字符只能使用 ASCII 编码,要想使用宽字符的编码方式,就得加上L前缀,例如L’A’、L’9’、L’中’、L’国’、L’。’。
注意,加上L前缀后,所有的字符都将成为宽字符,占用 2 个字节或者 4 个字节的内存,包括 ASCII 中的英文字符。给字符串加上L前缀就变成了宽字符串,它包含的每个字符都是宽字符,一律采用 UTF-16 或者 UTF-32 编码。
wchar_t a = L'A'; //英文字符(基本拉丁字符)
wchar_t b = L'9'; //英文数字(阿拉伯数字)
wchar_t c = L'中'; //中文汉字
wchar_t d = L'国'; //中文汉字
wchar_t e = L'。'; //中文标点
wchar_t f = L'ヅ'; //日文片假名
wchar_t g = L'♥'; //特殊符号
wchar_t h = L'༄'; //藏文
wchar_t web_url[] = L"http://c.biancheng.net";
wchar_t *web_name = L"C语言中文网";
将不加L前缀的字符称为窄字符,将加上L前缀的字符称为宽字符。窄字符使用 ASCII 编码,宽字符使用 UTF-16 或者 UTF-32 编码。
宽字符(串)的输出
putchar、printf 只能输出不加L前缀的窄字符,对加了L前缀的宽字符无能为力,我们必须使用 <wchar.h> 头文件中的宽字符输出函数,它们分别是 putwchar 和 wprintf:
- putwchar 函数专门用来输出一个宽字符,它和 putchar 的用法类似;
- wprintf 是通用的、格式化的宽字符输出函数,它除了可以输出单个宽字符,还可以输出宽字符串。宽字符对应的格式控制符为%lc, 宽字符串对应的格式控制符是%ls。
在输出宽字符之前还要使用 setlocale 函数(setlocale 函数位于 <locale.h> 头文件中)进行本地化设置,告诉程序如何才能正确地处理各个国家的语言文化。(先记住)
希望设置为中文简体环境:
Windows 下请写作:
setlocale(LC_ALL, "zh-CN");
在 Linux 和 Mac OS 下请写作:
setlocale(LC_ALL, "zh_CN");
代码演示:
#include <wchar.h>
#include <locale.h>
int main()
wchar_t a = L'A'; //英文字符(基本拉丁字符)
wchar_t b = L'9'; //英文数字(阿拉伯数字)
wchar_t c = L'中'; //中文汉字
wchar_t d = L'国'; //中文汉字
wchar_t e = L'。'; //中文标点
wchar_t f = L'ヅ'; //日文片假名
wchar_t g = L'♥'; //特殊符号
wchar_t h = L'༄'; //藏文
//将本地环境设置为简体中文
setlocale(LC_ALL, "zh_CN");
//使用专门的 putwchar 输出宽字符
putwchar(a); putwchar(b); putwchar(c); putwchar(d);
putwchar(e); putwchar(f); putwchar(g); putwcharC语言学习笔记-入门整合篇(十万字长文)