数据类型

整形

概念: 表示整数类型的数据

1
int a = 123 ;      

①先向系统申请一片内存,取名为 a;②确定该内存大小为 4 字节(1 字节 byte = 8 位 bit);③把 123 转化为二进制存放在该内存中

  1. 介绍

    • 整型在 32 位系统以及 64 位系统中都占用 4 字节

    • 取值范围

    1
    2
    3
    4
    $ getconf INT_MAX
    2147483647
    $ getconf INT_MIN
    ‐2147483648
  2. 整型的修饰符:

    • short 短整型,用于修饰整型的尺寸变为原本的一半,减少内存的开支,缩小取值范围
    • long 长整型,用于修饰整型的尺寸使其尺寸变大(取决与系统),增加内存开支,扩大取值范围
    • long long 长长整型,用于修饰整型的尺寸使其尺寸变大(取决与系统),增加内存开支,扩大取值范围 (在64位系统中 long 与 long long 的大小一致)
    • unsigned 无符号整型,用来修饰整型并去掉符号位,使得整型数据没有负数,可以提正整数的取值范围 (0 - 4294967295)
    • 整型数据在二进制存储时最高位(第31位)表示符号位,如果为1 则表示负数反之则表示正数
  3. 整数的存储方式:

    • 原码: 正整数是直接使用原码进行存储的,比如100这个正整数,则直接把100 转换成二进制直接存储。

      100 --> 0000 0000 0000 0000 0000 0000 0110 0100

    • 补码:负数则是使用补码来存储,补码 = 原码的绝对值取反+ 1,取反和加1时符号位都不变

      比如-100:

      1
      2
      3
      100 --> 1000 0000 0000 0000 0000 0000 0110 0100
      取反-> 1111 1111 1111 1111 1111 1111 1001 1011
      加1 --> 1111 1111 1111 1111 1111 1111 1001 1100

    注:取反加1 的最前面的数字 1 不改变,他为符号位,表示正负,0 为正,1 为负

  4. 溢出:

    概念:当超过取值范围时则会变成相邻的最小值/最大值

  5. 整型输出:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    int c = 100;
    printf("十六进制:%#x\n" , c); //`#` 是格式说明符的一部分
    printf("十进制:%d\n" , c);
    printf("八进制:%#o\n" , c);

    // 输出结果:
    十六进制:0x64
    十进制:100
    八进制:0144
  6. sizeof 运算符

    用于计算变量/类型的大小。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    int main()
    {
    int a=123;
    printf("sizeof(short):%ld\n",sizeof(short));
    printf("sizeof(int):%ld\n",sizeof(int));
    printf("sizeof(long):%ld\n",sizeof(long));
    printf("sizeof(long long):%ld\n",sizeof(long long));
    }
    //注意括号内部可以写变量类型, 也可以写变量名

    //输出结果
    sizeof(a):4
    sizeof(short):2
    sizeof(int):4
    sizeof(long):8
    sizeof(long long):8

    C-type1

    在 32 位平台下,int 型和 long 型是一致的,都是占用 4 个字节。Long long 是 8 个字节。
    在 64 位平台下,int 型是占用 4 个字节,而 long 和 long long 都是占用 8个字节的。
    尽量不要使用 long 和 long int,因为 c/c++标准中,只限制了 long int 长度不小于 int,并未限制 long 必须是 4 个字节或者 8 个字节,也就是跟平台相关,这主要是因为有一些历史兼容性原因。所以要使用明确的 long long 或者 uint64_t


浮点型(实型):

  1. 介绍
  • 概念:用来表达一个实数的数据类型
  • 分类
    • 单精度浮点型 float , 典型尺寸 4 字节
    • 双精度浮点型 double , 典型尺寸 8 字节
    • 长双精度 long double ,典型尺寸 16 字节
      占用的内存越多则精度越高
  1. 浮点数的存储

    • 虽然浮点数储存的也是 0,1 ,但和整形不一样
    • 整形是原码补码储存
    • 浮点型是通过计算得出的浮点数二进制码

    IEEE 浮点标准采用如下形式来表示一个浮点数

    C-type2

    1
    2
    3
    float f = 3.14; // 浮点数3.14 通过以上公式计算得到的二进制码,存放在在内存f中
    printf("f:%f\n",f); // 使用浮点的计算方法来解析内存f中的值(二进制)
    printf("d:%d\n",f);// 直接使用整型的计算方法来直接解析内存f 中的值 (二进制)

    虽然所有的数据都会被转换成二进制进行存储,但如果想到的到正确的数据,必须使用正确的理解方式(类型),来解析二进制数据。所以不能用%d


