C语言---指针(指针与数组,指针与函数)

指针

指针是C语言的一个重要组成部分,是C语言的核心,精髓所在,用好指针可以在C语言编程中祈祷事半功倍的效果。一方面,可以提高程序的编译效率和执行速度以及实现动态的存储分配;另一方面,使用指针可使程序更灵活,便于表示各种数据结构,编写高质量的程序

指针概念

在计算机中,所有的数据都是存放在存储器中的。一般把存储器中的一个字节称为一个内存单元,不同的数据类型所占用的内存单元数不等,例如int型占4个内存单元,char型占1个内存单元。为了正确地访问这些内存单元,必须为每个内存单元编上号,根据一个内存单元的编号即可准确地找到该内存单元。内存单元的编号也叫做地址。既然根据内存单元的编号或地址就可以找到所需的内存单元,所以通常也把这个地址称为指针。可以说,指针就是内存地址

在C语言中,允许用一个变量来存放指针,这种变量称为指针变量。因此,一个指针变量的值就是某个内存单元的地址

访问内存中的数据有两种方式:直接访问和间接访问。

直接访问:通过变量来实现,因为变量是内存中某一块存储区域的名称;
间接访问:通过指针来实现。指针并不是用来存储数据的,而是用来存储数据在内存中的地址的。

指针类型

区分指针的类型和指针所指向的类型:

指针的类型:将指针声明语句中去掉指针名字,而剩下的部分;
指针所指向的类型:将指针声明语句中去掉指针名字和指针声明符(*),而剩下的部分。例如:

int* ptr;

指针的类型:int;指针所指向的类型:int*。

指针值

指针的值是指指针本身存储的数值,这个值将被编译器当做一个地址,而不是一个一般的数值。在32位程序里,所有类型的指针的值都是一个32位整数,因为32位程序里内存地址全部都是32位长

指针所指向的内存区就是从指针的值所代表的那个内存地址开始,长度为sizeof(指针所指向的类型)的一片内存区。换句话说,指针的值即为指针指向了以该值为首地址的一片内存区;我们说一个指针指向了某块内存区域,就相当于说该指针的值是这块内存区的首地址

指针运算符&和*

C语言中提供了地址运算符(&)来表示变量的地址,指针运算符(*)来表示获取指针变量所指向的变量的值。

&p和&p的区别:

因为*和&具有相同的优先级,且是右结合性(从右向左),故分析可得:

&*p:相当于&(*p),首先进行一次p运算再取地址。若p是一个非指针变量,p是非法的;若p是一个指针变量,&(p)=p;
&p:相当于(&p),先取地址再做
运算。若p是一个非指针变量,
(&p)=p;若p是一个指针变量,
(&p)=p。
可能你会有疑问,为什么p为指针变量,*(&p)=p?

p为指针变量时,表示指针的值为一个地址,但是这个地址也存储在一个变量里,也存储在一个内存单元里。&p的意义,就是存放了一个地址的内存单元的地址。

下面结合一个例子来具体分析理解:

#include<stdio.h>
 
int main()
{
    
    
	int a=0;
	int* p = &a;
 
	printf("%d %d\n", a, &a);
	printf("%d %d %d\n", p, *p, &p);
	printf("%d %d\n", a, *&a);
	printf("%d %d %d\n", p, *&p, &*p);
 
	return 0;
}

这段程序的运行结果为:

0 5240964
5240964 0 5240952
0 0
5240964 5240964 5240964
请按任意键继续. . .

这段程序主要理解两个点:当a为一个非指针变量的时候,*a是非法的;当p为一个指针变量的时候,&p的意义是存储了一个地址的内存单元的地址。

指向变量的指针

指针变量的使用
变量的指针就是变量的地址,存放变量地址的变量就是指针变量。指针变量中只能存放指针(地址),不要将一个非零数(或任何其他非地址类型的数据)赋予一个指针变量。如:

int* p = 2;            /* 错误的赋值,需要指向一个地址或0 */
int* p = 0;            /* 正确的赋值,表示指针指向空 */

指针变量被初始化为0,表示为空指针。C语言中用NULL表示空指针,它没有指向任何对象。一般来说,所有的指针变量在定义的时候都必须初始化,如果不明确要给指针赋什么值,就赋值为0。

指针变量作为函数参数

函数的参数不仅仅可以为整型、实型、字符型等数据,还可以是指针类型。它的作用是将以个变量的地址传送到另一个函数中。也就是说,如果想要将函数的过程中的操作结果反馈到主调函数,指针变量做参数是一个不错的选择。

例子1:

#include<stdio.h>
 
void swap(int* point1, int* point2);
 
int main()
{
    
    
	int a,b;
	int* p1 = &a;
	int* p2 = &b;
 
	scanf_s("%d %d", &a, &b);
	swap(p1, p2);
	printf("%d %d\n", a, b);
	printf("%d %d\n", *p1, *p2);
 
	return 0;
}
 
