二 .C语言的数据类型

C语言提供了多种数据类型,可以用于存储不同类型的数据。以下是C语言中常用的一些数据类型:

   什么是数据?        

有用的信息称为数据

1. 基本数据类型:


   - int:用于表示整数类型,通常为32位,4个字节。
   - char:用于表示单个字符,一个字节。
   - float:用于表示单精度浮点数,通常为32位,4个字节。
   - double:用于表示双精度浮点数,通常为64位,8个字节。
   - void:表示空类型,用于表示无返回值或不具有特定类型的指针。

2. 修饰符类型:


   - short:用于表示短整数类型,通常为16位,2个字节。
   - long:用于表示长整数类型,通常为32位,4个字节。
   - signed:用于表示带符号整数类型。
   - unsigned:用于表示无符号整数类型。

3. 复合数据类型:


   - 数组:用于存储相同类型的多个元素。
   - 结构体:用于组合多个不同类型的数据。
   - 联合:用于在同一内存空间存储不同类型的数据。
   - 枚举:用于定义一组具有命名值的整数常量。

4. 指针类型:用于存储内存地址。


   - int*:指向整数类型的指针。
   - char*:指向字符类型的指针。
   - void*:用于表示未指定类型的指针。

这仅是C语言中一些常用的数据类型。可以通过使用这些数据类型来定义变量、函数参数和返回类型,并根据需要对它们进行操作和处理。

计算机内部的数据类型

- 字节(byte B):计算机存储容量的一种单位
- 比特位(bit) :二进制数,cpu计算的基本单位
- 二进制数 0 1
 - 1字节 = 8比特位(bit)
 - 1个千字节(KB) = 1024字节
 - 1M = 1024KB
 - 1G = 1024M
 - 1T = 1024G
 

1.整型

