C语言入门教程,C语言学习教程(第一部分:编程基础 )二

发布时间:2024年01月09日

九、进制详解:二进制、八进制和十六进制

我们平时使用的数字都是由 0~9 共十个数字组成的,例如 1、9、10、297、952 等,一个数字最多能表示九,如果要表示十、十一、二十九、一百等,就需要多个数字组合起来。

例如表示 5+8 的结果,一个数字不够,只能”进位“,用 13 来表示;这时”进一位“相当于十,”进两位“相当于二十。

因为逢十进一(满十进一),也因为只有 0~9 共十个数字,所以叫做十进制(Decimalism)。十进制是在人类社会发展过程中自然形成的,它符合人们的思维习惯,例如人类有十根手指,也有十根脚趾。

进制也就是进位制。进行加法运算时逢X进一(满X进一),进行减法运算时借一当X,这就是X进制,这种进制也就包含X个数字,基数为X。十进制有 0~9 共10个数字,基数为10,在加减法运算中,逢十进一,借一当十。

1、二进制

我们不妨将思维拓展一下,既然可以用 0~9 共十个数字来表示数值,那么也可以用0、1两个数字来表示数值,这就是二进制(Binary)。例如,数字?0、1、10、111、100、1000001 都是有效的二进制。

在计算机内部,数据都是以二进制的形式存储的,二进制是学习编程必须掌握的基础。本节我们先讲解二进制的概念,下节讲解数据在内存中的存储,让大家学以致用。

二进制加减法和十进制加减法的思想是类似的:

  • 对于十进制,进行加法运算时逢十进一,进行减法运算时借一当十;
  • 对于二进制,进行加法运算时逢二进一,进行减法运算时借一当二。

下面两张示意图详细演示了二进制加减法的运算过程。

1) 二进制加法:1+0=1、1+1=10、11+10=101、111+111=1110

图1:二进制加法示意图

2) 二进制减法:1-0=1、10-1=1、101-11=10、1100-111=101

图2:二进制减法示意图

2、八进制

除了二进制,C语言还会使用到八进制。

八进制有 0~7 共8个数字,基数为8,加法运算时逢八进一,减法运算时借一当八。例如,数字 0、1、5、7、14、733、67001、25430 都是有效的八进制。

下面两张图详细演示了八进制加减法的运算过程。

1) 八进制加法:3+4=7、5+6=13、75+42=137、2427+567=3216

图3:八进制加法示意图

2) 八进制减法:6-4=2、52-27=23、307-141=146、7430-1451=5757

图4:八进制减法示意图

3、十六进制

除了二进制和八进制,十六进制也经常使用,甚至比八进制还要频繁。

十六进制中,用A来表示10,B表示11,C表示12,D表示13,E表示14,F表示15,因此有 0~F 共16个数字,基数为16,加法运算时逢16进1,减法运算时借1当16。例如,数字 0、1、6、9、A、D、F、419、EA32、80A3、BC00 都是有效的十六进制。

注意,十六进制中的字母不区分大小写,ABCDEF 也可以写作 abcdef。

下面两张图详细演示了十六进制加减法的运算过程。

1) 十六进制加法:6+7=D、18+BA=D2、595+792=D27、2F87+F8A=3F11

图5:十六进制加法示意图

2) 十六进制减法:D-3=A、52-2F=23、E07-141=CC6、7CA0-1CB1=5FEF

图6:十六进制减法示意图


十、进制转换:二进制、八进制、十六进制、十进制之间的转换

对于基础薄弱的读者,本节的内容可能略显晦涩和枯燥,如果你觉得吃力,可以暂时跳过,基本不会影响后续章节的学习,等用到的时候再来阅读。

上节我们对二进制、八进制和十六进制进行了说明,本节重点讲解不同进制之间的转换,这在编程中经常会用到,尤其是C语言。

1、将二进制、八进制、十六进制转换为十进制

二进制、八进制和十六进制向十进制转换都非常容易,就是“按权相加”。所谓“权”,也即“位权”。

假设当前数字是 N 进制,那么:

  • 对于整数部分,从右往左看,第 i 位的位权等于Ni-1
  • 对于小数部分,恰好相反,要从左往右看,第 j 位的位权为N-j。

更加通俗的理解是,假设一个多位数(由多个数字组成的数)某位上的数字是 1,那么它所表示的数值大小就是该位的位权。

(1)整数部分

例如,将八进制数字?53627 转换成十进制:

53627 =?5×8^{4}?+ 3×8^{3}?+ 6×8^{2}+ 2×8^{1}?+ 7×8^{0}?=?22423(十进制)

从右往左看,第1位的位权为 8^{0}=1,第2位的位权为 8^{1}=8,第3位的位权为 8^{2}=64,第4位的位权为 8^{3}=512,第5位的位权为 8^{4}=4096 …… 第n位的位权就为 8^{n-1}。将各个位的数字乘以位权,然后再相加,就得到了十进制形式。

注意,这里我们需要以十进制形式来表示位权。

再如,将十六进制数字 9FA8C 转换成十进制:

9FA8C =?9×16^{4}?+ 15×16^{3}?+ 10×16^{2}?+ 8×16^{1}?+ 12×16^{0}?=?653964(十进制)

从右往左看,第1位的位权为 16^{0}=1,第2位的位权为 16^{1}=16,第3位的位权为 16^{2}=256,第4位的位权为 16^{3}=4096,第5位的位权为 16^{4}=65536 …… 第n位的位权就为 16n-1。将各个位的数字乘以位权,然后再相加,就得到了十进制形式。

将二进制数字转换成十进制也是类似的道理:

11010 = 1×2^{4}?+ 1×2^{3}?+ 0×2^{2}?+ 1×2^{1}?+ 0×2^{0}?= 26(十进制)

从右往左看,第1位的位权为 2^{0}=1,第2位的位权为 2^{1}=2,第3位的位权为 2^{2}=4,第4位的位权为 2^{3}=8,第5位的位权为2^{4}=16 …… 第n位的位权就为 2n-1。将各个位的数字乘以位权,然后再相加,就得到了十进制形式。

(2) 小数部分

例如,将八进制数字 423.5176 转换成十进制:

423.5176?=?4×8^{2}?+ 2×8^{1}?+ 3×8^{0}?+ 5×8^{-1}?+ 1×8^{-2}?+ 7×8^{-3}?+ 6×8^{-4}?=?275.65576171875(十进制)

小数部分和整数部分相反,要从左往右看,第1位的位权为 8^{-1}=1/8,第2位的位权为 8^{-2}=1/64,第3位的位权为8^{-3}=1/512,第4位的位权为 8^{-4}=1/4096?…… 第m位的位权就为 8-m。

再如,将二进制数字 1010.1101 转换成十进制:

1010.1101 = 1×2^{3}?+ 0×2^{2}?+ 1×2^{1}?+ 0×2^{0}?+ 1×2^{-1}?+ 1×2^{-2}?+ 0×2^{-3}?+ 1×2^{-4}?=?10.8125(十进制)

小数部分和整数部分相反,要从左往右看,第1位的位权为 2^{-1}=1/2,第2位的位权为2^{-2}=1/4,第3位的位权为 2^{-3}=1/8,第4位的位权为 2^{-4}=1/16?…… 第m位的位权就为 2^{-m}

更多转换成十进制的例子:

  • 二进制:1001 = 1×2^{3}+ 0×2^{2}?+ 0×2^{1}?+ 1×2^{0}?= 8 + 0 + 0 + 1 = 9(十进制)
  • 二进制:101.1001 = 1×2^{2}?+ 0×2^{1}?+ 1×2^{0}?+?1×2^{-1}?+ 0×2^{-2}?+ 0×2^{-3}?+ 1×2^{-4}?= 4 + 0 + 1 + 0.5 + 0 + 0 + 0.0625 = 5.5625(十进制)
  • 八进制:302 = 3×8^{2}?+ 0×8^{1}?+ 2×8^{0}?= 192 + 0 + 2 = 194(十进制)
  • 八进制:302.46 = 3×8^{2}?+ 0×8^{1}?+ 2×8^{0}?+ 4×8^{-1}?+ 6×8^{-2}?= 192 + 0 + 2 + 0.5 + 0.09375= 194.59375(十进制)
  • 十六进制:EA7 = 14×16^{2}+ 10×16^{1}?+ 7×16^{0}=?3751(十进制)

