NeoRAGEx2002曾经有一篇文章提到这个问题,但是有很多内容并没有包括,例如const和__declspec。

最近我遇到一些这方面的问题,感觉有必要做一个系统性的总结。后来经过一些实验,得出了一些结论,在这里分享给大家。

C风格变量声明

C风格的变量声明,如

  1. extern __declspec(dllexport) void(__stdcall * const p[10])(int a, int b);

和其他语言都不一样,规则很不直观,需要一些理解

要理解它为什么是现在这样,首先需要理解它的思路,最初C的类型的写法,模拟了变量的实际使用

例如

  1. #include <functional>
  2. int add(int a, int b)
  3. {
  4. return a + b;
  5. }
  6. int main(int argc, char **argv)
  7. {
  8. int a[10] = {};
  9. int a0 = a[0];
  10. int *p = &a0;
  11. int p0 = *p;
  12. int (*f)(int a, int b) = add;
  13. int result = (*f)(1, 2);
  14. int result2 = f(1, 2); //使用函数指针时可以将(*f)简记为f,但声明时不能,因为会和函数原型混淆
  15. int g(int a, int b); //函数原型
  16. auto h = static_cast<int (*)(int a, int b)>(f); //转换时,需要用到没有变量名的类型声明,此时只需要去掉变量名本身
  17. auto i = std::function<int(int a, int b)>(h); //std::function的参数是函数本身的类型
  18. typedef int FuncType(int a, int b);
  19. auto j = std::function<FuncType>(i); //可以用typedef来将函数本身的类型起一个名字
  20. return 0;
  21. }

因此,要声明一个变量,可以先写出它的实际的用法,再转换成变量声明

比如说我想要写一个包含10个函数指针元素的数组,函数为(int, int) -> int,可以先写调用

  1. int b = (*a[0])(1, 2);

再从调用写声明

  1. int (*a[10])(int a, int b);

注意C中的运算符结合优先级,首先是按括号,然后是右边的索引[]、函数()、成员.或者->,然后是左边的*

C风格变量声明 - const, method const

  1. int add(int a, int b)
  2. {
  3. return a + b;
  4. }
  5. class A
  6. {
  7. private:
  8. int a{};
  9. int b{};
  10. public:
  11. void foo() const //函数后方的const表示函数调用时this为const的
  12. {
  13. this->a = 0; //编译错误,this为A * const;
  14. b = 0; //编译错误
  15. const_cast<A *>(this)->a = 0; //使用const_cast将this转换为A *则可以编译成功
  16. }
  17. };
  18. int main(int argc, char **argv)
  19. {
  20. //const加一个类型名和类型名加const效果相同
  21. const int a1 = 0;
  22. int const a2 = 0;
  23. //const不与后方的*结合
  24. const int *a3 = 0;
  25. int const *a4 = 0;
  26. a3 = a4; //a3、a4为到不可改变的int的指针,但指针本身可以改变
  27. //const与前方的*结合
  28. int (* const a5) = 0;
  29. int * const a6 = 0;
  30. a5 = a6; //编译错误,a5不可改变
  31. //函数指针加const的规则与正常指针相同
  32. int (* const f)(int a, int b) = add;
  33. //成员函数指针加const的规则与正常指针相同
  34. void (A::* const g)() const = &A::foo;
  35. return 0;
  36. }

C风格变量声明 - extern, __declspec, __stdcall等

extern、__declspec(Visual C++)以及一些其他链接标记,是变量的属性,直接加在最前面

__stdcall(Visual C++)等调用规范,是函数的属性,加在函数名称前面或者函数指针的*前面

这样我们就可以明白下面这个声明的含义

  1. extern __declspec(dllexport) void(__stdcall * const p[10])(int a, int b);
  2. //一个extern __declspec(dllexport)的变量p
  3. //p是一个长度为10的数组,数组的每个元素是void(__stdcall * const)(int a, int b)
  4. //这些元素是一个const的函数指针,函数本身的调用规范是__stdcall
  5. //函数的参数是(int a, int b),返回值是void