字符类型

字符

  1. 介绍

    char c = 'K' ;
    ①申请一片内存并且命名为 c
    ②确定内存的大小为 char (1字节)
    ③把字符‘K’的 ASCII 码值转换为二进制,并存储到该内存中

    计算机中存储的所有数据都是以二进制的形式存在的,因此字符必须映射某一个数字才能
    够被存放到计算机中,这个映射的表就成为 ASCII 表,可以使用 man 手册来查看 man ascii

    字符之间的比较就是比较他们的 ASCII 码

    点击查看 man ascii 解释
    1. oct(Octal): 这一列显示了 ASCII 字符的八进制(octal)表示。ASCII 字符可以用三位八进制数字表示,例如,大写字母’A’对应的八进制表示为101。
    2. dec(Decimal): 这一列显示了ASCII字符的十进制(decimal)表示。ASCII字符可以用十进制数字表示,例如,大写字母’A’对应的十进制表示为65。
    3. hex(Hexadecimal): 这一列显示了ASCII字符的十六进制(hexadecimal)表示。ASCII字符可以用两位十六进制数字表示,例如,大写字母’A’对应的十六进制表示为41。
    4. cher(Character): 这一列显示了对应的可打印字符或控制字符。可打印字符是可以显示在屏幕上的字符,如字母、数字和符号。控制字符是用于特殊控制目的的字符,如换行符(newline)和回车符(carriage return)。

    例:

    1
    2
    3
    char c = '1' ;
    printf("字符:%c\n",c); // 以字符的形式来解析内存 c 的内容得到 对应的字符 1
    printf("整型ASCII值:%d\n",c); //以十进制整型来解析内存c 的内容 ,得到1多对应的ASCII值
  2. 注意:

    字符实质上是一个单字节的整型,因此支持所有整型的操作

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    int main()
    {
    char k = 'H' ;
    printf("k + 1: %c\n" , k + 1 );
    printf("k - 1: %c\n" , k - 1 );
    }

    //输出结果
    k + 1: I
    k - 1: G

字符串

字符串的表现形式有两种:

  1. 数组(可读,可写):(存储)

    1
    char s1[] = "Hello" ;   //使用一个数组来存放字符串 "Hello"

    以上语句其实是把一个字符串常量 “Hello” ,复制到数组 s1 所代表的内存当中

    C-type3

  2. 指针 (只读):(指向)

    1
    char *s2 = "Even"; // 使用一个指针来指向常量字符串

    C-type4

    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main()
    {
    char s1[] = "abc";
    char *s2="def";
    printf("%s %s\n",s1,s2);
    }

    //输出结果
    abc def

布尔类型

概念: 布尔类型用来表示真/假 (非零则真)

真:true 假:false

注意在使用布尔类型是需要包含他的头文件: <stdbool.h>

1
2
3
4
bool a = 1 ; // 真
bool a = 0 ; // 假
bool a = true // 真
bool a = false // 假

一般布尔类型的变量,可以用于逻辑判断比如 if / while ,或者用于函数的返回值。

布尔类型的大小:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
bool a = true ;
printf("sizeof(bool):%ld \n ",sizeof(bool) );
printf("sizeof(a):%ld \n ",sizeof(a) );
printf("sizeof(true):%ld \n ",sizeof(true) );
printf("sizeof(false):%ld \n ",sizeof(false) );
printf("false:%d \n ",false );
printf("true:%d \n ",true );


//运行结果:
sizeof(bool):1
sizeof(a):1
sizeof(true):4
sizeof(false):4
false:0
true:1

常量与变量

概念: 不可以被改变的内存,被称为常量,可以被改变的内存则成为变量