2、将十进制转换为二进制、八进制、十六进制

将十进制转换为其它进制时比较复杂,整数部分和小数部分的算法不一样,下面我们分别讲解。

(1)整数部分

十进制整数转换为 N 进制整数采用“除 N 取余,逆序排列”法。具体做法是:

  • 将 N 作为除数,用十进制整数除以 N,可以得到一个商和余数;
  • 保留余数,用商继续除以 N,又得到一个新的商和余数;
  • 仍然保留余数,用商继续除以 N,还会得到一个新的商和余数;
  • ……
  • 如此反复进行,每次都保留余数,用商接着除以 N,直到商为 0 时为止。

把先得到的余数作为 N 进制数的低位数字,后得到的余数作为 N 进制数的高位数字,依次排列起来,就得到了 N 进制数字。

下图演示了将十进制数字 36926 转换成八进制的过程:

从图中得知,十进制数字 36926 转换成八进制的结果为 110076。

下图演示了将十进制数字 42 转换成二进制的过程:

从图中得知,十进制数字 42 转换成二进制的结果为 101010。

(2)小数部分

十进制小数转换成 N 进制小数采用“乘 N 取整,顺序排列”法。具体做法是:

  • 用 N 乘以十进制小数,可以得到一个积,这个积包含了整数部分和小数部分;
  • 将积的整数部分取出,再用 N 乘以余下的小数部分,又得到一个新的积;
  • 再将积的整数部分取出,继续用 N 乘以余下的小数部分;
  • ……
  • 如此反复进行,每次都取出整数部分,用 N 接着乘以小数部分,直到积中的小数部分为 0,或者达到所要求的精度为止。

把取出的整数部分按顺序排列起来,先取出的整数作为 N 进制小数的高位数字,后取出的整数作为低位数字,这样就得到了 N 进制小数。

下图演示了将十进制小数 0.930908203125 转换成八进制小数的过程:


从图中得知,十进制小数 0.930908203125 转换成八进制小数的结果为 0.7345。

下图演示了将十进制小数 0.6875 转换成二进制小数的过程:


从图中得知,十进制小数 0.6875?转换成二进制小数的结果为 0.1011。

如果一个数字既包含了整数部分又包含了小数部分,那么将整数部分和小数部分开,分别按照上面的方法完成转换,然后再合并在一起即可。例如:

  • 十进制数字 36926.930908203125 转换成八进制的结果为?110076.7345;
  • 十进制数字 42.6875 转换成二进制的结果为?101010.1011。

下表列出了前 17 个十进制整数与二进制、八进制、十六进制的对应关系:

十进制012345678910111213141516
二进制0110111001011101111000100110101011110011011110111110000
八进制01234567101112131415161720
十六进制0123456789ABCDEF10

注意,十进制小数转换成其他进制小数时,结果有可能是一个无限位的小数。请看下面的例子:

  • 十进制 0.51 对应的二进制为?0.100000101000111101011100001010001111010111...,是一个循环小数;
  • 十进制 0.72 对应的二进制为 0.1011100001010001111010111000010100011110...,是一个循环小数;
  • 十进制 0.625 对应的二进制为 0.101,是一个有限小数。

3、二进制和八进制、十六进制的转换

其实,任何进制之间的转换都可以使用上面讲到的方法,只不过有时比较麻烦,所以一般针对不同的进制采取不同的方法。将二进制转换为八进制和十六进制时就有非常简洁的方法,反之亦然。

(1) 二进制整数和八进制整数之间的转换

二进制整数转换为八进制整数时,每三位二进制数字转换为一位八进制数字,运算的顺序是从低位向高位依次进行,高位不足三位用零补齐。下图演示了如何将二进制整数 1110111100 转换为八进制:

从图中可以看出,二进制整数 1110111100 转换为八进制的结果为 1674。

八进制整数转换为二进制整数时,思路是相反的,每一位八进制数字转换为三位二进制数字,运算的顺序也是从低位向高位依次进行。下图演示了如何将八进制整数 2743 转换为二进制:

从图中可以看出,八进制整数 2743 转换为二进制的结果为 10111100011。

(2)二进制整数和十六进制整数之间的转换

二进制整数转换为十六进制整数时,每四位二进制数字转换为一位十六进制数字,运算的顺序是从低位向高位依次进行,高位不足四位用零补齐。下图演示了如何将二进制整数 10 1101 0101 1100?转换为十六进制:

从图中可以看出,二进制整数 10 1101 0101 1100 转换为十六进制的结果为 2D5C。

十六进制整数转换为二进制整数时,思路是相反的,每一位十六进制数字转换为四位二进制数字,运算的顺序也是从低位向高位依次进行。下图演示了如何将十六进制整数 A5D6 转换为二进制:

从图中可以看出,十六进制整数 A5D6 转换为二进制的结果为 1010 0101 1101 0110。

在C语言编程中,二进制、八进制、十六进制之间几乎不会涉及小数的转换,所以这里我们只讲整数的转换,大家学以致用足以。另外,八进制和十六进制之间也极少直接转换,这里我们也不再讲解了。

3、总结

本节前面两部分讲到的转换方法是通用的,任何进制之间的转换都可以采用,只是有时比较麻烦而已。二进制和八进制、十六进制之间的转换有非常简洁的方法,所以没有采用前面的方法。


十一、数据在内存中的存储(二进制形式存储)

计算机要处理的信息是多种多样的,如数字、文字、符号、图形、音频、视频等,这些信息在人们的眼里是不同的。但对于计算机来说,它们在内存中都是一样的,都是以二进制的形式来表示。

要想学习编程,就必须了解二进制,它是计算机处理数据的基础。

内存条是一个非常精密的部件,包含了上亿个电子元器件,它们很小,达到了纳米级别。这些元器件,实际上就是电路;电路的电压会变化,要么是 0V,要么是 5V,只有这两种电压。5V 是通电,用1来表示,0V 是断电,用0来表示。所以,一个元器件有2种状态,0 或者 1。

我们通过电路来控制这些元器件的通断电,会得到很多0、1的组合。例如,8个元器件有 28=256 种不同的组合,16个元器件有 216=65536 种不同的组合。虽然一个元器件只能表示2个数值,但是多个结合起来就可以表示很多数值了。

我们可以给每一种组合赋予特定的含义,例如,可以分别用 1101000、00011100、11111111、00000000、01010101、10101010 来表示 C、语、言、中、文、网 这几个字,那么结合起来 1101000 00011100 11111111 00000000 01010101 10101010 就表示”C语言中文网“。

一般情况下我们不一个一个的使用元器件,而是将8个元器件看做一个单位,即使表示很小的数,例如 1,也需要8个,也就是 00000001。

1个元器件称为1比特(Bit)或1位,8个元器件称为1字节(Byte),那么16个元器件就是2Byte,32个就是4Byte,以此类推:

  • 8×1024个元器件就是1024Byte,简写为1KB;
  • 8×1024×1024个元器件就是1024KB,简写为1MB;
  • 8×1024×1024×1024个元器件就是1024MB,简写为1GB。

现在,你知道1GB的内存有多少个元器件了吧。我们通常所说的文件大小是多少 KB、多少 MB,就是这个意思。

单位换算:

  • 1Byte = 8 Bit
  • 1KB = 1024Byte = 210Byte
  • 1MB = 1024KB = 220Byte
  • 1GB = 1024MB = 230Byte
  • 1TB = 1024GB = 240Byte
  • 1PB = 1024TB = 250Byte
  • 1EB = 1024PB = 260Byte

我们平时使用计算机时,通常只会设计到 KB、MB、GB、TB 这几个单位,PB 和 EB 这两个高级单位一般在大数据处理过程中才会用到。

