概述
一、地址和指针
一个计算机的内存由很多存储单元组成,这些存储单元是以字节为单位的一个连续存储空间。每个空间都有自己的内存单元编号。内存单元的编号即叫作地址,也称为指针。
在程序中定义了一个变量,在编译时系统就在内存中给这个变量分配了内存空间。系统会根据变量的类型,分配一定长度的空间。C系统为整型变量分配4个字节,为字符型变量分配1个字节。
每一个变量都有一个内存位置,每一个内存位置都定义了可使用 & 运算符访问的地址,它表示了在内存中的一个地址。
举个例子,程序中的语句“ int i =3 , int b =5 ; ”编译时系统分配内存编号为1000~1003的4个字节给 i ,内存编号为1004~1007的4个字节给 j ,并将数值3,5分别存入这两个区域,如图所示。
对变量值的存取时通过地址进行的。例如“ printf("%d",i); ”,执行过程是:根据变量名 i 与地址的对应关系,找到变量 i 的地址1000,然后从1000到1003的4个字节中取出数据(即变量的值3)输出。例如“ scanf("%d",&i); ”语句,就将键盘输入的值存入变量 i 对应的地址1000开始的整型存储区域。这种根据变量名对应的地址存取变量值的方式称为“ 直接访问 ”方式。
还有一种称为“间接访问”的方式,将变量 i 的地址存放在另一个内存单元中。C语言规定,在程序中可以定义一种特殊变量,用来存放地址。可以通过以下语句将 i 的地址存放到 p 中: p=&i;
这时 p 的值就是1000,即变量i所占用单元的起始地址,通常称为p指向了 i 。要存取变量 i 的值,也可以采用间接方式,先找到存放“ i 的地址”的单元地址( p 的地址),从中取出 i 的地址(1000),再到该地址中取出 i 的值(3)。如果有一个变量专门用来存放另一个变量的地址,则称它为指针变量。上述变量 p 就是一个指针变量。
二、指针变量
在程序定义变量后,系统会根据变量的类型,为变量在内存中分配多少字节的存储空间,最后确定变量的单元地址。有了变量的地址,就可以立即找到该变量所在的存储单元,并进行数据的存取操作。指针变量是专门存放变量地址的变量。
语法结构: 数据类型名 *指针变量名; 星号(*)为定义指针变量的标志,称为指针运算符。
例如: int *p; 表示定义指针变量p,它指向整数变量
例题:通过指针变量访问整数变量。
#include <stdio.h>
int main(){
int a,b;
int *p; //定义指针变量p
a=5;
p=&a; //取a的地址
b=*p+5; //*p是取地址中的值
printf("a=%d,p=%d,b=%d",a,*p,b);
return 0;
}
运行结果: a=5,p=5,b=10
&和*为单目运算符,优先级别仅次于括号和成员运算符,具有右结合性。运算符“ & ”的操作数允许是一般变量或指针变量,运算符“ * ”的操作数必须为指针变量或地址型表达式。
取地址符号与取值符号是互为逆运算:
#include <stdio.h>
int main(){
int a=5; //定义a等于5
int *p; //定义指针变量p
p=&a; //取a的地址
printf("%d,%d,%dn",&a,p,&(*p)); //输出都是a的地址
printf("%d,%d,%dn",a,*p,*(&a)); //输出都是a的值
return 0;
}
&a 、p 、&(*p)取值是相同的,表示指针变量 p 存放的变量 a 的地址。
a 、*p 、*(&a)取值是相同的,表示指针变量 p 所指向的变量 a 的值。
指针变量必须先赋值,再使用。
三、指向数组的指针
一个变量有地址,一个数组包含若干个元素,每个数组元素都在内存中占用存储单元,它们也有相应的地址。指针变量既然可以指向变量,当然也可以指向数组和数组元素。数组元素的指针即数组元素的地址。
数组名代表数组的起始地址,这是一个不允许赋值的指针(地址常量)。对数组元素的访问,既可以采用数组下标法( 如a[5] ),也可以采用指针法。指针法是通过数组元素的指针找到所需元素。使用指针法访问数组元素能使目标程序质量更高(占内存少,运行速度快)。
1.指向一维数组的指针
数组元素相当于一个普通变量,定义数组元素的指针与以前介绍的定义普通变量指针的方法相同。
例如:
int a[5]; //定义a为包含10个整型的数组
int *p; //定义p为指向整数变量的指针变量
p = &a[0]; //将数组元素a[0]的地址赋值给指针变量p,p指向数组a中的a[0]元素
数组名a是数组的首地址, 它与&a[0]是同一值。因此p=&a[0]也可以写成p=a。
数组a不代表整个数组,“p=a”的作用是“把a数组的首地址赋给指针变量p” ,而不是“把数组a各元素的值赋给指针变量p ”。
2.数组指针的运算
指针变量p指向数组a的某个元素,p+1是指向数组的下一个元素(而不是将p的值加1)。
例如:
#include <stdio.h>
int main(){
int a[3]; //定义数组a
int *p; //定义指针变量p
p=a; //a的首地址赋给指针变量p
printf("%d,%d,%dn",p,p+1,p+2);
printf("%d,%d,%dn",a,a+1,a+2);
return 0;
}
运行结果
数组元素是整型,每个元素占4个字节,p指向a[0],则p+1意味着使p的值加4个字节,使它指向下一个元素a[1]。p+1所代表的地址实际上是p+1*d,d是一个数组元素所占有的字节数。
a+i 和 p+i 是 a[i] 的地址;*(p+i)和*(a+i) 是 p+i和a+i 所指向的数组元素。
对于指针变量p,可以做以下运算: p++,p--,p+i,p-i,p+=i,p-=i 等。
指针也可以进行关系运算:
(1)若p1和p2指向同一个数组,则:
p1<p2表示p1指的元素在前;
p1>p2表示p1指的元素在后;
p1=p2表示p1与p2指向同一个元素。
(2)若p1和p2不指向同一个数组,比较毫无意义。
3. 指向二维数组的指针
1.二维数组元素的地址
与一维数组一样,二维数组名a是数组的首地址。 但二者不同的是,二维数据名的基类型不是数组元素类型,而是一维数据类型,因此,二维数组a是一个行指针。
例如二维数组 int a[3][4]; 二维数组a的行指针、列指针和数组如下表:
表中,二维数组a包含3个行元素:a[0],a[1],a[2],它们又都是一维数组名,因此也是地址常量;它们的类型与数组元素类型一致。a+1的值是数组 a 的起始地址加上1行元素占据的字节数的和,即a[1]的地址。a+i 的值是数组a的起始地址加上 i 行元素(4*i个整数)所占据的字节数的和,即 a[i] 的地址,所有称 a 为行指针。
a[0]是第0行的首地址;a[1]是第1行的首地址;a[2]是第2行的首地址。
a[0]+1是数组元素a[0][1]的地址,a[0]+2是数组元素a[0][2]的地址,a[1]+1是数组元素a[1][1]的地址。以此类推,任意数组元素a[ i ][ j ]的地址是a[ i ]+j,所有称a[ i ]为列指针。
二维数组元素的地址表示形式比较多,每种地址形式都有对应的数组元素引用方法。
如数组元素地址: &a [ i ] [ j ],a [ i ] + j,*( a + i ) + j
对应的数组元素为:a [ i ] [ j ],*( a [ i ] + j ),*( *( a+ i ) +j )
2.指向二维数组元素的指针变量(列指针)
二维数组是由若干行、若干列组成的。C语言中二维数组在内存中按照行顺序存放。因此在数组中,将一般简单变量的指针称作元素的指针。
当元素指针p指向某一个数组元素时,p+1将指向的下一个元素刚好是同行的下一列元素。因此,在对二维数组操作时,经常将元素指针称作列指针(下一个元素就是同行的下一列元素)。用列指针操作二维数组,只要知道二维数组中数组元素在内存中的存放顺序即可。
使用列指针输出二维数组元素:
#include <stdio.h>
int main(){
int a[2][3]={{1,2,3},{4,5,6}},*p;
for(p=a[0];p<a[0]+6;p++){
if((p-a[0])%3==0){
printf("n");
}
printf("%dt",*p);
}
return 0;
}
运行结果
3.指向二维数组元素的指针变量(行指针)
行指针p是用来存放地址的变量。当p指向二维数组a中的数组元素 a [ i ] [ j ] 时,p+1 将指向同列的下一行元素 a [ i + 1 ] [ j ],所有行指针不能按照一般指针变量的方法定义。行指针定义时,必须说明数组每行元素的个数。
语法结构: 数据类型名 ( *指针变量名 ) [ 行元素个数 ]; 例如: int (*p) [3];
该语句定义了一个指向每行3个整型元素的行指针p。p的基类型是一个包含3个整型元素的一维数组。如果有int a[4][3],则p与a的基类型相同,因此通常称p为指向二维数组的指针。
进一步的理解,行指针是指向列指针的指针变量,列指针为一级指针,行指针为二级指针。通过行指针确定数组元素所在的行首地址,通过列指针最后确定数组元素所在的列地址。
使用行指针输出二维数组元素:
#include <stdio.h>
int main(){
int a[3][3]={1,2,3,4,5,6,7,8,9};
int (*p) [3],i;
for(p=a;p<a+3;p++){
for(i=0;i<3;i++){
printf("%dt",*(*p+i));
}
printf("n");
}
return 0;
}
运行结果
分析: 这里把数组a看成一维数组,它的元素有a[0],a[1],a[2]。指针p与数组名a表示的地址常量的基类型相同,所有可以用p=a,使指针变量p指向数组a的第一个元素a[0],*p为a[0]的值,即为二维数组a中的第0行的首地址。*(*p+1)表示二维数组元素a[0][1]。还有其他方式表示二维数组元素a[i][j]: *( *(p+i) +j ) *(a[ i ] + j ) *( *( a+i) +j )
4.指针数组
如果一个数组的元素都是指针类型,则该数组称为指针数组,即数组的元素都是指针变量。
一维指针数组的定义形式为: 类型名 *数组名[ 常量表达式 ];
例如: int *p[5];
指针比较合适用来指向若干个字符串,使字符串处理更加方便灵活。
例如:图书馆的书有若干本书,想把书名放在一个数组中,对这些书进行排序和查询。
#include <stdio.h>
#include <string.h>
int main(){
char *book[]={"Computer network","Vissual C++","Operating system","Data sturcture","Java"};
int n=5,i,j,k;
char *temp;
for(i=0;i<n-1;i++){
k=i;
for(j=i+1;j<n;j++){
if(strcmp(book[k],book[j])>0){
k=j;
}
if(k!=i){
temp=book[i];
book[i]=book[k];
book[k]=temp;
}
}
}
for(i=0;i<n;i++){
printf("%sn",book[i]);
}
return 0;
}
解析:strcmp是字符串比较函数,book[k]和book[j]是第k个和第j个字符串的起始地址。strcmp(book[k],book[j)的值为:如果book[k]所指向的字符串大于book[j]所指的字符串,函数值为正值;若相等,函数值为0;若小于,函数值为负值。if语句的作用是将两个字符串中“小”的那个字符串的序号(i或者j)保留在变量k中。执行完内循环for语句后,从第i个字符串到第n个字符串中,第k个字符串最“小”。如果k不等于i,那么最小的字符串不是第 i 串。因此,将book[i]和book[k]对换。
四、用指针变量作为函数参数
指针变量作为函数形参时,对应的实参必须为它提供确定的地址类型的表达式的值。通过函数中的形参指针,可以间接地访问实参地址中的数据。被调用函数如果向该地址单元赋给新的值,调用结束后主调函数可以使用这个数据。使用指针参数最重要的作用是,除了用return返回一个值之外,还可以通过指针参数返回多个数据。
例:指针变量作为形参,交换函数实参变量的值。
#include <stdio.h>
void swap(int *p,int *q){
int t;
t=*p;
*p=*q;
*q=t;
}
int main(){
int a=3,b=5;
printf("没使用函数前:a=%d,b=%dn",a,b);
swap(&a,&b);
printf("使用函数后:a=%d,b=%dn",a,b);
return 0;
}
运行结果
五、返回指针值的函数
一个函数可以返回一个整型值、字符值、实型值等,也可以返回指针型的数据,即地址。这种返回指针的函数一般定义形式为: 类型名 *函数名( 参数列表 );
例如 : int *a( int x , int y );
a是函数名,调用它以后能得到一个指向整型数据的指针(地址)。()的优先级高于 * ,所以 a( int a ,int b )是函数,前面加个 * ,表示此函数是指针型函数(函数返回值是指针)。最前面的int表示返回的指针指向整型变量。
在使用函数返回指针类型时,要注意不能返回已经释放的内存地址。
最后
以上就是不安红牛为你收集整理的C语言总结(指针)一、地址和指针二、指针变量三、指向数组的指针 的全部内容,希望文章能够帮你解决C语言总结(指针)一、地址和指针二、指针变量三、指向数组的指针 所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复