学习笔记 | 数据类型和变量

75 阅读7分钟

今天是我的第二篇学习笔记。

数据类型

今天学习的内容是数据类型。如果是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_MAXunsigned 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。

image-20231008220442191

所以,我们想要一个数据表示 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;
}

执行结果:

image-20231008223416843

思考一下,全局变量和局部变量在内存中是怎么存放的?是否存放在同一个地方?

学习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;
}
  • 前置++:先++、后使用。
  • 后置++:先使用、后++。

执行结果:image-20231008235610056

不管前置还是后置,都会进行+1操作,只是在赋值上会有所区别。

强制类型转换

形式:

(类型)

代码:

int main()
{
    int a = (int)3.14;
    printf("%d\n", a);
    return 0;
​
}

小结

image-20231009001139923