1. 内存和地址
1.1 内存
⼀楼: 101 , 102 , 103. ..⼆楼: 201 , 202 , 203. ......
有了房间号,如果你的朋友得到房间号,就可以快速的找房间,找到你。
生活中,每个房间有了房间号,就能提高效率,能快速的找到房间。
bit - 比 特位byte - 字节KBMBGBTBPB
1b yte = 8b it1 KB = 1024b yte1 MB = 1024 KB1 GB = 1024 MB1 TB = 1024 GB1 PB = 1024 TB
![](https://img-blog.csdnimg.cn/direct/bbde741f9e8d48049c407469149f0350.png)
1.2 究竟该如何理解编址
2. 指针变量和地址
2.1 取地址操作符(&)
#include <stdio.h>
int main()
{
int a = 10;
return 0;
}
0x006FFD700x006FFD710x006FFD720x006FFD73
#include <stdio.h>
int main()
{
int a = 10;
&a;//取出a的地址
printf("%p\n", &a);
return 0;
}
2.2 指针变量和解引用操作符(*)
2.2.1 指针变量
比如:
#include <stdio.h>
int main()
{
int a = 10;
int * pa = &a;//取出a的地址并存储到指针变量pa中
return 0;
}
指针变量也是⼀种变量,这种变量就是用来存放地址的,存放在指针变量中的值都会理解为地址。
2.2.2 如何拆解指针类型
int a = 10 ;int * pa = &a;
![](https://img-blog.csdnimg.cn/direct/7a5c44eccb564103b1450cec326253cb.png)
char ch = 'w' ;pc = &ch; //pc 的类型怎么写呢?
2.2.3 解引用操作符
#include <stdio.h>
int main()
{
int a = 100;
int* pa = &a;
*pa = 0;
return 0;
}
2.3 指针变量的大小
#include <stdio.h>
//指针变量的⼤⼩取决于地址的⼤⼩
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{
printf("%zd\n", sizeof(char *));
printf("%zd\n", sizeof(short *));
printf("%zd\n", sizeof(int *));
printf("%zd\n", sizeof(double *));
return 0;
}
3. 指针变量类型的意义
3.1 指针的解引用
![](https://img-blog.csdnimg.cn/direct/7c87ac6ba962485fbae0aee287f849b9.png)
调试我们可以看到,代码1会将n的4个字节全部改为0,但是代码2只是将n的第⼀个字节改为0。
结论:
指针类型决定了指针进行解引用操作的时候访问多大的空间,int*的指针解引用访问4个字节,char*的解引用访问1个字节
3.2 指针+-整数
#include<stdio.h>
int main() {
int a = 0;
int* pa = &a;
char* pc = &a;
printf("pa = %p\n", pa);
printf("pa+1 = %p\n", pa+1);
printf("pc = %p\n", pc);
printf("pc+1 = %p\n", pc + 1);
return 0;
}
![](https://img-blog.csdnimg.cn/direct/3015f6dd8c004bf68d267cce5a125f3c.png)
![](https://img-blog.csdnimg.cn/direct/056a699fdbaf4c5d9484a50e6228225f.png)
3.3 void* 指针
int main()
{
int a = 10;
int* pa = &a;
char* pc = &a;
return 0;
}
![](https://img-blog.csdnimg.cn/direct/58440dac54c54fa9942352f28c35b600.png)
使用void*类型的指针接收地址:
#include <stdio.h>
int main()
{
int a = 10;
void* pa = &a;
void* pc = &a;
*pa = 10;
*pc = 0;
return 0;
}
![](https://img-blog.csdnimg.cn/direct/47695aa5e6fe47e0a7c1bb03f5769476.png)
4. const 修饰指针
4.1 const修饰变量
#include <stdio.h>
int main()
{
int m = 0;
m = 20;//m是可以修改的
const int n = 0;
n = 20;//n是不能被修改的
//const修饰了n之后,n不能被修改了,但n还是变量,n是常变量,具有常量的变量
return 0;
}
接下来证明由const修饰的n依旧是常量
int main() {
const int n = 10;
int arr[n] = { 0 };
return 0;
}
但在c++中conts修饰n就是常量,语法不同罢了。
int main() {
const int n = 10;
printf("n=%d\n", n);
int* p = &n;
*p = 20;
printf("n=%d\n", n);
return 0;
}
输出结果:
4.2 const修饰指针变量
1.p是指针变量,存放了其他变量的地址。
2.p是指针变量,*p是指针指向的对象n。
3.p是指针变量,p也有自己的地址&p。
1、int * p; // 没有 const 修饰2、int const * p; //const 放在 * 的左边做修饰3、int * const p; //const 放在 * 的右边做修饰
我们看下面代码,来分析具体分析⼀下:
void test1()
{
int n = 10;
int m = 20;
int *p = &n;
*p = 20;//ok?
p = &m; //ok?
}
void test2()
{
int n = 10;
int m = 20;
const int* p = &n;
*p = 20;//ok?
p = &m; //ok?
}
void test3()
{
int n = 10;
int m = 20;
int * const p = &n;
*p = 20; //ok?
p = &m; //ok?
}
void test4()
{
int n = 10;
int m = 20;
int const * const p = &n;
*p = 20; //ok?
p = &m; //ok?
}
int a = 0;int *p = &a;const int *p = &a;//限制了*p,*p = 10;err,p = &b;okint * const p = &a;//与上述正好相反
• const如果放在*的左边,修饰的是指针指向的内容,保证指针指向的内容不能通过指针来改变。 但是指针变量本⾝的内容可变。• const如果放在*的右边,修饰的是指针变量本⾝,保证了指针变量的内容不能修改,但是指针指向的内容,可以通过指针改变。
5. 指针运算
5.1 指针+- 整数
int arr[ 10 ] = { 1 , 2 , 3 , 4 , 5 , 6 , 7 , 8 , 9 , 10 };
![](https://img-blog.csdnimg.cn/direct/801ee644afea4d3281919438e7b613f9.png)
#include<stdio.h>
int main() {
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = &arr[0];
for (int i = 0; i < sz; i++)
{
printf("%d ", *(p+i));
}
return 0;
}
int main()
{
int arr[10] = { 0 };
printf("%d\n",arr[9]-arr[0]);//9
printf("%d\n",arr[0]-arr[0]);//-9
return 0;
}
![](https://img-blog.csdnimg.cn/direct/44646c455cfb48369b2f910aceaf5a09.png)
#include<stdio.h>
int my_strlen(char* p)
{
int count = 0;
while (*p != '\0')
{
count++;
p++;
}
return count;
}
int main() {
printf("%d\n", my_strlen("abc"));
return 0;
}
库函数strlen的引用需要头文件string.h
字符串传给my_strlen的是首元素的地址,将传过去的地址用另外一个指针(地址)*p保存,记作新指针(地址)*p,经过while循环后新指针-原指针=字符串个数
5.3 指针的关系运算
用指针的关系运算实现数组中各元素的打印
#include<stdio.h>
int main() {
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int sz = sizeof(arr) / sizeof(arr[0]);
int* p = &arr[0];
//指针大小的比较,p为指针,arr为数组首元素指针,arr+sz是指针与整数的运算
while (p < arr + sz) {
printf("%d", *p);
p++;
}
return 0;
}
指针大小的比较,p为指针,arr为数组首元素指针,arr+sz是指针与整数的运算
打印范围为arr[0]~arr[0]+sz
6. 野指针
6.1 野指针成因
1. 指针未初始化
#include <stdio.h>
int main()
{
int *p;//局部变量指针未初始化,默认为随机值
*p = 20;
return 0;
}
2. 指针越界访问
当指针指向的范围超出数组arr的范围时,p就是野指针
#include <stdio.h>
int main()
{
int arr[10] = {0};
int *p = &arr[0];
int i = 0;
for(i=0; i<=11; i++)
{
//当指针指向的范围超出数组arr的范围时,p就是野指针
*(p++) = i;
}
return 0;
}
3. 指针指向的空间释放
原本不为野指针,后来使用之后存储的空间释放了,原空间就变成了野指针
当n出test函数后便被销毁了,p便成为了野指针
#include <stdio.h>
int* test()
{
int n = 100;
return &n;
}
int main()
{
int*p = test();
printf("%d\n", *p);
return 0;
}
6.2 如何规避野指针
6.2.1 指针初始化
# if def __cplusplus# define NULL 0# else# define NULL ((void *)0)# end if
#include<stdio.h>
int main() {
int a = 10;
int* pa = &a;
*pa = 20;
printf("%d\n", *pa);
int* p = NULL;
*p = 20;//err
return 0;
}
使用指针时一定要进行初始化
#include <stdio.h>
int main()
{
int num = 10;
int*p1 = #
int*p2 = NULL;
return 0;
}
使用解引用操作时进行判断是否为空指针
int* p = &a;
if(p != NULL){
*p = 20;
}
6.2.2 小心指针越界
6.2.3 指针变量不再使用时,及时置NULL,指针使用之前检查有效性
int main()
{
int arr[10] = {1,2,3,4,5,67,7,8,9,10};
int *p = &arr[0];
for(i=0; i<10; i++)
{
*(p++) = i;
}
//此时p已经越界了,可以把p置为NULL
p = NULL;
//下次使⽤的时候,判断p不为NULL的时候再使⽤
//...
p = &arr[0];//重新让p获得地址
if(p != NULL) //判断
{
//...
}
return 0;
}
6.2.4 避免返回局部变量的地址
7. assert 断言
assert(p != NULL );
#define NDEBUG
#include <assert.h>
8. 指针的使用和传址调用
8.1 strlen的模拟实现
库函数strlen的功能是求字符串长度,统计的是字符串中 \0 之前的字符的个数。
size_t strlen ( const char * str );
size_t是无符号整形,相当于unsigned int
参考代码如下:
#include<stdio.h>
#include<assert.h>
size_t my_strlen(const char* c) {
size_t count = 0;
assert(*c != NULL);
while (*c != '\0') {
count++;
c++;
}
return count;
}
int main() {
char arr[] = "abcdef";
size_t len = my_strlen(arr);
printf("%zd\n", len);
return 0;
}
1,加入assert是用来阻碍传入my_strlen的是空指针
2,对传入的指针进行const限制是防止my_strlen内部对传入指针的改变
3,size_t是用来防止返回类型是负数。
8.2 传值调用和传址调用
#include <stdio.h>
void Swap1(int x, int y)
{
int tmp = x;
x = y;
y = tmp;
}
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;
}
![](https://img-blog.csdnimg.cn/direct/5591a6b5774a41a59e42f388b7234d4a.png)
#include<stdio.h>
void Swap(int* a, int* b) {
int tmp = 0;
tmp = *a;
*a = *b;
*b = tmp;
}
int main() {
int a = 10;
int b = 20;
printf("交换前:a=%d b=%d\n", a, b);
Swap(&a, &b);
printf("交换后:a=%d b=%d\n", a, b);
return 0;
}
![](https://img-blog.csdnimg.cn/direct/b764a12d0e42493f9f8ea943432e2c9f.png)