• 在定义指针的时候,写成「int* p;」和「int *p;」哪个更好?


    在学习到C++的“引用”之前,我是支持在纯C中使用

    int *p;

    这种写法的。相比直接将int*理解为指针(虽然更human friendly一些),上面的“运算式”写法避免了对*符号理解的二义性,即可以永远将*理解为“按址取值”的一个运算符,“p类型为指针”的结论由编译器根据“*pint类型”这个声明反推出来。更重要的是这个理解方式可以应用到更加复杂的类型声明语句中,比如典型的

    char *argv[];

    要声明的变量名argv按照既定的运算顺序(一元运算符优先级最高,且遵循从右向左的结合顺序),先执行数组索引运算,再执行按址取值运算,最终得到char类型,那么就可以反推出

    argv[i]为指向char的指针,从而argv为一个由char指针组成的数组。当然这个例子即使用char* argv[]理解也是OK的,还不能体现前者的优势,那么像下面这样带括号的呢?

    int (*p)[N];

    p在这里先执行按址取值运算,再执行数组索引运算,最终得到int类型,从而反推出

    *p为一个长度为N的int数组,从而p为指向这个数组的一个指针

    这样子理解虽然对编译器比较友好而反人类,但确实是精准无歧义的。

    再比如二维数组,

    1. int a[M][N];
    2. int (*p)[N] = a;

    凭什么下面的p能够无需类型转换地指向上面的a?仔细研究下就能体会到这种贴近编译器的理解方式的强大之处。甚至包括函数指针都可以按上述方式理解。

    ========分割线========

    顺嘴可以说下强制类型转换把声明表达式中的变量名和分号去掉,两侧套上括号不就结了?反正变量名的位置可以根据运算符的前后缀特性推出来,并不影响编译器的类型推导。比如

    p2 = (int (*)[N])some_pointer;

    虽然int (*)[N]这个类型转换符看起来挺复杂,但是只需考虑到*是前缀运算符,[]是后缀运算符,隐藏的变量名位置就显而易见了(没错正是int (*p)[N],这个填充是唯一的)。

    ========又一分割线========

    int *p = &a;

    该作何解释的问题。这个问题其实只要换成一个像上面一样复杂的声明就好理解了

    1. int a[M][N];
    2. some_type line = a[0]; /* a的第一行,一个长度N的数组 */
    3. int (*p)[N] = &line; /* p作为一个指向数组的指针的声明初始化 */

    你会觉得这个初始化语句等价于

    1. int (*p)[N];
    2. p = &line;

    还是

    1. int (*p)[N];
    2. (*p)[i] = &line;

    呢?

    即使不好分析p是什么,(*p)[i]是一个int是显然的结论,由此可看出无论p的声明表达式有多复杂,初始化语句的等号左边永远都会是一个由保留关键字int/char/long/float/double定义的基本数据类型,如果后者成立,一切派生类型的初始化就都完犊子了。所以合理的理解方式只能是前者。

  • 相关阅读:
    Pinpoint--基础--03--安装部署
    Spring Cloud Gateway 使用示例
    数据库上机实验5 数据库安全性
    Django 安装
    嵌入式软件设计专栏导读
    Docker高级-4.可视化工具Portainer/容器监控之CAdvisor+InfluxDB+Granfana
    Spring Boot 请求/actuator/beans 无法访问 返回404
    基于springboot+vue的西藏特产销售购物商城系统 elementui
    测试自动化的边缘:DevTestOps 和 DevSecOps
    java的序列化和反序列化
  • 原文地址:https://blog.csdn.net/m0_64407685/article/details/128006084