你看,在内存中没有abc这样的字符,也没有gif、jpg这样的图片,只有0和1两个数字,计算机也只认识0和1。所以,计算机使用二进制,而不是我们熟悉的十进制,写入内存中的数据,都会被转换成0和1的组合。

我们将在《C语言调试》中的《查看、修改运行时的内存》一节教大家如何操作C语言程序的内存。

1、程序员的幽默

为了加深印象,最后给大家看个笑话。

程序员A:“哥们儿,最近手头紧,借点钱?”

程序员B:“成啊,要多少?”

程序员A:“一千行不?”

程序员B:“咱俩谁跟谁!给你凑个整,1024,拿去吧。”

你看懂这个笑话了吗?请选出正确答案。

A) 因为他同情程序员A,多给他24块
B) 这个程序员不会数数,可能是太穷饿晕了
C) 这个程序员故意的,因为他独裁的老婆规定1024是整数
D) 就像100是10的整数次方一样,1024是2的整数次方,对于程序员就是整数


十二、载入内存,让程序运行起来

如果你的电脑上安装了QQ,你希望和好友聊天,会双击QQ图标,打开QQ软件,输入账号和密码,然后登录就可以了。

那么,QQ是怎么运行起来的呢?

首先,有一点你要明确,你安装的QQ软件是保存在硬盘中的。

双击QQ图标,操作系统就会知道你要运行这个软件,它会在硬盘中找到你安装的QQ软件,将数据(安装的软件本质上就是很多数据的集合)复制到内存。对!就是复制到内存!QQ不是在硬盘中运行的,而是在内存中运行的。

为什么呢?因为内存的读写速度比硬盘快很多。

对于读写速度,内存 > 固态硬盘 > 机械硬盘。机械硬盘是靠电机带动盘片转动来读写数据的,而内存条通过电路来读写数据,电机的转速肯定没有电的传输速度(几乎是光速)快。虽然固态硬盘也是通过电路来读写数据,但是因为与内存的控制方式不一样,速度也不及内存。

所以,不管是运行QQ还是编辑Word文档,都是先将硬盘上的数据复制到内存,才能让CPU来处理,这个过程就叫作载入内存(Load into Memory)。完成这个过程需要一个特殊的程序(软件),这个程序就叫做加载器(Loader)

CPU直接与内存打交道,它会读取内存中的数据进行处理,并将结果保存到内存。如果需要保存到硬盘,才会将内存中的数据复制到硬盘。

例如,打开Word文档,输入一些文字,虽然我们看到的不一样了,但是硬盘中的文档没有改变,新增的文字暂时保存到了内存,Ctrl+S才会保存到硬盘。因为内存断电后会丢失数据,所以如果你编辑完Word文档忘记保存就关机了,那么你将永远无法找回这些内容。

1、虚拟内存

如果我们运行的程序较多,占用的空间就会超过内存(内存条)容量。例如计算机的内存容量为2G,却运行着10个程序,这10个程序共占用3G的空间,也就意味着需要从硬盘复制 3G 的数据到内存,这显然是不可能的。

操作系统(Operating System,简称 OS)为我们解决了这个问题:当程序运行需要的空间大于内存容量时,会将内存中暂时不用的数据再写回硬盘;需要这些数据时再从硬盘中读取,并将另外一部分不用的数据写入硬盘。这样,硬盘中就会有一部分空间用来存放内存中暂时不用的数据。这一部分空间就叫做虚拟内存(Virtual Memory)

3G - 2G = 1G,上面的情况需要在硬盘上分配 1G 的虚拟内存。

硬盘的读写速度比内存慢很多,反复交换数据会消耗很多时间,所以如果你的内存太小,会严重影响计算机的运行速度,甚至会出现”卡死“现象,即使CPU强劲,也不会有大的改观。如果经济条件允许,建议将内存升级为 4G,在 win7、win8、win10 下运行软件就会比较流畅了。

总结:CPU直接从内存中读取数据,处理完成后将结果再写入内存。

CPU、内存、硬盘和主板的关系

图1:CPU、内存、硬盘和主板的关系


十三、ASCII编码,将英文存储到计算机

前面我们已经讲到,计算机是以二进制的形式来存储数据的,它只认识 0 和 1 两个数字,我们在屏幕上看到的文字,在存储之前都被转换成了二进制(0和1序列),在显示时也要根据二进制找到对应的字符。

可想而知,特定的文字必然对应着固定的二进制,否则在转换时将发生混乱。那么,怎样将文字与二进制对应起来呢?这就需要有一套规范,计算机公司和软件开发者都必须遵守,这样的一套规范就称为字符集(Character Set)或者字符编码(Character Encoding)。

严格来说,字符集和字符编码不是一个概念,字符集定义了文字和二进制的对应关系,为字符分配了唯一的编号,而字符编码规定了如何将文字的编号存储到计算机中。我们暂时先不讨论这些细节,姑且认为它们是一个概念,本节中我也混用了这两个概念,未做区分。

字符集为每个字符分配一个唯一的编号,类似于学生的学号,通过编号就能够找到对应的字符。

可以将字符集理解成一个很大的表格,它列出了所有字符和二进制的对应关系,计算机显示文字或者存储文字,就是一个查表的过程。

在计算机逐步发展的过程中,先后出现了几十种甚至上百种字符集,有些还在使用,有些已经淹没在了历史的长河中,本节我们要讲解的是一种专门针对英文的字符集——ASCII编码。

1、拉丁字母(开胃小菜)

在正式介绍 ASCII 编码之前,我们先来说说什么是拉丁字母。估计也有不少读者和我一样,对于拉丁字母、英文字母和汉语拼音中的字母的关系不是很清楚。

拉丁字母也叫罗马字母,它源自希腊字母,是当今世界上使用最广的字母系统。基本的拉丁字母就是我们经常见到的 ABCD 等26个英文字母。

拉丁字母、阿拉伯字母、斯拉夫字母(西里尔字母)被称为世界三大字母体系。

拉丁字母原先是欧洲人使用的,后来由于欧洲殖民主义,导致这套字母体系在全球范围内开始流行,美洲、非洲、澳洲、亚洲都没有逃过西方文化的影响。中国也是,我们现在使用的拼音其实就是拉丁字母,是不折不扣的舶来品。

后来,很多国家对 26 个基本的拉丁字母进行了扩展,以适应本地的语言文化。最常见的扩展方式就是加上变音符号,例如汉语拼音中的ü,就是在u的基础上加上两个小点演化而来;再如,áà就是在a的上面标上音调。

总起来说:

  • 基本拉丁字母就是 26 个英文字母;
  • 扩展拉丁字母就是在基本的 26 个英文字母的基础上添加变音符号、横线、斜线等演化而来,每个国家都不一样。

2、ASCII 编码

计算机是美国人发明的,他们首先要考虑的问题是,如何将二进制和英文字母(也就是拉丁文)对应起来。

当时,各个厂家或者公司都有自己的做法,编码规则并不统一,这给不同计算机之间的数据交换带来不小的麻烦。但是相对来说,能够得到普遍认可的有 IBM 发明的 EBCDIC 和此处要谈的 ASCII。

我们先说 ASCII。ASCII 是“American Standard Code for Information Interchange”的缩写,翻译过来是“美国信息交换标准代码”。看这个名字就知道,这套编码是美国人给自己设计的,他们并没有考虑欧洲那些扩展的拉丁字母,也没有考虑韩语和日语,我大中华几万个汉字更是不可能被重视。

但这也无可厚非,美国人自己发明的计算机,当然要先解决自己的问题

