C语言指针

2021/7/23 23:32:57

本文主要是介绍C语言指针,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

善于利用指针

    • 一、指针是什么
    • 二、指针变量
      • 指针变量作为函数参数
    • 三、通过指针引用数组
      • 数组元素的指针
      • 在引用数组元素时的指针运算
      • 利用指针引用数组元素
      • 用数组名作函数参数
      • 以变量名和数组名作为函数参数的比较
      • 通过指针引用多维数组
        • 多维数组的地址
        • 指向多维数组元素的指针变量

一、指针是什么

内存区的每一个字节都有一个编号,这就是“地址”。通过地址能找到所需的变量单元,可以说地址指向该变量单元。形象化的将地址称为“指针

数据是分类型的,对不同类型的数据,在内存中分配的存储单元的大小(字节数)和存储方式是不同的(如整数是以二进制补码的形式存放,实数则以指数形式存放)

C语言中的地址包括位置信息(内存编号、或称纯地址)和它所指向的数据的类型信息,或者说是“带类型的地址”

指针和指针变量是两个概念:指针是一个地址,而指针变量是存放地址的变量。

二、指针变量

所有的指针类型存储的都是内存地址,内存地址都是一个无符号十六进制整形数。(32位操作系统占4字节,64位操作系统占8字节)

定义指针变量:*类型名 指针变量名;左边的类型名是“基类型”,用来指定此指针变量可以指向的变量的类型。

指针变量作为函数参数

典型实例:

#include<stdio.h>
int main()
{
	void sort(int *a,int *b,int *c);
	int a,b,c;
	printf("please enter three numbers:");
	scanf("%d%d%d",&a,&b,&c);
	sort(&a,&b,&c);
	printf("The sorted numbers are:%d,%d,%d",a,b,c);
	return 0;
}
void sort(int *a,int *b,int *c)	//形参是指针变量,定义sort函数,对三个整数进行排序
{
	if(*a<*b)
		swap(a,b);
	if(*a<*c)
		swap(a,c);
	if(*b<*c)
		swap(b,c);
}
void swap(int *x,int *y)	//形参是指针变量,定义swap函数交换*x和*y的值
{
	int temp;		//整型变量temp作为临时辅助变量实现*x和*y的值交换
	temp=*x;
	*x=*y;
	*y=temp;
}	
/*
错误写法:
void swap(int *x,int *y)	
{
	int *temp;		//*temp是指针变量temp所指向的值,未给temp赋值(野指针),*temp指向的值不可预见
	*temp=*x;		//对*temp赋值是向一个未知的储存单元赋值,这个储存单元里面可能存着一个有用的数据,可能会破坏系统的正常工作状态
	*x=*y;
	*y=*temp;
}
void swap(int *x,int *y)	//不能企图改变指针形参的值而改变指针实参的值
{
	int *temp;
	temp=a;
	a=b;
	b=temp
}
*/

三、通过指针引用数组

数组元素的指针

数组元素的指针就是数组元素的地址,引用数组元素可以用下标法(如a[3]),也可以用指针法(通过数组元素的指针找到所需的元素)

数组名不能代表整个数组,只代表数组中首元素的地址,是一个指针型常量。

在引用数组元素时的指针运算

在指针已指向一个数组元素时可以进行以下运算

  • 加一个整数(用+或+=),如p+1;
  • 减一个整数(用-或-=),如p-1;
  • 自加运算,如p++,–p
  • 自减运算,如p–,--p
  • 两个指针相减,如p1-p2(只有p1和p2都指向同一数组中的元素时才有意义)
  1. 数组元素是float型,每个元素占四字节。p+1意味着使p的值(是地址)加四个字节,以使他指向下一个元素。
  2. p2-p1的结果是:p2-p1的值(两个地址之差)初以数组元素的长度

利用指针引用数组元素

设p开始时指向数组a的首元素(即p=a)

  1. p++;
    *p;
    

    p++使p指向下一元素a[1],然后在执行*p,则得到下一个元素a[1]的值

  2. *p++
    

    由于*和++同级优先,结合方向为自右向左,因此等价于*(p++)

    for(i=0;i<10;i++,p++)
        printf("%d",*p)
    等价于:
    for(i=0;i<10;i++)
        printf("%d",*p++)
    

    作用都是先输出*p的值,再使p加1

  3. *(p++)和*(++p)作用是不一样的,*(p++)是先取p的值,再使p加1。*(++p)是先使p加1,再取*p

  4. *(arr+i)和arr[i]是无条件等价的

用数组名作函数参数

函数的形参会退化为指针,失去精度(不知道数组元素个数了)

fun(int arr[],int n)
{
    ……
}
等价于
fun(int *arr,int n)
{
    ……
}

以变量名和数组名作为函数参数的比较

实参类型变量名数组名
要求形参的类型变量名数组名或指针变量
传递的信息变量的值实参数组首元素的地址
通过函数调用是否能改变实参的值不能改变实参变量的值能改变实参数组的值

例:将数组arr中的n个整数按相反顺序存放

#include<stdio.h>
int main()
{
	void exchange(int arr[],int n);
	int arr[]={10,9,8,7,6,5,4,3,2,1};
	int i;
	printf("The original array:\n");
	for(i=0;i<10;i++)
		printf("%d ",arr[i]);
	exchange(arr,10);		//数组名是数组首元素地址,注意arr不是指针变量,是一个指针类型常量
	printf("\nThe array have been exchanged:\n");
	for(i=0;i<10;i++)
		printf("%d ",arr[i]);
	return 0;
}

