指针详解上
前言
在学习c语言时,指针可以是被认为初学者难以理解的知识,而本篇文章将详解介绍指针,以便
提示:以下是本篇文章正文内容,下面案例可供参考
一、指针的概念?
要想了解指针,可以引入一个生活案例,在生活中当我们入住酒店时,需要门牌号才能找到我们入住的房间,而指针也可以被类比为“门牌号”,通常将其理解为地址,通过地址来查找在内存中存储的变量。
1. 指针变量的定义
定义形式如:数据类型*指针名
int *x ;
float *f;
char *c;
这里分别定义了整形指针变量x、浮点型指针变量f、字符型指针变量c。
注意:这里变量名不是*x、*f、*y,这里 * 表示他们为指针变量。
2. 指针变量使用
① 取地址操作符
代码如下:
#include <stdio.h>
int main()
{
int a=10;//将10赋值给变量a
int* p=&a;//将整形a的地址赋值给指针变量p
}
此时已经通过&操作符将a的地址赋值给了指针变量*p
②解引用操作符
解引用操作符 *,可以通过地址查询内存中存储内容
#include <stdio.h>
int main()
{
int a=10;//将10赋值给变量a
int* p=&a;//将整形a的地址赋值给指针变量p
printf("%d",*p);
}
二、指针变量类型
1. 指针变量类型的意义
指针变量的大小和类型无关,只要是指针变量,在同一平台下,大小都是一样的,只是访问内存空间的大小不一样,因此不同类型的变量的地址就应该放在对应的指针变量中。
案列如下:
int main()
{
int a = 0;
int* pa = &a;
double b = 0;
double* pb = &pb;
char c = 'w';
char* pc = &c;
return 0;
}
2.指针±整数
指针变量±整数时,跳过该变量类型的大小
例如:
int main()
{
int n = 10;
char* pc =(char*) & n;
int* pi = &n;
printf("&n=%p\n", &n);
printf("pc=%p\n", pc);
printf("pc+1=%p\n", pc+1);
printf("pi=%p\n", pi);
printf("pi+1=%p\n", pi+1);
}
可以看出,char类型跳过一个字节,int类型跳过四个字节。
3.void* 指针
在指针类型有一种特殊的类型,void*类型,可以理解为无具体类型的指针,它用来接受任意类型的地址,但是不能用来解引用和指针的±,因为并不清楚它能访问几个字节
用途:
常用来接受不同类型数据的地址,方便处理,达成实现泛型编程。
三、指针的用处
指针可以方便我们处理问题,那么指针到底能解决什么样的问题,不去使用它可不可以呢?
看下面案例
1.传值调用和传址调用
当我们想利用函数将两个变量的值交换该怎么操作呢?
当我们不会使用指针时,可能会这样写
#include <stdio.h>
void swap(int x,int y)
{
int temp;
temp=x;
x=y;
y=temp;
}
int main()
{
int a=10;
int b=20;
printf("交换前a=%d, b=%d\n",a,b);
swap(a,b);//交换函数
printf("交换后a=%d, b=%d\n",a,b);
}
运行结果如下:
可以看出a和b的值并没有改变,这是为什么呢?
原来当我们将a和b传给函数时,函数x和y接受了a和b的值,被称为传值调用,这是一份临时的拷贝,所以我们只是交换了函数里面x和y的临时拷贝,并没有真正改变a和b变量。
那么到底该如何进行交换呢?
这里就运用到了指针,上文说到,指针是地址,可以通过地址查找内存存储变量,其实还可以通过地址来改变内存中存储的变量
案例如下:
#include <stdio.h>
void swap(int* x, int* y)
{
int temp;
temp = *x;
*x = *y;
*y = temp;
}
int main()
{
int a = 10;
int b = 20;
printf("交换前a=%d, b=%d\n", a, b);
swap(&a, &b);//交换函数
printf("交换后a=%d, b=%d\n", a, b);
}
运行结果如下:
可以看到成功的改变了a和b变量,这就是传址调用,将a和b的地址传给x和y,通过地址来改变内存中存储的地址。
四、指针与数组的关系
1.数组的基本概念
数组名就是首元素的地址,有二个例外,一个是在siezof(数组名),这里计算的是数组中所有元素大小,另一个是
&数组名,这里带表取出的整个数组的地址。
2.用指针访问数组
数组因中元素被存储在一段连续开辟的内存空间中,因此我们可以通过指针来访问数组中元素。
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int i;
int* p=arr;
for(i=0;i<10;i++)
{
printf("arr[i]=%d",i,*(p+i));
}
指针p指向了数组首元素的地址,我们依旧可以通过它来访问内存中值,以及改变内存中的值。
如:
1.p+1等价于指针指向跳过4个字节,指针指向arr[0]的下一个元素,可以通过p±整数来遍历整个数组。
2.*(p±i)等价于arr[i],可以来访问数组中存储的元素。
3.*p用来改变数组中存储的元素。
3.指针数组与数组指针
① 指针数组概念:
概念:int* p[n] ,(注意优先级:()>[]> *)
因此int *p[n],p会先与[ ]结合,p是数组,存放整形指针( int *)类型的数组。
② 示例分析:指针数组来模拟二维数组
#include <stdio.h>
int main()
{
int arr1[] = {1,2,3,4,5};
int arr2[] = {6,7,8,9,10};
int arr3[] = {11,12,13,14,15};
int* parr[3] = { arr1,arr2,arr3 };
for (int i = 0; i < 3; i++)
{
for (int j = 0; j < 5; j++)
{
printf("%d ", *(*(parr + i) + j));
}
printf("\n");
}
}
parr是指针数组,存放了arr1、arr2、arr3的首元素地址,通过分别访问数组的首元素地址来访问这三个数组的元素。
综上:指针数组可以用数组来管理同类型的指针,通过索引数组方式找到存放的地址,再通过地址来访问指针指向的对象。
③ 数组指针概念
概念:是指针,指向数组的指针,用来存数组的地址。
int arr[10];
int (*p)[10]=&arr;
下图为解释:
注意:此时p+1跳过的是整个数组,因为p存的是整个数组的地址,即跳过40个字节。
④ 示例分析:二维数组传参的本质
#include<stdio.h>
void test(int(*p)[5], int row, int col)
{
for (int i = 0; i < row; i++)
{
for (int j = 0; j < col; j++)
{
printf("%d ", *(*(p + i) + j));
}
printf("\n");
}
}
int main()
{
int arr[3][5] = { {1,2,3,4,5},{6,7,8,9,10},{11,12,13,14,15}};
test(arr, 3, 5);
}
通过以上案例,可以发现二维数组传参,传的就是二维数组第一行第一列的地址,即数组的地址,那么形参就可以写成数组指针,再通过指针访问数组所存元素。