开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 17 天,点击查看活动详情
二进制
二进制加减法和十进制加减法的思想是类似的:
- 对于十进制,进行加法运算时逢十进一,进行减法运算时借一当十;
- 对于二进制,进行加法运算时逢二进一,进行减法运算时借一当二。
下面两张示意图详细演示了二进制加减法的运算过程。
1、二进制加法:1+0=1、1+1=10、11+10=101、111+111=1110
2、二进制减法:1-0=1、10-1=1、101-11=10、1100-111=101
基本数据类型
原码、反码和补码
首先介绍一下符号位的概念:指二进制数字从左往右第一位,0表示正数,1表示负数。通过看最高位是0还是1就可以辨别正负数了。
原码
所谓原码就是二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负,其余位表示数值的大小。
在数值前直接加一符号位的表示法。
例如: 符号位 数值位
[+7]原= 0 0000111 B
[-7]原= 1 0000111 B
注意:
数0的原码有两种形式:
[+0]原= 00000000B
[-0]原= 10000000B
位二进制原码的表示范围:-127~+127
反码
反码表示法规定:正数的反码与其原码相同;负数的反码是对其原码逐位取反,但符号位除外。
正数:正数的反码与原码相同。
负数:负数的反码,符号位为“1”,数值部分按位取反。
例如: 符号位 数值位
[+7]反= 0 0000111 B
[-7]反= 1 1111000 B
注意:
a. 数0的反码也有两种形式,即
[+0]反=00000000B
[- 0]反=11111111B
b. 8位二进制反码的表示范围:-127~+127
补码
补码表示法规定:正数的补码与其原码相同;负数的补码是在其反码的末位加1。
正数:正数的补码和原码相同。
负数:负数的补码则是符号位为“1”。并且,这个“1”既是符号位,也是数值位。数值部分按位取反后再在末位(最低位)加1。也就是“反码+1”。
例如: 符号位 数值位
[+7]补= 0 0000111 B
[-7]补= 1 1111001 B
a.正数的补码即是它所表示的数的真值,而负数的补码的数值部份却不是它所表示的数的真值。
采用补码进行运算,所得结果仍为补码。
b. 与原码、反码不同,数值0的补码只有一个,即
[0]补=00000000B。
整数类型
整数就是不包含小数点的数据,比如1、3,等数字。整数包含以下几种数据类型:
- int:占用4字节,默认一般使用此类型。
- long:占用8字节。
- short:占用2字节。
浮点类型
浮点类一般用于保存小数。
- float:单精度浮点,占用4个字节。
- double:双精度浮点,占用8个字节。
字符类型
每一个字符都可以使用字符类型来保存:
- char:占用1个字节(-128~127),可以表示所有的ASCII码字符,每一个数字对应的是编码表中的一个字符:
- 某些无法直接显示的字符(比如换行,换行也算一个字符),需要使用转义字符。
C程序基本格式
在后面,将进行代码的编写,所以在此简单介绍一下c语言程序的基本格式以供了解。
main.c
#include <stdio.h> // 引入系统库为我们提供的函数,以后编写一个C语言程序,就按照固定模式:
int main() { // C语言程序入口点就是`main`函数(
printf("Hello World!");
return 0;
}
变量
在学习变量之前,请先了解占位符。(就目前阶段而言,常用的有%d和%s这两个)
| 格式控制符 | 说明 |
|---|---|
| %c | 输出一个单一的字符 |
| %hd、%d、%ld | 以十进制、有符号的形式输出 short、int、long 类型的整数 |
| %hu、%u、%lu | 以十进制、无符号的形式输出 short、int、long 类型的整数 |
| %ho、%o、%lo | 以八进制、不带前缀、无符号的形式输出 short、int、long 类型的整数 |
| %#ho、%#o、%#lo | 以八进制、带前缀、无符号的形式输出 short、int、long 类型的整数 |
| %hx、%x、%lx %hX、%X、%lX | 以十六进制、不带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字也小写;如果 X 大写,那么输出的十六进制数字也大写。 |
| %#hx、%#x、%#lx %#hX、%#X、%#lX | 以十六进制、带前缀、无符号的形式输出 short、int、long 类型的整数。如果 x 小写,那么输出的十六进制数字和前缀都小写;如果 X 大写,那么输出的十六进制数字和前缀都大写。 |
| %f、%lf | 以十进制的形式输出 float、double 类型的小数 |
| %e、%le %E、%lE | 以指数的形式输出 float、double 类型的小数。如果 e 小写,那么输出结果中的 e 也小写;如果 E 大写,那么输出结果中的 E 也大写。 |
| %g、%lg %G、%lG | 以十进制和指数中较短的形式输出 float、double 类型的小数,并且小数部分的最后不会添加多余的 0。如果 g 小写,那么当以指数形式输出时 e 也小写;如果 G 大写,那么当以指数形式输出时 E 也大写。 |
| %s | 输出一个字符串 |
变量的使用
声明变量的格式为:数据类型 变量名称 = 初始值; 示例:
int a = 10; //变量类型为int(常用),变量名称为a,变量的初始值为10
int a = 10, b = 20; //多个变量可以另起一行编写,也可以像这样用逗号隔开,注意类型必须是一样的
变量命名有以下规则:
- 不能重复使用其他变量使用过的名字。
- 只能包含英文字母或是下划线、数字,并且严格区分大小写。
- 虽然可以包含数字,但是不能以数字开头。
- 不能是关键字。
初始值可以是一个常量数据(比如直接写10、0.5这样的数字)也可以是其他变量,或是运算表达式的结果,这样会将其他变量的值作为初始值。
我们可以通过给变量赋值,然后进行一些简单的计算。
#include <stdio.h>
int main() {
int a = 10; //将10作为a的值
int b = 20;
int c = a + b; //注意变量一定要先声明再使用,这里是计算a + b的结果,并作为c的初始值
}
以上代码就是简单对变量进行赋值,下面我们想知道变量的值到底是多少,那么就需要进行打印这个操作
#include <stdio.h>
int main() {
int a = 10;
int b = 20;
int c = a + b;
printf(c); //直接打印变量c
}
发现并没有任何结果在控制台输出,因为在C语言中,输出内容需要使用占位符,若有遗忘,请往上看。以下才是正确操作。
printf("c的结果是:%d", ); //使用%d来代表一个整数类型的数据(占位符),在打印时会自动将c的值替换上去
下面是小数的计算
#include <stdio.h>
int main() {
double a = 0.5;
float b = 2.5f; //直接写2.5默认表示的是一个double类型的值,需要再后面加一个f或是F表示是flaot类型值
printf("a + b的结果是:%f", a + b); //根据上表,小数类型需要使用%f表示,这里我们可以直接将a + b放入其中
}
再来看字符类型
char c = 'A'; //字符需要使用单引号囊括,且只能有一个字符,不能写成'AA',这就不是单个字符了
//注意这里的A代表的是A这个字符,对应的ASCII码是65,实际上c存储的是65这个数字
#include <stdio.h>
int main() {
char c = 'A';
printf("变量c的值为:%c 对应的ASCII码为:%d", c, c); //这里我们使用%c来以字符形式输出,%d输出的是变量数据的整数形式,其实就是对应的ASCII码
}
当然,我们也可以直接让char存储一个数字(ASCII码),同样也可以打印出对应的字符:
#include <stdio.h>
int main() {
char c = 66;
printf("变量c的值为:%c 对应的ASCII码为:%d", c, c);
}
那要是我们不对变量设定初始值呢?那么变量会不会有默认值:
#include <stdio.h>
int main() {
int a, b, c, d;
printf("%d,%d,%d,%d", a, b, c, d);
}
根据结果发现并不会,可以看到,虽然定义变量但是我们没有为其设定初始值,那么它的值就是不确定的了(原理是内存分配机制)
下面再看一种特殊情况:
#include <stdio.h>
int main() {
char c = 127; //已经到达c的最大值了
c = c + 1; //再进行+1的操作
printf("%d", c); //这时会得到什么结果?
}
由于位数不够,导致运算结果值溢出:
- 127+1= 01111111+1
- 由于现在是二进制,满2进1,所以最后变成
- 10000000 = 补码形式的 -128
在我们的运算中,可能也会存在一些一成不变的值,比如π的值永远都是3.1415....,在我们的程序中,也可以使用这样不可变的变量,我们成为常量。
定义常量和变量比较类似,但是需要在前面添加一个const关键字,表示这是一个常量:
可以看到,常量在一开始设定初始值后,后续是不允许进行修改的。
无符号数
我们知道,所有的数据底层都是采用二进制来进行保存的,而第一位则是用于保存符号位,但是如果我们不考虑这个符号位,那么所有的数都是按照正数来表示,比如考虑了符号位的char类型:
- 考虑符号表示范围:-128~127
- 不考虑符号:0~255
我们也可以直接使用这些不带符号位的数据类型:
int main() {
unsigned char c = -65; //数据类型前面添加unsigned关键字表示采用无符号形式
printf("%u", c); //%u以无符号形式输出十进制数据
}
结合我们前面学习的基础知识,我们来看看为什么得到的是191这个数字。首先char类型占据一个字节,8个bit位:
- 00000000 -> 现在赋值-65 -> -65的补码形式 -> 10111111
- 由于现在没有符号位,一律都是正数,所以,10111111 = 128 + 32 + 16 + 8 + 4 + 2 + 1 = 191
#include <stdio.h>
int main() {
int i = -1;
printf("%u", i); //%u以无符号形式输出十进制数据
}
得到无符号int的最大值。
类型转换
一种类型的数据可以转换为其他类型的数据,这种操作我们称为类型转换,类型转换分为自动类型转换和强制类型转换,比如我们现在希望将一个short类型的数据转换为int类型的数据:
#include <stdio.h>
int main() {
short s = 10;
int i = s; //直接将s的值传递给i即可,但是注意此时s和i的类型不同
float a = 3; //包括这里我们给的明明是一个int整数3但是却可以赋值给float类型,说明也是进行了自动类型转换
}
这里其实就是一种自动类型转换,自动类型转换就是编译器隐式地进行的数据类型转换,这种转换不需要我们做什么,我们直接写就行,会自动进行转换操作。
如果我们使用一个比转换的类型最大值都还要大的值进行类型转换,比如:
#include <stdio.h>
int main() {
int a = 511;
char b = a; //最大127
printf("%d", b);
}
很明显char类型是无法容纳大于127的数据的,因为只占一个字节,而int占4个字节,如果需要进行转换,那么就只能丢掉前面的就只保留char所需要的那几位了,所以这里得到的就是-1:
- 511 = int -> 00000000 00000000 00000001 11111111
- char -> 11111111 -> -1
我们也可以将整数和小数类型的数据进行互相转换:
#include <stdio.h>
int main() {
int a = 99;
double d = a;
printf("%f", d);
}
不过这里需要注意的是,小数类型在转换回整数类型时,会丢失小数部分(不是四舍五入,是直接丢失小数)
#include <stdio.h>
int main() {
double a = 3.14;
int b = a;
printf("%d", b);
}
除了赋值操作可以进行自动类型转换之外,在运算中也会进行自动类型转换,比如:
#include <stdio.h>
int main() {
float a = 2;
int b = 3;
double c = b / a;
printf("%f", c);
}
下图是类型转换的规则:
下面介绍强制类型转换,我们可以为手动去指定类型,强制类型转换格式如下:
(强制转换类型) 变量、常量或表达式;
比如:
#include <stdio.h>
int main() {
int a = (int) 2.5; //2.5是一个double类型的值,但是我们可以强制转换为int类型赋值给a,强制转换之后小数部分丢失
printf("%d", a);
}
我们也可以对一个算式的结果进行强制转换
#include <stdio.h>
int main() {
double a = 3.14;
int b = (int) (a + 2.8);
printf("%d", b);
}
在我们需要得到两个int相除之后带小数的结果时,强制类型转换是一种有效的方式
#include <stdio.h>
int main() {
int a = 10, b = 4;
double c = a / b; //不进行任何的类型转换,int除以int结果仍然是int,导致小数丢失
double d = (double) a / b; //对a进行强制类型转换,现在是double和int计算,根据上面自动类型转换规则,后面的int自动转换为double,结果也是double了,这样就是正确的结果了
printf("不进行类型转换: %f, 进行类型转换: %f", c, d);
}