void swap(int* point1, int* point2) {
    
    
	int temp;
	temp = *point1;
	*point1 = *point2;
	*point2 = temp;
}

这段程序的运行结果为:

1 2
2 1
2 1
请按任意键继续. . .

例子2:

#include<stdio.h>
 
void swap(int point1, int point2);
 
int main()
{
    
    
	int a,b;
 
	scanf_s("%d %d", &a, &b);
	swap(a, b);
	printf("%d %d\n", a, b);
 
	return 0;
}
 
void swap(int point1, int point2) {
    
    
	int temp;
	temp = point1;
	point1 = point2;
	point2 = temp;
}

这段程序的运行结果为:

1 2
1 2
请按任意键继续. . .

两段程序对应着看,例子1使用指针传递地址,将函数的过程中的操作结果反馈到主调函数;而例子2没有使用指针,函数过程中的操作与主调函数之间没有任何关系。

指针与数组

指针与一维数组的关系
每个变量有地址,每个数组包含若干个元素,同样每个数组元素都在内存中占用存储单元,都有相对应的地址。指针变量既然可以指向变量,同样也可以指向数组元素(把某一元素的地址放到一个指针变量中)

定义一个指向数组元素的指针变量的方法,与上文指向变量的指针变量相同。如果要让指针指向某个数组,只需要将这个数组的元素地址赋值给指针即可:

int a[5], *p;
p = a;                /* 数组名表示数组的首地址,可将数组名直接赋值给指针变量 */
p = &a[0];            /* 数组第一个元素的地址也就是整个数组的首地址 */

p=a,并不是将a数组的所有元素都赋值给p,只是把数组a的第一个元素的地址即首地址赋值给指针p。C语言规定:数组名代表数组的首地址,也就是第0个元素的地址
例子:

#include <stdio.h>
 
int main()
{
    
    
	int a[10] = {
    
     0,1,2,3,4,5,6,7,8,9 };
	int* p;
	p = &a[0];
 
	printf("%d %d %d %d\n", a[0], &a[0], &a, a);
	printf("%d %d\n", p, *p);
 
	return 0;
}

这段程序的运行结果为:

0 2948652 2948652 2948652
2948652 0
请按任意键继续. . .

这里着重看一下:数组a的地址&a和数组名a的值都等于收元素a[0]处的地址。

指针的运算
对于指向数组的指针变量,可以加上或减去一个整数n,这意味着把指针从当前指向的位置(指向的某个数组元素)向后或向前移动n个位置。

这里应当注意:

数组指针变量向前向后移动一个位置和地址加1或减1在概念上是不同的。因为数组的元素可以由不同的数据类型,所占用的字节长度也是不同的。如指针变量加1,即表示指针变量指向下一个元素的首地址,而不是在原地址的基础上加1;
指针变量的加减运算只能对数组指针变量进行,对只想其他数据类型的指针变量做加减运算是毫无意义的。
两个指针变量之间的运算:只有指向同一数组的两个指针变量才能进行运算,否则运算是毫无意义的。
两指针变量相减:两指针变量相减所得之差,是两个指针所指向的数据元素之间相差的元素个数,乘以该数组每个数据元素的长度(字节数);
两指针变量相加:两指针变量不能进行加法运算,毫无实际意义.

指针与二维数组的关系
通过指针来访问二维数组,主要有列指针、行指针(数组指针)、指针数组(列指针数组)三种方式来进行访问。下面依次进行讲解:

二维数组与一维数组的关系
在C语言中,二维数组是按行优先的规律转换为一维数组地址存放在内存中的,因此可以通过指针访问二维数组中的元素,如果有:

int a[M][N];

则将其转换为一维线性地址,一般公式如下:

线性地址=a+i×M+j

其中,a为数组的首地址。

从这里可以看出,我们需要的是一个列指针来进行转化访问的。

#include <stdio.h>
 
int main()
{
    
    
	int a[2][3] = {
    
     1,2,3,4,5,6 };
	int i;
	int* p;
	p = *a;
 
	for (i = 0; i < 6; i++) 
	{
    
    
		printf("%d\n", *p);
		p++;
	}
 
	return 0;
}

这段程序的运行结果为:

1
2
3
4
5
6
请按任意键继续. . .

这里使用列指针来遍历,将二维数组转化为一维数组的方式来处理。需要注意的是:

	int* p;
	p = *a;

因为p的定义,是一个指向整型的指针(列指针),而a是一个指向一个一维数组的指针(行指针),需要通过*a转化为列指针赋值才行,如果直接写p=a的话会出错。

猜你喜欢

转载自blog.csdn.net/weixin_43491077/article/details/112388206