ASCII 的标准版本于 1967 年第一次发布,最后一次更新则是在 1986 年,迄今为止共收录了 128 个字符,包含了基本的拉丁字母(英文字母)、阿拉伯数字(也就是 1234567890)、标点符号(,.!等)、特殊符号(@#$%^&)以及一些具有控制功能的字符(往往不会显示出来)。

在 ASCII 编码中,大写字母、小写字母和阿拉伯数字都是连续分布的(见下表),这给程序设计带来了很大的方便。例如要判断一个字符是否是大写字母,就可以判断该字符的 ASCII 编码值是否在 65~90 的范围内。

EBCDIC 编码正好相反,它的英文字母不是连续排列的,中间出现了多次断续,给编程带来了一些困难。现在连 IBM 自己也不使用?EBCDIC 了,转而使用更加优秀的 ASCII。

ASCII 编码已经成了计算机的通用标准,没有人再使用?EBCDIC 编码了,它已经消失在历史的长河中了。

3、ASCII 编码一览表

标准 ASCII 编码共收录了 128 个字符,其中包含了 33 个控制字符(具有某些特殊功能但是无法显示的字符)和 95 个可显示字符。

ASCII 编码一览表(淡黄色背景为控制字符,白色背景为可显示字符)

二进制十进制十六进制字符/缩写解释
00000000000NUL (NULL)空字符
00000001101SOH (Start Of Headling)标题开始
00000010202STX (Start Of Text)正文开始
00000011303ETX (End Of Text)正文结束
00000100404EOT (End Of Transmission)传输结束
00000101505ENQ (Enquiry)请求
00000110606ACK (Acknowledge)回应/响应/收到通知
00000111707BEL (Bell)响铃
00001000808BS (Backspace)退格
00001001909HT (Horizontal Tab)水平制表符
00001010100ALF/NL(Line Feed/New Line)换行键
00001011110BVT (Vertical Tab)垂直制表符
00001100120CFF/NP (Form Feed/New Page)换页键
00001101130DCR (Carriage Return)回车键
00001110140ESO (Shift Out)不用切换
00001111150FSI (Shift In)启用切换
000100001610DLE (Data Link Escape)数据链路转义
000100011711DC1/XON
(Device Control 1/Transmission On)
设备控制1/传输开始
000100101812DC2 (Device Control 2)设备控制2
000100111913DC3/XOFF
(Device Control 3/Transmission Off)
设备控制3/传输中断
000101002014DC4 (Device Control 4)设备控制4
000101012115NAK (Negative Acknowledge)无响应/非正常响应/拒绝接收
000101102216SYN (Synchronous Idle)同步空闲
000101112317ETB (End of Transmission Block)传输块结束/块传输终止
000110002418CAN (Cancel)取消
000110012519EM (End of Medium)已到介质末端/介质存储已满/介质中断
00011010261ASUB (Substitute)替补/替换
00011011271BESC (Escape)逃离/取消
00011100281CFS (File Separator)文件分割符
00011101291DGS (Group Separator)组分隔符/分组符
00011110301ERS (Record Separator)记录分离符
00011111311FUS (Unit Separator)单元分隔符
001000003220(Space)空格
001000013321!
001000103422"
001000113523#
001001003624$
001001013725%
001001103826&
001001113927'
001010004028(
001010014129)
00101010422A*
00101011432B+
00101100442C,
00101101452D-
00101110462E.
00101111472F/
0011000048300
0011000149311
0011001050322
0011001151333
0011010052344
0011010153355
0011011054366
0011011155377
0011100056388
0011100157399
00111010583A:
00111011593B;
00111100603C<
00111101613D=
00111110623E>
00111111633F?
010000006440@
010000016541A
010000106642B
010000116743C
010001006844D
010001016945E
010001107046F
010001117147G
010010007248H
010010017349I
01001010744AJ
01001011754BK
01001100764CL
01001101774DM
01001110784EN
01001111794FO
010100008050P
010100018151Q
010100108252R
010100118353S
010101008454T
010101018555U
010101108656V
010101118757W
010110008858X
010110018959Y
01011010905AZ
01011011915B[
01011100925C\
01011101935D]
01011110945E^
01011111955F_
011000009660`
011000019761a
011000109862b
011000119963c
0110010010064d
0110010110165e
0110011010266f
0110011110367g
0110100010468h
0110100110569i
011010101066Aj
011010111076Bk
011011001086Cl
011011011096Dm
011011101106En
011011111116Fo
0111000011270p
0111000111371q
0111001011472r
0111001111573s
0111010011674t
0111010111775u
0111011011876v
0111011111977w
0111100012078x
0111100112179y
011110101227Az
011110111237B{
011111001247C|
011111011257D}
011111101267E~
011111111277FDEL (Delete)删除

上表列出的是标准的 ASCII 编码,它共收录了 128 个字符,用一个字节中较低的 7 个比特位(Bit)足以表示(27?= 128),所以还会空闲下一个比特位,它就被浪费了。

如果您还想了解每个控制字符的含义,请转到:ASCII码一览表,ASCII码对照表

4、ASCII 编码和C语言

稍微有点C语言基本功的读者可能认为C语言使用的就是 ASCII 编码,字符在存储时会转换成对应的 ASCII 码值,在读取时也是根据 ASCII 码找到对应的字符。这句话是错误的,严格来说,你可能被大学老师和C语言教材给误导了。

C语言有时候使用 ASCII 编码,有时候却不是,而是使用后面两节中即将讲到的 GBK 编码和 Unicode 字符集,我们将在《C语言到底使用什么编码?谁说C语言使用ASCII码,真是荒谬!》一节中展开讲解。


十四、GB2312编码和GBK编码,将中文存储到计算机

计算机是一种改变世界的发明,很快就从美国传到了全球各地,得到了所有国家的认可,成为了一种不可替代的工具。计算机在广泛流行的过程中遇到的一个棘手问题就是字符编码,计算机是美国人发明的,它使用的是 ASCII 编码,只能显示英文字符,对汉语、韩语、日语、法语、德语等其它国家的字符无能为力。

为了让本国公民也能使用上计算机,各个国家(地区)也开始效仿 ASCII,开发了自己的字符编码。这些字符编码和 ASCII 一样,只考虑本国的语言文化,不兼容其它国家的文字。这样做的后果就是,一台计算机上必须安装多套字符编码,否则就不能正确地跨国传递数据,例如在中国编写的文本文件,拿到日本的电脑上就无法打开,或者打开后是一堆乱码。

下表列出了常见的字符编码:

字符编码说明
ISO/IEC 8859欧洲字符集,支持丹麦语、荷兰语、德语、意大利语、拉丁语、挪威语、葡萄牙语、西班牙语,瑞典语等,1987 年首次发布。

ASCII 编码只包含了基本的拉丁字母,没有包含欧洲很多国家所用到的一些扩展的拉丁字母,比如一些重音字母,带音标的字母等,ISO/IEC 8859 主要是在 ASCII 的基础上增加了这些衍生的拉丁字母。
Shift_Jis日语字符集,包含了全角及半角拉丁字母、平假名、片假名、符号及日语汉字,1978 年首次发布。
Big5繁体中文字符集,1984 年发布,通行于台湾、香港等地区,收录了 13053 个中文字、408个普通字符以及 33 个控制字符。
GB2312简体中文字符集,1980 年发布,共收录了 6763 个汉字,其中一级汉字 3755 个,二级汉字 3008 个;同时收录了包括拉丁字母、希腊字母、日文平假名及片假名字母、俄语西里尔字母在内的 682 个字符。
GBK中文字符集,是在 GB2312 的基础上进行的扩展,1995 年发布。

GB2312 收录的汉字虽然覆盖了中国大陆 99.75% 的使用频率,满足了基本的输入输出要求,但是对于人名、古汉语等方面出现的罕用字(例如朱镕基的“镕”就没有被 GB2312 收录),GB2312 并不能处理,所以后来又对 GBK 进行了一次扩展,形成了一种新的字符集,就是 GBK。

GBK 共收录了?21886 个汉字和图形符号,包括 GB2312 中的全部汉字、非汉字符号,以及 BIG5 中的全部繁体字,还有一些生僻字。
GB18030中文字符集,是对 GBK 和 GB2312 的又一次扩展,2000 年发布。

GB18030?共收录?70244 个汉字,支持中国国内少数民族的文字,以及日语韩语中的汉字。

由于 ASCII 先入为主,已经使用了十来年了,现有的很多软件和文档都是基于 ASCII 的,所以后来的这些字符编码都是在 ASCII 基础上进行的扩展,它们都兼容 ASCII,以支持既有的软件和文档。

兼容 ASCII 的含义是,原来 ASCII 中已经包含的字符,在国家编码(地区编码)中的位置不变(也就是编码值不变),只是在这些字符的后面增添了新的字符。

1、如何存储

标准 ASCII 编码共包含了 128 个字符,用一个字节就足以存储(实际上是用一个字节中较低的 7 位来存储),而日文、中文、韩文等包含的字符非常多,有成千上万个,一个字节肯定是不够的(一个字节最多存储 2^{8}?= 256 个字符),所以要进行扩展,用两个、三个甚至四个字节来表示。

在制定字符编码时还要考虑内存利用率的问题。我们经常使用的字符,其编码值一般都比较小,例如字母和数字都是 ASCII 编码,其编码值不会超过 127,用一个字节存储足以,如果硬要用多个字节存储,就会浪费很多内存空间。

为了达到「既能存储本国字符,又能节省内存」的目的,Shift-Jis、Big5、GB2312 等都采用变长的编码方式:

  • 对于原来的 ASCII 编码部分,用一个字节存储足以;
  • 对于本国的常用字符(例如汉字、标点符号等),一般用两个字节存储;
  • 对于偏远地区,或者极少使用的字符(例如藏文、蒙古文等),才使用三个甚至四个字节存储。

总起来说,越常用的字符占用的内存越少,越罕见的字符占用的内存越多。

2、具体讲一下中文编码方案

GB2312 --> GBK --> GB18030 是中文编码的三套方案,出现的时间从早到晚,收录的字符数目依次增加,并且向下兼容。GB2312 和 GBK 收录的字符数目较少,用 1~2个字节存储;GB18030 收录的字符最多,用1、2、4 个字节存储。

1) 从整体上讲,GB2312 和 GBK 的编码方式一致,具体为:

  • 对于 ASCII 字符,使用一个字节存储,并且该字节的最高位是 0,这和 ASCII 编码是一致的,所以说 GB2312 完全兼容 ASCII。
  • 对于中国的字符,使用两个字节存储,并且规定每个字节的最高位都是 1。

例如对于字母A,它在内存中存储为?01000001;对于汉字,它在内存中存储为?11010110??11010000。由于单字节和双字节的最高位不一样,所以字符处理软件很容易区分一个字符到底用了几个字节。

2) GB18030 为了容纳更多的字符,并且要区分两个字节和四个字节,所以修改了编码方案,具体为:

  • 对于 ASCII 字符,使用一个字节存储,并且该字节的最高位是 0,这和 ASCII、GB2312、GBK 编码是一致的。
  • 对于常用的中文字符,使用两个字节存储,并且规定第一个字节的最高位是 1,第二个字节的高位最多只能有一个连续的 0(第二个字节的最高位可以是 1 也可以是 0,但是当它是 0 时,次高位就不能是 0 了)。注意对比 GB2312 和 GBK,它们要求两个字节的最高位为都必须为 1。
  • 对于罕见的字符,使用四个字节存储,并且规定第一个和第三个字节的最高位是 1,第二个和第四个字节的高位必须有两个连续的 0。

例如对于字母A,它在内存中存储为?01000001;对于汉字,它在内存中存储为?11010110??11010000;对于藏文???,它在内存中的存储为?10000001??00110010??11101111??00110000。

字符处理软件在处理文本时,从左往右依次扫描每个字节:

  • 如果遇到的字节的最高位是 0,那么就会断定该字符只占用了一个字节;
  • 如果遇到的字节的最高位是 1,那么该字符可能占用了两个字节,也可能占用了四个字节,不能妄下断论,所以还要继续往后扫描:
    • 如果第二个字节的高位有两个连续的 0,那么就会断定该字符占用了四个字节;
    • 如果第二个字节的高位没有连续的 0,那么就会断定该字符占用了两个字节。

可见,当字符占用两个或者四个字节时,GB18030 编码要检测两次,处理效率比 GB2312 和 GBK 都低。

3、GBK 编码最牛掰

GBK 于 1995 年发布,这一年也是互联网爆发的元年,国人使用电脑越来越多,也许是 GBK 这头猪正好站在风口上,它就飞起来了,后来的中文版 Windows 都将 GBK 作为默认的中文编码方案。

注意,这里我说 GBK 是默认的中文编码方案,并没有说 Windows 默认支持 GBK。Windows 在内核层面使用的是 Unicode 字符集(严格来说是 UTF-16 编码),但是它也给用户留出了选择的余地,如果用户不希望使用 Unicode,而是希望使用中文编码方案,那么这个时候 Windows 默认使用 GBK(当然,你可以选择使用 GB2312 或者?GB18030,不过一般没有这个必要)。

下节我们会讲解 Unicode 字符集和 UTF-16 编码方案。

实际上,中文版 Windows 下的很多程序默认使用的就是 GBK 编码,例如用记事本程序创建一个 txt 文档、在 cmd 或者控制台程序(最常见的C语言程序)中显示汉字、用 Visual Studio 创建的源文件等,使用的都是 GBK 编码。

可以说,GBK 编码在中文版的 Windows 中大行其道。


十五、Unicode字符集,将全世界的文字存储到计算机

ASCII、GB2312、GBK、Shift_Jis、ISO/IEC 8859 等地区编码都是各个国家为了自己的语言文化开发的,不具有通用性,在一种编码下开发的软件或者编写的文档,拿到另一种编码下就会失效,必须提前使用程序转码,非常麻烦。

人们迫切希望有一种编码能够统一世界各地的字符,计算机只要安装了这一种字编码,就能支持使用世界上所有的文字,再也不会出现乱码,再也不需要转码了,这对计算机的数据传递来说是多么的方便呀!

就在这种呼吁下,Unicode 诞生了。Unicode 也称为统一码、万国码;看名字就知道,Unicode 希望统一所有国家的字符编码。

Unicode 于 1994 年正式公布第一个版本,现在的规模可以容纳 100 多万个符号,是一个很大的集合。

有兴趣的读取可以转到? Unicode 符号表 - 所有 Unicode 字符及其代码都在一页上 (???) SYMBL?查看 Unicode 包含的所有字符,以及各个国家的字符是如何分布的。

这个网站不太稳定,随时可能无法访问,不要问我为什么,访问不了也不要找我,没有比它更好的网站了。

Windows、Linux、Mac OS 等常见操作系统都已经从底层(内核层面)开始支持 Unicode,大部分的网页和软件也使用 Unicode,Unicode 是大势所趋。

不过由于历史原因,目前的计算机仍然安装了 ASCII 编码以及 GB2312、GBK、Big5、Shift-JIS 等地区编码,以支持不使用 Unicode 的软件或者文档。内核在处理字符时,一般会将地区编码先转换为 Unicode,再进行下一步处理。

1、Unicode 字符集是如何存储的

本节我们多次说 Unicode 是一套字符集,而不是一套字符编码,它们之间究竟有什么区别呢?

严格来说,字符集和字符编码不是一个概念:

  • 字符集定义了字符和二进制的对应关系,为每个字符分配了唯一的编号。可以将字符集理解成一个很大的表格,它列出了所有字符和二进制的对应关系,计算机显示文字或者存储文字,就是一个查表的过程。
  • 而字符编码规定了如何将字符的编号存储到计算机中。如果使用了类似 GB2312 和 GBK 的变长存储方案(不同的字符占用的字节数不一样),那么为了区分一个字符到底使用了几个字节,就不能将字符的编号直接存储到计算机中,字符编号在存储之前必须要经过转换,在读取时还要再逆向转换一次,这套转换方案就叫做字符编码。

有的字符集在制定时就考虑到了编码的问题,是和编码结合在一起的,例如 ASCII、GB2312、GBK、BIG5 等,所以无论称作字符集还是字符编码都无所谓,也不好区分两者的概念。而有的字符集只管制定字符的编号,至于怎么存储,那是字符编码的事情,Unicode 就是一个典型的例子,它只是定义了全球文字的唯一编号,我们还需要 UTF-8、UTF-16、UTF-32 这几种编码方案将 Unicode 存储到计算机中。

Unicode 可以使用的编码方案有三种,分别是:

  • UTF-8:一种变长的编码方案,使用 1~6 个字节来存储;
  • UTF-32:一种固定长度的编码方案,不管字符编号大小,始终使用 4 个字节来存储;
  • UTF-16:介于 UTF-8 和 UTF-32 之间,使用 2 个或者 4 个字节来存储,长度既固定又可变。

UTF 是 Unicode Transformation Format 的缩写,意思是“Unicode转换格式”,后面的数字表明至少使用多少个比特位(Bit)来存储字符。

(1) UTF-8

UTF-8 的编码规则很简单:

  • 如果只有一个字节,那么最高的比特位为 0,这样可以兼容 ASCII;
  • 如果有多个字节,那么第一个字节从最高位开始,连续有几个比特位的值为 1,就使用几个字节编码,剩下的字节均以 10 开头。

具体的表现形式为:

  • 0xxxxxxx:单字节编码形式,这和 ASCII 编码完全一样,因此 UTF-8 是兼容 ASCII 的;
  • 110xxxxx 10xxxxxx:双字节编码形式(第一个字节有两个连续的 1);
  • 1110xxxx 10xxxxxx 10xxxxxx:三字节编码形式(第一个字节有三个连续的 1);
  • 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx:四字节编码形式(第一个字节有四个连续的 1)。

xxx 就用来存储 Unicode 中的字符编号。

下面是一些字符的 UTF-8 编码实例(绿色部分表示本来的 Unicode 编号):

字符字母N符号?中文?
Unicode 编号(二进制)010011101110011000101110 11101100
Unicode 编号(十六进制)4EE62E EC
UTF-8 编码(二进制)0100111011000011?1010011011100010?10111011?10101100
UTF-8 编码(十六进制)4EC3 A6E2 BB AC

对于常用的字符,它的 Unicode 编号范围是 0 ~ FFFF,用 1~3 个字节足以存储,只有及其罕见,或者只有少数地区使用的字符才需要 4~6个字节存储。

(2)UTF-32

UTF-32 是固定长度的编码,始终占用 4 个字节,足以容纳所有的 Unicode 字符,所以直接存储 Unicode 编号即可,不需要任何编码转换。浪费了空间,提高了效率。

(3) UTF-16

UFT-16 比较奇葩,它使用 2 个或者 4 个字节来存储。

对于 Unicode 编号范围在 0 ~ FFFF 之间的字符,UTF-16 使用两个字节存储,并且直接存储 Unicode 编号,不用进行编码转换,这跟 UTF-32 非常类似。

对于 Unicode 编号范围在 10000~10FFFF 之间的字符,UTF-16 使用四个字节存储,具体来说就是:将字符编号的所有比特位分成两部分,较高的一些比特位用一个值介于 D800~DBFF 之间的双字节存储,较低的一些比特位(剩下的比特位)用一个值介于?DC00~DFFF 之间的双字节存储。

如果你不理解什么意思,请看下面的表格:

Unicode 编号范围
(十六进制)
具体的 Unicode 编号
(二进制)
UTF-16 编码编码后的
字节数
0000 0000 ~ 0000 FFFFxxxxxxxx xxxxxxxxxxxxxxxx xxxxxxxx2
0001 0000---0010 FFFFyyyy yyyy yyxx xxxx xxxx110110yy yyyyyyyy 110111xx xxxxxxxx4

位于 D800~0xDFFF 之间的 Unicode 编码是特别为四字节的 UTF-16 编码预留的,所以不应该在这个范围内指定任何字符。如果你真的去查看 Unicode 字符集,会发现这个区间内确实没有收录任何字符。

UTF-16 要求在制定 Unicode 字符集时必须考虑到编码问题,所以真正的 Unicode 字符集也不是随意编排字符的。

2、对比以上三种编码方案

首先,只有 UTF-8 兼容 ASCII,UTF-32 和 UTF-16 都不兼容 ASCII,因为它们没有单字节编码。

1) UTF-8 使用尽量少的字节来存储一个字符,不但能够节省存储空间,而且在网络传输时也能节省流量,所以很多纯文本类型的文件(例如各种编程语言的源文件、各种日志文件和配置文件等)以及绝大多数的网页(例如百度、新浪、163等)都采用 UTF-8 编码。

UTF-8 的缺点是效率低,不但在存储和读取时都要经过转换,而且在处理字符串时也非常麻烦。例如,要在一个 UTF-8 编码的字符串中找到第 10 个字符,就得从头开始一个一个地检索字符,这是一个很耗时的过程,因为 UTF-8 编码的字符串中每个字符占用的字节数不一样,如果不从头遍历每个字符,就不知道第 10 个字符位于第几个字节处,就无法定位。

不过,随着算法的逐年精进,UTF-8 字符串的定位效率也越来越高了,往往不再是槽点了。

2) UTF-32 是“以空间换效率”,正好弥补了 UTF-8 的缺点,UTF-32 的优势就是效率高:UTF-32 在存储和读取字符时不需要任何转换,在处理字符串时也能最快速地定位字符。例如,在一个 UTF-32 编码的字符串中查找第 10 个字符,很容易计算出它位于第 37 个字节处,直接获取就行,不用再逐个遍历字符了,没有比这更快的定位字符的方法了。

但是,UTF-32 的缺点也很明显,就是太占用存储空间了,在网络传输时也会消耗很多流量。我们平常使用的字符编码值一般都比较小,用一两个字节存储足以,用四个字节简直是暴殄天物,甚至说是不能容忍的,所以 UTF-32 在应用上不如 UTF-8 和 UTF-16 广泛。

3) UTF-16 可以看做是 UTF-8 和 UTF-32 的折中方案,它平衡了存储空间和处理效率的矛盾。对于常用的字符,用两个字节存储足以,这个时候 UTF-16 是不需要转换的,直接存储字符的编码值即可。

Windows 内核、.NET Framework、Cocoa、Java String 内部采用的都是 UTF-16 编码。UTF-16 是幕后的功臣,我们在编辑源代码和文档时都是站在前台,所以一般感受不到,其实很多文本在后台处理时都已经转换成了 UTF-16 编码。

不过,UNIX 家族的操作系统(Linux、Mac OS、iOS 等)内核都采用 UTF-8 编码,我们就不去争论谁好谁坏了。

3、宽字符和窄字符(多字节字符)

有的编码方式采用 1~n 个字节存储,是变长的,例如 UTF-8、GB2312、GBK 等;如果一个字符使用了这种编码方式,我们就将它称为多字节字符,或者窄字符。

有的编码方式是固定长度的,不管字符编号大小,始终采用 n 个字节存储,例如 UTF-32、UTF-16 等;如果一个字符使用了这种编码方式,我们就将它称为宽字符。

Unicode 字符集可以使用窄字符的方式存储,也可以使用宽字符的方式存储;GB2312、GBK、Shift-JIS 等国家编码一般都使用窄字符的方式存储;ASCII 只有一个字节,无所谓窄字符和宽字符。

说了这么多,C语言到底使用哪种字编码方式呢?其实这个问题有点复杂,我们将在《C语言到底使用什么编码?谁说C语言使用ASCII码,真是荒谬!》一节中展开讲解。


十六、程序员的薪水和发展方向大全

如果你是一名初学者,对编程非常感兴趣,想成为一名合格的程序员,那么这篇文章就是为你写的。

从初学者成长为一名合格的程序员需要一段时间的磨练,每个人付出的时间和做事的效率不同,我不好评判你需要多长时间才能学有所成。站在求职的角度,能开发出实用的软件、网站、APP等才叫学有所成。

1、程序员的发展方向

计算机涉及的知识非常广泛,不可能在短时间内全部学完,即使公司的CTO也不可能样样精通,初学者要选定一个方向,不要想着把客户端软件、网站、APP都开发出来,这在短时间内是不现实的。相信我,你不是神!

(1) Windows开发

主要是开发客户端(PC机上的软件),如QQ、迅雷、360、Chrome 等。

能够进行 Windows 客户端开发的编程语言有多种,包括 C/C++、C#、VB、Java、Delphi、易语言等。这意味着,Windows 开发有多种学习路线,大家任选其一。不过,公司一般使用 C/C++、C#、Java,自己编写小工具也可以使用 VB、Delphi、易语言。

(2) 游戏开发(游戏客户端开发)

需要你有C/C++基础,再学习 Unreal(虚幻)、Frostbite(寒霜)、CryEngine(CE)等游戏引擎。如果你希望了解游戏引擎原理,让自己更加优秀,那么还需要学习图形库(例如 DirectX、OpenGL)和计算机图形学。

(3) Linux 开发/游戏后台开发

需要在 C/C++ 的基础上再学习 Linux 操作系统,主要包括 Linux 基本操作、Shell、文件系统、进程线程、内存、Socket 通信、内核等,甚至还需要与算法、Qt 等相结合。

另外,也可以使用Go语言进行 Linux 开发,Go语言在全球已经有相当多的应用案例了。

游戏的后台服务器大部分也是基于 Linux的,也会用到以上技能。

(4)单片机/嵌入式

单片机/嵌入式是软件和硬件的结合,不仅要会写代码,还要了解硬件,所以入门门槛比较高,知识也比较庞杂,学习时间长。选择该方向最好有数字电路、模拟电路和汇编的基础,非常适合电子信息工程专业的同学。

这个方向的同学,大部分去了中兴、华为等以生产电子产品为主的公司,工资虽然没有一般的程序员高,但也不错。

(5) 算法

NB的程序员都在搞这些,一般不注重编程语言,而是侧重解决问题的方法和效率。工资比普通的程序员略高。

(6) 网站开发

也称 Web 开发,分为前端和后台。

后台主要负责服务器端的编程,除了需要学习 Java、PHP、Python 等编程语言,还需要学习 MySQL、MongoDB、Oracle 等数据库。

前端主要负责网页界面的设计以及特效的实现,需要学习HTML、CSS、JavaScript等。

JavaScript 本来只能用于 Web 前端,它可以实现一些特效,或者和服务器通信,后来有人把 JavaScript 移植到了服务器上,并起名 Node.js,这样 JavaScript 也能进行 Web 后台开发了。

也就是说,只要需要学习 JavaScript 一门语言,就可以搞定网站的前端和后台,成为全栈工程师。

(7) 移动开发

包括IOS和Android,你可以开发APP,也可以开发游戏,需要学习Java(针对Android)、Objective-C(针对IOS)、Swift(针对IOS)等。

(8)测试(QA)

一款产品问世需要大量的测试才能投放市场,QA(Quality Assurance,译为“品质保证”)人员就是为程序员把关的,如果程序员的作品不符合产品需求或者Bug太多,QA有权驳回,这时就会影响程序员的绩效。QA不但要能看懂代码(大概理解什么意思),还要掌握一定的测试技巧,更重要的是心思缜密,有耐心有毅力,女生比例很高。

(9)大数据

需要掌握 Java、Python、R 或 Scala 编程语言,并学习 Linux 操作系统、Linux 集群搭建、数据库等,Hadoop、Spark、Hive 等大数据框架的学习是重点内容。

(10)人工智能/机器学习/深度学习

除了需要掌握 Python、R 或 Java 编程语言,还需要学习数学(大都集中在微积分、线性代数、概率与统计几个领域)和算法(例如逻辑回归、深度神经网络、线性回归、K均值、协同过滤等),这是重点内容。

2、程序员的待遇

IT行业的待遇比很多行业要高,程序员尤为突出,刚刚毕业的大学生,进入百度、腾讯、阿里巴巴等这些大企业,年薪一般在15万以上,经验丰富的可以拿到20多万,30万的就是神一样的存在,有,但是极少。这是第一梯队,一般重点大学的毕业生才能进入,怎么也得是个一本吧。

拿到融资的创业公司、规模不大的公司、一些国企等给的待遇也不错,年薪也可以超过10万。

很多小公司,老板一个人说了算,也没有融资,待遇一般都不会高,一个月几千块钱。这样的公司招人难,进入的门槛低,对学历的要求也可以忽略,能干点活就行。但是往往是这样的公司最折磨人,你什么都需要做,涨薪没有明文规定,老板经常画饼,还会威胁你说完不成任务就走人。

每个城市的待遇也不一样,北京、上海、广州、杭州这些一线城市都有大公司,待遇最高;成都、大连、西安这些二三线城市的待遇就一般了。


十七、不要这样学习C语言,这是一个坑!

对于大部分初学者,学习C语言的目的是希望做一名合格的程序员,开发出靠谱的软件来。但是学了C语言的基本语法后,发现只能开发“黑底白字”的DOS程序,完全没有漂亮的界面和生动的交互。于是学数据结构,学算法,学操作系统,越陷越深,越来越难,最后迷茫了,不知道学C语言能做什么,认为学习编程很难,开始怀疑自己,甚至想放弃。

其实,这是很多初学者都会踩到的一个坑!C语言本身是一门很简单的语言,提供的实用功能不多,大部分要借助操作系统、第三方库、单片机来完成。也就是说,只学C语言基本什么也做不了,也基本找不到工作。

C语言是一门通用性的语言,并没有针对某个领域进行优化,在实际项目中,C语言主要用于较底层的开发,例如:

  • Windows、Linux、Unix 等操作系统的内核90%以上都使用C语言开发;
  • 开发硬件驱动,让硬件和操作系统连接起来,这样用户才能使用硬件、程序员才能控制硬件;
  • 单片机和嵌入式属于软硬件的结合,有很多使用C语言的地方;
  • 开发系统组件或服务,用于支撑上层应用;
  • 编写PHP扩展,增强PHP的功能;
  • 如果对软件某个模块(例如算法和搜索部分)的效率要求较高,也可以使用C语言来开发。

既然C语言的应用这么多,为什么很多读者觉得它什么也做不了呢?

我们先说一个概念,就是库(Library)。库就是编程专家写好的代码,我们可以拿来直接使用,这样能够节省开发成本,提高开发效率,并且库代码的执行效率、严谨性、安全性和规范性要明显优于我们自己编写的代码,市场上有很多优秀的库,有的收费,有的免费,我们要善于利用这些库,尽量不要重复造轮子。

库一般分为两种:

  • 编程语言的开发者在开发编程语言的时候,一般都要预先写好常用的代码,或者说常用的功能,例如输入输出、数学计算、文件操作、网络操作、日期时间、错误处理、字符串处理等,这些由官方编写的库称为标准库(Standard Library),它们随编程语言一起发布,可以认识是编程语言的一部分。
  • 有一些组织机构或者个人也会开发一些库,有的是为了盈利,有的是业余爱好,有的是本公司正在使用的代码,开源出来造福人类,这些库称为第三方库(Third-party Library)

第三方库不是由官方开发,没有质量把控,良莠不齐,但是有相当一部分也非常优秀,已经得到了大家的认可,已经应用在大公司的项目开发中,这些库能够和标准库媲美。

标准库是我们在学习编程语言时就要一起学习的,例如C语言的输入输出、文件操作、日期时间、字符串处理、内存管理等都是标准库提供的功能,它们并不是C语言语法的内容。

如果一门编程语言的标准库强大,初学者经过简单的学习后就很容易开发出实用的项目。例如Java,它的标准库包含了GUI(图形界面)、图形处理、网络通信、网络服务器、HTML解析、HTTP协议、多线程、多进程、正则表达式、压缩文件、加密解密、数据校验、音频视频处理、数据库操作、XML操作等常用功能,初学者学了以后立马就能够开发网站、开发PC软件,感觉很实用,也感觉学到了东西。Python、C#、VB、PHP、JavaScript、Ruby 等都是非常实用的语言,学了就能做出东西来。

反观C语言,它的标准库只有输入输出、文件操作、日期时间、数学计算等基本功能,都是在黑黑的控制台下进行的,跟网站、PC软件、APP等八竿子打不着,所以初学者觉得C语言没有用。

那么,C语言到底能不能开发网站、PC软件或者APP呢?

C语言的标准库肯定不能干这些事情啦,就得依靠第三方库了,遗憾的是,C语言的第三方库大都也是底层库,支持应用开发的库寥寥无几,只有一个 GTK 库能够开发出 PC 软件来,而没有与网站开发和APP开发相关的库。

GTK 库在PC软件开发中也很少用了,PC软件开发已经是 C++、C#、VB、Java、Delphi 的天下了。换句话说,开发PC软件基本不使用C语言,而是使用 C++、C#、VB、Java、Delphi 等其它语言。

记住,C语言几乎不用来做软件、网站、APP等这些应用层开发,其它的编程语言能够更好地完成任务,没必要非得使用C语言,C语言基本都是用来做底层开发,也就是看不见摸不着的、在后台默默提供服务的那些项目,而这样的项目对初学者来说基本没有实用价值,初学者也不知道它们该怎么使用。

初学者想要的C语言没有,C语言能做的初学者用不到,就是这种矛盾导致初学者非常迷茫。

有人可能会问,C语言不是还可以用来开发单片机或者嵌入式吗?是的没错,但是这个方向是软硬件结合的,不是在我们的电脑上进行开发,而是在特殊的开发板上进行开发,并且还需要学习数字电路、模拟电路、汇编、ARM、Linux 等方面的知识,只学C语言也没有用武之地。

如果你觉得学了C语言没用,那么恭喜你,你是对的,应用层的开发一般真的用不上它。

但是,没用也要学,学习C语言并不一定是要应用它,C语言可以夯实你的编程基础,尤其是数据结构、算法、内存、线程、进程、通信、操作系统等底层的计算机知识,没有C语言基础是学不好的。

这些底层知识并不一定能够直接应用在实际开发中,但是它们会让你有底气,会让你透彻地理解编程概念,会让你站的“低”看得远,会让你避免很多低级错误,会让你心中有“架构师”的思维。不学C语言是码农,学了C语言是程序员。

1、初学者必须C语言开始吗?

建议从C语言开始,然后学习数据结构、算法、内存、线程、进程、通信、操作系统等基本的概念,它们是学习编程的基础,不管是应用层开发还是底层开发,这些知识都是必须的。

如果你非要跳过C语言,从其他语言开始,比如 Java、Python、PHP、JavaScript、C# 等,也不是不可以;但是,在学习的过程中你会有一种雾里看花、空中楼阁的感觉,很多东西只会用,却理解不了,深入不了,原因就是没有计算机基础,没学会走就想跑了,这个时候,还得老老实实回来学习C语言。

2、如何学习底层知识

关于数据结构、算法、内存、线程、进程、通信、操作系统等这些基本的知识,重要的是理解概念,知道计算机是怎么回事,千万不要深入细节,把自己绕进去,耽误一两年的功夫,要尽早跳出来去做应用开发,找到兴趣点,获得成就感。

这个时候,C语言主要的作用是让你入门,了解编程语言的基本语法,强化编程思维,学习计算机底层知识,为以后的职业生涯打下坚实的基础,而不是用它来做实际开发。

在实际开发中,遇到问题,或者哪里理解不透了,可以再来回顾这些底层知识,这个时候就可以深入细节了。因为有了实际开发经验,再学习底层知识就知道哪里是重点了,不会像无头的苍蝇一样乱飞,什么都学。

3、【拓展】C语言为什么没有应用层开发的库

C语言是一门“古老”的语言了,它只支持面向过程编程,不支持面向对象编程和泛型编程,在中大型的应用层项目开发中,C语言已经显得捉襟见肘了,C++、Java、Python、C# 等其他编程语言能够更好地胜任,为C语言开发应用层的库简直是费力不讨好,所以几乎没人这么做。

GTK 算是一个应用层的库,但是它也比较老了,新版的 GTK+ 已经支持 C++ 了,不再仅仅支持C语言了。

我们先不管面向过程、面向对象、泛型这些晦涩的编程概念,简单地理解就是,C语言支持的特性少,用起来费劲,开发效率低,而 C++、Java、Python、C# 等支持的特性多,用起来方便,开发效率高。

C语言的优势是运行效率极高,这正是底层开发所看重的。底层开发有时候就是一个模块,或者是一个服务,规模不算大,但是对效率有严格的要求,此时用C语言就非常合适,所以针对底层开发的C语言库较多,因为它们有非常大的实用价值。


十八、明白了这点才能学好编程,否则参加什么培训班都没用

首先要明确告诉你的是,在大学课堂或培训班学到的知识远远达不到企业开发的要求。如果你觉得大学毕业就应该找个好工作,培训班投入一万多RMB就应该找个7千以上的工作,对不起,你想多了。

听听课就能找份月入七八千、甚至上万的工作,想想都不靠谱。有实力的培训班所谓的包就业就是安排招聘会,有很多企业会来,你可以同时接触到大量机会。但是那又怎样,企业只会招聘有能力的员工,不会花钱招不能干活的员工。再者,现在的招聘网站一大堆、各地都有人才市场,去哪里找不到大量的职位需求。

且不论你的学历怎么样,投入多少钱,不妨问下自己,以现在的编程水平,能够为公司创造多少价值。你的工资与你的能力是成正比的,而不是你的学历、你的投入。不仅仅是程序员,任何职位都要摆正心态,当你的待遇比别人低时,大部分情况下是能力不及别人。

程序员需要很强的自学精神,即使没有老师,没有培训班,你也应该一样优秀。

一名合格的程序员,不仅仅需要有理论基础和系统的知识,更重要的是大量编写代码、不断实践,丰富自己的经验,强化编程思维。只有这样,拿到项目才能立马想到解决方案。这是真正的程序员与纯科班出身的伪程序员的区别,他们的知识体系可能不比你小,但是拿到项目时却不知道从何入手。

大学和培训班的价值在于:

  • 给你安排系统的学习思路,让你知道学什么,怎么学。
  • 老师除了讲课,还会答疑,解决你学习和实践中遇到的问题。
  • 创造学习氛围,与大家共同交流学习,乐在其中。

大学和培训班绝对不包就业,也没有这个能力!更多的知识需要你自己去实践,谁都帮不了你!

对于动辄上万的线下培训班,例如达内、传智播客、北大青鸟、黑马等,我认为只要入门了,就达到目标了,钱就花得值。至于把学员培养成大神,是不可能的事情,后面的路需要你自己走。

1、学习的方法论

大家在学习和实践过程中遇到问题首先要自己解决,解决不了就借助搜索引擎,而不是先去问别人。这足以解决大部分问题了。

不过我相信你肯定还有小部分问题拿捏不准,需要有人给你拍板,这个时候老师或学长学姐就会发挥作用了,他们凭借自己的经验告诉你最佳方案或思路,让你恍然大悟,你就真的懂了。

2、如何找到师傅

很多初学者都希望找位大神拜师,让他来指导自己,这样就能进步很快。但是那些每天在群里喊“求大神带”“求代码”的人,多半没人搭理。大神都很忙,忙着工作、忙着学习、忙着玩,手下已经有好几个小弟,你的问题又没有挑战性,为毛要搭理你。你埋怨大神清高脾气大,大神看你就是菜鸟,遍地都是。

其实,不是这些人自私不乐于分享,而是问问题的人太多,会耗费大量的精力。大部分初学者问题会非常多,而且都是基础的,一般借助搜索引擎就能解决,却要来问,这是比较头疼的。所以要注意提问的艺术,自己实在解决不了再问。

那么,如何才能找到大神,拜师学艺呢?

其实这跟谈客户差不多,要经常沟通,慢慢熟悉,多交流而不是问一堆问题。逢年过节,送点礼物,偶尔一块吃饭。最好能经常见面,成为朋友。

注意:师傅的指导是非常重要的,你不要自己闷头学习,会走很多弯路,而且会有不少“野路子”,不伦不类,水分很多。如果你周围没有学习环境,又不想花钱去培训班,一定要找一位师傅带你!

文章来源:https://blog.csdn.net/sinat_41942180/article/details/135062690
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。