目录
前言
学习指针之前,我们先来看看什么是内存地址
每一个变量都有一个内存位置,每一个内存位置都定义了可使用 & 运算符访问的地址,它表示了在内存中的一个地址。
请看下面的实例,它将输出定义的变量地址:
#include <stdio.h>
int main ()
{
int a= 10;
int *p;
// 定义指针变量(*表示p存储的是一个地址,int*表示p存储的是一个整型变量的位置)
p = &a;
printf("a变量的地址: %p\n", p); //%p用于输出地址
return 0;
}
当上述代码执行时,输出结果为
通过上述结果我们可以知道指针p访问的是a的地址,10是a的变量值,可理解为下图
一、认识指针
1.指针的定义
指针也就是内存地址,指针变量是用来存放内存地址的变量。就像其他变量或常量一样,必须在使用指针存储其他变量地址之前,对其进行声明。指针变量声明的一般形式为:
type *a;
在这里,type 是指针的类型,它必须是一个有效的 C 数据类型,a是指针变量的名称。指针的星号 * 与乘法中使用的星号是相同的。但是,在这个语句中,星号是用来指定一个变量是指针(故在书写指针时*不可丢失)。以下是有效的指针声明:
int *a; /* 一个整型的指针 */
double *b; /* 一个 double 型的指针 */
float *c; /* 一个浮点型的指针 */
char *d; /* 一个字符型的指针 */
注意:指针类型必须与变量类型保持一致
2.指针变量占用内存大小
#include<stdio.h>
int main()
{
int a=10;
int *p1=&a;
printf("%d\n",sizeof(p1));
char b='a';
char *p2=&b;
printf("%d\n",sizeof(p2));
float c=3.14;
float *p3=&c;
printf("%d\n",sizeof(p3));
return 0;
}
得到结果如图
通过该代码可知,指针无论是什么类型,其占用内存均相同。在64位中为8字节,在32位中为4字节。 (均为16进制)
3.指针变量赋值,解引用。
#include <stdio.h>
int main ()
{
int a = 20; // 实际变量的声明
int *p=&a; // 指针变量的声明,存储 a 的地址
printf("p 变量的地址: %p\n", &p );//在指针中存储的地址
//解引用
// 在指针变量中存储的地址
printf("p 变量存储的地址: %p\n", p );
// 使用指针访问值
printf("*p 变量的值: %d\n", *p );
//若是对*p进行重新赋值,则改变a的值
*p=200;
printf("a的值为 %d",a);
//步骤进行 1.从p中取出值 2.找到该值对应的所处空间 3.修改该地址值
return 0;
}
代码运行结果
4.野指针和空指针
野指针:(悬挂指针)指向非法的内存地址指针叫作野指针(Wild Pointer),也叫悬挂指针(Dangling Pointer),意为无法正常使用的指针。
出现原因:1.使用未初始化的指针 2.指针所指的对象已经消亡 3.指针释放后之后未置空
#include<stdio.h>
int main()
{
//未定义指针
int *p;
printf("%d",*p);
//所处对象死亡
int *retAddr()
{
int num=10;
return #
}
int main()
{
int *p=NULL;
p=retAddr();
printf("%s",&p);
printf("%d",*p);
最后一行,输出的并非想象中的num的值10,因为变量num是存储在栈空间的局部变量,离开函数超出其作用域后就会被释放掉,因此输出的值就是不确定的值了。
}
//指针定义后未悬空
指针p被free或者delete之后,没有置为NULL,让人误以为p是个合法的指针。对指针进行free和delete,只是把指针所指的内存空间给释放掉,但并没有把指针本身置空,此时指针指向的就是“垃圾”内存。释放后的指针应立即将指针置为NULL,防止产生野指针。考察如下程序。
#include <stdio.h>
int main()
{
int *p=NULL;
p=new int[10];
delete p;
printf("%d",*p);
}
5.指针优点
在C语言中参数传递有两种途径 1.默认值传递 2.地址传递(数组传递也为地址传递)
对于默认值传递,无法在两个函数间修改参数,如下例
#include<stdio.h>
void a(int t)
{
t=200;
}
int main()
{
int c=100;
a(c);
printf("c的值为%d",c);
return 0;
}
运行结果
由图可看出在函数中,值是无法传递的,而指针就解决了这一问题。
#include<stdio.h>
void a(int *t)
{
*t=200;
}
int main()
{
int c=100;
a(&c);
printf("c的值为%d",c);
return 0;
}
输出结果
二、指针运用
1.指针的算术运算
- 指针的每一次递增,它其实会指向下一个元素的存储单元。
- 指针的每一次递减,它都会指向前一个元素的存储单元。
- 指针在递增和递减时跳跃的字节数取决于指针所指向变量数据类型长度,比如 int 就是 4 个字节。
1.递增指针
#include <stdio.h>
int main ()
{
int max=3;
int var[] = {10, 100, 200};
int i, *p;
/* 指针中的数组地址 */
p = var;
for ( i = 0; i <max; i++)
{
printf("存储地址:var[%d] = %p\n", i, p );
printf("存储值:var[%d] = %d\n", i, *p );
/* 指向下一个位置 */
p++;
}
return 0;
}
使用指针代替数组,变量指针可以递增,而数组不能递增,数组可以看成一个指针常量。
输出结果
2.递减指针:同递增指针,此处不一一赘述
3.指针比较
指针可以用关系运算符进行比较,如 ==、< 和 >。如果 p1 和 p2 指向两个相关的变量,比如同一个数组中的不同元素,则可对 p1 和 p2 进行大小比较。其本质是对地址比较
#include <stdio.h>
int main ()
{
int max=3
int var[] = {10, 100, 200};
int i, *ptr;
/* 指针中第一个元素的地址 */
p = var;
i = 0;
while ( p <= &var[max - 1] )
{
printf("存储地址:var[%d] = %p\n", i, ptr );
printf("存储值:var[%d] = %d\n", i, *ptr );
/* 指向下一个位置 */
ptr++;
i++;
}
return 0;
}
2.指针数组
1.指针数组优缺点
1.普通数组优缺点:数组具有独立性;在数据较大时,效率低;不具备修改其他变量的能力
2.指针数组
优点:可以通过数据元素修改外部变量的值,无论数据多大,都只用拷贝4个字节
缺点:能力太强,和外部数据关系过于紧密,外部数据被释放,就变为野指针。
2.指针数组分类
1.字符串数组
指针数组中看到双引号要把它当成地址
#include<stdio.h>
#include<string.h>
int main()
{
char *p[]={"abc","bcd","fgb"};
int l=sizeof(p);
for(int i=0;i<l;i++)
{
printf("字符串内容 %s,字符串长度 %d,首字母 %c\n",p[i],strlen(p[i]),*p[i]);
}
return 0;
}
运行结果
2.数字类数组
#include <stdio.h>
int main() {
int num1 = 10, num2 = 20, num3 = 30;
// 声明一个整数指针数组,包含三个指针
int *ptrArray[3];
// 将指针指向不同的整数变量
ptrArray[0] = &num1;
ptrArray[1] = &num2;
ptrArray[2] = &num3;
// 使用指针数组访问这些整数变量的值
printf(" %d\n", *ptrArray[0]);
printf(" %d\n", *ptrArray[1]);
printf(" %d\n", *ptrArray[2]);
return 0;
}
3.const和指针
const在语法角度限制变量不允许修改
- const 放在*右侧,表示指针指向不能修改,指针常量
- const 放在*左侧,表示指针指向空间值不能修改,常量指针
- 指针传递会赋予函数额外修改外部变量的能力,const 可以限制指针能力
#include<stdio.h>
int main()
{
//*在const左侧,表示指向不能改,值可以改
int a=100;
int * const p=&a;
*p =200;
printf("a的值为 %d\n",a);
//*在const右侧,值不能改
int b=300;
int const *p2=&a;
p2=&b;
printf("p2的值为 %d",*p2);
return 0;
}
输出结果
4.多级指针
优点:需要修改指针的值,通过多级指针直接修改其地址值,会大大减少代码冗余,提高代码质量。
缺点:语法复杂,访问数据效率降低
# include <stdio.h>
int main(void)
{
int i = 10;
int *p = &i;
int **q = &p;
int ***r = &q;
printf("i = %d\n", ***r);
return 0;
}
输出结果是:
i = 10
练习:利用指针实现冒泡排序
//使用指针实现冒泡排序
#include<stdio.h>
//定义调用函数
void mp(int* p, int n)
{
int i,j;
int t;//定义交换变量
for (i = 0; i < n - 1; i++)//外层循环:一共要进行n-1躺循环
{
for (j = 0; j < n - 1 - i; j++)
{
if (*(p + j) > *(p + j + 1))
{
t = *(p + j);
*(p + j) = *(p + j + 1);
*(p + j + 1) = t;
}
}
}
//输出元素
printf("则冒泡排序后的结果为:\n");
for (i = 0; i < n; i++)
{
printf("%d", *(p+i));
}
}
int main()
{
int a[1000]={0};
int n;//定义个数
int i;
printf("请输入要比较元素的个数:");
scanf("%d", &n);
printf("请输入元素:");
for (i = 0; i < n; i++)
{
scanf("%d", &a[i]);
}
//调用函数
mp(a, n);
}