所有的 C 语言教程,都会介绍这种语言的各种变量类型,譬如 char 用来放字符,int 用来放整数,float 用来放浮点数等等,后面还会说指针也是一种类型。但是在我看来,C 语言的变量,其实只有以下简单的含义:
每个变量,都代表了计算机的一块内存。
不管变量是什么类型的,内存里面放的都是数字
表达式里的变量名,表示读出这块内存的内容
通过 = 操作符把数字写入内存
变量的类型,代表了对应的运算规则
变量类型决定了变量对应的内存的长度
运算符根据类型进行不同规则的数学计算
C 语言里,一切内容都是数字;一切操作都是数学计算
char 类型,是 C 语言最常用的变量类型。因为一个 char 类型的变量,就是一个字节(Byte)的内存。一个字节由 8 个二进制数组成,可以存放的正整数范围是 0~255。虽然这个类型,从名字上看,应该是用来存一个“字符”的,但实际上只是存的一个数字而已。我们可以写个程序来理解下:
#include <stdio.h>
int main()
{
char a = 'z';
char b = 'A';
char c = a - b;
printf("a: %c-%d, b: %c-%d, c: %c-%d", a,a, b,b, c,c);
}
这个程序编译运行之后,显示的结果是 a: z-122, b: A-65, c: 9-57
看这个代码之前,我先介绍一下 printf() 的用法:
printf() 的第一个参数,表示要显示一个什么格式的字符串。其中由变量替换的地方,用 '%' 跟一个字母代表
printf() 的第二个,以及后续的参数,会按顺序,根据第一个参数字符串中的 '%' 定位符,依次把内容显示出去
这里用的 '%c' 表示,把对应的变量,以字符的方式显示。而 '%d' 表示,把对应的变量,以十进制数字的方式显示。
char a = 'z';
char b = 'A';
回到代码,我们定义了两个字符类型的变量 a 和 b,里面分别存了小写字符 'z' 和大写字符 'A'。实际上,计算机分别找了两块内存,各自都是 1 个字节的,然后在里面分别写了数字 122 和数字 65。为什么是这两个数字呢?因为 ASCII 编码表,规定了 122 代表 z,而 65 代表 A。
ASCII 全称为 American Standard Code for Information Interchange,就是“美国信息交换标准代码”,最早规定了怎么用数字来表示英文字符的一套编码表
然后我们就可以用数学运算来对这两个变量进行运算。 char c = a - b; 这行代码,用了减法这个数学运算。从类型“字符”来看,是不是很奇怪呢?两个字符居然可以相减!实际上 C 语言只是把存在变量 a,b 里面的数字相减了而已。减完的结果就是 57,存到变量 c 里面了。而 57 这个数字,在 ASCII 编码表里面,对应的是字符 9 ,所以按字符打印的时候,就打印出来一个 “9” 了。回顾开头提到的“变量”的含义,是不是表明了,所有存在变量里的,不管是什么类型,其实都是数字呢?而且我们用 = 等号操作符进行了写入,用 - 减号操作符进行了数学计算。
这个例子,粗粗一看,似乎没有什么实际含义,但实际上,不管是图形还是声音,对于计算机来说都是数字,而计算机程序要做的绝大多数操作,就是把这些数字在不同的内存之间搬过来搬过去。譬如显示一个照片,就是把图片文件的数字,读取到内存里,然后再把这些内存里的数字,按照图形压缩的计算方法,进行加加减减的数学运算,解压成可以直接显示的图形数据(也是一堆数字),然后送到显卡的内存上。所以即便是最复杂的程序,都离不开这个例子中的做法:用变量来存放数字,用运算符来计算数字。
int 这种“用来表示整数”的类型,其变量的长度,一般是 4 个字节。刚刚我们讨论过的 char 是 1 个字节,那么,1 个 int 变量,能不能和 4 个 char 变量等同起来使用呢?完全是可以的! 我们可以写一个下面的程序来说明:
#include <stdio.h>
int main()
{
char s[4] = {0, 0, 0, 0};
int i = 1145258561;
char *p = (char *)&i;
s[0] = p[0];
s[1] = p[1];
s[2] = p[2];
s[3] = p[3];
printf("%c %c %c %c\n", s[0], s[1], s[2], s[3]);
}
这个程序运行后显示:A B C D
这里的第一行 char s[4] = {0,0,0,0}; 定义了一个叫 s 的字符数组,意思就是连续 4 个字符类型变量。数组可以用下标,来获得对应位置的变量内容,如 s[1] 表示数组里面顺序第二的变量。(经典笑话:程序员数数从 0 开始数)这里的 s 字符数组,一开始里面四个字符变量放的都是数字 0。
第二行 int i = 1145258561; 定义了一个 int 整数类型的变量,然后写了一个很大的数字进去。为什么是这个数字呢?这个数字不会乱写的。后面会解释。
第三行 char *p = (char *)&i; 定义了一个字符指针 p,然后把 i 的指针,强行转换并赋值给了 p。这里虽然有点绕,但简单的理解,就是把变量 i,强行变成一个字符数组 p 来使用。这里的 &i 表示读取变量 i 的内存地址,这个地址也是一个数字,然后以 char * 字符指针的形式,复制写入变量 p 的内存里去。
后面四行比较简单,就好像两个字符数组变量的复制,把 p 这个数组的元素,依次 1、2、3、4 的写入 s 数组中去。实际上就是把整数变量 i 的内容,按照内存顺序,一个个的放到四个字符变量中去。
最后打印了四个字符变量的内容,正好是 "A B C D",因为整数 1145258561 放在四个字节长度的内存里面,刚好是:“65 66 67 68”,而这四个字节内存里面的数字,就是 ASCII 编码表里面的 "A B C D" 的对应编码
整数 1145258561 在内存中存放的一个是 32 位二进制数 01000100 01000011 01000010 01000001 ,我们按照这个二进制数,分别去看四块一个字节的内存,里面就是十进制数 68 67 66 65,如果写成 16 进制,则是 44 43 42 41
对于 C 语言来说,内存中的数字到底是什么含义,完全可以让程序员去决定。一个 int 变量完全可以被当作 4 个 char 变量进行操作,反过来也是可以的。我们需要关注的是:哪种类型的变量,以及这种变量的运算符,能最快的满足我们自己的需求。在实际开发中,经常会用 char 数组,来代表对“任何数据”的内存进行处理。因为 char 的长度是 1 个字节,非常便于我们对于内存中的数据进行自己的处理和拷贝。
对于整数 int 和字符 char 来说,一般的运算符如加减乘除,计算规则其实都一样的。char 你可以认为是一个字节的整数。但是浮点数 float 类型的数字,在内存中的存放是比较复杂的。如果你使用加法 + 加上 1 进行运算,并不是和整数一样处理:最低位反转一下。我们可以写一段程序来窥探一下,float 类型变量的数学运算,反应在内存上到底是什么一个样子。
#include <stdio.h>
int main()
{
float f = 3.1415926535;
char *p = (char *)&f;
printf("before: %u %u %u %u\n", p[3], p[2], p[1], p[0]);
f = f + 1;
printf("after: %u %u %u %u\n", p[3], p[2], p[1], p[0]);
}
输出内容为:
before: 64 73 15 4294967259
after: 64 4294967172 4294967175 4294967278
上面的程序,先建立了一个 float 浮点数变量 f,初始化给的内容是圆周率(一部分),是个小数。然后让这个数字加上了整数 1。代码通过 char 数组的方式,把前后两次 f 变量内存里面的内容打印了出来。
我们根据 +1 前后的内存情况看,根本看不出有任何规律。为啥这个变量只是相差了整数 1,内存里面就差别那么大呢?原因就是浮点数,在内存中的表示,是根据一种特殊的规则来存放的,这套规则的名字叫 IEEE-754 。这套规则比整数存放在内存里的规则可复杂多了。所以即便只是相差了整数 1,在内存里面的记录的信息可以相差非常远。有兴趣的读者可以专门去网上搜索“浮点数 存储”一类的文章了解。
同样是数学的加法,C 语言在实现整数加法,和浮点数加法上,其处理方法都是很不一样的。我们也不能简单的认为:C 语言就是提供了一种“像数学符号”的工具来操作内存。起码对于 float 类型变量来说,C 语言还是帮程序员们做了一些很复杂的事情。
我们没有讨论另外一个重要部分,就是“布尔运算”,或者叫“逻辑运算”。在 C 语言中,数字 0 代表了 false,所有其他数字都代表了 true。if 关键字基本上就是一个“判断是否 0”的选择器,这部分比较接近日常的“是、否”逻辑,所以没有专门提及。
C 语言提供了一套和数学很像的操作符,给程序员们去操作计算机的内存,譬如 = 等号表示写入内存, +-*/ 等数学运算符,会根据变量类型进行运算。变量就代表了内存,变量类型就是内存的长度,内存里面存的就是数字。C 语言允许程序员以任何自己喜欢的方法,去读写这些数字。这一套处理数字的办法,不但是 C 语言,很多后来发明的计算机语言,基本都沿用了 C 语言对于整数、浮点数的处理方法。这套操作内存和数字运算的方法,基本上已经可以完成一切计算机要处理的任务了。 除此之外,C 语言已经没有更多其他的“功能”或者内容了——真的是非常的简洁!
有了这一篇的基本认识,下一篇可以真正的开始讲 C 语言最有特色的内容:指针
评论区
共 5 条评论热门最新