- 地址和指针的概念
内存区的每一个字节有一个编号,他就是此字节的“地址”。
在程序中定义了一个变量,在程序运行时系统会给这个变量分配内存单元。
变量的首地址称为该变量的“指针”。(变量所占内存的首地址指向该内存单元)有一类专门用来存放其他变量地址的变量,称为“指针变量”。
例如:i_pointer就是一个指针变量
- 变量的指针和指向变量的指针变量
可以保存地址值的变量称为指针变量。
- 怎样定义指针变量:
基类型*指针变量名;
#include<stdio.h>
int main()
{
int i,j,k;
char c1;
int *p_1,*p_2;
char *p_3;
p_1=&i;
p_2=&j;
p_3=&c1;
}
%p格式符,表示输出变量a,b,c的值。
地址值使用一个十六进制(以16为基)的无符号整数表示的,其字长一般与主机的字长相同。
在定义和使用指针变量时要注意:
- 指针变量前面的*,表示该变量的类型为指针型变量。
例如:float *pointer_1;
指针变量名是:pointer_1而不是*pointer_1
- 在定义指针变量时必须指定基类型,使用指针变量时,要与基类型一致。
- 指针变量中只能存放地址,不要将一个整数赋给一个指针变量。
- 怎样引用指针变量:
在引用指针变量时,可能有三种情况:
- 给指针变量赋值
- 输出指针变量的值
- 引用指针变量指向的变量
有关的两个运算符:
- &:取地址运算符 &a是变量a的地址
- *:指针运算符(或称间接访问运算符)
- *p是指针变量p指向的对象的值。
典例:通过指针变量访问整型变量
#include<stdio.h>
int main()
{
int a,b;
int *p1,*p2;
a=100;
b=10;
p1=&a;
p2=&b;
printf("a=%d,b=%d\n",a,b);
printf("*p1=%d",*p1);
printf("*p2=%d",*p2);
}
结果: a=100,b=10
*p1=100*p2=10
典例2:输出a和b两个整数,按先大后小的顺序输出
#include<stdio.h>
int main()
{
int *p1,*p2,*p,a,b;
scanf("%d,%d",&a,&b);
p1=&a;
p2=&b;
if(a<b)
{
p=p1;
p1=p2;
p2=p;
}
printf("a=%d,b=%d\n",a,b);
printf("max=%d,min=%d\n",*p1,*p2);
}
- 指针变量做函数参数
典例:对输入的两个整数按大小顺序输出
#include<stdio.h>
int main()
{
void swap(int *p1,int *p2);
int a,b;
int *p1,*p2;
scanf("%d,%d",&a,&b);
p1=&a;
p2=&b;
if(a<b)
swap(p1,p2);
printf("%d,%d\n",a,b);
}
void swap(int *p1,int *p2)
{
int temp;
temp=*p1;
*p1=*p2;
*p2=temp;
}
- 通过指针引用数组
- 指针与一维数组
数组名就是数组所占连续空间的首地址,即数组名就是指针(指针常量),他存放的是第一个元素的首地址。
- 指向一维数组指针的运算
当指针指向一维数组时:
- 加一个整数(用+或+=) p+n结果是指针
- 减一个整数(用-或-=) p-n结果是指针
- 自加运算,如p++,++p
- 自减运算,如p1-p2 注意:只有p1,p2都指向同一数组中的元素是才有意义。
- 如果指针变量p已指向数组中的一个元素,则p+1指向同一数组中的下一个元素,p-1指向同一数组中的上一个元素。
- 如果p的初值为&a[0],则p+i和a+i就是数组元素a[i]的地址。
- *(p+i)或*(a+i)是p+i或a+i所指向的数组元素即a[i]。
- 如果p原来指向a[0],执行++p后p的值在p的原值基础上加d(基类型的字节数),这样p就指向数组的下一个元素a[1]。
- 如果指针变量p1和p2都指向同一数组,如执行p2-p1,结果是两个地址之间的元素个数(两个地址不能相加,无意义)。
实际上,在编译时,对数组元素a[i]就是按*(a+i)处理的,即按照数组首元素的地址加上相对位移量得到要找的元素的地址,然后找出该单元的内容。
例:指针间的运算:
#include<stdio.h>
int main()
{
int a[10];
int *p,*q;
p=a;
q=&a[2];
printf("%d\n",q-p);
printf("%d\n",p-q);
}
例如数组a的首地址为1000,设数组为float型,则a[3]的地址是这样计算的:
1000+3*4=1012
1012单元的内容就是a[3]的值
[]实际上是变址运算符。
注意:a是指针常量,只能a[i]或*(a+i),而不能a++,但指向数组的指针p可以p++等。
- 通过指针引用数组元素
- 下标法:利用中括号运算符访问数组元素的方式称为下标方式。
- 指针法:利用星号运算符访问数组元素的方式称为指针方式。
例如:*(a+i)或*(p+i)或*p++等
其中:a是数组名,p是指向数组元素的指针变量,起初值p=a
下标方式:求和
#include<stdio.h>
int main()
{
int a[10],i,s=0;
for(i=0;i<10;i++)
scanf("%d",&a[i]);
for(i=0;i<10;i++)
s=s+a[i];
printf("\n%d\n",s);
}
数组名指针方式:求和
#include<stdio.h>
int main()
{
int a[10],i,s=0;
for(i=0;i<10;i++)
scanf("%d",&(*(a+i)));
for(i=0;i<10;i++)
s=*(a+i)+s;
printf("\n%d\n",s);
}
指针方式:求和:
#include<stdio.h>
int main()
{
int a[10],*p,s=0;
for(p=a;p<(a+10);p++)
scanf("%d",p);
for(p=a;p<(a+10);p++)
s=s+*p;
printf("%d\n",s);
}
使用指针变量指向数组元素时,要注意以下几点:
- 可以通过改变指针变量的值指向不同的元素
- 要注意指针变量的当前值
- 指向数组的指针变量也可以带下标
- 用数组名作函数参数
int main()
{
void f(int arr[],int n);
int array[10];
……
f(array,10);
……
}
void f(arr[],int n)
{
……
}
强调指出:
C语言中实参和形参之间的数据传递是单向的“数值传递”,出书组外任何类型的变量都不会把形参的变化带回给实参,包括指针类型的实参和形参也遵从这一原则。
但是,可以通过指针作为参数(实参和形参)来改变实参指针所指变量的值。
即:若希望在被调函数中数据的变化带回给调用函数,可设计指向此类数据的指针作为形参。在被调函数中,通过指针间接访问运算符*操作实参所指数据。当然实参必须是被操作数据的地址(或指向操作数据的指针)。
数据类型的变量却能把形参的“变化”,带回给实参。即:当实参为数组或指向数组的指针,形参是数组或指针(实质均是指针),那么,在被调函数中操作数的形参实质是在操作实参数组。
- 指针与二维数组
二维数组是一个“数组的数组”
定义指向二维数组指针的方法: int (*p)[N]
- 定义一个指针指向这个二维数组a,int(*p)[N]=a; N时二维数组的列数
意义:定义了一个只想一维整型数组的指针(二位整型元素的指针)p
比较: int a[3][4]; int (*p)[4];指针P相当于二维数组的a[i]
差异: int *p[5];此定义是一个有5个指向整形指针的一维数组。
- 访问二维数组元素的方法(数组的访问归根到底是访问数组元素)
下标法:p[i][j] 指针法: *(p[i]+j) 或者*(*(p+i)+j)
典例1:用指向由N个元素组成的一维数组的指针变量输出二维数组任一行任一列元素的值。
#include<stdio.h>
int main()
{
int a[3][4]={1,3,5,7,9,11,13,15,17,19,21,23};
int (*p)[4];
int i,j;
p=a;
scanf("%d %d",&i,&j);
printf("a[%d][%d]=%d\n",i,j,*(*(p+i)+j));
}
- 对于任何类型的数组,数组名就是这个数组的首地址,即数组名实际上就是任一指针(常量指针)
- 对于一维数组,例如 int a[10][10],数组名a就是他的指针,他存放的是第一个数组元素a[10]的首地址。数组的每一个元素都是整型变量。
- 定义一个指针指向数组a方法: int *p=a;此时,p是一个指向整形的指针变量,并且得到的初值为a的第一个元素的首地址(并不是得到整个数组)
- 从这里开始,P与a具有完全相同的含义:
- 任意数组元素a[i],用p[i]来表示变得很自然。
- a[i]实际执行过程:*(a+i),所以p[i]等价于*(p+i)
- 一般的程序设计过程:
- for(p=a;p<a+N;p++) //N为数组的长度
printf(“%d”,*p) //对*p的操作
- for(i=0;i<N;i++) //经常用在被调函数中,实参a
prinf("%d",*p++)
- 通过指针引用字符串
- 字符串的表示形式
用字符串初始化字符数组,是把此字符串依次存入字符数组所占空间中,这个数组是有名字的。
C语言对字符串常量是在内存中开辟了一个字符数组用来存放该字符串常量,但是这个数组是没有名字的,不能通过数组名来引用,只能通过指针变量来引用。
典例1:将字符串a复制给字符串b--指针变量形式
#include<stdio.h>
int main()
{
char a[]="I am a boy.",b[20],*p1,*p2;
int i;
p1=a;
p2=b;
for(;*p1!='\0';p1++,p2++)
*p2=*p1;
*p2='\0';
printf("string a is:%s\n",a);
printf("string b is:");
for(i=0;b[i]!='\0';i++);
printf("%c",b[i]);
}
- 字符指针做函数参数
用函数调用实现字符串的复制
#include<stdio.h>
int main()
{
void copy_string(char *from,char *to);
char *a="I am a teacher.";
char b[]="You are a student.";
char *p=b;
printf("String a=%s\n String b=%s\n",a,p);
copy_string(a,p);
printf("\n String a=%s\n String b=%s\n",a,b);
}
void copy_string(char *from,char *to)
{
for(;*from!='\0';from++,to++)
*to=*from;
*to='\0';
}
- 对使用字符指针变量和字符数组的讨论:
- 字符数组和指向字符串的指针均可带下标引用串中的字符。
字符数组中各元素的值是可以改变的(可以对他们再赋值),但字符指针变量指向的字符串中的内容是不可以被取代的(不能对他们在赋值)
- 赋值方式
- 字符数组——只能整体用串赋值而不能整体对字符数组赋值
- 字符指针变量——即可用字符串赋初值,也可用字符串赋值
- 字符数组
可以用输入函数为其输入字符串,而字符指针变量却不可以。
④ 指针变量的值是可以改变的