C/C++ 数据类型

2023-11-07

        在C语言中,数据类型指的是用于声明不同类型变量或者函数的一个广泛的系统或者抽象。变量类型决定了变量存储占用的空间,以及如何解析存储的位模式。也是说:(1)数据类型可以理解为固定内存大小的别名;(2)数据类型是创建变量的模子,具体使用哪种磨具(包含自定义)需要基于实际问题抽象结果确定。

C中可以分为以下几种数据类型:

序号 类型与描述
1 基本类型:
它们是算术类型,包括两种类型:整数类型和浮点类型。
2 枚举类型:
它们也是算术类型,被用来定义在程序中只能赋予其一定的离散整数值的变量。
3 void 类型:
类型说明符 void 表明没有可用的值。
4 派生类型:
它们包括:指针类型、数组类型、结构类型、共用体类型和函数类型。

数组类型和结构类型统称为聚合类型,函数类型指的是函数的返回值类型。

1. 整数类型

对于每种类型的整数类型,可以使用sizeof来确定其大小。下表列出了关于标准整数类型的存储大小和值范围的细节:

类型 存储大小 值范围
char 1 字节 -128 到 127 或 0 到 255
unsigned char 1 字节 0 到 255
signed char 1 字节 -128 到 127
int 2 或 4 字节 -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int 2 或 4 字节 0 到 65,535 或 0 到 4,294,967,295
short 2 字节 -32,768 到 32,767
unsigned short 2 字节 0 到 65,535
long 4 字节 -2,147,483,648 到 2,147,483,647
unsigned long 4 字节 0 到 4,294,967,295

2. 浮点类型

下表列出了关于标准浮点类型的存储大小、值范围和精度的细节:

类型 存储大小 值范围 精度
float 4 字节 1.2E-38 到 3.4E+38 6 位有效位
double 8 字节 2.3E-308 到 1.7E+308 15 位有效位
long double 16 字节 3.4E-4932 到 1.1E+4932 19 位有效位

头文件 float.h 定义了宏,在程序中可以使用这些值和其他有关实数二进制表示的细节。下面的实例将输出浮点类型占用的存储空间以及它的范围值:

 3. void 类型

void 类型指定没有可用的值。它通常用于以下三种情况下:

序号 类型与描述
1 函数返回为空
C 中有各种函数都不返回值,或者您可以说它们返回空。不返回值的函数的返回类型为空。例如 void exit (int status);
2 函数参数为空
C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如 int rand(void);参数为空和任意参数或者变可变参数是两个不同的概念,没有参数值得是在进行函数调用的时候直接使用没有任何入参。
3 指针指向 void
类型为 void * 的指针代表对象的地址,而不是类型。例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。void类型指针需要注意:(1) C语言规定只有相同类型的指针才可以相互赋值; (2)void* 指针作为左值用于“接收”任意类型的指针,这个特点类型的指针在我们试图将函数变成一个较为通用的二级指针函数时非常有用; (3) void* 指针作为右值使用时需要进行强制类型转换

3.1 void函数与返回值说明

#include <stdio.h>
void f(void) //无参无返回值
{

}

void main()

{
    f();    //ok

    // 不存在void变量,C语言没有定义void究意是多大内存的别名,没有void标尺,无法在内存中裁出大小
    void var;        //error,不可以是void型的变量
    void array[5];   //error,同上
    void* pv;        //ok,void*的指针是可以的。

    // 在ANSI C编译器中无法通,支持GNU标准的gcc下为void大小为1,是合法的
    printf("sizeof(void)=%d\n",sizeof(void));
}

3.2 void指针

int* pI = (int*)malloc(sizeof(int));
char* pC = (char*)malloc(sizeof(char));
void* p = NULL;
int* pni = NULL;
char* pnc = NULL;
p = pI; //ok,void*指针p可接收任何类型的指针
pni = p; //error,void*须强制类型转换,即pni =(int*)p;
p = pC;  //ok
pnc = p; //error,应为pnc=(char*)p;


#include <stdio.h>

void MemSet(void* src, int length, unsigned char n)
{
    unsigned char* p = (unsigned char*)src;
    int i = 0;

    for(i=0; i<length; i++)
    {
        p[i] = n;
    }
}



int main()

{
    int a[5];//这里可以是任何其他类型,如char a[5];double a[5]等。
    int i = 0;

    MemSet(a, sizeof(a), 0);
 
    for(i=0; i<5; i++)
    {
        printf("%d\n", a[i]);
    }

    return 0;
}

4. 类型转换

