- 学习参考B站郝斌老师的视频,文章内的源码如有需要可以私信联系。
指针
- 指针就是地址,地址就是指针
- 地址是内存单元的编号(从0开始的非负整数)
- 指针变量是存放地址的变量
- 指针可以直接对主函数中的变量进行修改,但是需要把主函数变量的地址传给定义函数
- 指针只能进行减运算,不能进行加乘除运算
- 控制线:负责控制数据是调入CPU还是调出CPU
- 数据线:负责传输数据
- 地址线:负责内存条中哪个内存单元编号中的数据的读取或写入
指针概述
int *
表示指针变量的类型*p
表示以指针变量为地址的值&p
表示指针变量存放的地址
/*指针概述*/
# include <stdio.h>
int main(void)
{
int * p; //int *表示p变量存放的是int类型变量的地址,p是变量名
int i = 3;
p = i; //error,p存放的是整形地址,i是整形数据
p = 5; //error,原因同上
p = &i; //正确,&i表示去变量i的地址
return 0;
}
p = &i
变量 p 保存了变量 i 的地址,因此 p 指向 i- p 不是 i ,i 也不是 p,修改值不影响彼此的值
- 如果指针变量指向普通变量,则
*指针变量
就等同于普通变量
- 所有出现
*指针变量
和普通变量
的位置都可以互换
例:
/*指针变量&普通变量*/
# include <stdio.h>
int main(void)
{
int * p; //指针变量,int *表示的是变量p的数据类型,p是变量名
int i = 5;
p = &i; //指针变量指向普通变量
printf("变量i的值是:%d
", i);
printf("变量i存放的地址是:%d
", &i); //输出的是变量i存放的地址
printf("变量p的值是:%d
", p); //输出的是变量p的值
printf("变量p的地址是:%d
", &p); //输出变量p存放的地址
printf("*p的值是:%d
", *p); //*p可以替换成i
i = 3;
printf("变量i的值是:%d
", i);
printf("变量p的值是:%d
", p); //修改变量i的值,不影响变量p的值
*p = 3; //表示指针变量p指向的值是3
printf("*p的值是:%d
", *p);
return 0;
}
/*运行结果*/
变量i的值是:5
变量i存放的地址是:1703720
变量p的值是:1703720
变量p的地址是:1703724
*p的值是:5
变量i的值是:3
变量p的值是:1703720
*p的值是:3
Press any key to continue
- 定义指针变量 p,定义整形变量 i
- 变量 p 的地址是:1703724,变量 p 的值是变量 i 的地址,即1703720
- 变量 i 的地址是:1703720,变量 i 的值是 5
*p
表示 p 指向的地址存放的值,即1703720存放的值(i的值)5- 修改变量 i 的值,实际上 i 的存放地址没有变化,所以变量 p 也不会被影响
- 修改变量 p 的值,p 存放的是 i 的地址,所以也不会影响 i 的值
指针的重要性
- 指针可以表示一些复杂的数据结构
- 可以快速传递数据
- 使函数返回一个以上的值
return
只能返回一个值,返回一个值,就会结束函数
- 能直接访问硬件
- 可以方便的处理字符串
指针常见错误
例:
/*指针常见问题*/
# include <stdio.h>
int main(void)
{
int * p;
int i = 5;
printf("%d
", p); //变量p没有初始化,使用垃圾数据(-858993460)填充
*p = i;
printf("%d
", *p);
return 0;
}
- 变量 p 没有被初始化,会使用垃圾数据填充
- 系统只分配变量 p、i的地址空间,而
*p = i;
则是将垃圾数据对应地址空间的值进行修改,则会存在问题
例:
/*指针常见问题*/
# include <stdio.h>
int main(void)
{
int i = 5;
int * m;
int * n;
m = &i; //m的值为存放i变量的地址
*n = m; //error,变量n没有初始化,为垃圾数据,且*n(int)和m(int *)的数据类型不同
*n = *m; //error,数据类型相同,但变量n没有初始化,无法修改垃圾数据的值
m = n; //n是垃圾数据,n赋值给m,m也是垃圾数据
printf("%d
", *n);
/*
变量n的内存单元被分配给本程序,所以可以读写n内存单元中的数据
但变量n没有初始化,使用垃圾数据填充,所以本程序不能读写*n的数据
因为*n所代表的内存单元,即垃圾数据使用的内存单元,本程序没有权限读写其中的值
*/
return 0;
}
- 变量 p 没有初始化,所以系统使用垃圾数据填充
- 所以变量 p 的地址和值都是使用垃圾数据
- 因为垃圾数据的内存单元没有分配给本程序,所以本程序不能读取垃圾数据的内存单元编号和数值
- 即:没有权限读写
*p
的内容
经典指针程序
- 互换两个数字
例:指针程序,互换两个数字
/*使用指针,互换两个数*/
# include <stdio.h>
void Exchange_1(int , int); //函数声明,可以不用写形参
void Exchange_2(int *, int *);
void Exchange_3(int *, int *);
void Exchange(int * p, int * q) //正确,修改的*p,*q的值,即修改了主函数内a,b的值
{
int t; //修改*p,*q的值,则t需要定义为int类型
t = *p;
*p = *q; //*p是变量p存储地址的变量值,即主函数变量a的值
*q = t;
}
int main(void)
{
int a = 3;
int b = 5;
Exchange(&a, &b); //定义函数中是int *类型,所以要取a,b的地址
printf("a = %d, b = %d
", a, b);
return 0;
}
//不能实现互换
void Exchange_1(int a, int b) //改变的是定义函数内的a,b的值,并没有改变主函数内的值
{
int t;
t = a;
a = b;
b = t;
return;
}
//不能实现互换
void Exchange_2(int * p, int * q) //改变的是定义函数内的p,q的值,并没有改变主函数内的a,b的值
{
int * t; //互换p,q的值,则t需要定义为int *类型
t = p;
p = q;
q = t;
return;
}
/*运行结果*/
a = 5, b = 3
Press any key to continue
- 星号代表的意义
- 乘法运算
- 定义指针变量
- 取指针变量所代表的地址的值
指针和数组
- 指针和一维数组
- 数组名是指针常量,存放的是一维数组的第一个元素的地址
- p是指针变量,则p[i]等价于*(p + i)
- 数组名是指针常量,不能改变
- 指针变量指向同一块连续空间的不同存储单元,则两个指针变量可以相减
- 指针和二维数组
例:一维数组名与数组的第一个元素的关系
/*一维数组名与数组的第一个元素*/
# include <stdio.h>
int main(void)
{
int a[5];
printf("%#X
", &a[0]); //%#X表示十六进制输出
printf("%#X
", a);
return 0;
}
- 数组名对应的就是数组中第一个元素对应的地址
%d
对int类型数据输出%ld
对long int类型数据输出%f
对float类型数据输出%lf
对double类型数据输出%c
对char类型数据输出
/*运行结果*/
0X19FF1C
0X19FF1C
Press any key to continue
例:确定一个一维数组需要几个参数
/*确定一个一维数组需要几个参数*/
# include <stdio.h>
void f(int * p, int len) //主函数中的a表示的是数组中第一个元素对应的地址,所以定义变量要使用int *类型
{
int i;
for (i = 0; i < len; ++i)
printf("%d ", *(p + i)); //数组中的值存放的地址是连续的
printf("
");
}
int main(void)
{
int a[5] = {-1, -2, 0, 4, 5};
int b[6] = {1, 2, 3, 4, 5, 6};
int c[10] = {1, 2, 3, 4};
f(a, 5);
f(b, 6);
f(c, 10);
return 0;
}
- 数组名表示的是第一个元素的地址,对于int类型数据,没有特殊的值标识着数组的结束,所以需要再读取数组的长度来结束数组
- 对于char类型的数据,