再议C风格变量声明的更多相关文章

  1. 再议Java中的static关键字

    再议Java中的static关键字 java中的static关键字在很久之前的一篇博文中已经讲到过了,感兴趣的朋友可以参考:<Java中的static关键字解析>. 今天我们再来谈一谈st ...

  2. TypeScript 素描-变量声明

    博文读自 TypeScript 官方文档而来,不具有学习性,仅是本人学习时记录以供日后翻阅 ,有学习TypeScript的朋友还请去看更为详细的官方文档 /* 变量声明在之前的js中一直是使用var关 ...

  3. 变量声明---let,const,解构

    let在很多方面与var是相似的,但是可以帮助大家避免在JavaScript里常见一些问题. const是对let的一个增强,它能阻止对一个变量再次赋值. 块作用域 当用let声明一个变量,它使用的是 ...

  4. JavaScript变量声明提前

    上周四吃完午饭,leader发了一道JavaScript的题目给我们做,我们Team里面有做前端的,有做后台的,也有做mobile web的,所以大家对题目的理解各自都不一样,然后在QQ讨论组里面进行 ...

  5. 浅谈C语言变量声明的解析

    C语言本身提供了一种不甚明确的变量声明方式——基于使用的声明,如int *a,本质上是声明了*a的类型为int,所以得到了a的类型为指向int的指针.对于简单类型,这样声明并不会对代码产生多大的阅读障 ...

  6. 变量声明提升 Vs. 函数声明提升

    1. 变量声明提升 先看以下代码: 1)var in_window = "a" in window; console.log(in_window); 2)var in_window ...

  7. Python的类变量和对象变量声明解析

    Python的类和C++一样,也都是存在两种类型的变量,类变量和对象变量!前者由类拥有,被所有对象共享,后者由每个对象独有.这里我主要想讨论一下他们的声明办法. 首先说的是对象变量: 只要是声明在类的 ...

  8. JavaScript中的作用域与函数和变量声明的提升

      var foo = 1; function bar() {     if (!foo) {         var foo = 10;     }     alert(foo); } bar(); ...

  9. 【C语言探索之旅】 第一部分第四课第二章:变量的世界之变量声明

    内容简介 1.课程大纲 2.第一部分第四课第二章:变量的世界之变量声明 3.第一部分第四课第三章预告:变量的世界之显示变量内容 课程大纲 我们的课程分为四大部分,每一个部分结束后都会有练习题,并会公布 ...

随机推荐

  1. .NET(C#)中的DataSet、string、DataTable等对象转换成Json

    ConvertJson.cs类 using System; using System.Collections.Generic; using System.Text; using System.Data ...

  2. 15、java中的内部类介绍

    内部类顾名思义就是定义在类中的类,下面做一个简单介绍: 内部类的访问规则:1,内部类可以直接访问外部类中的成员,包括私有. 之所以可以直接访问外部类中的成员,是因为内部类中持有了一个外部类的引用,格式 ...

  3. 在Ubuntu14.04系统POWER8服务器上搭建Docker Registry服务

    本文描述了如何在POWER8服务器上搭建一个本地化的Docker镜像仓库,主要涉及镜像制作,Docker Registry服务启动等.希望能够对在非X86服务器上搭建Docker仓库的同学提供参考. ...

  4. SQL 递归树 子父节点相互查询

    if object_id('[tb]') is not null drop table [tb] go create table [tb]([modeid] int,modename varchar( ...

  5. opengl入门学习

    OpenGL入门学习 说起编程作图,大概还有很多人想起TC的#include <graphics.h>吧? 但是各位是否想过,那些画面绚丽的PC游戏是如何编写出来的?就靠TC那可怜的640 ...

  6. PL/SQLDeveloper导入导出Oracle数据库方法

    前一篇博客介绍了Navicat工具备份Oracle的方法,这篇博客介绍一下使用PL/SQL Developer工具导入导出Oracle数据库的方法. PL/SQL Developer是Oracle数据 ...

  7. 常见jquery插件

    1.JQuery Tooltipster 2.Bootstrap 3.jQuery UI 1.10

  8. java.lang.ArrayIndexOutOfBoundsException: 1

    数组越界 但是我这个也不是这个原因: 在CuiShouDetail.jsp 里,如果 添加上 QiTaDianHua,如果为空就会报错,别的都么有问题null,或者是空格,或者是有数据 1. Stri ...

  9. 浏览器内核控制Meta标签

    国内的主流浏览器都是双核浏览器:基于Webkit内核用于常用网站的高速浏览.基于IE的内核用于兼容网银.旧版网站.以360的几款浏览器为例,我们优先通过Webkit内核渲染主流的网站,只有小量的网站通 ...

  10. 详解c++指针的指针和指针的引用(转)

    http://www.cnblogs.com/li-peng/p/4116349.html