在C语言中,整型(Integer)是一种用于表示整数数值的数据类型。C语言提供了多种整型数据类型,具体选择哪个整型类型取决于所需的数值范围和内存占用,64位系统典型的大小是4字节(VS code软件是32位的 。

以下是C语言中常见的整型数据类型:

  • int:用于表示普通的整数类型,通常为32位

  • short:用来缩短整型变量的尺寸,减少取值范围并节省内存,称为整型。

  • long:用来增长整型变量的尺寸,增大取值范围并占用更多的内存,称为长整型

  • long long:用来增长整型变量的尺寸,增大取值范围并占用更多的内存,称为长长整型

  • unsigned:用来去除整型变量的符号位,使得整型变量只能表达非负整数

int a;//整形
short int a; // 短整型 %hd  half表示一半的意思
long int b; // 长整型 %ld
long long int c; // 长长整型 %lld

unsigned int e; // 无符号整型 %u

unsigned short int f; // 无符号短整型 %hu
unsigned long int g;  // 无符号长整型 %lu
unsigned long long int h; // 无符号长长整型 %llu

使用整型修饰符后,关键字int可以省略:

int a;//整形
short a; // 短整型  
long b;  // 长整型  
long long c; // 长长整型 

unsigned e;  // 无符号整型

unsigned short f; // 无符号短整型
unsigned long g;  // 无符号长整型
unsigned long long h; // 无符号长长整型

格式控制符

  • int 整型:%d

  • unsigned int 无符号整型 : %u

  • short整型:%hd,h代表half,即一半的存储字节

  • long整型:%ld

  • long long整型:%lld

  • 显示不同进制的前缀 : %#o(八进制)、%#x(十六进制)

  • 符号位:

    • 有符号的整型数据,首位为符号位,0表示正数,1表示负数。

    • 无符号的整型数据,没有符号

demo

#include <stdio.h>

int main(int argc, char const *argv[])
{
    // 申请2个字节的空间,并取名为a,俗称定义变量a
    // 空间a的内容为随机数
    // a空间只能存放整数
    // a空间存放的最大值为2字节的数据
    short int a;
    // 在同一个作用域({})下,不能出现两个同名的变量名
    //short int a;
    a = 10; // 将10存放到空间a里面

    // 申请空间并将100放到里面
    // 即定义变量并初始化
    short b = 100;
    printf("%hd\n",b);//h的意思为half即int 的一半(这样做节省空间)
    
    /* h的意思为half即int 的一半(unsigned是无符号的
    所以只能是正数,如果ub输入负数则发生数据溢出,以后会说) */
    unsigned short ub = 200;
    printf("%hu\n",ub);

    /* char类型只有一个字节,而%d输出4个字节,
    所以hhd是一半的一半即一个字节 */
    char c = -15;
    printf("c = %hhd\n",c);

    unsigned char uc = 15;
    printf("%hhu\n",c);
    
    int d = -1024;
    printf("%d\n",d);// %d即能表示负数也能表示正数

    unsigned int ud = 1024;
    printf("%u\n",ud); // %u为无符号输出

    long int le = 1000L; // L表示1000为长整型
    printf("%ld\n",le);

    long long int lle = 100000LL;
    printf("%lld\n",lle); // LL表示100000为长长整型

    float f = 3.14;
    printf("%f\n",f); // 浮点型

    double lf = 3.1415;
    printf("%lf\n",lf);  // 双精度浮点型

    // 在32位机里面,此类型越界无效(即最大值超过2^32)
    long double llf = 3.1415926;
    printf("%Lf\n",llf);

    int number = 10;
    printf("%#o\n",number);// 八进制输出
    printf("%#x\n",number);// 十六进制输出

    return 0;
}

表格:C语言中全部的数据类型及它的取值范围,取值范围计算过程,以表格的形式呈现出来

unsigned char                      0~255                    (1111 1111)(0~2^8-1)
char                                     -128... + 127          (0111 1111)

short                                    -32768~32767
unsigned short                     0~65535                (1111 1111 1111 1111) 2^16-1
unsigned int                         0... 2^32-1             4294967295

取值范围的计算过程如下:

对于带符号整数类型,范围计算基于补码表示法。一般而言,对于一个 n(n为bit,1字节8bit) 位的带符号整数类型,其范围可以计算如下:

最小值:-2^(n-1)
最大值:2^(n-1)-1

无符号整数类型的范围计算方式是将所有位都用于非负数部分,因此其范围计算如下:

最小值:0
最大值:2^n-1

编码形式

把一个整数转换为二进制的数

55=(0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0000 0011 0111)

-55 = (1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1111 1100 1001)

二进制的数在考虑正负中,首位数字为符号位,0为+,1为-,且符号位不参与运算

补码

正数的补码不变(还是正数本身),负数的补码绝对值取反加一

衍生——计算机数据溢出问题

1.当编译器以整型输出的时候%d,是以补码还原的方式解读
2.当cpu将数据进行运算的时候,直接以内存中存放的方式来运行,也就是以补码的方式参与运算
3.%u输出的时候,值区域范围:0-4294967295(有符号转为无符号的时候)(2^32)
4.%hhu方式打印,值域范围:0-255 (2^8)

 

解析:int a的取值范围是0—255,不发生溢出

           char b的取值范围是-128—+127,输入255发生溢出,255对应-1号位,所以%d输出-1

          而在u的取值范围内-1对应0的后一位即最大值(4294967295),所以输出4294967295

 

 解析:unsigned short a的取值范围为0—65535,a赋值-1发生溢出,

            int b=a;不发生溢出

 作业题

//作业题

unsigned char a = 257;
char b = 129;
printf("a:%hhu\n",a);//half
// 练习
printf("b:%hhd\n",b);
// 作业1
printf("b:%hhu\n",b);
//先算出%hhd在绝对值取反加一就是%hhu(注意借位问题)

 进制与进制转换

  • 进制:源码中可以使用八进制、十进制或者十六进制,但实际数据中一律是二进制

    • 十进制(默认,0~9),比如1234 %d %u

    • 八进制(0~7),比如013 %#o

    • 十六进制(0~15),比如0x6FF0A %#x

  • 进制转换

    • 十进制--》二进制、八进制、十六进制

    • 十进制转二进制除2取余数,倒序排序,高位补0

 十进制转八进制;12(10)=14(8).

 二进制八进制十六进制转十进制

 最小二乘法(小数转二进制 ) 

2.浮点型

  • 概念:用来表达实数(有理数和无理数)的数据类型

  分类:

  • 单精度浮点型(float),典型尺寸是4字节                    

  • 双精度浮点型(double),典型尺寸是8字节

  • 长双精度浮点型(long double),典型尺寸是16字节

  • 占用内存越多,能表达的精度越高

float f1;//单精度
double f2;//双精度
long double f3;//长双精度

作业题

3.字符

char ch1 = 'a'; // 'a'是字符常量,代表字母a
char ch2 = '\n'; // '\n'是不可见字符常量,代表回车

ASCII码表

char a = 'a';// 存储在内存中是97
char b = '1';// 存储在内存中是49
char c1 = 20;
char c2 = c1 + 'a'; // 等价于char c2 = 20+97;

printf("%c\n",c2); // 以字符形式输出117,即 'u'
printf("%d\n",c2); // 以整型形式输出117

// 小写字符转大写字符 ' ' = 32
char a7 = 'b'- ' ';
printf("%c\n",a7);

转义字符

转义字符是在编程中用于表示特殊字符的一种机制。在C语言中,你可以使用转义字符来表示不能直接键入或显示的字符,或者在字符串中插入特殊的字符序列。

以下是C语言中常用的一些转义字符:

- \\:反斜杠
- \':单引号
- \":双引号
- \n:换行
- \r:回车
- \t:制表符(tab键)
- \b:退格(Backspace)
- \f`:换页
- \a:响铃(需要电脑有蜂鸣器)
- \0:空字符(字符串结束标志)

-\ddd:表示八进制数,打印出来的是该数字对应的ascii码

八进制数ddd范围在  0 - 7,且八进制数转化为十进制的数不能超过127,否则乱码(ASCII码就只有128位,十六进制同理)

-\xhh:表示十六进制 打印出来的是该数字对应的ascii码

使用转义字符的方式是在需要表示特殊字符的地方,将其前面加上反斜杠`\`。

以下是一些示例:
 

printf("Hello, World!\n");    // 换行
printf("This is a \"quote\".");    // 引号
printf("C:\\path\\to\\file.txt");   // 路径中的反斜杠
printf("Tab\tSeparated");    // 制表符
printf("Beep\aBeep\a");    // 响铃
printf("Line1\rLine2");    // 回车
printf("Backspace\b");    // 退格
printf("Form feed\f");    // 换页
printf("Null character\0");    // 空字符(字符串结束标志)

printf("%c\n",'\102');    // 八进制
printf("%c\n",'\x41');    //A

    printf("efg\a");
    printf("abcd\b");
    printf("efg\n");
    // 转义字符---八进制'\xxx',注意x的大小为0-7
    // 注意转义字符的范围一定是127以内,不要超出char类型范围
    int a = '\102';
    printf("%o,%d,%c\n",a,a,a);
    //printf("%o\n",'\109');// 不合法,单个字符是0-7
    
    // 转义字符---十六进制'\xhh',注意x的大小为0-F
    int b = '\x4f';
    printf("%x,%d,%c",b,b,b);

注意

在使用转义字符时,一定要将其放在合适的上下文中,以确保能够正确解释和显示特殊字符的含义。

4.字符串

// 字符串的定义方式有两种:指针和数组
char *s1 = "abcd"; // 使用字符指针来表示字符串
char s2[]= "abcd"; // 使用字符数组来表示字符串

// 注意,使用数组来定义字符串时,方括号[]里面的数字可以省略
// 不省略也可以,但必须必字符串实际占用的内存字节数要大,比如:
char s3[] = "apple";

char a = 'a' //就是单个字符(一个字节)
char *a="abcd" //"abcd"有五个字节即 "abcd\0",'\0'为字符串结束标志字符

"funny story"

 字符串以"\0"结尾且"\0"占用空间

5.布尔类型数据

概念:布尔型数据只有真、假两种取值,非零为真,零和false为假。

语法:

bool a = 1; // 逻辑真,此处1可以取其他任何非零数值
bool b = 0; // 逻辑假 c

备注:

bool类型非0即为真,0和false为假,且bool的头文件是stdbool.h

#include <stdio.h>
#include <stdbool.h>

int main(int argc, char const *argv[])
{
    // 在c语言,非0为真,0为假
    // bool为布尔类型,一般只有两种状态 true:真  false:假
    // 作用是作为标志位,增加代码的可读性
    bool flage = true;
    bool flage1 = false;
    printf("%d\n",flage1);                //0
    //enum 为枚举类型
    enum color{yellow=10,gree,blue,red};
    printf("%d",yellow);                  //10  
    printf("%d",red);                    //13
    return 0;
}

6.常量与变量

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

int a = 100;    // a是变量,而100是常量
float f = 3.14; // f是变量,而3.14是常量
char s[] = "abcd"; // s是变量,"abcd"是常量

7.标准输入

概念:键盘是系统的标准输入设备,从键盘中输入数据被称为标准输入

scanf(); // 格式化输入函数
fgets(); // 字符串输入函数

int a;
float f;
scanf("%d", &a);  // 从键盘输入一个整型,放入指定的内存地址 &a 中
scanf("%f", &f); // 从键盘输入一个浮点数,放入指定的内存地址 &f 中
// 从键盘依次输入一个整型和一个浮点型数据,用空白符隔开
scanf("%d%f", &a, &f); 

char c;
char s[10];
// 从键盘输入一个字符,放入指定的内存地址 &f 中
scanf("%c", &c);  
// 从键盘输入一个单词,放入指定的数组 s 中(注意不是&s)
scanf("%s", s );  //用s就找不到地址

fgets(s, 10, stdin); // 从键盘输入一行字符串,放入数组 s 中

注意1:

  • scanf() 与 printf() 不同,scanf() 的格式控制串不可乱写,尤其是结尾处的 \n

  • 用户必须完全按照 scanf() 中描述的输入格式控制串来输入数据,否则将出错。

// 此处输入时必须带逗号
scanf("%d,%d", &a, &b);

// 此处必须先输入a=,然后才能输入整数
scanf("a=%d", &a); 

// 此处结束输入时按下的回车符将被scanf()误以为格式控制符,无法正常结束输入
scanf("%d\n", &a); 

注意2:

  • scanf() 的返回值,代表成功从键盘读取的数据的个数

  • 无法匹配 scanf() 指定格式的数据,将被遗留在输入缓冲区中,不会消失

// scanf() 试图从键盘读取两个整数
// 返回值 n 代表成功读取的个数,比如:
// 输入100 200,则 n 将等于2
// 输入100 abc,则 n 将等于1
// 输入abc xyz,则 n 将等于0;输入abc 200,n也将等于0
/* 如果成功读取一个整数,则n等于1;如果成功读取两个整数,
则n等于2;如果无法按照预期的格式读取整数,则n等于0 */
int n = scanf("%d%d", &a, &b);

// 根据 scanf() 的返回值,判断用户是否输入了正确的格式
while(n != 2)
{
    // 需要清空缓冲区并提示用户重新输入
    char s[50];
    fgets(s, 50, stdin);
    printf("请重新输入两个整数\n");

    n = scanf("%d%d", &a, &b);
}

demo

#include <stdio.h>

int main()
{
    printf("input msg : ");
    int a;
    float b;
    char c;
    // &a获取a的地址
    // scanf不要添加任何除了格式化输出的字符%外的所有任何字符
    //scanf("%c,%d\n,%f",&c,&a,&b);
    // 取到缓冲区的数据,返回实际取到的内容的个数
    // 取不到缓冲区的数据,返回0
    int n = scanf("%d",&a);//为判断,如果scanf("%d",&a)拿取成功则n为1,失败则为0
    // 用getchar()将输入缓冲区的内容取走(缓冲区有什么就拿什么),
    int getMsg = getchar();
    printf("n : %d\n",n);
    printf("get msg : %d\n",getMsg);//以转义字符的形式输出
    scanf("%f\n",&b);
    scanf("%c",&c);
    printf("show msg : %d,%f,%c\n",a,b,c);
    return 0;
}
/* 输入a=1时,
scanf("%d",&a)拿1,
n判断为真(1),
然后回车int getMsg = getchar()拿到\t(回车),
printf("n : %d\n",n)输出1,
getMsg输出\t(回车)的ASCII值为10,
然后继续输入scanf("%f\n",&b)为3.14,
继续输入scanf("%c",&c)为c,
最后的结果为1,3.140000,c   */

/* 输入a=q时,
scanf("%d",&a)拿空值(0),
n判断为假(0),
int getMsg = getchar()拿到q,
printf("n : %d\n",n)输出0,
getMsg输出q的ASCII值为113,
然后继续输入scanf("%f\n",&b)为3.14,
继续输入scanf("%c",&c)为c,
最后的结果为0,3.140000,c     */

8.类型转换

  • 概念:不一致但相互兼容的数据类型,在同一表达式中将会发生类型转换。

  • 转换模式:

    • 隐式转换:系统按照隐式规则自动进行的转换

    • 强制转换:用户显式自定义进行的转换

  • 隐式规则:从小类型向大类型转换,目的是保证不丢失表达式中数据的精度

隐式转换示例

char  a = 'a';
int   b = 12;
float c = 3.14;
float x = a + b - c; // 在该表达式中将发生隐式转换,所有操作数被提升为float

强制转换:

用户强行将某类型的数据转换为另一种类型,此过程可能丢失精度

char  a = 'a';
int   b = 12;
float c = 3.14;
int d = (int)c;
float x = a + b - (int)c; // 在该表达式中a隐式自动转换为int,c被强制转为int

注意:不管是隐式转换,还是强制转换,变换的都是操作数在运算过程中的类型,是临时的,操作数本身的类型不会改变,也无法改变。

数据类型的本质

  • 概念:各种不同的数据类型,本质上是用户与系统对某一块内存数据的解释方式的约定。

  • 推论:

    • 类型转换,实际上是对先前定义时候的约定,做了一个临时的打破。

    • 理论上,可以对任意的数据做任意的类型转换,但转换之后的数据不一定有意义。

整型数据尺寸

  • 概念:整型数据尺寸是指某种整型数据所占用内存空间的大小

  • C语言标准并未规定整型数据的具体大小,只规定了相互之间的 “ 相对大小 ” ,比如:

    • short 不可比 int 长

    • long 不可比 int 短

    • long 型数据长度等于系统字长(32位操作系统(2^32)long是占用4字节,64位操作系统(2^64)long 是占用8字节)(考试会考)

  • 系统字长:CPU 一次处理的数据长度,称为字长。比如32位系统、64位系统。

  • 典型尺寸:

    • char 占用1个字节

    • short 占用2个字节

    • int 在16位系统中占用2个字节,在32位和64位系统中一般都占用4个字节

    • long 的尺寸等于系统字长

    • long long 在32位系统中一般占用4个字节,在64位系统中一般占用8个字节

  • 存在问题:

    • 同样的代码,放在不同的系统中,可能会由于数据尺寸发生变化而无法正常运行。

    • 因此,系统标准整型数据类型,是不可移植的,这个问题在底层代码中尤为突出。

可移植性整型

  • 概念:不管放到什么系统,尺寸保持不变的整型数据,称为可移植性整型(一个整形数据(类型)在所有系统下都不报错)

  • 关键:typedef

typedef int int32_t;  // 将类型 int 取个别名,称为 int32_t
typedef long int64_t; // 将类型 long 取个别名,称为 int64_t
  • 思路:

    1. 为所有的系统提供一组固定的、能反应数据尺寸的、统一的可移植性整型名称

    2. 在不同的系统中,为这些可移植性整型提供对应的 typedef 语句

  • 系统预定义的可移植性整型:

int8_t
int16_t
int32_t
int64_t
​
uint8_t
uint16_t
uint32_t
uint64_t
//练习:自定义以上数据类型
pid_t
time_t
size_t

demo:

#include <stdio.h>
​
// 给变量取别名称为可移植数据类型
typedef char int8_t;
typedef unsigned char uint8_t;
typedef short int16_t;
typedef unsigned short uint16_t;
typedef int int32_t;
typedef unsigned int uint32_t;
​
​
int main(int argc, char const *argv[])
{
    int8_t a = 'p';
    printf("%c\n",a);
    return 0;
}

​ ​

提问:

有时候我们需要使用 int32_t 类型变量代替 int 类型变量的原因是什么?

int是系统基本的数据类型,其长度在不同平台下的大小尺寸是有区别的,为了使同一份代码能够在不同的操作系统下面运行,并且尺寸不发生改变,一般使用类似于int32_t这样的可移植类型来定义数据,这些类型是不同平台下对基本数据类型的封装,然后统一发布,这些移植的数据类型一般是放在头文件中,比如/usr/include/stdin.h

  • 7
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值