C语言丨指针和数组的用法,不要再傻傻分不清了
指针是 C 语言中的一个特点,也是内存地址,是内存单元的编号,指针变量是用来存放内存地址的变量,不同类型的指针变量所占用的存储单元长度是相同的,而存放数据的变量因数据的类型不同,所占用的存储空间长度也不同。
有了指针以后,不仅可以对数据本身,也可以对存储数据的变量地址进行操作;一般把指针称为指针变量,指向的对象可以是变量或者数组等;指针指向数组时,它内容存储的是数组的首地址,所以数组和指针就产生了一定的关系。那什么是数组呢?具有相同类型的若干元素按有序的形式组织起来的一种集合就叫做数组,下面会对指针、指针和数组相结合的一些用法进行分析。
1、指针
1、1 定义 int * 类型的指针变量以及修改这个指针变量,其他类型的指针变量写法也类似
int * p; //p是变量的名字,int * 表示 p 变量存放的是 int 类型变量的地址;它并不是表示定义了名叫 *p 的变量
int i = 5;
p = &i; //p 保存了 i 的地址,所以 p 指向 i
int i2 = 6;
p = &i2; //修改 p 的值不影响 i 的值
int * p2 = &i;
i = 9; //修改 i 的值不影响 p2 的值
printf("%d\n",*p2); //*p2 输出为9,*p2 等同于 i 的值,p2 等同于 i 的地址;*p2 可以理解为以 p2 的内容为地址的变量,p2 的内容就是 i 的地址
1、2 指针与指针变量是不同的概念
系统为每一个内存单元分配一个地址值,C 语言中把这个地址值称为“指针”。例如 int j = 6; int p=&j; 存放变量 j 的内存单元的编号 &j 被称为指针,它的本质是一个操作受限的非负整数。“指针变量”则是存放前述“地址值”的变量,也可以表述为,“指针变量”是存放变量所占内存空间“首地址”的变量。把 j 的指针 &j 赋给了int 型指针变量p,就是说 p 存入着 &i,因此说指针变量是存放指针的变量。
1、3 当某一类型的指针变量没有指向任何一个地址时,它的值为垃圾值
int i = 6;
int * p;
int * p2;
p = &i;
*p = p2; //这里语法编译错误,因为 *p 和 p2 是两个不同的类型,*p 表示 int 类型的内容,p2 表示 int * 类型变量的地址
*p = *p2; //错误的,因为 p2 是垃圾值,所以 *p2 也是垃圾值
p = p2; //p2 是垃圾值,p2 赋值给 p,p 也变成了垃圾值
printf("%d\n",*p); //p 的空间是属于当前程序的,因此当前程序可以读写 p 的内容,如果 p 的内容是垃圾值,那么当前程序不能读写 *p 的内容,因为 *p 所代表的内存单元的控制权限并没有分配给当前程序,所以运行到这一行会出错
1、4 在当前函数中通过获取2个某一类型(一般是基本数据类型)变量的地址并传入另外一个函数进行内容交换,可实现2个某一类型变量内容的交换
void swop(int * p1,int * p2)
{
int t = 0; //这里的 t 必须定义为 int 类型的,因为 *p1 和 *p2 都是 int 类型的
t = *p1;
*p1 = *p2;
*p2 = t;
/*
** 以下这种交换的写法是错误的,只是交换了 p1 和 p2 指向的地址
** 并没有交换 以 p1 和 p2 的内容为地址的变量,即 i1 和 i2
**
** int * t;
** t = p1;
** p1 = p2;
** p2 = t;
*/
}
int main()
{
int i1 = 2;
int i2 = 3;
swop(&i1,&i2);
printf("i1 = %d,i2 = %d\n",i1,i2); //这个时候输出时,i1 = 3,i2 = 2;真正完成了交换
return 0;
}
1、5 “*” 代表的含义
1.5.1 表示乘法
1.5.2 表示定义指针变量,举个例子
int * p1; //定义了名叫 p1 的变量,int 代表 p1 只能存放 int 变量的地址
1.5.3 表示指针运算符,该运算符放在定义好的指针变量的面前,举个例子
如果 p 是一个已经定义好的指针变量,那么 *p 代表以 p 的内容为地址的变量
1、6 假设 n >= 2 , n 级指针变量只能指向 n - 1 级指针变量的地址
int i = 3; //假设 i 的地址为 1001h
int * p = &i; //p 存放的内容是 i 的地址,假设 p 的地址为 1002h
int ** q = &p; //q 存放的内容是 p 的地址,假设 q 的地址为 1003h
int *** r = &q; //r 存放的内容是 q 的地址,假设 r 的地址为 1004h
//int *** r2 = &p; //error,只能存放二级指针变量的地址
printf("*p = %d\n", *p); //这里输出 i 的内容 3
printf("*q = %d\n", *q); //这里输出 p 存放的地址,即 i 的地址
printf("**q = %d\n", **q); //这里输出 *p,因为 *(*q) 等同于 *p
printf("*r = %d\n", *r); //这里输出 q 存放的地址
printf("**r = %d\n", **r); //这里输出 p 存放的地址,因为 *r = q,**r = *q = p
printf("***r = %d\n", ***r); //这里输出 i 的内容,即 3;***r = **q = *p = i
2 指针和数组
2、1 指针变量指向一维数组时,它存放的是一维数组的地址同时也是一维数组第一个元素的地址
int a[5] = {1,2,3,4,5};
int * p = a;
int * p2 = &a[0];
int boolean = p == p2;
printf("boolean的值为%d\n",boolean); //boolean 的值为1,证明了 a 数组的地址和 a[0] 元素的地址相等
2、2 如果 p 是一个指针变量且指向一个一维数组,可以使用它输出数组元素,且 p[i] 等价于 *(p i)
int a[5] = {1,2,3,4,5};
int * p = a;
int i = 0;
for (i = 0; i < 5;i ) {
printf("p[i]的值为%d\n",p[i]); //p[i] 等价于 a[i]
printf("*(p i)的值为%d\n",*(p i)); //因为 a 数组的地址是连续的,p i 等价于 a 的地址加上 i,即 p i 等价于 a[i] 的地址,那么 *(p i) 等价于 a[i] 的元素
}
2、3指针变量不能相加,不能相乘,也不能相除;如果两个指针变量指向的是同一块连续空间中的不同存储单元,那么这两个指针变量才可以相减
int a[5] = {1,2,3,4,5};
int * p = a;
int * p2 = &a[3];
int j = p2 - p; //正确
int j2 = p2 p; //error,不能相加
int j3 = p2 / p; //error,不能相除
int j4 = p2 * p; //error,不能相乘
2、4一个指针变量,无论它指向的地址的变量占多少个字节,该指针变量本身只占4个字节
char c = 'c';
int i = 0;
double d = 2.0;
char * p = &c;
int * p2 = &i;
double * p3 = &d;
printf("p所占的字节数为%d\n",sizeof(p)); //sizeof(p) 输出为 4
printf("p2所占的字节数为%d\n",sizeof(p2)); //sizeof(p2) 输出为 4
printf("p3所占的字节数为%d\n",sizeof(p3)); //sizeof(p3) 输出为 4
2、5静态数组长度必须事先确定,而且只能是常整数,不可以是变量
int length = 4;
int a[2]; //正确
int a2[length]; //错误的,数组长度不可以是变量
2、6 使用 malloc 函数动态构造数组
int length = 0;
int * pArr;
printf("请输入数组的长度\n");
scanf("%d",&length);
/**
** 1、使用 malloc 函数,要添加 malloc.h 头文件
** 2、malloc 函数只有一个形参而且只能是整形的,它表示请求系统为当前程序分配的字节数
** 3、malloc 函数返回的是第一个字节的地址
** 4、一共分配了 8 * length 个字节,pArr 变量占 4 * length 个字节,pArr 指向的内存也占 4 * length 个字节
** 5、pArr 本身所占的内存是静态分配的,pArr 指向的内存是动态分配的
** 6、动态构造一个一维数组,假设 length = 5,类似 int pArr[20] 数组
*/
pArr = (int *)malloc(4 * length);
int i = 0;
for(i = 0; i < length;i ) {
pArr[i] = 2 * i;
}
for(i = 0; i < length;i ) {
printf("%d\n",pArr[i]);
}
2、7 数组动态内存和静态内存的比较
2、7、1 静态数组内存是由系统自动分配,是在栈分配的,由系统自动释放;静态定义的数组,它的内存无法让程序员手动释放,在一个函数运行期间,系统为该函数中数组分配的空间会一直存在,直到该函数运行完毕时,数组的空间才会释放;数组的长度一旦定义,其长度不可以更改;
函数中定义的数组,函数运行完后数组无法再给其他函数使用。
2、7、2 动态数组可以解决静态数组的一些缺陷;动态数组内存是由程序员手动分配的,且是在堆中分配的,需要手动释放。
好了,本篇文章写到这里就结束了,由于本人技术水平有限,难免会有出错的地方,欢迎批评指正,谢谢大家的阅读。
文章来源:https://segmentfault.com/a/1190000039207472?utm_source=tuicool&utm_medium=referral
最后
特别推荐一个分享C/C 和算法的优质内容,学习交流,技术探讨,面试指导,简历修改...还有超多源码素材等学习资料,零基础的视频等着你!
还没关注的小伙伴,可以长按关注一下: