前言
内存的使用和和管理
1.内存划分为一个个的内存单元,每个内存单元的大小是一个字节
bit 比特位
Byte 字节 1Byte=8bit
KB 1KB=1024Byte
MB 1MB=1024KB
GB 1GB=1024MB
内存单元的编号=地址=指针
一、指针基础
关键点:指针变量保存的是内存地址,利用解引用运算符(*)可以访问指针指向的特定内存位置中的数据。
指针变量,通常简称为指针(pointer),用来保存内存地址,即指针变量的值是内存地址。通常,一个变量包含一个数据值,如,一个整数、一个浮点数、一个字符。然而一个指针包含的则是另一个变量的内存地址,那个变量保存一个数据值。
1. 指针变量和地址
1.1取地址操作符(&)
在C语⾔中创建变量其实就是向内存申请空间,⽐如:
⽐如,上述的代码就是创建了整型变量a,内存中 申请4个字节,⽤于存放整数20,其中每个字节都 有地址,上图中4个字节的地址分别是:
1 0x000000A10D36F884
2 0x000000A10D36F885
3 0x000000A10D36F886
4 0x000000A10D36F8847
1e=1*16+14
那我们如何能得到a的地址呢? 这⾥就得学习⼀个操作符(&)-取地址操作符
int* pa=&a(pa是变量用来存放a的地址pa叫指针变量)
当“与符号”(&)放在一个变量之前时,称为地址运算符(address operator),此时它是
一个单目运算符,返回变量的地址。所以,这里&a是a的地址。
1.2 指针变量
那我们通过取地址操作符(&)拿到的地址是⼀个数值,⽐如:0x006FFD70,这个数值有时候也是需要 存储起来,⽅便后期再使⽤的,那我们把这样的地址值存放在哪⾥呢?答案是:指针变量中。
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;//取出a的地址并存储到指针变量pa中
//pa的类型是int*
return 0;
}
指针变量也是⼀种变量,这种变量就是⽤来存放地址的,存放在指针变量中的值都会理解为地址。
1.3如何拆解指针类型
我们看到pa的类型是 int* ,我们该如何理解指针的类型呢?
int a = 10;
int * pa = &a;
这⾥pa左边写的是 int* , * 是在说明pa是指针变量,⽽前⾯的 int 是在说明pa指向的是整型(int) 类型的对象。
1.4解引用操作符
我们将地址保存起来,未来是要使⽤的,那怎么使⽤呢?
在现实⽣活中,我们使⽤地址要找到⼀个房间,在房间⾥可以拿去或者存放物品。 C语⾔中其实也是⼀样的,我们只要拿到了地址(指针),就可以通过地址(指针)找到地址(指针) 指向的对象,这⾥必须学习⼀个操作符叫解引⽤操作符(*)。
int main()
{
int a = 100;
int* pa = &a;
*pa = 0;
return 0;
}
上⾯代码中第5⾏就使⽤了解引⽤操作符, *pa 的意思就是通过pa中存放的地址,找到指向的空间, *pa其实就是a变量了;所以*pa=0,这个操作符是把a改成了0.
1.5指针变量的⼤⼩
• 32位平台下地址是32个bit位,指针变量⼤⼩是4个字节
• 64位平台下地址是64个bit位,指针变量⼤⼩是8个字节 X64环境输出结果
• 注意指针变量的⼤⼩和类型是⽆关的,只要指针类型的变量,在相同的平台下,⼤⼩都是相同的。
1.6指针变量类型的意义
char *, shor*, int*, float*等的大小都是4个字节,类型并不影响指针变量的大小
a的地址是0x003FF8F4
当pa的类型是int*时*pa改变的是a的四个字节,而当pa的类型为char*时,解引用只改变了a的一个字节
结论:指针的类型决定了对指针解引用时的权限有多大(一次能操作几个字节)
const修饰指针变量
const修饰的变量不能更改
一般来讲const修饰指针变量,const可以放在*的左边或者右边,意义不同
int main()
{
int n=10;
int m=100;
int const *p =&n;
//放在*的左边,限制的是指向的内容,不能通过指针变量修改它所指向的内容
*p=20//error
//int *const p=&n;
//*p=20 ok
//放在*的右边,限制的是指针变量本身(3),可以通过指针变量修改它所指向的内容
p=&m;//如果const在*的右边则error
return 0;
}
关于p有三个相关的值
1 p,p里面存放着一个地址
2 *p,p指向那个的对象
3 &p,表示的是取指针变量p的地址
指针运算
指针的运算有三种
1指针±整数
2指针—指针
3指针的关系运算
1 指针+-整数
int main()
{
int arr[10]={1,2,3,4,5,6,7,8,9,10};
int sz=sizeof(arr)/sizeof(arr[0]);
int i=0;
int* p=arr;//数组名其实就是数组首元素的地址
for(i=0;i<sz;i++)
{
printf("%d ",*p)//printf("%d ",*(p+i));前者改变了p的地址,后者没改变p的地址
p++;
}
return0;
}
2|指针—指针|得到的是指针和指针之间元素的个数。
前提条件是两个指针指向相同的空间
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
printf("%d " ,& arr[9] - &arr[0]);
return 0;
}//屏幕打印9
//模拟strlen函数
size_t my_strlen(char* p)
{
char* end = p;
while (*end != 0)
{
end++;
}
return end - p;
}
int main()
{
char arr[10] = "abcdefghi";
size_t len = 0;
len = my_strlen(arr);
printf("%d", len);
return 0;
}屏幕打印9
指针的关系运算
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,0 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = arr;
while (p < &arr[sz])
{
printf("%d ", *p);
p++;
}
return 0;
}
野指针
int main()
{
//一个局部变量如果它不初始化的,它的值是随机的
int *p;//p是局部变量没有初始化,它的值是随机的,如果将p中存放的值当地址解引用操作符就会形成非法访问
*p=20;//p就是野指针
int arr[10]={0};
int i=0;
int*p=arr;
for(i=0;i<=10;i++);
{
*p=i;
p++
}
//越界访问i=10时
return 0;
}
如何规避野指针
不明确知道指针指向的空间时,可以给指针赋值NULL
指针的传值调用和传址调用
\\写一个函数交换两个整型变量的值
void Swap1(int x,int y)
{
int z=0;
z=x;
x=y;
y=z;
}
int main()
{
int a=0;
int b=0;
scanf("%d %d",&a,&b);
printf("交换前:a=%d b=%d\n",a,b);
Swap1(a,b);
printf("交换后:a=%d b=%d\n",a,b);
return 0;
}//结果并没有交换
可以清楚发现
a 0x012ffa04
b 0x012ff9f8
x 0x012ff920
y 0x012ff924
a和x的地址不一样,b和y的地址不一样
传值调用,其实x,y是形参,出了函数就会销毁。x,y的值不影响a,b
void Swap2(int* x, int* y)
{
int z = 0;
z = *x;
*x = *y;
*y = z;
}
int main()
{
int a = 0;
int b = 0;
scanf("%d %d", &a, &b);
printf("交换前:a= % d b = % d\n", a, b);
Swap2(&a, &b);
printf("交换后:a= % d b = % d\n", a, b);
return 0;
}传址调用