1. 指针的基本用法
C程序设计中使用指针可以
使程序简洁、紧凑、高效
有效地表示复杂的数据结构
动态分配内存
得到多于一个的函数返回值
地址和变量
在计算机内存中,每一个字节单元,都有一个编号,称为地址
指针
在C语言中,内存单元的地址称为指针,专门用来存放地址的变量,称为指针变量,在不影响理解的情况中,有对地址、指针和指针变量不区分,通称为指针
指针变量的说明
一般形式如下:
<存储类型> <数据类型> *<指针变量名>
例如:char *pName
指针在说明的同时,也可以被赋予初值,称为指针的初始化,一般形式为:
<存储类型> <数据类型> *<指针变量> = <地址变量>;
例如:int a,*pa = &a;
上边语句把变量a的地址作为初值赋予了int型指针pa
指针指向的内存区域中的数据称为指针的目标
如果它指向的区域使程序中的一个变量的内存空间,则这个变量称为指针的目标变量。简称为指针的目标。
/*案例测试:测试指针和变量的联系,指针的内容,指针的地址,指针变量所占空间大小 demo1.c*/
#include <stdio.h>
int main(int argc,char *argv[])
{
int a = 10;
int *p;
p = &a;
printf("%p %p %d %d\n",p,&a,*p,a); //打印指针变量p的值(为a的地址)、a的地址、p中存储的地址的值(即a的值)、a的值
printf("&p = %p size = %d\n",&p,sizeof(p)); //打印指针变量的地址,打印指针变量的内存大小
return 0;
}
/*测试结果:
linux@ubuntu:~/C/day06$ gcc demo1.c -Wall
linux@ubuntu:~/C/day06$ ./a.out
//前两行为Ubuntu下的命令行代码,意思是编译源代码,执行编译后的代码,一下才是结果
0xbff0b748 0xbff0b748 10 10
&p = 0xbff0b74c size = 4
*/
引入指针要注意程序中的px,*px和&px三种表示方法的不同意义。设px为一个指针,则:
px——指针变量,它的内容使地址量
*px——指针所指向的对象,它的内容使数据
&px——指针变量占用的存储区域的地址,是个常量
指针的赋值运算指的是通过赋值运算符向指针变量送一个地址
向一个指针变量赋值时,送的指必须是地址常量或指针变量,不能是普通的整数(除了赋0以外)
指针赋值运算常见的有以下几种形式:
把一个普通变量的地址赋给一个具有相同数据类型的指针
double x = 15,*px = &x;
把一个已有地址值的指针变量赋给具有相同数据类型的另一指针变量
float a,*px,*py;
px = &a;
py = px;
把一个数组的地址赋给具有相同数据类型的指针
int a[20],*pa;
pa = a; //等价pa = &a[0]
/*案例:demo2*/
#include <stdio.h>
int main(int argc,char *argv[])
{
int a = 10;
int *p1;
int *p2;
char b[] = "xiaoqiang";
char *p3;
p1 = &a;
p2 = p1;
printf("a =%d *p1=%d *p2=%d\n",a,*p1,*p2); //打印a的值,指针p1,p2所指内存中所存的值
printf("&a=%p p1=%p p2=%p\n",&a,p1,p2); //打印a的地址,p1,p2指针变量中所存的地址
printf("&p1=%p &p2=%p\n",&p1,&p2); //打印p1,p2指针变量的地址
puts("---------p3=b-----------");
p3 = b;
printf("b=%s,*p3=%c,p3=%s\n",b,*p3,p3); //p3=b,是将b[0]的地址传入,*p3是指向b[0]所存储的内容,p3按字符串打印,头地址即为字符串地址
printf("&b=%p,p3=%p,&p3=%p\n",&b,p3,&p3); //p3按地址打印
return 0;
}
/*测试结果:
linux@ubuntu:~/C/day06$ gcc demo2.c -Wall
linux@ubuntu:~/C/day06$ ./a.out
a =10 *p1=10 *p2=10
&a=0xbf84b194 p1=0xbf84b194 p2=0xbf84b194
&p1=0xbf84b198 &p2=0xbf84b19c
---------p3=b-----------
b=xiaoqiang,*p3=x,p3=xiaoqiang
&b=0xbfebd3e2,p3=0xbfebd3e2,&p3=0xbfebd3dc
*/
2. 指针的运算
指针运算是以指针变量所存放的地址作为运算量而进行运算
指针运算的实质就是地址的计算
指针运算的种类是有限的,它只能进行赋值运算、算数运算和关系运算
指针的运算:
+ px+n 指针向地址大的方向移动n个数据
- px-n 指针向地址小的方向移动n个数据
++ px++ 指针向地址大的方向移动1个数据
-- px-- 指针向地址小的方向移动1个数据
- px-py 两指针之间相隔数据元素的个数
指针的关系运算
> 大于 px>py
< 小于 px<py
>= 大于等于 px>=py
<= 小于等于 px<=py
!= 不等于 px!=py
== 等于 px==py
俩指针之间的关系运算表示它们指向的地址位置之间的关系。指向地址大的指针大于指向地址小的指针
指针与一般整数变量之间的关系运算没有意义。但可以与0进行等于或不等于的关系运算,判断指针是否为空
/*案例1:指针的+,-,++,--的运算 demo3*/
#include <stdio.h>
int main(int argc,char *argv[])
{
char a[] = "abcdefz",*p,*p1;
p = a;
p1 = &a[4];
printf("%s\n",a); //abcdefz
printf("p=%p p+2=%p\n",p,p+2); //p=0xbf9edb54 p+2=0xbf9edb56
printf("*p=%c *(p+2)=%c\n",*p,*(p+2)); //*p=a *(p+2)=c
printf("*p1=%c\n",*p1); //*p1=e
p1++;
printf("p1++=%c,&p1++=%p\n",*p1,p1); //p1++=f,&p1++=0xbf9edb59
p1--;
printf("p1--=%c,&p1--=%p\n",*p1,p1); //p1--=e,&p1--=0xbf9edb58
printf("p1-p=%d\n",p1-p); //p1-p=4
return 0;
}
/*测试结果:
linux@ubuntu:~/C/day06$ gcc demo3.c -Wall
linux@ubuntu:~/C/day06$ ./a.out
abcdefz
p=0xbf9edb54 p+2=0xbf9edb56
*p=a *(p+2)=c
*p1=e
p1++=f,&p1++=0xbf9edb59
p1--=e,&p1--=0xbf9edb58
p1-p=4
*/
/*案例2:利用指针将输入的数存入数组中,在用指针打印出来 demo4*/
#include <stdio.h>
#define N 5
int main(int argc,char *argv[])
{
int a[N];
int *p,i;
p = a;
for(i = 0;i < N;i++)
scanf("%d",p++);
p = a;
for(i = 0;i < N;i++)
printf("%d ",*p++);
puts("");
return 0;
}
/*测试结果:
linux@ubuntu:~/C/day06$ gcc demo4.c -Wall
linux@ubuntu:~/C/day06$ ./a.out
1 2 3 4 5 6 7 8
1 2 3 4 5 //N为5,所以只取了前5个
*/
3. 指针与一维数组
在C语言中,数组的指针式指数组在内存中的起始地址,数组元素的地址是指数组元素在内存中的起始地址
一维数组的数组名为一维数组的指针(起始地址)
例如:
double x[8] //即x为x数组的起始地址
设指针变量px的地址值等于数组指针x(即指针变量px指向数组的首元素),则:
x[i]、*(px+i)、*(x+i)和px[i]具有完全相同的功能:访问数组第i+1个数组元素
注意:指针变量和数组在访问数组中元素时,一定条件下其使用方法具有相同的形式,因为指针变量和数组名都是地址量。但指针变量和数组的指针(或叫数组名)在本质上不同,指针变量是地址变量,而数组的指针式地址常量
/*案例一:将一个数组按上述四种形式打印输demo5*/
#include <stdio.h>
int main(int argc,char *argv[])
{
int a[] = {1 ,2 ,3 ,4 ,5 ,6 ,7 ,8 };
int *p, i, n;
p = a;
n = sizeof(a)/sizeof(int);
for(i = 0;i < n;i++){
printf("%d %d %d %d\n",a[i],*(p+i),*(a+i),p[i]);
}
puts("");
return 0;
}
/*打印结果:
linux@ubuntu:~/C/day06$ gcc demo5.c -Wall
linux@ubuntu:~/C/day06$ ./a.out
1 1 1 1
2 2 2 2
3 3 3 3
4 4 4 4
5 5 5 5
6 6 6 6
7 7 7 7
8 8 8 8
*/
/*案例2:利用指针将整型数组中n个数按反序存放并打印*/
#include <stdio.h>
int main(int argc,char *argv[])
{
int a[] = {1,2,3,4,5,6,7,8};
int *p,i,n,temp;
n = sizeof(a)/sizeof(int) - 1;
p = a;
for(i = 0;i < n;i++,n--){
temp = p[n];
p[n] = p[i];
p[i] = temp;
}
n = sizeof(a)/sizeof(int);
for(i = 0;i < n;i++)
printf("%d ",*(p+i));
puts("");
return 0;
}
/*运行结果:
linux@ubuntu:~/C/day06$ gcc demo6.c -Wall
linux@ubuntu:~/C/day06$ ./a.out
8 7 6 5 4 3 2 1
*/
4. 指针与二维数组
多维数组就是具有两个或两个以上下标的数组
在C语言中,二维数组的元素连续存储,按行优先存
可把二维数组看作由多个一维数组组成
比如int a[3][3],含有三个元素:a[0]、a[1]、a[2]
元素a[0]、a[1]、a[2]都是一维数组名
二维数组名代表数组的起始地址,数组名加1,是移动一行元素。因此,二维数组名常被称为行
行指针(数组指针)
存储行地址的指针变量,叫做行指针变量。形式如下:
<存储类型> <数据类型> (*<指针变量名>)[表达式]
例如:int a[2][3];int (*p)[3];
方括号中的常量表达式表示指针加1,移动几个数据,当用行指针操作二维数组时,表达式一般写成1行的元素个数,即列数。
/*案例1:用一级指针访问二维数组demo7*/
#include <stdio.h>
int main(int argc,char *argv[])
{
int a[][3] = {{1,2,3},{4,5,6},{7,8,9},{10,11,12}};
int *p, n, i;
n = sizeof(a)/sizeof(int);
p = &a[0][0];
for(i = 0;i < n;i++){
printf("%d ",p[i]);
}
puts("");
return 0;
}
/*运行结果:
linux@ubuntu:~/C/day06$ gcc demo7.c -Wall
linux@ubuntu:~/C/day06$ ./a.out
1 2 3 4 5 6 7 8 9 10 11 12
*/
/*案例2:使用行指针表示二维数组 demo8*/
#include <stdio.h>
int main(int argc,char *argv[])
{
int a[][3] = {{1,2,3},{4,5,6},{7,8,9},{10,11,12}};
int (*p)[3], i, j;
p = a;
for(i = 0;i < 4;i++){
for(j = 0;j < 3;j++)
printf("%d ",*(*(p+i)+j));
puts("");
}
return 0;
}
/*打印结果:
linux@ubuntu:~/C/day06$ gcc demo8.c -Wall
linux@ubuntu:~/C/day06$ ./a.out
1 2 3
4 5 6
7 8 9
10 11 12
*/