文章目录
嘻嘻,家人们,今天咱们剖析一下指针,好啦,废话不多讲,开干!
1:内存与地址
1.1内存
在讲解内存与地址之前,我们先来看生活中的一个案例.
如果把上面的例子对照到计算机中,又会是怎样呢?
1:bit --- 比特位
2:byte --- 字节 1byte = 8bit
3:KB 1KB = 1024byte
4:MB 1MB = 1024KB
5:GB 1GB = 1024MB
6:TB 1TB = 1024GB
7:PB 1PB = 1024TB
PS:一个比特位可以存储一个2进制的1或者0
1.2:如何理解编址
2:指针变量与地址
理解了内存与地址的关系后,再回到C语言,在C语言中创建变量其实就是向内存申请空间,我们看下面这段代码.
#include <stdio.h>
int main()
{
int value = 15;
return 0;
}
0x0113FE28
0x0113FE29
0x0113FE2A
0x0113FE2B
#include <stdio.h>
int main()
{
int value = 15;
printf("%p\n", &value);
return 0;
}
2.1:指针变量与解引用操作符
2.1.1:指针变量
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a = 20;
int* pa = &a;
return 0;
}
2.1.2:如何拆解指针类型
int a = 20;
int* pa = &a;
2.1.3:解引用操作符
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a = 100;
int* pa = &a;
*pa = 25;
printf("%d\n", a);
return 0;
}
2.2:指针变量的大小
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
printf("%d\n", sizeof(int*));
printf("%d\n", sizeof(char*));
printf("%d\n", sizeof(short*));
printf("%d\n", sizeof(double*));
return 0;
}
- 32位平台下地址是32个bit位,指针变量大小是4个字节
- 64位平台下地址是64个bit位,指针变量大小是8个字节
- 注意指针变量的大小和类型是无关的,只要是指针类型的变量,在相同的平台下,大小都是一样的.
3:指针变量类型的意义
代码1
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a = 10;
int* pa = &a;
*pa = 20;
return 0;
}
解引用修改前
解引用修改后
代码2
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a = 0x11223344;
char* pa = &a;
*pa = 20;
return 0;
}
解引用修改前
解引用修改后
1:char * 类型的指针解引用访问1个字节.
2:short * 类型的指针解引用访问2个字节.
3:int * 类型的指针解引用访问4个字节.
4:double*类型的指针解引用访问8个字节.
4:const修饰指针
4.1:const修饰变量
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int a = 20;
a = 30;
const int b = 25;
//b不可被修改
b = 0;
return 0;
}
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
const int b = 25;
int* pb = &b;
printf("b = %d\n", b);
*pb = 30;
printf("b = %d\n", b);
return 0;
}
4.2:const修饰指针变量
博主将通过以下几段代码来讲解const修饰指针变量,首先来看代码1.
代码1
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void test1()
{
int n = 10;
int m = 20;
int* p = &n;
*p = 20;
p = &m;
}
int main()
{
test1();
return 0;
}
代码2(const放在*左边)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void test2()
{
int n = 10;
int m = 20;
const int* p = &n;
*p = 20;
p = &m;
}
int main()
{
test2();
return 0;
}
代码3(const放在*右边)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void test3()
{
int n = 10;
int m = 20;
int* const p = &n;
*p = 20;
p = &m;
}
int main()
{
test3();
return 0;
}
代码4(const放在*左右两边)
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
void test4()
{
int n = 10;
int m = 20;
const int* const p = &n;
*p = 20;
p = &m;
}
int main()
{
test4();
return 0;
}
通过对比上面四段代码,我们可以得出以下结论
- const如果放在 * 的左边,修饰的是指针变量所指向的内容,此时无法通过指针改变指针所指向的内容,但是指针本身的内容可以改变.
- const如果放在 * 的右边,修饰的是指针变量本身,此时无法改变指针本身的内容,但是指针所指向的内容可以通过指针对其进行改变.
- const如果既在 * 的左边,又在 * 的右边,此时既修饰指针变量本身,又修饰指针变量所指向的内容,因此,既不能够通过指针改变指针变量所指向的内容,又不能够改变指针本身的内容.
5:指针运算
5.1:指针加减整数
代码1
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int n = 10;
char* p1 = (char *) & n;
int* p2 = &n;
printf("&n = %p\n", &n);
printf("\n");
printf("p1 = %p\n", p1);
printf("p1 + 1 = %p\n", p1 + 1);
printf("\n");
printf("p2 = %p\n", p2);
printf("p2 + 1 = %p\n", p2 + 1);
return 0;
}
代码2
#define _CRT_SECURE_NO_WARNINGS
#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;
for (int i = 0; i < sz; i++)
{
printf("%d ", *(p + i));
}
return 0;
}
5.2:指针-指针
代码1
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[10] = { 0 };
printf("%d\n", &arr[9] - &arr[0]);
printf("%d\n", &arr[0] - &arr[9]);
return 0;
}
字符串的结束标志是\0,而strlen函数是用于统计\0之前的字符个数.
代码2
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int mystrlen(char * str)
{
char* begin = str;
while (*str != '\0')
{
str++;
}
return str - begin;
}
int main()
{
char arr[100] = "0";
scanf("%s", arr);
int result = mystrlen(arr);
printf("%d\n", result);
return 0;
}
5.3:指针的关系运算
#define _CRT_SECURE_NO_WARNINGS
#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;
//指针的大小比较
while (p < arr + sz)
{
printf("%d ", *p);
p++;
}
return 0;
}
6:野指针
6.1:野指针成因
6.1.1:指针未初始化
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
//局部变量指针未初始化,默认为随机值
int* p;
*p = 20;
return 0;
}
6.1.2:指针越界访问
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
for (int i = 0; i < 11; i++)
{
*p = i;
p++;
}
return 0;
}
6.1.3:指针指向的空间释放
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int * test()
{
int n = 25;
return &n;
}
int main()
{
int* p = test();
printf("%d\n", *p);
return 0;
}
6.2:如何规避野指针
学习了野指针的成因以后,那么该如何去避免野指针呢?有以下几种方式。
6.2.1:指针初始化
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int n = 25;
int* p1 = &n;
int* p2 = NULL;
return 0;
}
6.2.2:小心指针越界
一个程序向内存申请了哪些空间,通过指针也只能访问所对应的空间,不能够超出访问,超出了就是越界访问.
6.2.3:指针变量不再使用时,及时置NULL,指针使用之前检查有效性
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
int* p = arr;
for (int i = 0; i < 10; i++)
{
//虽然后置++的优先级高于解引用操作符,但是由于是先使用再++,因此一开始对p解引用得到是数值为1的元素.
*p++ = i;
}
p = NULL;
return 0;
}
6.2.4:避免返回局部空间的地址
野指针成因的第三个例子.
7:二级指针
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int n = 25;
int a = 20;
int* p = &n;
int** pp = &p;
printf("&n = %p\n", &n);
printf("*pp = %p\n", *pp);
//等价于 p = &a;
*pp = &a;
//等价于 a = 30
**pp = 30;
printf("&a = %p\n", &a);
printf("*pp = %p\n", *pp);
printf("%d\n", **pp);
printf("a = %d\n", a);
return 0;
}
8:指针数组
8.1:语法
int * 数组名[元素个数]
Eg:int * arr[5]
8.2:指针数组模拟实现二维数组
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
int main()
{
int arr1[5] = {1,2,3,4,5};
int arr2[5] = {6,7,8,9,10};
int arr3[5] = {11,12,13,14,15};
int* parr[3] = { arr1,arr2,arr3 };
int Sz = sizeof(parr) / sizeof(parr[0]);
int sz = sizeof(arr1) / sizeof(arr1[0]);
for (int i = 0; i < Sz; i++)
{
for (int j = 0; j < sz; j++)
{
printf("%2d ", parr[i][j]);
//printf("%2d ", (*(arr + i))[j]);
}
printf("\n");
}
return 0;
}
9:assert断言
讲完了指针相关的基础知识后,接下来博主将为大家介绍assert断言
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <assert.h>
int main()
{
int* p = NULL;
assert(p != NULL);
return 0;
}
assert()的使用对程序员是非常友好的,使用assert有以下几个好处:
- 能自动标识文件和出问题的行号.
- 无需更改代码就能开启或关闭assert()的机制。
#define _CRT_SECURE_NO_WARNINGS
#define NDEBUG
#include <assert.h>
int main()
{
int* p = NULL;
assert(p != NULL);
return 0;
}
好啦,家人们,关于指针初阶这块的相关细节知识,博主就讲到这里了,如果uu们觉得博主讲的不错的话,请动动你们滴滴给博主点个赞,你们滴鼓励将成为博主源源不断滴动力!