数据类型转换:C 语言中如果一个表达式中含有不同类型的常量和变量,在计算时,会将它们自动转换为同一种类型;在 C 语言中也可以对数据类型进行强制转换;类型转换可以是隐式的,由编译器自动执行,也可以是显式的,通过使用强制类型转换运算符来指定。在编程时,有需要类型转换的时候都用上强制类型转换运算符,是一种良好的编程习惯。

4.1 自动转换规则:

(1)隐式类型的转换——编译器主动进行的类型转换

  char c = 0; //变量c占用1个字节

  short s = c;  //c到s隐式类型转换

  int i = s;    //s到i隐式类型转换

  long l = i;   //i到l隐式类型转换

▲注意:低类型到高类型的隐式类型转换是安全的,不会产生截断;高类型到低类型的隐式类型转换是不安全的,导致不正确的结果

(2)隐式类型转换的发生点

  ①算术运算式中,低类型转换为高类型

  ②赋值表达式中,表达式的值转换为左边变量的类型

  ③函数调用时,实参转换为形参的类型

  ④函数返回值,return表达式转换为返回值类型

  •  a)浮点数赋给整型,该浮点数小数被舍去;
  •  b)整数赋给浮点型,数值不变,但是被存储到相应的浮点型变量中;

 4.2. 强制类型转换形式: (类型说明符)(表达式) (type_name) expression

(1)强制类型转换的语法:(Type)var_name或(Type)value

(2)强制类型转换的结果

  ①目标类型能够容纳目标值:结果不变

  ②目标类型不能容纳目标值:结果将产生截断

(3)注意:不是所有的强制类型转换都能成功,当不能进行强制类型转换,编译器将报错。

4.5. C++强制类型转换

类型转换是项目中难以避免的,但是C语言转换过于粗暴,编译器难以帮助进行错误判定,出错以后难以定位。C++中新增了四种强子类型转换static_cast、const_cast、reinterpret_cast、dynamic_cast。其相应的应用场景:

(1)static_cast:用于基本类型之间的转换,用于有继承关系类对象之间的转换,不能用于基本类型指针间的转换。

(2)const_cast:用于指针或者引用之间的强制类型转换,目标去除对象的只读属性

(3)reinterpret_cast:用于指针类型间的转换或者指针和整数之间的类型转换

(4)dynamic_cast:用于有继承关系或者交叉关系之间类指针之间的转换,具有类型检查的功能,同时需要虚函数的支持

#include <stdio.h>

void static_cast_demo()
{
    int i = 0x12345;
    char c = 'c';
    int* pi = &i;
    char* pc = &c;

    c = static_cast<char>(i);
    pc = static_cast<char*>(pi);
}

void const_cast_demo()
{
    const int& j = 1;
    int& k = const_cast<int&>(j);

    const int x = 2;
    int& y = const_cast<int&>(x);

    int z = const_cast<int>(x);

    k = 5;

    printf("k = %d\n", k);
    printf("j = %d\n", j);

    y = 8;

    printf("x = %d\n", x);
    printf("y = %d\n", y);
    printf("&x = %p\n", &x);
    printf("&y = %p\n", &y);
}

void reinterpret_cast_demo()
{
    int i = 0;
    char c = 'c';
    int* pi = &i;
    char* pc = &c;

    pc = reinterpret_cast<char*>(pi);
    pi = reinterpret_cast<int*>(pc);
    pi = reinterpret_cast<int*>(i);
    c = reinterpret_cast<char>(i);
}

void dynamic_cast_demo()
{
    int i = 0;
    int* pi = &i;
    char* pc = dynamic_cast<char*>(pi);
}

int main()
{
    static_cast_demo();
    const_cast_demo();
    reinterpret_cast_demo();
    dynamic_cast_demo();

    return 0;
}

根据我们上面的分析,第11行是错误的,22行是错误的,47行是错误的,54行是错误的。 

一、C强制类型转换

格式: TYPE a = (TYPE) b;

缺点: 1.多个转换之后无法查找具体的转换类型,即转换不明确;

            2.不能进行错误检查,容易出错。

二、C++四种类型转换方式:

(一)、const_cast

简介: const_cast是一个基于C语言编程开发的运算方法,其主要作用是:修改类型的const或volatile属性。使用该运算方法可以返回一个指向非常量的指针(或引用),就可以通过该指针(或引用)对他的数据成功元任意改变。

格式:  type-id a = const_cast <type-id>( expression )  

          说明:上述格式中,除了const或volatile修饰之外,type_id和expression的类型是一样的。(1).常量指针被转换成非常量指针,并且仍然指向原来的对象; (2).常量引用被转换为非常量的引用,并且仍然指向原来的对象。(3).const_cast一般用于修改形如const char *p指针。

