当前位置: 首页 > news >正文

建设学院2级网站的作用番禺网站

建设学院2级网站的作用,番禺网站,佛山专业做淘宝网站推广,河南省内 在哪个网站做商检表前言 C的类型描述方式是从C语言继承来的#xff0c;并且进行了扩充#xff08;例如引用、非静态成员函数、模板实参等#xff09;。但由于C语言中的类型描述方式就略微有点「反人类」#xff0c;再经C扩展后就有点「反碳基生物」了~ 是的#xff0c;当我第一次看到这种描…前言 C的类型描述方式是从C语言继承来的并且进行了扩充例如引用、非静态成员函数、模板实参等。但由于C语言中的类型描述方式就略微有点「反人类」再经C扩展后就有点「反碳基生物」了~ 是的当我第一次看到这种描述符的时候我也觉得能写出这玩意的肯定不是碳基生物……没准是用偏硅酸盐合成的新型物种…… void (Test::*(Test::*const )() const)() const更离谱的是上面这种类型如果通过type_traits以后会变成什么 using type void (Test::*(Test::*const )() const)() const; std::remove_const_ttype; // 这又是个啥类型呢好吧但愿这个引子可以让读者产生兴趣而不是劝退才怪……。 但真的理解了以后emmm…确实也是人类能理解的吧或许当我理解它的那一刻我的体内就已经在合成SiO32−SiO_3^{2-}SiO32−​了吧……【手动捂脸】 因此本篇就来盘一盘C的类型描述符带读者由浅入深一步一步征服它。 先从指针类型说起 指针类型其实是指针的默认解类型 首先我们应当知道「指针类型」本身指的就是「用于保存内存地址的变量类型」。而对于内存地址来说都是一样的不存在XXX类型的内存地址这种说法。所以照理说「指针类型」应该就都是一种类型表示这种类型的数据应当解释为内存地址。我们这里暂且把这种指针类型起名为ptr_t void Demo() {using ptr_t void *; // 可以先忽略这一行int a;double b;ptr_t p1 nullptr; // 空地址ptr_t p2 a; // 用于保存a的地址ptr_t p3 b; // 用于保存b的地址ptr_t p4 p2; // 用于保存p2的地址 }只是通常情况下我们仅仅拿到一个内存地址是没什么意义的难道只是为了把它打印出来吗拿这个内存肯定是为了操作这个内存上的数据而我们只知道这个数据的地址是不够的我们还得知道要用什么样的方式来解释存在这里的数据也就是「指针的解类型」。例如我们「用int的方式来解p2指针」也就是从p2的值所表示的地址处开始向高地址方向取sizeof(int)个字节的数据按照小端序组装起来并把首位认为是符号位然后读出或者写入这个整数。比如说把读出来的这个值赋给另一个变量y代码写作 int y *(int *)p2; // 表示把p2这个指针按照int方式解出来得到的值赋值给y但如果每次都去指定指针的解类型会很麻烦所以我们就希望能给这个指针添加一个「默认解类型」也就是说在定义这个指针类型的时候给它指定一个默认的解类型如果后续不指定类型直接解指针的话就用这种默认的解类型。 从C的语法上来说类型星号表示定义一个指定了默认解类型的指针类型。比如说 int *p5 a; // p5是指针类型默认解类型是intint z *p5; // 没有指定解类型的时候选用默认的解类型也就是int类型同理如果「默认解类型」是「一个指针类型」的话也是一样的 ptr_t *p6 p5; // p6的默认解类型是指针类型ptr_t p7 *p6; // 解出来就是ptr_t类型所以p7也是指针类型 // 但是因为ptr_t是未指定解类型的指针类型所以解的时候必须要指定解类型 int w *(int *)p7;那么如果我还希望解出来的指针类型也含有默认解类型的话就可以用「默认解类型」是「一个指定了『默认解类型』的指针」的指针类型 int **p8 p5; // p8的默认解类型是int *类型 // 所以对p8直接解指针后得到的就应该是int *类型 int *p9 *p8; // 而p9是默认解类型为int的指针类型解指针后得到int类型 int t *p9;上面的例子想表明的是 只要是指针类型都是用来保存内存地址的也就是说它的值仅仅表示地址。指针类型中星号前面的部分表示的「指针的默认解类型」。多级指针本质是「默认解类型为『指针类型』的指针类型」所以无论前面的类型多么复杂它都属于这个指针的默认解类型而不影响这个指针本身。 泛型指针类型其实是无默认解类型的指针类型 再回头来看看刚才这个ptr_t刚才有一句我让大家先忽略的那一行定义 using ptr_t void *;我们希望表示的是「不含默认解类型」的指针类型按照语法默认解类型是T的指针类型就是T *那么「不含」默认解类型的就可以理解为默认解类型是「空」的自然就是void *。 所以我们常说的「泛型」指针之所以能「泛」其实就是因为没有指定默认解类型而已并没有什么稀奇的。 指针类型转换本质是指针默认解类型转换 既然我们知道了指针类型其实表示的是它的默认解类型那么指针类型的转换自然是表示它默认解类型的转换了 void *p a; int *p2 (int *)p; // void *转换为int *其实就是默认解类型从空变为int所以前面例子中我们「指定解类型」的解指针方式本质就是把指针转换为「默认解类型是对应类型」的指针类型再去解指针 int y *(int *)p; // 其实是把p转换为int *类型再解指针自然得到int类型那么把「含有默认解类型的指针类型」转换为「不含默认解类型的指针类型」应当是一种较为安全的静态转换因此我们使用static_cast来代替之前C风格的转换 int *p a; void *p2 static_castvoid *(p); // int * → void *另外上面这种转换也支持隐式转换 int *p a; void *p2 p; // int * → void *同理给「不含默认解类型的指针类型」赋予一个默认解类型变为「含有默认解类型的指针类型」也是一种较为安全的静态转换所以同样使用static_cast void *p a; int *p2 static_castint *(p); // void * → int *不过这种转换不支持隐式转换必须强转。 而「默认解类型为A的指针类型」转换为「默认解类型是B的指针类型」这里的AB都不是void则被认为是一种相对不安全的转换因为改变指针的默认解类型相当于「重新解释了」指针所指数据的含义。因此这里要使用reinterpret_cast int *p a; char *p2 reinterpret_castchar *(p); // int * → char *const修饰的指针 const关键字在C/C中并不是代表真正的常量而是应当理解为read-only也就是只读。用const修饰的类型不可被修改只能读取。 而对于指针来说指针本身既然是一种数据类型那么也就存在「只读的指针类型」。另一方面指针的默认解类型也可能是一种只读类型所以我们主要是要区分这个const修饰的是指针类型本身还是指针的默认解类型中的类型。 int *p1; // 指针本身可变默认解类型是int const int *p2; // 指针本身可变默认解类型是const int int *const p3; // 指针本身不可变默认解类型是int const int *const p4; // 指针本身不可变默认解类型是const int当我们理解了何为指针的解类型后其实就很好判断了。如果const出现在解类型中那么它与指针本身是否可变无关只有在修饰指针本身的时候才表示这个指针变量是个只读变量。 而在指针类型的表达式中我们首先应当找到那个「特殊的星号」由这个星号隔开其余的内容都是解类型。 比如在int *const p3中星号只有一个自然就是那个特殊的或者说最内层的星号前面外面的都是解类型而这个const出现在特殊型号的右边内部因此它修饰的是这个变量本身那么我们就说p3是只读类型。而剩下的部分是它的解类型也就是int。 同理在const int *p2中也只出现了一个星号它就是特殊的那个。星号后面没有const修饰所以p2是可变的而它的解类型是const int也就是说这里的const修饰的是解类型。 C中提供了一个模板工具std::remove_const用于去掉类型的const修饰这里要注意的是它去掉的是类型本身的const而跟解类型是完全没有关系的会原样保留比如说 std::remove_const_tconst int *; // const int * std::remove_const_tint *const; // int * std::remove_const_tconst int *const; // const int *那么对于多级指针呢同理我们需要找到特殊的星号最内层的星号由他隔离开外边都是解类型。 int *const *p1; // p1可变解类型是int *const int **const *p2; // p2可变解类型是int **const const int **p3; // p3可变 解类型是const int * int *const *const p4; // p4不可变解类型是int *const 所以它们如果去掉const也是同理只会去掉那个修饰变量本身的const而解类型不会改变 std::remove_const_tint *const *; // int * const * std::remove_const_tint **const *; // int **const * std::remove_const_tconst int **; // const int ** std::remove_const_tint *const *const; // int *const *总结就是一句找到最内层的星号目前的例子其实都是最右边的星号由它分隔外面目前例子都是左边都表示解类型与变量本身无关里面目前例子都是右边才是修饰变量本身的如果出现了const就表示变量本身不可变。 后面的章节将会介绍真正的「里面」和「外面」并不符合前面的「右边」和「左边」规律的例子。 数组类型 单纯的数组类型 笔者采访过一些C程序员以C为主要开发语言的从业者惊奇地发现有一多半的人都不了解「数组类型」。尽管他们可能天天见、天天使用但从来没有意识到这种类型的存在形态。 举个例子来说 int arr[] {1, 2, 3};请问arr是什么类型数组类型指针类型int *类型 揭晓答案arr是int [3]类型解释为含有3个int元素的数组类型。我相信大家对「数组类型」肯定不陌生也能解释清楚它的元素类型、个数等。但是乍一看到这个int [3]类型还是有很多人会懵圈的。 的确我们并不容易注意到arr的类型就是int [3]这主要是因为C的数组类型通常情况下只会在定义的时候用到之后就全部改用指针和偏移量去操作了。 那么现在就请读者知晓数组类型本身包含了「元素类型」和「元素个数」这两个因素的。它是独立存在一种类型并不是指针/结构体/整数等的语法糖。只不过数组类型可以隐式转换为首元素的指针类型 auto p arr; // p是int *类型 // 也就是等价于 int *p (int *)arr;所以我们一定要清楚数组是数组指针是指针这是两种不同的类型只是可以隐式转换而已。要想验证也很简单用std::is_same来验证或者直接通过sizeof也可以间接验证 int arr[] {1, 2, 3}; auto p arr;std::is_same_vdecltype(arr), decltype(p); // false std::is_same_vint [3], int *; // false// 假设64位环境 sizeof(arr); // 12 sizeof(p); // 8 sizeof(int [3]); // 12 sizeof(int *); // 8识别清数组类型会对我们在模板实例化时避坑有很大帮助。比如说下面的写法就是有问题的 std::shared_ptrint * p new int[5];因为p被识别为int *类型的智能指针那么在p析构时只会调用delete方法而不是delete []使得这片堆空间没有被正确释放。 正确的写法是 std::shared_ptrint[] p new int[5]; // 要用数组类型而不是指针类型再比如模板的自动类型推导中如果传入数组也会被识别为数组类型 template typename T struct Test {Test(const T t) {} };void Demo() {int arr[] {1, 2, 3};auto p arr;Test t1{arr}; // t1是Testint[3]类型Test t2{p}; // t2是Testint *类型 }const数组类型 那么是否存在不可变数组类型呢我们知道数组一旦确定它的元素类型不可变元素个数也不可变所以但从数组的两个因素来讲所有的数组都是不可变的因此也就不存在所谓可变还是不可变数组类型。 那么对于数组来说唯一可以控制是否可变的就是元素类型因此只存在const T [N]类型而不存在类似于T (const) [N]之类的。注意T const [N]和const T [N]等价const都是修饰元素类型的。 既然const是修饰元素类型的那么它隐式转换为指针后这个const也一定修饰的是解类型而不是指针本身 const int arr[] {1, 2, 3}; auto p arr; // p的类型是const int *数组指针类型 数组指针类型其实就是指「默认解类型是数组类型的指针类型」。一定要注意这跟「数组首元素指针类型」是不同的数组类型不能转化成它而是要通过取地址运算得到 int arr[] {1, 2, 3}; auto p arr; // p的类型是int (*)[3]这里我们不得不引出C/C中类型描述符的一大绕不开的「缺陷」了那就是类型描述符并不一定是从左向右而是可能从里向外。前面章节我们提到过「内部」和「外部」的说法也是为了跟这种类型描述符的特点相对应。 从「数组类型」开始就已经符合这种由内向外的描述方式了 int arr[3];arr是int [3]类型但并没有写作int[3] arr而是写作了int arr[3]。我们注意到变量名被夹在了类型描述符的中间。对于更复杂的这种类型描述方式来说我们需要由内向外来解释首先要找到变量名然后逐层向外来阅读。例如 int (*p)[3];首先找到变量名p由括号限定的最内层有一个型号表示p本身是一个指针。那么再向外一层则表示指针的解类型这里它的解类型是int [3]。所以综合来说p是一个解类型为int [3]类型的指针也就是我们通常所说的「数组指针」类型。 与之对应的一个容易搞混的是 int *q[3];同样先找到变量名q向外一层则是数组右边表示数组元素个数左边表示数组元素类型。所以q是数组元素类型是int *也就是我们通常说的「指针数组」。 【第二篇待更】
http://www.hkea.cn/news/14257527/