1
2
3
int a = 100 ;                     // a是一个变量, 而 100 则是常量
float f = 3.1415; // f 是一个变量, 而 3.1415 则是常量
char s1[] = "abcdefg" ; // s1 是一个变量 , 而"abcdefg" 则是常量 (字符串常量)

常量类型:

  • 100 :整型常量
  • 100L : 长整型 long
  • 100LL : 长长整型 long long
  • 100UL : 无符号的长整型 unsigned long
  • 3.14 : 编译器默认升级为双精度浮点型
  • 3.14L : 长的双精度浮点型
  • ‘a’ : 字符常量
  • “Hello” : 字符串常量(指针 char * )

标准输入与标准输出(格式化输出)

标准输入

概念: 标准输入一般指的是键盘的设备文件,从键盘获取数据就成为标准输入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
scanf( ); // 扫描键盘 (格式化输入数据 ‐‐> 从键盘中获得指定类型的数据) 

头文件:
#include <stdio.h>
函数原型:
int scanf(const char *format, ...);
参数分析:
format ‐‐> 格式化
... ‐‐> 省略号 , 根据format 所写的格式控制符,对应一个内存地址
返回值:
成功 返回具体获取到的项目数
失败 返回0


getchar( ) ;
// 获取一个字符 (默认从标准输入文件中获取),与scanf( )配合,清空 由 scanf 所留在缓冲区的内容(比如回车\n等)

函数原型:
int getchar(void);
参数:

返回值:
成功 返回一个ASCII值,代表获得的字符 (实际上为unsigned char )
失败 返回 EOF 也就是 ‐1

补充:

1
2
char * p = malloc(32); // p是指针,使用 p 而不是 &p,因为 p 就是一个地址
scanf ("%[^\n]s", p); // [^\n] 表示只有回车符才是结束标记

实例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
int main(int argc, char const *argv[])
{
int num = 0;
char c = '0';
int ret_val = scanf("%d", &num ); // & 取地址符号,获得 内存 num 的地址
while(getchar() != '\n'); // 清空 由 scanf 所留在缓冲区的内容
// 如果不清空有可能会导致下一次使用标准输入缓冲区异常(有上一次的数据没有被读取)


printf("返回值:%d , 获取的数据为:%d\n" , ret_val , num );
ret_val = scanf("%c", &c );
while(getchar() != '\n'); // 清空 由 scanf 所留在缓冲区的内容
printf("返回值:%d , 获取的数据为:%c\n" , ret_val , c );
printf("EOF:%d\n" , EOF ); //EOF 实际上是一个值为‐1 的宏
return 0;
}

注:如果以后需要从标准输入中获取数据得到乱码或未知数据,则可以尝试使用 getchar 进行清空再获取。

标准输出(格式化输出)

概念:屏幕(终端)对应的就是标准输出的设备文件,如果往该文件中输出内容则可以成为标准输出。

printf( );

  1. 注意语法点

    • % 必须有的,格式化的开头标记
    • 对齐的方式, 向左对齐(空格在右) ,如果没有则是向右对齐(空格在左)
    • m.n m 指的域宽(需要的字符数), n 指的是精度(默认精度为6)
      m 的值如果比实际数据小则按实际输出,反之则用空格来填补
    • l 指的是 long 表示长整型
    • h 指的是短整型
  2. 格式控制符

    • 十进制的整型: %d %md %ld %u(无符号) %lu
    • 八进制: %o %#o # --> 输出进制的符号
    • 十六进制: %x %#x %#X
    • 字符: %c
    • 字符串: %s
    • 单精度浮点: %f %.nf --> n 表示精度
    • 双精度: %lf
    • 长双精度 : %Lf
    • 地址: %p
    1
    2
    3
    printf("%d\n" , 0123 ); // 使用十进制的格式来输出一个八进制数据 0123 ‐‐>83

    printf("%o\n" , 0123 ); // 使用八进制的格式来输出一个八进制数据 0123 ‐‐>123

类型的转换