用法: 

 说明: volatile和const类似。

(二)、static_cast

简介: static_cast  是一个C++运算符,功能是把一个表达式转换为某种类型,但没有运行时类型检查来保证传唤的安全性。

格式:  type-id a = static_cast <type-id>( expression )     

用法: 

1.用于类层次结构中基类(父类)和派生类(子类)之间的指针或引用的和转换。进行向上转换(将派生类的指针或引用转换成基类表示)是安全的;进行向下转换(将基类指针或者引用转换为派生类表示)时,由于没有动态类型的检查,所以是不安全的。

2.用于基本数据类型之间的转换,如把int转换为char,把int转换为enum。这种转换的安全性也要开发人员来保证

3.把空指针转换成目标类型的空指针。

4.把任何类型的表达式转换为void类型。

注意事项: static_cast 不能转换掉expression的const、volatile或者__unaligned属性。

(三)、dynamic_cast

简介: dynamic_cast是将一个基类对象指针(或引用)转换到继承类指针,dynamic_cast会根据基类指针是否真正指向继承类指针来做相应处理。dynamic_cast运算符涉及到编译器的属性设置,而且牵扯到的面向对象的多态性跟程序运行时的状态也有关系,所以不能完全的使用传统的替换方式来代替。但是也因此它最常用,是最不可缺少的一个运算符。

格式:  type-id a = dynamic_cast<type-id>( expression )

             说明:该运算符把expression转换成type-id类型的对象。Type-id 必须是类的指针、类的引用或者void*;如果 type-id 是类指针类型,那么expression也必须是一个指针,如果 type-id 是一个引用,那么 expression 也必须是一个引用。dynamic_cast运算符可以再执行期决定真正的类型。如果downcast是安全的(也就是说,如果基类指针或者引用确实指向一个派生类对象)这个运算符会传回适当转型过的指针。如果downcast不安全,这个运算符会传回空指针(即基类指针或者引用没有指向一个派生类对象)。--->此部分来源于百度

用法: 

1.dynamic_cast在类层次间进行向上转换时,效果和static_cast一样。

2.dynamic_cast在类层次间进行向下转换时,具有类型检查功能,更安全。

说明:  Parent类一定要有虚函数,否则会编译出错;static_cast没有这个限制。

                C++面向对象的思想中,虚函数是实现多态的必要路径,如果一个类有虚函数,则编译器会构建出一个虚函数表(virtual method table)来指示这些函数的地址,如果之类中重写了该虚函数,则虚函数表会将该函数指向新的地址。如果基类有虚函数表,子类会自动继承该表。另外:如果要用继承,那么一定要让析构函数是虚函数;如果一个函数时虚函数,那么在子类中也要是虚函数。

3.dynamic_cast的交叉转换。

说明: 对于单继承和多继承来说 : 单继承中,Base* pD = new Derived1;,可以用dynamic_cast和static_cast转换pd为Derived1;如果是多继承,Derived1继承Base和Base1,则只能用dynamic_cast将pd转换为Base1类型。

(四)、reinterpret_cast

简介: reinterpret_cast,是C++里强制类型转换符。

格式:  type-id a = reinterpret_cast<type-id>( expression )  

          说明:上述格式中,type_id必须是一个指针、引用、算术类型、函数指针或者成员指针。它可以把一个指针转换为一个整数,也可以把一个整数转换为一个指针(先把一个指针转换为一个整数,再把该整数转换为原来类型的指针,还可以得到原来的指针值)。

用法: 

操作符修改了操作数类型后,仅仅是重新解释了给出的对象的比特模型而没有进行二进制转换。---》危险

举个栗子:

4.4. 小结

(1)强制类型转换由程序员负责完成

  ①转换可能产生截断;

  ②转换不区分类型的高低

  ③转换不成功时,编译器给出错误信息

(2)隐式类型转换由编译器自动完成

  ①低类型向高类型的转换是安全的

  ②高类型向低类型的转换是不安全的

  ★标准C编译器的类型检查是比较宽松的,因此隐式类型转换可能带来意外的错误

