一、数据类型介绍以及signed和unsigned
现实生活中,你会见到各种各种数据,比如这是某饮料的营养成分表
通过该表你能看到有文本类型的数据,如:能量、蛋白质......
有整数数据:100、217
有小数数据:1.8、1.4......
同样,C语言也内置了四种数据类型,分别是: 字符型、整型、浮点型和布尔类型
同时C语言使用 signed 和 unsigned 关键字修饰字符型和整型类型
signed 关键字,表示一个类型带有正负号,包含负值;
unsigned 关键字,表示该类型不带有正负号,只能表示零和正整数。
下面一一介绍这几种类型以及取值范围。
1.1字符型
字符的英文单词是character,所以C语言中用char表示字符类型。
char //character
[signed] char //有符号的
unsigned char //无符号的
字符是什么见故事的开始总是极具温柔——C语言常见概念-优快云博客第九节
取值范围:
signed char c; // 范围为 -128 到 127
unsigned char c; // 范围为 0 到 255
注意,C语言规定 char 类型默认是否带有正负号,由当前系统决定。这就是说, char 不等同于 signed char ,它有可能是 signed char ,也有可能是unsigned char 。
1.2整型
整数的英文单词是integer,所以C语言中用int表示整型类型。
int //定义整型变量
signed int
unsigned int
long int b; //定义长整型变量
signed long int
unsigned long int
long long int c; //定义更长的整型变量
signed long long int
unsigned long long int
short int d; //定义短整型
signed short int
unsigned short int
对于 int 类型,默认是带有正负号的,也就是说 int 等同于 signed int 。 由于这是默认情况,关键字 signed ⼀般都省略不写,但是写了也不算错。
取值范围:
16位系统,int(signed int)取值范围是(),
unsigned int取值范围是()
32位系统,int(signed int)取值范围是(),
unsigned int取值范围是()
里如果你学过数字电路,应该很快就能理解,这里以16位为例:
16位的无符号位的int,可以表示0000 0000 0000 0000~1111 1111 1111 1111,共种,所以取值范围是
;
有符号位的int,最高位表示符号位,其中1表示负号,0表示正号,所以16位的有符号位的int,可以表示1000 0000 0000 0000~0111 1111 1111 1111,也是共种,但是取值范围是
。
授人以鱼不如授人以渔,这里介绍一个函数sizeof()。
每⼀种数据类型都有自己的长度,使用不同的数据类型,能够创建出长度不同的变量,变量长度的不同,存储的数据范围就有所差异。
#include <stdio.h>
int main()
{
printf("%zd\n", sizeof(short));
printf("%zd\n", sizeof(int));
printf("%zd\n", sizeof(long));
printf("%zd\n", sizeof(long long));
return 0;
}
X64配置下运行结果:
这里的单位是字节,一字节=8bit,每个bit可以存放一位2进制数
以第一个short为例,运行出来2字节,换算出来就是2*8=16bit,所以有符号位的取值范围就是也就是
最后再强调一下,取值范围和所配置的环境有关,所以取值范围并不一定都一样!!!
1.3浮点型
float
double
long double
1.4布尔类型
布尔类型的使用得包含头文件
布尔类型变量的取值是: true 或者 false 。
#define bool _Bool
#define false 0
#define true 1
如:
bool flag = true;
if (flag)
printf("i like C\n");
已经定义了flag为布尔类型的变量,且为真,所以if里面的条件成立,打印printf
故运行结果:
2. 变量
什么是变量?顾名思义就是变化的量。
现实生活中有很多变量,比如身高、体重、温度、年龄......
与之相对的还有常量,比如:光在真空中的传播速度......(本来想举例性别,怕有人说去趟泰国可以变,还想举例水的密度,怕又有人说水在不同温度下的密度是变化的,所以举这个例子,这个总不会变吧)
2.1变量的创建
数据类型说了那么多,有啥用呢?
欸,就是用来创建变量的。
变量创建的语法形式如下:
data_type name;
data_type是变量类型,name是你定义这个变量的名字。
举例1:
int age;
这里的data_type是整型,name是age,所以定义的是名为age的整型变量。
举例2:
char ch;
这里的data_type是字符型,name是ch,所以定义的是名为ch的字符型变量。
举例3:
double weight;
这里的data_type是浮点型,name是weight,所以定义的是名为weight的浮点型变量。
变量名命名规则:
• 只能由字母(包括大写和小写)、数字和下划线(`_`)组成。
• 不能以数字开头。
• 长度不能超过63个字符。
• 变量名中区分大小写的。
• 变量名不能使用关键字。
在创建一个变量的时候就给定一个初始值就叫变量的初始化。
int age = 18;
char ch = 'w';
double weight = 48.0;
unsigned int height = 100;
2.2全局变量和局部变量
这个直接上代码吧
#include<stdio.h>
int i = 2024;
int main()
{
int i=2025;
printf("%d", i);
return 0;
}
这个其实很好理解
大括号外面定义的变量是全局变量,大括号内部定义的变量是局部变量。
这里的i=2024是全局变量,i=2025是局部变量
全局变量括号内也能使用,使用的范围更广
局部变量只能括号内使用,括号外不能使用
还有就是局部变量的使用优先级最高,也就是括号里面定义了优先用里面变量,没定义就用括号外的。
所以这个代码运行结果是
如果你是个像我一样的小白,你或许根本不会关注变量的存储地址。
需要注意的是一般我们在学习C/C++语言的时候,我们会关注内存中的三个区域:栈区、堆区、静态区。
1. 局部变量是放在内存的栈区
2. 全局变量是放在内存的静态区
3. 堆区是用来动态内存管理的
想了解关于存储更详细的内容,请移步《计算机组成原理》和《数据结构》
3. 算术操作符:+、-、*、/、%
这个更没啥好说的,不就是简单的加减乘除和取余嘛,小学二年级都知道的东西。
稍微要补充的知识点就是:
3.1 /
#include<stdio.h>
int main()
{
int a = 10 / 4 ;
float b = 10 / 4 ;
printf("%d\n", a );
printf("%f\n", b );
return 0;
}
定义了a为整型变量,计算结果直接取整数部分,小数部分直接忽略,而不是四舍五入。
定义了b为浮点类型,但由于表达式的分子分母都是整型,所以计算类型直接变成了整型2,前面加了float设计类型转换,又把int类型的2转变成float类型的2.000000,所以结果是:
这个时候你想说了,如果我就想计算出对的值(2.5)咋办?
直接把分母分子的任意一个写成小数形式,这样运算后的类型也是浮点型,这样就保留了2.5这个结果。
#include<stdio.h>
int main()
{
float a = 10.0 / 4;
float b = 10 / 4.0;
printf("%f\n", a);
printf("%f\n", b);
return 0;
}
结果:
试试身手:
#include<stdio.h>
int main()
{
int score = 5;
score = (score / 20) * 100;
printf("%d", score);
return 0;
}
如果你还说这个score结果为25,那就大错特错了。
运行的优先级和小学学的一样,有括号先算括号里面的
首先score定义成整型的5,分母是整型的20,两个整型相除,结果是整型的,前面说过,整型的取值是直接略去小数部分的,所以结果为0
0再乘以100,结果还是为0
所以这道题目的答案为:
那要怎么把这个结果变成我们小学二年级所学过的正确答案呢?
前面也说了,这个运算的数学错误究其本质是因为括号内把小数忽略了,所以我们把分母改成小数,使得括号内运行结果变成浮点类型,不就对了吗?
#include<stdio.h>
int main()
{
int score = 5;
score = (score / 20.0) * 100;
printf("%d", score);
return 0;
}
答案:
3.2 %
首先这个运算符只能用于整数,不能用于浮点数。
比如:
#include<stdio.h>
int main()
{
int x = 6 % 4.0; // 2
printf("%d", x);
return 0;
}
运行结果:
其次负数求模的规则是,结果的正负号由第一个运算数的正负号决定。
#include <stdio.h>
int main()
{
printf("%d\n", 11 % -5);
printf("%d\n",-11 % -5);
printf("%d\n",-11 % 5);
return 0;
}
结果:
从答案可以看出,结果的正负号只和%前面值的正负号有关。
4. 赋值操作符:=和复合赋值
这个要区别于后面的==,=是赋值,后面赋值给前面。
比如:
int a=10;//初始化
a=20;//赋值,把20赋给a
4.1连续赋值
赋值操作符也可以连续赋值,如:
int a=1;
int b=2;
int c=3;
c=b=a-1;
连续操作也是从右向左依次赋值,这里是先计算a-1,然后赋值给b,再把b赋值给c。
4.2复合赋值
在写代码的时候,我们可能对一个数进行自增或者自减的操作,如:
int x=0;
x=x+2;
x=x-7;
C语言提供了更加方便的写法:
int x=0;
x+=2;
x-=7;
其他复合赋值符号还有:
+= -= /= *= %=
还有(以下操作符后期再说):
>>= <<= &= |= ^=
5. 单目操作符:++、--、+、-
以加法为例,运行如下代码:
#include <stdio.h>
int main()
{
int a = 0;
int b = 0;
int c = 0;
int d = 0;
a = a + 1;//语句一
b += 1;//语句二
c++;//语句三
++d;//语句四
printf("%d %d %d %d", a,b,c,d);
return 0;
}
你会发现a,b,c,d的结果都为1,学过前面的内容你也知道了,语句二是语句一的简短表达,运行的过程没有任何区别,那c++和++d有区别吗?这就涉及了是先赋值还是先++了。
5.1++和--
这里主要要介绍前置++与--和后置++与--的区别
5.1.1后置++与--
前置的特征就是符号在前,这里计算的区别是先++,再赋值
#include <stdio.h>
int main()
{
int i = 1;
int j = 0;
j = i++;
printf("%d",j );
return 0;
}
比如这段代码,咋一看 j 等于多少?
有人说,诶,我知道,i++是i+1,初始i=0,加一后就是1,再赋值给 j ,所以打印出来j=1。
但是运行一看
不对啊,这和分析不服啊,是不是编译器出问题了?
nonono,注意看原代码,是i++,先赋值,后++
也就是说先将 i 赋值给 j ,然后 i 再加一,所以段代码等效于
#include <stdio.h>
int main()
{
int i = 1;
int j = 0;
j = i;
i=i+1;
printf("%d",j );
return 0;
}
后置--同理,不再赘述了。
5.1.2前置++和--
举完上面的例子,这个也就很容易理解了,前置++就是先++后赋值
也就是说代码
int i = 1;
int j = 0;
j = ++i;
等效于
int i = 1;
int j = 0;
i = i + 1;
j = i;
前置--同理,也不再赘述了。
巩固一下
#include <stdio.h>
int main()
{
int a = 5;
int b = 5;
int c = 5;
int d = 5;
printf("%d %d %d %d\n", a++, ++b, c--, --d);
printf("%d %d %d %d\n", a, b, c, d);
return 0;
}
以上代码两个printf函数打印的分别会是什么值?
分析:
a++:先赋值后++,所以先把 a 赋值给第一个 printf 函数,第一个 printf 打印 5 ;再 ++,所以第 二个 printf 打印 6 。
++b:先++后赋值,所以先运行 b=b+1,再赋值给第一个printf,所以两个 printf 均打印6。
c--:先赋值后 --,所以先把c赋值给第一个 printf 函数,第一个 printf 打印 5;再 -- ,所以第二个 printf 打印 4 。
--d:先 -- 后赋值,所以先运行 d=d-1,再赋值给第一个printf ,所以两个 printf 均打印4。
综上分析:第一个 printf 打印:5 6 5 4;第二个 printf 打印:6 6 4 4
结果:
5.2 +和-
这里的 + 是正号,- 是负号,和我们小学六年级所学的符号一致。
运算符 + 对正负值没有影响,是一个完全可以省略的运算符,但是写了也不会报错。
int a = +10; 等价于 int a = 10;
运算符 - 用来改变一个值的正负号,负数的前面加上 - 就会得到正数,正数的前面加上 - 会得到负 数。
int a = 10;
int b = -a;
int c = -10;
printf("b=%d c=%d\n", b, c);//这⾥的b和c都是-10
int a = -10;
int b = -a;
printf("b=%d\n", b); //这⾥的b是10
这个理解都不难,所以也不赘述了。
6. 强制类型转换
在操作符中还有一种特殊的操作符是强制类型转换,语法形式很简单,形式如下:
(类型)
比如这个代码:
int e = 2.71828;
printf("%d", e);
e定义的是int类型,但是赋值确是小数,所以用的时候编译器会报警告
为了消除这个警告,我们可以使用强制类型转换:
int e =(int)2.71828;
printf("%d", e);
意思是将2.71828强制类型转换为int类型,这种强制类型转换只取整数部分
了解有这个功能就好,能不用就不用。
7. printf和scanf介绍
7.1 printf
7.1.1基本用法
关于这个已经在故事的开始总是极具温柔——C语言常见概念-优快云博客第七节介绍过了,所以直接进入占位符的介绍。
7.1.2 占位符
快上课了,你一边马不停蹄的往教室赶,一边联系你的好兄弟:“帮我占个座位。”
你好兄弟听完,啪!把一本书甩在一个座位上,其他人看到这本书也不会选择这个位置入座。
等你赶到教室,坐到你好兄弟占座的位置,你好兄弟把书拿走,进行了你和书发生“替换”的过程。
这本书就好比C语言中的占位符,在使用printf打印的时候,你可以在其中增添占位符,并在最后加入你要添加的部分,printf打印过程中,就会默认用后面的值替换占位符。
举个例子:
#include <stdio.h>
int main()
{
printf("我得到%d元",100);
return 0;
}
这里的%d就是占位符,这个值用后面的100替代,所以打印出来的内容是:
占位符的第一个字符一律为百分号 %,第二个字符表示占位符的类型。
%d 表示这里代入的值必须是一个整数。
7.1.2.1 %a
%a:十六进制浮点数,字母输出为小写。
根据C语言标准,%a的标准输出格式为:,其中0x表示十六进制数,h是十六进制数字,小数点后包含尽可能多的数字,以准确表示该浮点数的精度。
为指数,
表示以2的
次方。
举例:
printf("%a\n", 0.5);//0x1.0000000000000p-1
printf("%a\n", -0.5);//-0x1.0000000000000p-1
printf("%a\n", 27.0);//0x1.b000000000000p+4
第一个,因为,所以输出为0x1.0000000000000p-1
第二个,因为,所以输出为-0x1.0000000000000p-1
第三个,因为,所以输出为0x1.b000000000000p+4
运行结果:
7.1.2.2 %A
%A:十六进制浮点数,字母输出为大写。
这个和%a差不多,唯一的区别就是大小写的区别。
比如:
printf("%A\n", 0.5);//0X1.0000000000000P-1
printf("%A\n", -0.5);//-0X1.0000000000000P-1
printf("%A\n", 27.0);//0X1.b000000000000P+4
对比%a你能发现,相比于%a,表示十六进制的0x变成了大写的0X,十六进制数b变成了大写的B,表示底数2的p变成了大写的P。
7.1.2.3 %c
%c:字符。//char
printf("%c\n", 'A'); // 输出: A
printf("%c\n", 65); // 输出: A(65是'A'的ASCII码)
%c占位字符型,碰到数字自动转换成ASCII码中的对应字符。
感觉没啥好说的,唯一注意的就是打印字符记得加 ' ' (单引号)
7.1.2.4 %d
%d:十进制整数(有符号的10进制整数)。//int
引入占位符就是这个例子,这里就不说了吧。
7.1.2.5 %e
%e:使用科学计数法的浮点数,指数部分的 e 为小写。
这个理解就简单了,就是小学所学的科学计数法,e表示10的几次方。
printf("%e\n", 16.0);//1.600000e+01
printf("%e\n", -16.0);//-1.600000e+01
printf("%e\n", 0.5);//5.000000e-01
第一个,;第二个,
;第三个,
。
7.1.2.6 %E
%E :使用科学计数法的浮点数,指数部分的 E 为大写。
这个和上面%e一样,就是用E表示10的几次方而已。
printf("%E\n", 16.0);//1.600000E+01
printf("%E\n", -16.0);//-1.600000E+01
printf("%E\n", 0.5);//5.000000E-01
7.1.2.7 %i
%i :整数,基本等同于%d 。
既然基本等于%d了,这里就不说了。
7.1.2.8 %f
%f:小数(包含 float 类型和 double 类型)。//float-%f double-%lf
对于printf函数来说,float类型和double类型都能用%f占位,这里与scanf函数进行区分,scanf中%f用于读取float类型变量(需指针传递),%lf用于读取double类型变量。默认保留6位小数。
float a = 1.0;
double b = 1.0;
printf("%f %f", a, b);//1.000000 1.000000
涉及小数,当然无法避免尾数保留问题,%.nf就能保留小数,其中n表示保留n位有效数字。
printf("%.2f\n", 2.7161);//2.72
printf("%.2f\n", 2.7151);//2.72
printf("%.2f\n", 2.7150);//2.71
printf("%.2f\n", 2.7149);//2.71
聪明的你可以发现,尾数取舍方式为4舍6入,如果为5,则再往下看下一位,下一位不为0则入,否则舍去(这句话理解起来困难的话就看上面代码,相信看完之后就好理解了)。
%m.nf表示指定总宽度为m字符,保留n位小数(右对齐,不足补空格)
7.1.2.9 %g
%g:6个有效数字的浮点数。整数部分一旦超过6位,就会自动转为科学计数法,指数部分的 e
为小写。
7.1.2.10 %G
%G :等同于 %g ,唯一的区别是指数部分的 E 为大写。
7.1.2.11 %hd
%hd :十进制 short int 类型。
7.1.2.12 %ho
%ho :八进制 short int 类型。
7.1.2.13 %hx
%hx :十六进制 short int 类型。
7.1.2.14 %hu
%hu :unsigned short int 类型。
7.1.2.15 %ld
%ld :十进制 long int 类型。
7.1.2.16 %lo
%lo :八进制 long int 类型。
7.1.2.17 %lx
%lx :十六进制 long int 类型。
7.1.2.18 %lu
%lu :unsigned long int 类型。
7.1.2.19 %lld
%lld :十进制 long long int 类型。
7.1.2.20 %llo
%llo :八进制 long long int 类型。
7.1.2.21 %llx
%llx :十六进制 long long int 类型。
7.1.2.22 %llu
%llu :unsigned long long int 类型。
7.1.2.23 %Le
%Le :科学计数法表示的 long double 类型浮点数。
7.1.2.24 %Lf
%Lf :long double 类型浮点数。
7.1.2.25 %n
%n :已输出的字符串数量。该占位符本身不输出,只将值存储在指定变量之中。
7.1.2.26 %o
%o :八进制整数。
7.1.2.27 %p
%p :指针(用来打印地址)。
7.1.2.28 %s
%s :字符串。
7.1.2.29 %u
%u :无符号整数(unsigned int)。
7.1.2.30 %x
%x :十六进制整数。
7.1.2.31 %zd
%zd : size_t 类型。
7.1.2.32 %%
%% :输出⼀个百分号。