#指针的类型、指针所指向的类型

A. 指针存储的数值是内存的一个地址
B. 只要把指针声明语句中的指针名去掉,剩下的部分就是指针的类型
C. 只要把指针声明语句中的指针名和名字左边的指针声明符*去掉,剩下的就是指针所指向的类型
 
例子
代码部分 指针类型 指针所指向的类型
int *ptr; int * int
char *ptr; char * char
int **ptr; int ** int *
int (*ptr)[3]; int (*)[3] int ()[3]
int *(*ptr)[4]; int *(*)[4] int *()[4]
 
#指针的值
A. 指针的值是指指针本身存储的数值,这个值会被编译器当做一个地址进行编译
B. 指针本身的大小是一个字长(32位机是4字节,64位机是8字节)
C. 指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度是sizeof(指针所指向的类型)的一片连续内存区域
   注1. 说一个指针的值是X,就是说该指针指向了以X为首地址的一片内存区域
   注2. 说一个指针指向了某块内存区域,相当于说该指针的值是这块内存区域的首地址
D. 指针所指向的内存区和指针所指向的类型是两个完全不同的概念
   注1. 指针光声明但不初始化时,它所指向的内存区域是不存在的
 
#指针运算
A. 指针可以加减一个整数,注意这种运算的意义和通常的数值加减运算的意义是完全不一样的
 
例子
代码部分 分析部分
int a[20]; 定义一个int类型的数组
int *ptr = a; 该指针的类型是int *,指向的类型是int,被初始化为a的首地址
ptr++; 把ptr的值加上sizeof(int),也就是指向了第2个元素的地址,即&a[1]
 
例子
代码部分 分析部分
char a[20]; 定义一个char类型的数组
int *ptr = a; 该指针的类型是int *,指向的类型是int,被初始化为a的首地址
ptr += 4; 把ptr的值加上sizeof(int),在32位机上就是加上了4个int类型的长度,即为4*4=16个字节
由于char类型的长度是1个字节,这样指针就指向了第17个元素的地址,即为&a[16]
#运算符&和*
A. & 取址符
B. * 取值符
 
例子
代码部分 分析部分
int a, b; 定义int类型的变量a、b
int *p, **ptr; 定义指向int类型的指针p、指针的指针ptr
p = &a; a是int类型,&a是取到a的地址,所以&a的类型是int *,赋值给同为int *类型的指针变量p
*p = 24;
ptr = &p; p是int *类型,&p取到的是的是p的地址,所以&p的类型是int **,赋值给同为int **类型的指针变量ptr
*ptr = &b; b是int类型,&b取到的是b的地址,所以&b的类型是int *,赋值给同为int *类型的指针变量*ptr
**ptr = 24; 
 
#指针表达式
A. 如果一个表达式的最后结果是一个指针,那么这个表达式就叫做指针表达式
 
例子
代码部分 分析部分
int a, b;
int array[10];
int *pa;
pa = &a a是int类型,&a是int *类型,所以&a是一个指针表达式
int **ptr = &pa; pa是int *类型,&pa是int **类型,所以&pa是一个指针表达式
*ptr = &b; b是int类型,&b是int *类型,所以&b是一个指针表达式
pa = array;
pa++; pa自增1,返回值同样是一个指针,是指针表达式
 
#指针和数组的关系
A. 数组的数组名可以看做是个指针(注意用词,“可以看做是”,只是作为右值自动转化为指针)
 
例子
代码部分 分析部分
int array[10] = {0,1,2,3,4,5,6,7,8,9}, value;
value = array[0]; 同value = *array
value = array[3]; 同value = *(array + 3)
value = array[4]; 同value = *(array + 4)
 
如该例,定义一个数组 TYPE array[n],则数组名array有如下几层含义
1). 它代表整个数组,类型是TYPE[n]
2). 它是一个指针,类型是TYPE *,指向的类型是TYPE
3). 有单独的内存区,和数组第0号单元占据的内存区不同
4). 该指针相当于一个指针常量,不可以修改
 
#指针和结构体
A. 可以自定义一个指向结构体类型对象的指针
 
例子
代码部分 分析部分
struct MyStruct
{
int a, b, c;
};
 
struct MyStruct s = {2, 3, 4};
struct MyStruct *p = &s; 使用p->a, p->b, p->c来访问每个成员变量
int *ptr = (int *)&s; 使用*ptr, *(ptr + 1), *(ptr + 2)来访问每个成员变量
 
#指针和函数
A. 可以把一个指针声明为一个指向函数的指针
 
例子
#include <stdio.h>
void fun(char *s)
{
printf("hello, %s\n", s);
}
 
int main (int argc, const char * argv[]) {
    // insert code here...
void (*pfun)(char *) = fun;
pfun("john");
return 0;
}
 
#一些复杂声明的解释
int (* func)(int *p)
1. 先找到未定义的标识符,这里是func,func被一对括号所包围,且前面有个*号,说明func是一个指针
2. 后面有括号,说明(* func)是一个函数,而func是一个指向函数的指针
3. 这个函数的形参是int *
4. 整个函数的返回值是个int
5. 结论,这是一个返回值是int、形参为int *类型的一个指针函数
 
int (* func)(int *p, int(*f)(int *))
1. (* func)是一个函数
2. 这个函数有两个形参,类型分别是int *和int (*)(int *),也就是一个返回值为int的指针函数
3. 整个函数的返回值是int
4. 结论,这是一个返回值是int、形参分别为int *和一个返回值为int的指针函数的一个指针函数
 
int (* func[5])(int *p)
1. (* func[5])是一个函数
2. func是一个有5个元素,每个元素都是一个函数指针
3. 这个函数有一个类型为int *的形参
4. 这是返回值是int、形参是int *类型的函数指针数组
 
int (* (* func)[5])(int *p)
1. func是一个指针数组
2. 这个指针数组的元素是指针
3. 总结,func是一个指向数组的指针,数组的元素是函数指针,这些指针指向具有int *形参,整个函数返回值为int