1. 指针的变量特性
答:
① 系统为指针分配内存空间;
② 指针有自己的地址;
③ 指针能够存值,但这个值比较特殊——地址。
2. 指针的类型怎么判断?和指针指向的类型
答:从语法的角度看,你只要把指针声明语句里的指名字掉,剩下的部分就是这个指针类型,这是指针本身所具有的类型。
只须把指针声明语句中的指针名字和名字左边的指针声明符“*”去掉,让我们看看例子中各个指针的类型
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]
3. 指针的值
答:也叫做指针所指向的内存区或地址。是指针本身存储的数值,这个值被编译器当作一个地址。
4. 指针的运算
答:指针加上一个整数的结果是另一个指针。问题是:它指向哪里?如果将一个字符指针加运算结果产生的指针指向内存中的下一个字符。foat占据的内存空间不止1个字节,如果将一个指向foat的指针加1,将会发生什么?它会不会指向该float值内部的某个字节呢?
幸运的是,答案是否定的。当一个指针和一个整数量执行算法运算时,整数在执行加法运算之前始终会根据合适的大小进行调整。这个“合适的大小、”就是指针所指向类型的大小“调整”就是把整数值和“合适的大小”相乘。例如,某台机器上,foat占4个字节,在运算foat型指针加3的表达式时,这个3将根据foat类型的大小(此例中为4)进行调整(相乘)。这样实际加到指针上的整数值为12。把3与指针相加使指针的值增加3个foat的大小,而不是3个字节。
5. 常量指针和指针常量
答:
const char * p;
char* const p = a;
char* p = “abc”;
语句(1)定义了一个常量指针,即指向一个常量的指针,指向的内容是常量,不可修改,放在常量区的,但指针本身可以修改,即“*p="b”是非法的,*p是p指向的常量的第一个字符,是个常量,不能改变的。“p=&q”这是可以的,指针可以指向不同的地址。
语句(2)定义了一个指针常量,即指针本身是个常量,不可修改,但指针指向的内容可以修改,一开始定义时让它指向数组a,“*p=b”这是可以的,但“p=&b”是非法的。
const 常量*指针,当 const 在*之前就是常量指针,而 const 在*之后,就是指针常量。例如,“ const char *p”即char*p是个常量,所以内容是常量;“char* const p 即指针p是个常量。
语句(3)中“char*p”定义的是一个指针变量p,指向字符串abc的首地址。这里特别要注意,在C语言中,(3)中定义的是一个常量字符串,它被放在静态存储区的常量区存储,而p是一个指针变量,放在栈上。如果“*p="b”在编译时能通过,但在运行时就会出现错误因为你试图去改变常量区的内容。
6. 指针数组与数组指针
答:
① 指针数组”typename *p[n]”定义了一个数组,数组包含了n个指针变量p[0], p[1],,,,,,,p[n - 1]。例如:
*p[3] = {“abc”, “defg”};
*p[1] = “abc”;
P = &p[0] (p + 1) = &p[1];
除了数组中的元素是指针以外,和一般数组没有区别。数组名p是个指针常量,不能直接进行运算。
② 二维数组名可以看作一个指向指针数组的指针
int (*p)[4];
因为p是指向一个数组一行元素的整体的指针,如果要对数组每个元素进行读写,需要强制转换,把指向4个int的指针转换为一个指向1个int的指针,(实际上就是把p所指向的第一个地址传递给一个“int*q”指针,因为数组是顺序存储结构,所以只需要知道首地和长度就可以了),然后用该指针来遍历数组。可以把指向数组的指针或数组名传递给函数来对二维数组进行操作。
7. 函数指针
答:在C语言中,一个函数总是占用一段连续的内存区,而函数名就是该函数所占内存区的首地址。我们可以把函数的这个首地址(或称入口地址)赋予一个指针变量,使该指针变量指向该函数。然后通过指针变量就可以找到并调用这个函数。我们把这种指向函数的指针变量,称为“函数指针变量”。
函数指针变量定义的一般形式为:
类型说明符 (*指针变量名)();
其中“类型说明符”表示被指函数的返回值的类型。“(* 指针变量名)”表示“*”后面的变量是定义的指针变量。最后的空括号表示指针变量所指的是一个函数。
例如:
int (*pf)();
表示 pf 是一个指向函数入口的指针变量,该函数的返回值(函数值)是整型。
8. 指针型函数
答: 前面我们介绍过,所谓函数类型是指函数返回值的类型。在C语言中允许一个函数的返回值是一个指针(即地址),这种返回指针值的函数称为指针型函数。
定义指针型函数的一般形式为:
类型说明符 *函数名(形参表)
{
…… /*函数体*/
}
其中函数名之前加了“*”号表明这是一个指针型函数,即返回值是一个指针。类型说明符表示了返回的指针值所指向的数据类型。
如:
int *ap(int x,int y)
{
...... /*函数体*/
}
表示 ap 是一个返回指针值的指针型函数,它返回的指针指向一个整型变量。
9. typedef的妙用
答:typedef 给你一种方式来克服“*只适合变量而不适合于类型”的弊端。例如:
typedef char * PCHAR
PCHAR p, q;
这里的p 和q 都被声明为指针(如果不使用typedef,q 将被声明为一个char变量)
10. const 修饰符
答:当你想阻止变量被改变,可能会用到const关键字。在给一个变量加上const修饰符的同时,通常需要对它进行初始化,因为以后的任何时候你将没有机会再去改变它。例如:
const int n=5;
int const m=10;
上述两个变量n和m其实是同一种类型,都是const int(整型恒量)。因为C++标准规定 const 关键字放在类型或变量名之前等价的。笔者更喜欢第一种声明方式,因为它更突出了 const 修饰符的作用。当 const 与指针一起使用时,容易让人感到迷惑。例如,我们来看一下面的p和q的声明
const int *p;
int const *q;
它们当中哪一个代表const int类型的指针(const直接修饰int),哪一个代表int类型的 const 指针(const直接修饰指针)呢?实际上,p和q都被声明为 const int 类型的指针。而int类型的const 指针应该这样声明:
int * const r = &n;
这里,p和q都是指向 const int类型的指针,也就是说,你在以后的程序里不能改变中p的值。而r是一个const指针,它在声明的时候被初始化指向变量n(即r=&n;)之后,r的值将不再允许被改变(但*r的值可以改变)。组合上述两种const修饰的情况,我们来声明个指向const int类型的指针,例如:
const int *const p = &n;