void exchange(int arr[],int n)	//exchange函数中的n用来接收需要处理的元素的个数,形参用数组名表示
    							//函数原型还可以写成void exchange(int *arr,int n),形参用指针变量表示
{
	int i,j,temp;
	for(i=0,j=9;i<10;i++,j--)
	{temp=0;
		if(i<j)
		{
			temp=arr[i];
			arr[i]=arr[j];
			arr[j]=temp;
		}else
			break;
	}
}

如果有一个实参数组,要想在函数中改变此数组中的元素的值,实参与形参有以下四种对应关系。(实际上都是地址的传递)

  1. 实参和形参都用数组名
  2. 实参用数组名,形参用指针变量
  3. 实参形参都用指针变量
  4. 实参为指针变量,形参为数组名

例:用指针的方法对十个整数按由大到小的顺序排序

#include<stdio.h>
#define N 10
int main()
{
	void sort(int *x,int n);
	int arr[N]={6,3,9,2,5,7,4,8,1,10};
	int i,*p;
    p=arr;		//指针变量指向a[0]
	printf("\n The original numbers are:\n");
	for(i=0;i<10;i++)
	printf("%d ",arr[i]);
	sort(p,N);
	printf("\n The sorted numbers are:\n");
	for(i=0;i<10;i++)
	printf("%d ",arr[i]);

	return 0;
}
void sort(int x[],int n)	//定义sort函数,x是形参数组名
{
	int i,j,temp;
	for(i=0;i<N-1;i++)
	{temp=0;
		for(j=0;j<N-i-1;j++)
		{
			if(x[j]<x[j+1])
			{
				temp=x[j];
				x[j]=x[j+1];
				x[j+1]=temp;
			}
		}
	}
}

通过指针引用多维数组

多维数组的地址

int a[3][4]={{1,3,5,7},{9,11,13,17},{19,23,27,31}};

a是二维数组名。a包含3行,即3个行元素:a[0],a[1],a[2]。每个行元素又是一个一维数组,它包含4个元素(即4个列元素),可以认为二维数组是“数组的数组”。

从二维数组角度看a代表的是二维数组首元素的地址,a+1代表序号为1的行的起始地址(即a[1]),a+2代表a[2]的起始地址。假设a=2000,则a+1=a[1]=2016,a+2=a[2]=2032

a[0],a[1],a[2]是一维数组名,代表首元素地址

a[0]+0,a[0]+1,a[0]+2,a[0]=+3 分别是a[0][0],a[0][1],a[0][2],a[0][3]元素的地址(即&a[0][0],&a[0][1],&a[0][2],&a[0][3])

a[0]和*(a+0)等价,a[1]和*(a+1)等价,a[i]和*(a+i)等价。因此a[0]+1和*(a+0)+1等价都是&a[0][1]

(a[0]+1)是a[0][1]的值,同理*(*(a+0)+1)或*(*a+1)也是a[0][1]的值,即**(a[i]+j)或*(*(a+i)+J)是a[i][j]的值**

**C语言的地址信息中既包含位置信息(内存编号),还包含他所指向的数据的类型信息。**a[0]是一维数组名,它是一维数组中起始元素的地址;a是二维数组名,它是二维数组首行起始地址,二者的纯地址是相同的,但它们的基类型是不同的,即它们所指向的数据的类型不同,前者是整型数据,后者是一维数组。如果用一个指针变量pt来指向此一维数组,应当这样定义:int (*pt)[4];

在二维数组中,a+i,a[i],*(a+i),&a[i],&a[i][0]的值相同,都表示同一地址,但基类型不同。

例:输出二维数组有关数据(地址和元素的值)

#include<stdlib.h>
int main()
{
	int a[3][4]={{1,3,5,7},{9,11,13,17},{19,23,27,31}};
	printf("%d,%d\n",a,*a);					//0行起始地址和0行0列元素地址
	printf("%d,%d\n",a[0],*(a+0));			//0行0列元素地址
	printf("%d,%d\n",&a[0],&a[0][0]);		//0行起始地址和0行0列元素地址
	printf("%d,%d\n",a[1],a+1);				//1行0列元素地址和1行起始地址
	printf("%d,%d\n",&a[1][0],*(a+1)+0);	//1行0列元素地址
	printf("%d,%d\n",a[2],*(a+2));			//2行0列元素地址
	printf("%d,%d\n",&a[2],a+2);			//2行起始地址
	printf("%d,%d\n",a[1][0],*(*(a+1)+0));	//1行0列元素的值
	printf("%d,%d\n",*a[2],*(*(a+2)+0));	//2行0列元素的值
	return 0;
}
            /*结果是:
            6422000,6422000
            6422000,6422000
            6422000,6422000
            6422016,6422016
            6422016,6422016
            6422032,6422032
            6422032,6422032
            9,9
            19,19
            */

指向多维数组元素的指针变量

  1. 指向数组元素的指针变量

    int *p;

  2. 指向由m个元素组成的一维数组的指针变量

    int (*P)[m];



这篇关于C语言指针的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程