C语言基础—指针变量与变量指针
引入
内存地址
- 字节:字节是内存的容量单位,英文名Byte,1 Byte = 8bits
- 地址:系统为了便于区分每一个字节而对它们逐一进行的编号(编号是唯一),称为内存地 址,简称地址。int a = 5;
基地址(首地址)
- 单字节数据:对于单字节数据而言,其地址就是其字节编号。举例:char a = 'A'
- 多字节数据:对于多字节数据而言,其地址是所有字节中编号最小的那个 ,称为基地址(首地 址)
取地址
- 每个变量都是一块内存,都可以通过取址符 & 获取其地址。
- 例如
int a = 100;
printf("整型变量a的地址是:%p\n",&a); // 0x7ffe382a8114 64位系统 是12位16进制
char c = 'x';
printf("字符变量c的地址是:%p\n",&c); // 0x7ffdf5482af3
- 虽然不同的变量的尺寸是不同的,但是它们的地址的尺寸是一致的。
- 不同的地址虽然形式上看起来是一样的,但由于它们代表的内存尺寸和类型都不同,因此它 们在逻辑上时严格区分的。
变量指针与指针变量
指针的概念
内存单元与地址机制
-
内存单元划分
-
系统将内存划分为连续的基本存储单元,每个单元的容量为1字节(8bits)
-
每个内存单元拥有唯一编号,称为内存地址(十六进制表示:如:0x7ffdf5482af3)
-
-
变量存储特性
-
变量根据数据类型占据不同数量的内存单元:
-
char
类型占1字节(1个单元) -
int
类型占4字节(4个单元) -
double
类型占8字节(8个单元)
-
-
变量的基地址(首地址)是其首个单元内存单元的地址(首地址一般是这一组编号中最小的那个)
-
变量指针与指针变量
对比维度 | 变量指针 | 指针变量 |
---|---|---|
本质 | 内存地址(首地址) 变量指针,其实就是变量的首地址 | 存储地址的普通法变量 |
操作符 | &(取地址符) | *(声明符,解引用符) |
代码示例 | &a(获取变量a的地址) | int* p = &a(int *p = &a) |
核心特性 | 不可修改(地址由系统分配) | 可修改指向(p = &b) |
指向:指针变量中存放谁的地址,就说明该指针变量指向了谁
指针的尺寸
注意: char *p = &a 中 char修饰的是a的类型,*p
永远都只能存放地址值
指针的本质
-
变量指针:数据的“门牌号”(&a)
-
指针变量:存储门牌号的"笔记本”(int *p)
-
指向操作:通过门牌号访问数据(*p)
内存数据的存取方式
在C语言中对内存数据(变量、数组元素等)的存取有两种方式:
直接存取
- 通过基本类型(整型、浮点型、字符型)的变量访问这个变量代表空间的数据
- 通过数组元素的引用,访问这个引用代表的内存空间的数据
间接存取
- 通过指针变量,间接的访问内存中的数据
- *:读作声明符或者解引用符,如果*前面有数据类型,读作(声明)指针:如果*前没有数据类型读作解引用
int *p; //声明符
printf("%d",*p); //解引用
指针变量的定义
语法:
数据类型* 变量列表;
注意:指针变量的值只能是8/12位的十六进制整数。
注意:
①虽然定义指针变量*a
,是在变量名前加上*
,但是实际变量名依然为a
②使用指针变量间接访问内存数据时,指针变量必须要明确的指向。
③如果想要借助指针变量间接访问指针变量保存的内存地址上的数据,可以使用指针变量前加*
来间接返回访问。指针变量前加*
,如果不带数据类型,就称之为对指针变量解引用。
④指针变量只能指向同类型的变量,借助指针变量访问内存,一次访问的内存大小是取决于指针变量的类型。
⑤指针变量在定义时可以初始化:这一点和普通变量是一样的。
int a = 5;
int *p = &a;//定义指针变景的同时初始化
printf("%d\n",*p);
int b;
int* p1 = &b; //指针初始化的时候不需要关注指向的变量空间中是否有值
printf("%d\n",*p1);
指针变量的使用
使用
- 指针变量的赋值
//方式1
int a,*p
p=&a; //先定义,后赋值
//1方式2
inta1,*p1,*q1=&a1; //定义并初始化
p1=q1 //其实就是变景的赋值,指针变景q1的值城值给了指针变量p1,此时q1和p1同时指向a1
- 操作指针变量的值
int a,*p,*q &a;
p=q; //将指针变量q的值赋值给指针变量p此时p和g都指向了变量a
printf("%p",p); //方问的是指针变量p的值(也就是变量a的地址
- 操作指针变量指向的值
inta=6,*q=&a,b=25;//一定要注意,指计变量q的变量名就是q
*q=10; //访问q指向的变量a的空间,其实就是间接的给a赋值(a=10)
printf("%d,%d\n",*q,a); //10 10
q=&b; //一个指针变量只能同一时刻指向一个变量,但是一个变量可以同时被多个指针变显指向
两个指针运算符的使用
-
&
:取地址运算符。&a是变量a的地址,这个是变量指针。 -
*
:指针运算符、解引用符,这个是指针变量
直接与间接交换数据的值
指针变量做参数函数
指针变量做函数参数往往传递的是变量的地址(基地址),借助与指针变量间接访问是可以修改实参变量数据的。
#include <stdio.h>
/**
* 定义一个函数,实现两个数的交换
*/
void swap(int *p_a, int *p_b)
{
int *p_t;
// 以下写法,只会改变指针指向,不会改变指向对象的值
p_t = p_a;
p_a = p_b;
p_b = p_t;
printf("交换后:%d,%d\n",*p_a,*p_b);
}
int main(int argc,char *argv[])
{
int a = 3, b = 5;
swap(&a,&b);
printf("a=%d,b=%d\n",a,b);
return 0;
}
指针变量指向数组元素
数组元素的指针
-
数组的指针就是数组中的第一个元素的地址,也就是数组的首地址
-
数组元素的指针是指数组的首地址。因此,同样可以用指针变量来指向数组或数组元素。
-
在C语言中,由于数组名代表数组的首地址,因此,数组名实际上也是指针。我们访问数组名就是访问数组首地址。
注意:虽然我们定义了一个指针变量接收了数组地址,但不能理解为指针变量指向了数组,而应该理解为指向了数组的元素(默认为第1个元素)。
指针的运算
指针运算:前提是指针变量必须要指向数组的某个元素。(指针运算只能发生在同一数组内)
① 如果指针变量 p 已指向数组中的一个元素,则 p+1 指向同一数组中的 下一个元素, p-1 指向同一数组中的上一个元素。即 p+1 或 p-1 也表示地址。但要注意的是,虽然指针变量 p 中存放的 是地址,但p+1 并不表示该地址加 1 ,而表示在原地址的基础上加了该数据类型所占的字节数 d (d = sizeof( 数据类型 ) ) 。② 如果 p 原来指向 a[0] ,执行 ++p 后 p 的值改变了,在 p 的原值基础上加 d ,这样 p 就指向数组的下一个元素 a[1] 。 d 是数组元素占的字节数。③ 如果 p 的初值为& a[0] 则 p+i 和 a+i 就是数组元素 a[i] 的地址,或者说,它们指向a数组的第 i个元素 。④ *(p+i) 或 *(a+i) 是 p+i 或 a+i 所指向的数组元素,即 a[i] 。⑤ 如果指针变量 p1 和 p2 都指向同一数组,如执行 p2-p1 ,结果是两个地址之差除以数组元素的 长度d 。
通过指针引用数组元素
引用数组元素,可以用:
①下标法:如arr[i]形式
②指针法:如*(arr+i)
或者*(p+i)
。其中arr是数组名,p是指向数组元素的指针变量,其初始值:p=arr;
#include <stdio.h>
void main()
{
int arr[10];
int *p, i;
// 给数组元素赋值
for(i = 0; i < 10; i++)
scanf("%d",&arr[i]);
// 遍历数组元素
for(p = arr; p < (arr + 10); p++)
printf("%-4d%",*p);
printf("\n");
}