指针是一个变量,它的值是内存地址。
指针的类型,就是从指针所指的值的类型。
指针所指的值,就是存储于内存中的数据片段,这个数据片段的长度由指针的类型来决定。这个数据片段的起始位置就是指针的值。
const int *p
,请和我一起念:
p
是一个变量;
*p
是一个指针(变量);
int *p
是一个指针,指向一个整型值;
const int *p
是一个指针,指向一个整型值,这个整型值是一个常量。
这种指针类型的变量叫做 常量指针 ,它指向一个常量。这就意味着,这个指针所指的值(内存中某种类型的数据)不可以被修改,但是这个指针的值(内存地址)是可变的。
int const *p
,请和我一起念:
p
是一个变量;
*p
是一个指针(变量);
const *p
是一个不变的指针(常量);
int const *p
是一个不变的指针,它指向一个整型值。
这样的指针就叫做 指针常量 ,它的值(内存地址)不可以被改变,但是它所指向的值(内存中某种类型的数据)可变的。
发现规律了么?规律就是 从右向左念 。
int *a[4]
,请和我一起念:
[4]
是包含 4 个元素的数组;
a[4]
是包含 4 个元素的数组,这个数组的名字叫 a
;
*a[4]
是包含 4 个指针类型的元素的数组,这个数组的名字叫 a
;
int *a[4]
,是包含 4 个指向整型值的指针类型的元素的数组,这个数组的名字叫 a
。
这样的数组,叫做 指针数组 ,因为它的元素是指针类型。
发现规律了么?规律就是 从右向左念 。
int (*a)[4]
,请和我一起念:
()
是一对括号,它从屏幕里伸了出来,以至于我们首先看到了它;
(*a)
括号里有一个指针;
(*a)[4]
括号里有一个指针,这个指针指向包含 4 个元素的数组;
int (*a)[4]
括号里有一个指针,这个指针指向一个包含 4 个整型值的数组。
这样的指针,叫做 数组指针 ,因为它指向的是一个数组类型。
发现规律了么?规律就是 从右向左念,但是先念括号里的,然后再从右向左念。将括号想象成是一根从屏幕里伸出来的柱子,我们看到了它在屏幕上的投影 。
int *a(char *s)
,请和我一起念:
()
是一根柱子,从屏幕里伸了出来;
(char *s)
是一根柱子,它的顶面有 char *s
,C 编译器将这样的柱子称为 函数 ;
a(char *s)
是一个函数,这个函数的名字 a
;
*a(char *s)
函数 a
返回指针;
int *a(char *s)
函数 a
返回整型指针。
这种函数,叫做 返回指针类型的函数 。
int (*a)(char *s)
,请和我一起念:
(*a)
是一个指针,与数组指针里的那个 (*a)
没什么本质上的不同;
(*a)(char *s)
是一个指针,它指向一个函数;
* int (*a)(void)
是一个指针,它指向一个函数,这个函数返回 int
。
这种指针,叫做 指向函数的指针 ,简称 函数指针 。
发现规律了么?规律就是先看有没有柱子。如果有多根柱子,那么就从左向右看这些柱子,将每根柱子所代表的东西念出来,然后再从右向左念。
typedef int MyInt
,请和我一起念:
MyInt
是我们自定义的一个类型,这个类型的名字叫 MyInt
;
typedef int MyInt
,是我们自定义的一个类型,这个类型的名字叫 MyInt
,它对应于 C 语言内建的 int
类型。
编译器在遇到 MyInt
类型时,就会将它替换为 int
。
发现规律了么?规律就是从右向左念,右边第一个出现的『单词』就是我们要定义的类型名,然后这个单词与 typedef
之间的部分,就是这个类型名所对应的类型。
typedef int (*MyFunc) (void)
,请和我一起念:
MyFunc
是我们自定义的一个类型,这个类型的名字叫 MyFunc
;
typedef int (*MyFunc) (void)
是我们自定义的一个类型,这个类型的名字叫 MyFunc
,它对应于一个指针类型 int (*) (void)
,这种类型的指针指向一种函数类型 int (void)
,这个函数类型是接受 void
,返回 int
。
发现规律了么?规律就是先看柱子,如果有多根柱子,那么最左边的柱子所代表的东西就是我们要定义的类型名,然后这个类型名与 typedef
之间的部分(从柱子的顶端往下看,然后再从右向左看)就是这个类型名所对应的类型。