C语言-解释复杂声明


目标:1.学会解析复杂声明的含义。如:int *(*x[10])(void);2.typedef简化复杂声明的名称;3.const的修饰

基本术语:

  • 声明符:“int a”就是一个声明符。
  • 标识符: 定义的变量名字,如: int a,那么a就是一个标识符。

1.两个原则:

  1. 始终从内往外读声明符,括号优先级高。即先定位用来声明的标识符,并且从此处的开始解释。

例1.声明 char *p。首先确定标识符为p;再解析*,它代表p是指针指向;再解析char,它表示指针指向char类型。即p是指向char类型的指针。
例2.声明 void (*pf)(int)。首先确定标识符为pf,在括号内解析*,pf是指针指向;其次(int),指针指向的类型是有int参数的函数;最后void,函数返回值是void。总结:pf是一个指针,它指向了一个参数是int,返回值是void的函数

  1. 在选择符号解析顺序时,始终是先[]和()后是*。如果"*“在标识符左边且”[]"在标识符右边(例如:*p[]),那么标识符表示的是数组,而不是指针。括号同理,它表示的是函数(例如:*p())。

例:void*pf(int)。首先先解析右边的(int),pf是函数,参数是int类型;其次void*,类型为void类型的指针。总结:pf是函数,函数有一个int类型的参数,返回值是void*

复杂点的例子:

int *(*x[10])(void)。首先解析括号内[10],x是数组;其次*,数组元素是指针指向;再解析(void),指针指向不带参数的函数;最后int *,函数返回值是int类型的指针。总结:x是有10个元素的数组,它的每个元素都是一个指针,指针指向参数为空并且返回值是int*的函数

2.typedef

我们定义一个无符号整型变量x,使用:unsigned long int x;。如果要简化unsigned long int 名称,可以使用typedef, 例如:把unsigned long int改名为x,则定义:

typedef unsigned long int x;

此时x不是一个变量,而是一个简化的unsigned long int别名,但也不能再用x做变量名。
简化别名后,下面两个语句作用相同:

x b;
unsigned long int b;

上面提到的int *(*x[10])(void)复杂的声明也同样适用,它要简化为别名x,则定义:

typedef int *(*x[10])(void);

x是个自定义类型。它的类型是有10个元素的数组,数组每个元素都是一个指针,指针指向参数为空并且返回值是int*的函数。下面两个语句作用相同。

x b;
int *(*b[10])(void);

3.const

  • const修饰变量。使变量的地址在只读段。const char a = ‘a’; 和char const a = 'a’是一样的效果。a初始化时被赋值,a的地址在只读段,a不能再被修改。
  • const修饰指针变量。既可以修饰指针指向的数据,也可修饰指针本身。直接说个技巧:当const在*右边并挨着标识符时,则修饰的是指针本身;当const在*左边时,修饰指针指向的数据。以程序举例证明:
#include <stdio.h>
const char *str = "abc";
const char * const const_p = str;
const char *p = str;

int main(void)
{
    printf("const pointer address:%p, const_p:%p\n", &const_p,  const_p);
    printf("pointer p address:%p, p:%p\n", &p,  p);
    return 0;
}

输出内容如下:

const pointer address:0x400600, const_p:0x4005f8
pointer p address:0x601038, p:0x4005f8

查看ELF格式文件:

$ readelf -a a.out #
...
  [Nr] Name              Type             Address           Offset
       Size              EntSize          Flags  Link  Info  Align
...
  [16] .rodata           PROGBITS         00000000004005f0  000005f0
       000000000000005a  0000000000000000   A       0     0     8
...
  [25] .data             PROGBITS         0000000000601028  00001028
       0000000000000018  0000000000000000  WA       0     0     8

可以从地址分段中看出来:

  1. const_p指向的内容在只读段。const_p的地址也被分配在只读段,所以const_p也不能再被赋值。
  2. p指向的内容在只读段。p的地址被分配在数据段,所以p可以被修改。

注意:如果用win10的WSL运行的话,使用gdb查看运行结果。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值