二 .C语言的数据类型
C语言提供了多种数据类型,可以用于存储不同类型的数据。以下是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
思路:
为所有的系统提供一组固定的、能反应数据尺寸的、统一的可移植性整型名称
在不同的系统中,为这些可移植性整型提供对应的 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
更多推荐
所有评论(0)