5. typedef 自定义类型别名

        我们可以使用typedef给任意的组合体取名为某种实际项目中使用的类型,方便在后续的代码书写中使用这个整体来进行数据联合体操作。用法:typedef type new_name。

        typedef用于给一个己经存在的数据类型重命名,实际项目使用过程中我们一般会为我们项目从新命名unsigned int等类型变量,但是这个基本就是最基本算是入门的东西,不是任何高超的技术。但是typedef本质上不能产生新的类型,即便是结构体也是一样的,只是为了我们后期方便使用使用typedef给结构体定义另外一个名字。typedef重命名的类型可以在typedef语句之后定义,不能被unsigned和signed修饰(即不能再定义为无符号或有符号)。

        枚举类型,枚举类型是一种特别数据类型,其内部构成是我们之前讲到的基本数据类型,这里通过枚举的方式也是将某一部分常用的常量数据集合在一起,方便代码书写过程使用。当然也可以结合typedef来对这个结构体进行重命名,将这个结构体重新定义为一个别名的数据类型,方便代码书写过程使用。

#include <stdio.h>

typedef int Int32;

//先定义类型后重命名类型

struct _tag_point
{
    int x;
    int y;
};
typedef struct _tag_point Point;

//定义一个未命名类型,并用typedef重命名
typedef struct
{
    int length;
    int array[];
} SoftArray;

//先重命名,再定义类型——看似不合法、别扭,但实际上正确的
//可理解为,typedef并不给进行类型检查,只是简单去给一种类型起个别名,
//如,本例本struct _tag_list_node重命名为ListNode,以后编译过程中遇到
//ListNode,就用其实际的类型为struct _tag_List_Node来替换.
typedef struct _tag_list_node ListNode; //先重命名
struct _tag_list_node                   //再定义类型
{
    ListNode* next;
};

int main()
{
    Int32 i = -100;        // int
    unsigned Int32 ii = 0; //error,不能为重命名类型指定义为signed或unsigned
    Point p;               // struct _tag_point
    SoftArray* sa = NULL;  
    ListNode* node = NULL; // struct _tag_list_node*

    return 0;
}

6. bool数据类型

C语言中没有bool数据类型,表达式值为0表示假,非0表示真。所以在条件表达式判断语句(if(...))非常灵活,甚至都可以是指针类型条件表达式。为了使程序更清晰,在C语言中我们常常使用如下宏定义类标示C语言bool数据

typedef BYTE BOOL
#define TURE 1
#define FALSE 0

C++在C语言的基本类型系统之上增加了bool。C++中的bool可取的值只有true和false(在程序编写上使用true或false,在编译器内部还是用1或0)。理论上bool只占用一个字节。需要注意:true代表真值,编译器内部用1来表示;false代表非真值,编译器内部用0来表示。在C++中为了兼容C语言,C语言中非0的结果会被强制转换为C++中的TRUE,这个是由编译器完成。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C/C++ 数据类型 的相关文章

