分类: C/C++
2015-05-12 14:23:32
一、指针是什么
由于通过地址能找到所需的变量单元,可以说,地址指向该变量单元。将地址形象化地称为“指针”。
指向就是通过地址来体现的。
由于通过地址能找到所需的变量单元,因此地说,地址指向该变量单元。
一个变量的地址称为该变量的“指针”。
二、指针变量
1、 怎样定义指针变量
定义指针变量的一般形式为
类型名 *指针变量名
如:
int *pointer_1, *pointer2;
说明:一个变量的指针的含义包括两个方面,一是以存储单元编号表示的地址(如编号为2000的字节),一是它指向的存储单元的数据类型(如int, char, float等。)
2、 如何表示指针类型
指向整型数据的指针类型表示为“int*“,读作”指向int的指针“或者简称为”int指针“。
3、 怎样引用指针变量
1) 给指针变量赋值。 例如:
p = &a; //把a的地址赋给指针变量p
指针变量p的值是变量a的地址,p指向a。
2) 引用指针变量指向的变量
如果已执行“p = &a;”,即指针变量p指向了整型变量a,则
printf(“%d”, *p);
其作用是以整数形式输出指针变量p所指向的变量的值,即变量a的值。
3) 引用指针变量的值。例如:
printf(“%o”, p);
作用是以八进制形式输出指针变量p的值,如果p指向了a,就是输出了a的地址,即&a。
注意:要熟练掌握两个有关的运算符
a)& 取地址运算符。&a是变量a的地址。
b)* 指针运算符(或称“间接访问”运算符),*p代表指针变量p指向的对象
4、指针变量作为函数参数
函数的参数不仅可以是整型、浮点型、字符型等数据,还可以是指针类型 。它的作用是将一个变量的地址传送到另一个函数中。
例子:输入3个整数a,b,c,要求按由大到小的顺序将它们输出。用函数实现。
解题思路:在函数中改变3个变量的值。用wamp函数交换两个变量的值,用exchange函数改变3个变量的值。
编写程序:
#include
int main()
{
void exchange(int *q1, int *q2, int *q3);
int a, b, c, *p1, *p2, *p3;
printf(“please enter three numbers:”);
scanf(“%d, %d, %d”, &a, &b, &c);
p1 = &a; p2 = &b; p3 = &c;
exchange(p1, p2, p3);
printf(“The order is:%d, %d, %d\n”, a, b, c);
return 0;
}
void exchange(int *q1, int *q2, int *q3)
//定义将3个变量的值交换的函数
{
void swap(int *pt1, int *pt2); //函数声明
if(*q1 < *q2) //如果a>b
swap(q1, q2); //交换a和b的值
if(*q1 < *q3) //如果a>c
swap(q1, q3); //交换a和c的值
if(*q2 < *q3) //如果b>c
swap(q2, q3); //交换b和c的值
}
void swap(int *pt1, int *pt2)
{
int temp;
temp = *pt1; //换*pt1和*pt2变量的值
*pt1 = *pt2;
*pt2 = temp;
}
三、通过指针引用数组
1、 数组元素的指针
一个变量有地址,一个数组包含若干元素,每个数组元素都在内存中占用存储单元,它们都有相应的地址。指针变量既然可以指向变量,当然也可以指向数组元素(把某一元素的地址放到一个指针变量中)。所谓数组元素的指针就是数组元素的地址。
2、 在引用数组元素时指针的运算
加一个整数(用+或+=), 如p+1;
减一个整数(用-或-=), 如p-1;
自加运算,如p++,++p;
自减运算, 如p--,--p;
两个指针相减,如p1-p2(只有p1和p2都指向同一数组中的元素时才有意义)。
3、 通过指针引用数组元素
1) 下标法,如a[i]形式;
2) 指针法,如*(a + i)或*(p + i)。其中a是数组名,p是指向数组元素的指针变量,其初值p = a。
4、 用数组名作函数参数
如果有一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参的对应关系有以下4种情况。
1) 形参和实参都用数组名。例如:
int main()
{
int a[10];
…
f(a, 10);
…
}
int f(int x[], int n)
{
…
}
1) 实参用数组名,形参用指针变量。例如:
int main()
{
int a[10];
…
f(a, 10);
…
}
void f(int *x, int n)
{
…
}
2) 实参和形参都用指针变量。例如:
int main()
{
int a[10], *p = a;
…
f(p, 10);
…
}
void f(int *x, int n)
{
…
}
3) 实参为指针变量,形参为数组名。例如:
int main()
{
int a[10], *p = a;
…
f(p, 10);
…
}
void f(int x[], int n)
{
…
}
5、 通过指针引用多维数组
1)多维数组元素的地址
由于分配内存情况不同,所显示的地址可能是不同的,都是上面显示的地址是有共同规律的。
2) 指向多维数组元素的指针变量
a) 指向数组元素的指针变量
a) 指向由m个元素组成的一维数组的指针变量
b) 用指向数组的指针作函数参数
四、通过指针引用字符串
1、 字符串引用的方式
1) 用字符数组存放一个字符串,可以通过数组名和下标引用字符串中一个字符,也可以通过数组名和格式声明“%s”输出该字符串。
2) 字符指针作函数参数
3) 使用字符指针变量和字符数组的比较
a) 字符数组由若干个元素组成,每个元素中放一个字符,而字符指针变量中存放的是地址(字符串第1个字符的地址),绝不是将字符串放到字符指针变量中。
b) 赋值方式。可以对字符指针变量赋值,但不能对数组名赋值。
c) 初始化的含义。
d) 存储单元的内容。编译时为数组分配若干存储单元,以存放各元素的值,而对字符指针变量,只分配一个存储单元。
e) 指针变量的值是可以改变的,而数组各代表一个固定的值(数组首元素的地址),不能改变。
f) 字符数组中各元素的值是可以改变的(可以对它们再复制),但字符指针变量指向的字符串常量中的内容是不可以被取代的(不能对它们再复制)。
g) 引用数组元素。对字符数组可以用下标法(用数组名和下标)引用一个数组元素,也可以用地址法
h) 用指针变量指向一个格式字符串,可以用它代替printf函数中的格式字符串。
五、指向函数的指针
1、 什么是函数指针
如果在程序中定义了一个函数,在编译时,编译系统为函数代码分配一段存储空间,这段存储空间的起始地址(又称入口地址)称为这个函数的指针。
2、 用函数指针变量调用函数
如果想调用一个函数,除了可以通过函数名调用以外,还可以通过指向函数的指针变量来调用该函数。
1) 通过函数名调用函数
2) 通过指针变量访问它所指向的函数
3、 怎样定义指向函数的指针变量
定义指向函数的指针变量的一般形式为
类型名(*指针变量名)(函数参数表列);
4、 用指向函数的指针作函数参数
指向函数的指针变量的一个重要用途是把函数的地址作为参数传递到其他函数。
六、返回指针值的函数
定义返回指针值的函数的一般形式为
类型名 *函数名(参数表列);
七、指针数组和多重指针
1、 什么是指针数组
一个数组,若其元素均为指针类型数据,称为指针数组。也就是说,指针数组中的
每一个元素都存放一个地址,相当于一个指针变量。
定义一维数组的一般形式为
类型名 *数组名[数组长度];
2、 指向指针数据的指针
3、 指针数组作main函数的形参
指针数组的一个重要应用是作为main函数的形参。在以往的程序中,main函数的第1行一般写成以下形式
int main()
或
int main(void)
括号中是空的或有“void”,表示main函数没有参数,调用main函数时不必给出
实参。这是一般程序采用的形式。实际上,在某些情况下,main函数可以有参数,例如:
int main(int argc, char *argv[])
命令行的一般形式为
命令名 参数1 参数2 …… 参数n
八、动态内存分配与指向它的指针变量
1、 什么是内存的动态分配
全局变量是分配在内存中的静态存储区的,非静态的局部变量(包括形参)是分配在内存中的动态存储区的,这个存储区是一个称为栈的区域。除此以外,C语言还允许建立内存动态分配区域,以存放一些临时用的数据,这些数据不必在程序的声明部分定义,也不必等到函数结束时才释放,而是需要时,随时开辟,不需要时,随时释放。这些数据是临时存放在一个特别的自由存储区,称为堆区。可以根据需要,向系统申请所需大小的空间。由于未在声明部分定义它们为变量或数组,因此不能通过变量名或数组名去引用这些数据,只能通过指针来引用。
2、 怎样建立内存的动态分配
对内存的动态分配是通过系统提供的库函数来实现的,主要有malloc、calloc、free、realloc这4个函数。
1) 使用malloc函数
其函数原型为
void *malloc(unsigned int size);
其作用是在内存的动态存储区中分配一个长度为size的连续空间。形参size的类型定为无符号整型(不允许为负数)。此函数的值(即“返回值”)是所分配区域的第一个字节的地址,或者说,此函数是一个指针型函数,返回的指针指向该分配域的开头位置。如:
malloc(100); //开辟100字节的临时分配域,函数值为其第一个字节的地址
注意指针的基类型为void,即不指向任何类型的数据,只提供一个地址。如果此函数未能成功地执行(例如内存空间不足),则返回空指针(NULL)。
2) 使用calloc函数
其函数原型为
void *calloc(unsigned n, unsigned size);
其作用是在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,足以保存一个数组。
用calloc函数可以为一维数组开辟动态存储空间,n为数组元素个数,每个元素长度为size。这就是动态数组,函数返回指向所分配域的起始位置的指针;如果分配不成功,返回NULL。如:
p = calloc(50, 4);
3) 使用free函数
其函数原型为
void free(void *p);
其作用是释放指针变量p所指向的动态空间,使这部分空间能重新被其他变量使用。p应是最近一次调用calloc或malloc函数时得到的函数返回值。如:
free(p); //释放指针变量p所指向的已分配的动态空间
free函数无返回值
4) 使用realloc函数
其函数原型为
void *realloc(void *p, unsigned int size);
如果已经通过malloc函数或calloc函数获得了动态空间,想改变其大小,可以用realloc函数重新分配。
用realloc函数将p所指向的动态空间的大小改变为size。p的值不变。如果重分配不成功,返回NULL。如:
realloc(p, 50); //将p所指向的已分配的动态空间改为50字节
3、 void指针类型
不要把“指向void类型”理解为能指向“任何的类型”的数据,而应理解为“指向空类型”或“不指向确定的类型”的数据。再将它的值赋给另一指针变量时由系统对它进行类型转换,使之适合于被赋值的变量的类型。如:
int a = 3; //定义a为整型变量
int *p1 = &a; //p1指向int型变量
char *p2; //p2指向char型变量
void *p3; //p3为无类型指针变量(基类型为void型)
p3 = (void *)p1; //将p1的值转换为void *类型,然后赋值给p3
p2 = (char *)p3; //将p3的值转换为char *类型,然后赋值给p2
printf(“%d”, *p1); //合法,输出a的值
p3 = &a;printf(“%d”, *p3); //错误,p3是无指向的,不能指向a
九、有关指针的小结
1、 首先要准确地弄清楚指针的含义。指针就是地址,凡是出现“指针”的地方,
都可以用“地址”替代,例如,变量的指针就是变量的地址,指针变量就是地址变量。
要区别指针和指针变量。指针就是地址本身。
2、什么叫“指向“?地址就意味着指向,因为能通过地址能找到具有该地址的对象。对于指针变量来说,把谁的地址存放在指针变量中,就说此指针变量指向谁。但应注意:并不是任何类型数据的地址都可以存放在同一个指针变量中的,只有与指针变量的基类型相同的数据的地址才能存放在相应的指针变量中。如:
int a, *p; //p是int*型指针变量,基类型是int型
float b;
p = &a; //a是int型,合法
p = &b; //b是float型,类型不匹配
3、要深入掌握在对数组的操作中正确地使用指针,搞清楚指针的指向。一维数组名代表数组首元素的地址。如:
int *p, a[10];
p = a;
p是指向int型类型的指针变量,显然,p只能指向数组中的元素(int型变量),而不是指向整个数组。在进行赋值时一定要先确定赋值号两侧的类型是否相同,是否允许赋值。
4、 指针运算
1) 指针变量加(减)一个整数。如:
p++,p--,p+i,p-i,p+=i;p-=i;等均是指针变量加(减)一个整数。
将该指针变量的原值(是一个地址)和它指向所占用的存储单元的字节数相加(减)。
2) 指针变量赋值
将一个变量地址赋给一个指针变量。如:
p = &a; //将变量a的地址赋给p
p = array; //将数组array首元素地址赋给p
p = &array[i]; //将数组array第i个元素的地址赋给p
p = max; //max为已定义的函数,将max的入口地址赋给p
p1 = p2; //p1和p2是基类型相同指针变量,将p2的值赋给p1
5、 指针变量可以有空值,即该指针变量不指向任何变量,可以这样表示:
p = NULL;