今天是我的第二篇学习笔记。
数据类型
今天学习的内容是数据类型。如果是DBA,都接触过 Oracle 中的 varchar2,number、blob等字段类型。当然其他数据库也有对应的数据类型。
那么 C 语言中,也是有对应的数据类型。所谓“类型”,就是相似的数据所拥有的共同的特征。不同的数据类型,有不同的操作方法。
C 语言中提供两大类的数据类型,即内置类型以及自定义类型;今天学习内容主要是内置数据类型。
数据类型分类如下:
-
内置类型
- 字符
- 整型
- 浮点型
- 布尔类型
-
自定义类型
- 数组
- 结构图-struct
- 枚举-enum
- 联合体
字符
字符用char来表示,表示单个字符;可以分为无符号字符和有符号字符类型。
[signed | unsigned] char
整型
整型可以分为短整型、整型、长整型、以及更长的整型(不知道这样子翻译是否正确)。
[signed | unsigned] short [int]
[signed | unsigned] int
[signed | unsigned] long [int]
[signed | unsigned] long long [int]
整型为什么需要分这么多个类型?
比如我们需要定义年龄,age,理论上我们可以使用上述中的任何一个数据类型;不过最佳实践肯定选择short int,因为人的年龄不可能是上万岁,上亿岁。
而且若使用long long这种类型定义年龄不仅浪费内存空间,对于性能也会有一定损耗,所以我们在定义变量时,按需获取即可。
在数据库中设计字段是一个道理。比如需要定义“男和女”,我可以使用char(1)来定义,0 表示男,1表示女,其余数字表示未知;完全没有必要定义char(2),这样会浪费空间,产生没必要的空洞。
浮点型
在数据库生涯中,float类型一次都没有用过,因为我们知道这个类型对于数字计算并不准确。具体使用场景,还需要深入学习。
对于小数的计算,double类型使用较多。
- float:单精度浮点型
- double:双精度浮点型
- long double:长双精度浮点型
布尔类型
早期 C 语言表示真和假的方式如下:
- 0 表示 false
- 非0 表示true
C99中引入了布尔类型
- _Bool
需引入头文件<stdbool.sh>,并且
_B需要大写
signed 和 unsigned
signed 和 unsigned 用来修饰字符和整型类型。
signed 表示带有正负号,比如 signed int 可以表示负数和正数。
unsigned 表示只有正号,没有负号。比如 unsigned int 只可以表示正整数。
整型数据了理性默认带有 signed 关键字,所以默认情况下,是可以定义为负数的。也就是说 int 与 signed int 相等。
但在字符中,默认是否带有正负号是由系统决定,所以 char 不等于 signed char;他有可能等于 signed char,或者等同于 unsigned char。
为什么会需要符号和无符号类型呢?
通常,int为四个字节,也就是32位。最大能表示的范围是-2,147,483,648 ~ 2,147,483,647,如果定义为无符号整型,那么他的范围是0 ~ 4,294,967,296;也就是说,他的最大值增长了一倍。
这在数据库中也比较常见,比如在MySQL数据库中,需要定义一个主键id,通常会设置为 int 类型,并且设置为自动增长。
数据类型的取值范围可以在头文件中查看,如:
limits.h ⽂件中说明了整型类型的取值范围。
float.h 这个头⽂件中说明浮点型类型的取值范围。
SCHAR_MIN , SCHAR_MAX :signed char 的最⼩值和最⼤值。
SHRT_MIN , SHRT_MAX :short 的最⼩值和最⼤值。
INT_MIN , INT_MAX :int 的最⼩值和最⼤值。
LONG_MIN , LONG_MAX :long 的最⼩值和最⼤值。
LLONG_MIN , LLONG_MAX :long long 的最⼩值和最⼤值。
UCHAR_MAX :unsigned char 的最⼤值。
USHRT_MAX :unsigned short 的最⼤值。
UINT_MAX :unsigned int 的最⼤值。
ULONG_MAX :unsigned long 的最⼤值。
ULLONG_MAX :unsigned long long 的最⼤值。
sizeof 是一个操作符,计算的是类型创建的变量所占用内存的大小,单位是字节。
代码演示:
int main()
{
printf("%d\n", sizeof(char));
printf("%d\n", sizeof(short));
printf("%d\n", sizeof(int));
printf("%d\n", sizeof(long));
printf("%d\n", sizeof(long long));
printf("%d\n", sizeof(float));
printf("%d\n", sizeof(double));
return 0;
}
运行结果
注意:
我们可以在编译器中看到,有这些告警。
这是因为 sizeof 返回有对应的值,那就是size_t。
size_t的本质是unsigned int,也就是无符号整型。
size_t在打印的时候,格式应该使用%zd。
当然,使用int也没有问题,
注意:下面这张图,我们可以发现,int类型与long类型返回的长度是一样的。
这是因为C语言中规定:sizeof(long) >= sizeof(int),所以long 与 int 类型的长度是有可能一样的。并不是说long 一定要大于 int。
所以,我们想要一个数据表示 8 个字节,那就要使用 long long,而不是使用long。
变量
变量,按字面意思,就是经常变化的值。上一章节说到数据类型,那么数据类型就是为了创建变量的。
我们知道计算机只能识别0 1 这种二进制,所以在内存中存储音频、视频、图片等都是 0 1 的组合。那么在这些 0 1 组合中,想要知道这些数字表示的是什么,就是需要使用数据类型来定义这些数据,比如 00010000 ,这个数字表示的是16?还是音频?
变量的创建
int a; //定义变量 int表示数据类型、 a 表示变量
a = 16; //初始化变量
我们如上定义 a 这个变量并且给a赋值时,那么在内存中存储的 00010000 就表示16这个数字。
变量的类型
- 全局变量:整个工程中都可以使用全局变量。也就是在大括号外部定义的变量。
- 局部变量:局部变量使用较为局限,在大括号内部定义的变量就是局部变量。
代码示例:
int g_var = 10; //全局变量
int main()
{
int a = 10; //局部变量
return 0;
}
如果全局变量与局部变量重名,局部全量优先。
同时存在局部变量和全局变量,那么都会打印出对应的值,也就是说这两个变量是两个不同的变量。
代码示例:
int a = 10;
int main()
{
{
int a = 20;
printf("%d",a);
}
printf ("%d",a);
return 0;
}
执行结果:
思考一下,全局变量和局部变量在内存中是怎么存放的?是否存放在同一个地方?
学习C/C++时,会关注内存中的三个区域,堆、栈、静态区。
- 堆:动态内存管理,malloc等
- 栈:存放局部变量。
- 静态区:存放全局变量
操作符
算数操作符
+、-、*、/、%
加、减、乘、除、取模,这些操作符叫做双目操作符。
负数取模的规则是,结果的正负号由第一个运算数的正负号决定。
代码示例:
int main()
{
int a = 10;
int b = 3;
printf("%d\n", a + b);
printf("%d\n", a - b);
printf("%d\n", a * b);
printf("%d\n", a / b);
printf("%d\n", a % b);
return 0;
}
赋值操作符
int main()
{
int a = 10;
int b = 3;
int c = 5;
int d = b = a + c;//连续赋值,从右向左边赋值
a += 4; // 复合赋值 += -+ *= /+ %= 都可以作为复合赋值
printf("%d\n", d);
return 0;
}
连续赋值在实际项目中不建议使用,因为会影响调试。
单目操作符
++、 --、 +、 -
代码如下:
int main()
{
int a = 5; // 初始化 a 为 5
a = a + 1; // a = 6
a++; // a = 7
++a; // a = 8
int b = ++a; // a = 9、b = 9
int c = a++; // a = 10、b = 9
printf("%d\n", b);
printf("%d\n", c);
printf("%d\n",a);
return 0;
}
- 前置++:先++、后使用。
- 后置++:先使用、后++。
执行结果:
不管前置还是后置,都会进行+1操作,只是在赋值上会有所区别。
强制类型转换
形式:
(类型)
代码:
int main()
{
int a = (int)3.14;
printf("%d\n", a);
return 0;
}