随机推荐

  • 2023华为OD机试真题-工作安排(JAVA、Python、C++)

    题目描述 小明每周上班都会拿到自己的工作清单 工作清单内包含n项工作 每项工作都有对应的耗时时长 单位h 和报酬 工作的总报酬为所有已完成工作的报酬之和 那么请你帮小明安排一下工作 保证小明在指定的工作时间内工作收入最大化 输入描述 输入的
  • Qt 事件过滤器

    通过前面的学习 我们已经知道 Qt 创建了QEvent事件对象之后 会调用QObject的event 函数处理事件的分发 显然 我们可以在event 函数中实现拦截的操作 由于event 函数是 protected 的 因此 需要继承已有类
  • 2023最新Web前端经典面试试题及答案-史上最全前端面试题(含答案)

    近期总结一一些面试题 都是企业的面试题笔记题 感觉薪资10k 15k的常见面试题 个人录制的最新Vue项目学习视频 B站 Vue2 第二版 后台管理系统项目实战 vue element ui vue经典全套系统案例讲解 哔哩哔哩 bilib
  • python中的 datetime 的使用

    python 中 datetime 的使用方法 介绍 所谓 datetime 其实就是 date time date 和 time的集合 下面介绍使用方法 常搭配logging记录日志 date from datetime import d
  • 三创赛优秀作品_三创赛优秀作品.doc

    PAGE PAGE 138 全国高校首届 创意 创新 创业 电子商务挑战赛 农舍吧 电子商务旅游网站 参赛策划书 北京邮电大学 开心吧 电子商务团队 团队成员 吴新军 林朝波 高有富 陈和磊 指导老师 胡 桃 2009年12月 TOC o
  • Map 和 Set 使用的区别和联系(建议收藏)

    我是目录 1 搜索 1 概念及场景 2 模型 2 Map 的使用 3 Set 的使用 表现 两个接口 Set 和 Map 接口 1 搜索 1 概念及场景 Map 和 set 是一种专门用来进行 搜索的容器 或者 数据结构 其搜索的效率与其具
  • 用递归求斐波那契数列

    2137 斐波那契数列 时间限制 1 Sec 内存限制 128 MB 提交 2116 解决 2242 提交 状态 讨论版 命题人 lym 题目描述 斐波那契数列 Fibonacci sequence 又称黄金分割数列 兔子数列 是数学家列昂
  • MyBatis-Plus:条件构造器Wrapper

    目录 1 Wrapper概述 1 1 Wrapper的继承关系 1 2 Wapper介绍 1 3 各个构造器使用区别 1 4 构造器常用方法 2 Wrapper常用构造器介绍 2 1 QueryWrapper 2 2 UpdateWrapp
  • 无向图的邻接矩阵与邻接表详细实现

    无向图的邻接矩阵 通过用邻接矩阵来表示无向图 如下无向图G1的邻接矩阵 无向图G1包含了 A B C D E F G 共七个顶点 而且包含了 A C A D A F B C C D E G F G 共七条边 由于这是无向图 所以 A C 和
  • Linux命令awk

    文章目录 Linux命令awk 1 搜索 etc passwd文件以root关键字开头的所有行 并输出该行的第7列 2 搜索 etc passwd文件以root关键字开头的所有行 并输出第1列 第6列 第7列 以逗号分隔 3 只显示 etc
  • PPT架构师架构技能图

    PPT架构师架构技能图 目录 概述 需求 设计思路 实现思路分析 1 软素质 2 核心输出 office输出 参考资料和推荐阅读 Survive by day and develop by night talk for import biz
  • 强智教务管理系统爬虫难关1

    强智教务系统的登录页面有个验证码 为了自动化和简单化 采用了联众答题模块 自动识别和验证 下面是我的点数 说明已经可以自动识别验证码并且可以使用了 当然 这个没有什么难度 今天解决的是账号密码加密问题 这个是我抓到的包 很显然 账号密码进行
  • Pyqt5 圆角窗口

    之前了解了通过样式表和绘画的方式 都感觉不太靠谱 样式表无法生效 绘画又会影响定义的其它窗口样式 后来发现还是setMask靠谱 def setMask self args setMask self QBitmap setMask self
  • 大端模式和小端模式

    一 什么是大端 什么是小端 0x123456在内存中的存储方式 大端模式 低地址 gt 高地址 0x12 0x34 0x56 小端模式 低地址 gt 高地址 0x56 0x34 0x12 不难看出大端模式比较符合人的直观认识 二 为什么会有
  • C++运算符重载总结

    一 C 操作符重载的意义 1 当运算符作用于类类型的运算对象时 可以通过运算符重载重新定义该运算符的含义 2 在模板编程实现的泛型编程中 对不同类型实现相同的语义 各自类通过操作符重载实现对应语义 例如 由模板类自己定义大小关系 FUNCT
  • 提升手机拍摄能力以满足用户社交需求,是个伪命题?

    本周话题 网上看到一个话题 分享给大家 目前各大厂商提升手机拍摄能力是为了满足用户社交需求 这是否是个伪命题 在所谓的社交满足感方面 拍照真的也许还不如微博手机型号的小尾巴 或是吃饭时把手机在桌扣过来露出的浴霸摄像头 18年款和19年款的手
  • [LeetCode] 7.整数反转 c++

    给你一个 32 位的有符号整数 x 返回将 x 中的数字部分反转后的结果 如果反转后整数超过 32 位的有符号整数的范围 2 31 2 31 1 就返回 0 假设环境不允许存储 64 位整数 有符号或无符号 输入 x 123 输出 321
  • QComboBox 中 activated信号与 currentIndexChanged信号的区别;及 items count,基本用法

    QComboBox中activated信号与currentIndexChanged信号的区别 QT ComBoBox的基本方法 int cnt ui gt comboBox abc gt maxCount int cntxx ui gt c
  • 【华为OD统一考试B卷

    华为OD统一考试A卷 B卷 新题库说明 2023年5月份 华为官方已经将的 2022 0223Q 1 2 3 4 统一修改为OD统一考试 A卷 和OD统一考试 B卷 你收到的链接上面会标注A卷还是B卷 请注意 根据反馈 目前大部分收到的都是
  • C/C++ 数据类型

    在C语言中 数据类型指的是用于声明不同类型变量或者函数的一个广泛的系统或者抽象 变量类型决定了变量存储占用的空间 以及如何解析存储的位模式 也是说 1 数据类型可以理解为固定内存大小的别名 2 数据类型是创建变量的模子 具体使用哪种磨具 包