概念:不同的数据类型但是可以兼容的类型之间,如果出现在同一表达式中则会发生类型的转换。

  1. 隐式转换(自动)

    若在表达式中用户没有手动进行转换,则系统会自动转为高精度的类型(由低精度转换为高精度)
    比如: float + double + int --> 则系统会自动全部转为 double

  2. 强制转换(手动)

    用户根据自己的需求进行强制的类型转换 ( int )a --> 强制性把 a 转换为整型

    注意:不管是隐式转换还是强制转换,准换的只是在运算的过程中,并不会影响到原本的数,这是一个临时的转换。

    运算中可能会丢失精度,但不会影响数据本来的值,只是临时转换

    例如:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    int main()
    {
    int a=100;
    char c='k';
    float f=3.14;
    double d=998526.153;

    //只是临时转换高精度的double m, a、c、f、d实际没变
    double m=a+c+f+d; //隐式转换
    int n=(int)a+(int)c+(int)f+(int)d; //强制转换
    printf("m= %lf,n=%d\n",m,n);
    }

精度排序:

  1. char:1个字节。通常用于存储字符和小整数值。
  2. short:2个字节。通常用于较小的整数值。
  3. int:4个字节。通常用于一般整数值。
  4. float:4个字节。单精度浮点数,通常用于存储小数值,具有较高的精度。
  5. double:8个字节。双精度浮点数,通常用于存储双精度小数值,具有更高的精度。
  6. long double:这个数据类型的大小可以因系统而异,通常为8个字节或更多。它提供了比 double 更高的精度,适用于需要更高精度的计算

补充

数据类型的本质:

  1. 概念:

    各种不同的数据类型,从本质上是用户与系统之间的一个约定,这个约定用来如何正确的解析内存中的二进制编码。

    比如整形使用原码补码,浮点型是通过计算得出的浮点数二进制码

  2. 推论:

    类型转换:实际上就是在临时打破之前的约定。
    理论上任何的数据都可以进行转换,但是转换之后是否有意义?

整型的尺寸

  1. 概念:尺寸指的是某一个数据需要多少内存来存储(占用的内存空间)

    在 C 语言中并没有规定某一个数据类型他的尺寸是多少,但是有个相对的大小约定

    Short 不可能比 int 长
    Long 不可能比 int 短
    Long 的长度等于系统的字长(系统位数,比如 32 位,64 位)

    如何查看系统字长:有很多方法

    1
    2
    3
    4
    5
    6
    //方法一,终端输入
    $ getconf LONG_BIT
    64

    //方法二,终端输入
    $ uname -a
  2. 典型尺寸:

    • char 占用 1 个字节
    • short 占用 2个字节
    • int 占用 4 个字节
    • long 占用 4(32 位系统) / 8(64 位系统) 个字节

可移植类型

相同的代码在不同(位数)的系统中,它所占用的尺寸会发生变化,有可能导致数据的精度出现问题,严重的可能会导致程序无法运行。
因此,系统中会提供一些可移植类型。

概念:不管在什么系统中,数据的尺寸都是固定不会发生变化的数据类型,称为可移植数据类型

关键词:typedef

1
2
3
typedef int zzz;   //相对于用typedef给int取个别名为zzz
typedef int __int32_t;
typedef long __long_64_t;

系统提供了一些预定义的

通过 cd /usr/include 后,运行 find -name types.h 会发现./x86_64-linux-gnu/bits/types.h
路径:/usr/include/x86_64-linux-gnu/bits/types.h

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/* Convenience types.  */
typedef unsigned char __u_char;
typedef unsigned short int __u_short;
typedef unsigned int __u_int;
typedef unsigned long int __u_long;


/* Fixed-size types, underlying types depend on word size and compiler. */
typedef signed char __int8_t; //有符号的8位整型
typedef unsigned char __uint8_t; //无符号的8位整型
typedef signed short int __int16_t;
typedef unsigned short int __uint16_t;
typedef signed int __int32_t;
typedef unsigned int __uint32_t;
#if __WORDSIZE == 64 //判断系统的位数(字长)
typedef signed long int __int64_t; //64位系统中,long int就是64位的有符号整型
typedef unsigned long int __uint64_t; ////64位系统中,long int就是64位的无符号整型
#else //判断系统的位数,如果不是64位系统,则判断为32位系统
__extension__ typedef signed long long int __int64_t; //在32位系统中 long long 才占用64位
__extension__ typedef unsigned long long int __uint64_t;
#endif