相关文章:

  • 可以做哪方面的网站建立新网站要多少钱
  • 天元建设集团有限公司青岛分公司张德平不干了温州网站排名优化
  • 无锡网站建设 推荐无锡立威云商wordpress网站无法登陆
  • 个人网站建设怎么样广州做公司网站的公司有哪些
  • 网站开发项目付款方式湘潭做网站品牌磐石网络
  • 济南网站建设(选聚搜网络)wordpress七牛云镜像
  • 网站运营一般做那些分析口碑营销名词解释
  • 注册网站要注意什么做移动网站快速排
  • 台州网站建设公司哪个好网站制作需要什么人员
  • 营销网站建设创意wordpress底部美化
  • 网站建设设计文档模板怎么帮别人做网站
  • 建设工程人才招聘信息网站wordpress 时间轴插件
  • 谁可以做网站优化排名推广做网站网站被抓没盈利
  • 租网站需要多少钱网站建设多少钱比较合适
  • 网站建设时间计划书互联网平台公司有哪些
  • 效果图网站密码破解网站建设的目标是什么?提供了哪些栏目?
  • 正规刷手机单做任务网站海口网站建设呢
  • 企业网站建设价钱修改wordpress 表格
  • 自建微网站服务器旅游网站建设受众分析
  • 网站备案掉了海口网站制作
  • 枣阳网站开发公司哪家好网站建设如何财务处理
  • 网站网页策略上线一个网站需要多少钱
  • 常州专业网站建设公司咨询wordpress会员插件
  • wordpress搭建英文网站打开小程序入口直接进入
  • 罗湖医院网站建设商洛网站建设公司电话
  • 做外贸网站公司网站源码什么意思
  • 网站设计原型图怎么做网站建设套餐怎么样
  • php 企业网站管理系统免费学编程国内网站
  • 凤翔做网站wordpress getshell
  • 手机版网站设计广西网站建设公司电话