初识指针
什么是指针:
指针是用来存储内存地址的变量。可以使用 *
符号来声明一个指针变量。
我们在定义一个普通变量时,都会有数据类型+变量名。但是,对于电脑来说,并不知道这个名字的含义。所以就需要一个能够让电脑认识的命令,同时代表这个变量。这时候,指针的作用就体现了。
对于指针更加生动的理解:
咱们举个例子:变量名,就是一个人的名字,但是指针,是一个人的身份证号。
咱们都知道,全局变量和局部变量可有相同的变量名,但是他们的内存地址不同。就像有两个姓名一样的人,但是他们的身份证号不同。
当通过姓名叫人时,相同名字的人可能发生冲突,但通过叫身份证号,可以很快区分,体现在C语言中,这便是指针的有趣且重要的作用了。
对于指针的定义
下面这段代码是之前定义普通变量时的形式
#include<stdio.h>
int main ()
{
int a;//定义一个整型变量
char b;//定义一个字符型变量
return 0;
}
下面这段代码是定义指针变量的格式
#include<stdio.h>
int main ()
{
int *a;//定义一个整型指针变量
char *b;//定义一个字符型指针变量
return 0;
}
所以咱们能够知道,定义指针变量和普通变量的其中一个区别是在变量名之前有一个 '*', 这个符号代表对指针变量的声明。
取地址运算符和解引用运算符
指针是存储内存地址的变量,那内存地址我们应该如何获取呢??
因此,有一个运算符-----'&',叫取地址运算符。这个符号的作用,就是获取普通变量的内存地址。
#include<stdio.h>
int main ()
{
int a = 10;//定义一个整型变量
int *p;
p = &a;//取a的内存地址,p指向a的内存地址
return 0;
}
在这段代码中,&a 获取了a的地址,并且指针p指向了a的地址。咱们可以通过变量名a访问内容,也可以通过指针的方式访问内容。
#include<stdio.h>
int main ()
{
int a = 10;//定义一个整型变量
int *p;
p = &a;//取a的内存地址,p指向a的内存地址
printf("%d\n",a);//通过变量名
printf("%d\n",*p);//通过用户定义的指针
printf("%d\n",*(&a));//通过指针
return 0;
}
在上述代码中
第一个printf,就是通过常规的变量名进行内容的访问。
第二个printf,通过指针访问。
我们可以看到一个面熟的符号 '*'。但是,这个和定义指针的符号含义不同。在这里,叫做解引用运算符,而在定义指针的语句中,只是对指针变量的声明,发挥声明作用。
解引用运算符,我们可以理解为:通过身份证号,知道一个人的姓名。通过内存地址,我们可以知道指向的变量,并且输出对应的内容。
通过对p指向的地址进行解引用,我们获取了变量的内容,通过'printf'输出。
第三个printf
同时出现了'*''&',说明,这两个符号的作用是相反的。取地址运算符--&,获取a变量的内存地址,解引用运算符--*,对内存地址进行解引用,获取到对应内容,最后通过printf输出内容。
%p输出内存地址
#include<stdio.h>
int main ()
{
int a = 10;//定义一个整型变量
int *p;
p = &a;//取a的内存地址,p指向a的内存地址
printf("%p\n",p);
printf("%p\n",&a);
return 0;
}
可以看到,指针p指向的就是变量a的地址
指针的间接访问和变量名的直接访问
#include<stdio.h>
int main ()
{
int a = 10;
int *p = &a;
printf("%d\n",a);
printf("%d\n",*p);
return 0;
}
指针p指向变量a的内存地址,通过解引用p指向的地址,间接对a的内容进行访问
指针的运算
在指针变量中:
1.对于指针变量的运算,有意义的就是加,减。乘除是不被允许的,因为我们无法明确运算后的结果指向了什么位置。
2.指针的运算与指针的类型相关。我们都知道,不同类型的变量在内存中占据不同的字节空间。整型int占据4个字节,字符型char占据1个字节,实型(浮点型)float占据4个字节
所以,对于整型指针,每加一时,会向前移动4个字节。字符型指针,每加一,会想前移动1个字节。
#include<stdio.h>
int main ()
{
int a = 10;//定义一个整型变量
int *p;
p = &a;//取a的内存地址,p指向a的内存地址
printf("%p\n",p);//输出p指向的内存地址
printf("%p\n",p+1);//输出运算后的内存地址
return 0;
}
在数组中:
咱们要知道,数组中的每一个元素,在内存中的内存地址都是连续的。
如果要想用自己定义的指针指向数组中的元素,数据形式一点要相同。
整型数组,每个元素的内存地址相差4
#include<stdio.h>
int main ()
{
int a[5] = {1,2,3,4,5};//定义一个整型数组
int *p;//定义一个整型指针
int *p2;
p = &(a[0]);
p2 = &(a[1]);
printf("第一个元素的地址是:%p\n",p);
printf("第二个元素的地址是:%p\n",p2);
return 0;
}
字符型数组,在内存地址中每个元素的内存地址相差1
#include<stdio.h>
int main ()
{
char a[5] = {'1','2','3','4','5'};//定义一个字符型数组
char *p;//定义一个字符型指针
char *p2;
p = &(a[0]);
p2 = &(a[1]);
printf("第一个元素的地址是:%p\n",p);
printf("第二个元素的地址是:%p\n",p2);
return 0;
}
指针的初步运用
运用指针实现数值的交换
咱们先复习之前普通变量的数值交换
#include<stdio.h>
void exchange(int num1,int num2)
{
int tmp = 0;
tmp = num1;
num1 = num2;
num2 = tmp;
printf("交换后的值:a = %d,b =%d\n",num1,num2);
}
int main ()
{
int a =10;
int b =20;
exchange(a,b);
return 0;
}
接下来运用指针进行交换
#include<stdio.h>
void exchange(int *num1,int *num2)//定义两个整型指针,分别指向a,b的地址
{
int tmp;
//内存地址的交换
tmp = *num1;
*num1 = *num2;
*num2 = tmp;
}
int main ()
{
int a =10;
int b =20;
exchange(&a,&b);//传入a,b的内存地址
printf("交换后的值:a = %d,b =%d\n",a,b);
return 0;
}
使用指针进行交换时,直接可以对内存地址进行交换,而不是进行变量名的内容交换。
当内存地址修改时,指向的内容也会随之改变
运用指针实现数组遍历
#include<stdio.h>
int main ()
{
int arr[5] = {1,2,3,4,5};
int *p;//定义一个整型指针
int i;
p = &(arr[0]);//用取地址运算符&,获取第一个元素的地址,p指向数组第一个元素的地址
for(i = 0;i<5;i++){//for循环实现遍历
printf("%d\n",*p);
p++;
}
return 0;
}
数组与指针
数组名与指针相同和不同
相同:
每一个数组名,代表数组的首地址,也就是指针。同时,每一个数组的第一个元素的地址,也代表该数组的首地址。所以,咱们可以直接用数组名代表指针,或者第一个元素进行数组的操作。
#include<stdio.h>
int main ()
{
int arr[5] = {1,2,3,4,5};
printf("%p\n",arr);
printf("%p\n",&(arr[0]));
return 0;
}
#include<stdio.h>
int main ()
{
int arr[5] = {1,2,3,4,5};
int *p = arr;//定义一个指针,指向数组
printf("%p\n",arr);
printf("%p\n",p);
printf("%p\n",&(arr[0]));
return 0;
}
不同点:
数组名,相当于指针常量,不可运算;而指针,可以进行运算。
#include<stdio.h>
int main ()
{
int arr[5] = {1,2,3,4,5};
int *p = arr;//定义一个指针,指向数组
arr++;
printf("%p\n",arr);
printf("%p\n",p);
printf("%p\n",&(arr[0]));
return 0;
}
结束语:
OK,对于指针的初步认识,解释完毕,大家还有什么疑惑,评论区讨论或者私信。希望大家在以后的C语言学习生涯中,更上一层楼!!!!