1.0 指针的定义
指针是内存中一个最小单元的编号(内存单元的编号称之为地址【地址就是指针指针就是地址】)指针通常是用来存放内存地址的一个变量。本质上指针就是地址:口语上说的指针起始是指针变量,指针变量就是一个变量,是一个用于存放地址的变量,指针指向的就是地址,通过这个地址可以找到对应的内存单元。
指针变量创建:
#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include <string.h>
#include "add.h"
#include <stdint.h>
/*
****************************
* DEF : 指针
* 参数 : 无参数
* 返回值 : 无返回值
* 时间 : 2024/7/14
* 作者 : _沧浪之水_
****************************
*/
int main()
{
// a 是一个整型变量,占用4个字节内存空间
int a = 10;
// pa 是一个指针变量,指针变量是用来存放地址的
int* pa = &a;
return 0;
}
总的来说,指针变量是用于存放地址的变量,(存放在指针中的值被当做是地址处理),在32位的平台下一个指针变量的大小是4个字节,在64位地址的机器上指针变量的大小是8个字节。
#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include <string.h>
#include "add.h"
#include <stdint.h>
/*
****************************
* DEF : 指针
* 参数 : 无参数
* 返回值 : 无返回值
* 时间 : 2024/7/14
* 作者 : _沧浪之水_
****************************
*/
int main()
{
char *pc = NULL;
short *ps = NULL;
int *pi = NULL;
double *pd = NULL;
printf("%zu\n", sizeof(pc));
printf("%zu\n", sizeof(ps));
printf("%zu\n", sizeof(pi));
printf("%zu\n", sizeof(pd));
return 0;
}
2.0 指针与指针类型
指针类型的含义
1. 指针的类型决定的指针被解引用的时候被访问几个字节【所谓的解引用是官方的叫法,实际上就去获取地址当中的值,或者说是将地址当中的值取出来】,例如:int * 的指针,解引用访问4个字节,char * 的指针解引用是访问一个字节,推广到其他的参数时也是一样的。
#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include <string.h>
#include "add.h"
#include <stdint.h>
/*
****************************
* DEF : 指针
* 参数 : 无参数
* 返回值 : 无返回值
* 时间 : 2024/7/14
* 作者 : _沧浪之水_
****************************
*/
int main()
{
int a = 0x11223344;
int* pa = &a;
*pa = 0;
char* pc = (char*)&a;
*pc = 0;
return 0;
}
2. 指针的步长:理解指针步长的意义
#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include <string.h>
#include "add.h"
#include <stdint.h>
/*
****************************
* DEF : 指针
* 参数 : 无参数
* 返回值 : 无返回值
* 时间 : 2024/7/14
* 作者 : _沧浪之水_
****************************
*/
int main()
{
int a = 0x11223344;
int* pa = &a;
char* pc = (char *) & a;
printf("pa = %p\n", pa);
printf("pa = %p\n", pa + 1);
printf("pa = %p\n", pc);
printf("pa = %p\n", pc + 1);
return 0;
}
指针类型决定了访问内存一次可以访问几个字节,如果是char * 的指针类型,那么访问内存只能一个字节一个字节的进行访问,如果是int * 类型的指针类型,那么访问内存就是以一次4个字节进行访问, int * pc如果是pc + 1 的话那么就是跳过前面的4个字节。
注:不同类型指针占用类型大小相同的指针是不能混用的,因为两者存储在内存中的空间是不同的,int 类型存储的是int类型,float类型存储的是float类型。
#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include <string.h>
#include "add.h"
#include <stdint.h>
/*
****************************
* DEF : 指针
* 参数 : 无参数
* 返回值 : 无返回值
* 时间 : 2024/7/14
* 作者 : _沧浪之水_
****************************
*/
int main()
{
int a = 0;
int* pi = &a; // pi 解引用访问4个字节,pi + 1 也是跳过4个字节
float* pf =(float *)& a;// pf 解引用访问4个字节,pf + 1 也是跳过4个字节
return 0;
}
3.0 野指针,指针,与指针运算
野指针的概念:指的是指针指向的位置是不可知的
int main
{
// p没有初始化,就意味着没有明确的指向
// 一个局部变量不初始化,放的是随机值
int *p;
// 这样做的方式就是非法的访问内存,这个时候p就是野指针
*p = 10;
}
#define _CRT_SECURE_NO_WARNINGS
#include "stdio.h"
#include "stdlib.h"
#include "math.h"
#include <string.h>
#include "add.h"
#include <stdint.h>
/*
****************************
* DEF : 指针
* 参数 : 无参数
* 返回值 : 无返回值
* 时间 : 2024/7/14
* 作者 : _沧浪之水_
****************************
*/
int main()
{
int arr[10] = { 0 };
// 数组名表示数组首元素地址arr[0]
int* p = arr;
// 指针的越界访问也会造成野指针
for (int i = 0; i <= 10; i++)
{
*p = i;
p++;
}
return 0;
}
野指针:涉及到局部变量和全局变量知识
函数执行进入主函数:main
这个时候test() 函数被调用
通过return 返回a的地址地址返回后int a 由于数局部变量出函数之后被销毁
这个时候 int *p 通过地址可以找到a这个空间
但是这个时候a空间已经被销毁,p这个时候无法访问和使用这个空间,这个时候这个p属于是野指针。
int * test()
{
int a = 10;
return &a;
}
int main()
{
int *p = test();
return 0;
}
4.0 如何避免野指针
NULL 的值相当于是0 ,0 空间是没有办法被指针访问的,避免野指针的方式可以在初始化不知道赋值为什么的时候赋值为一个NULL也就是空指针,同时在使用之前先判断指针是否为空,如果指针为空就不使用,如果指针不为空的时候就对指针变量进行相应的操作,具体的示例如下所示。
避免野指针:
1: 指针初始化,
2:小心数组下标越界,
3:指针指向空间释放及时设置为NULL,
4:避免返回局部变量的地址,
5:使用指针之前先检查指针的有效性。
int * p = NULL;
int mian(void)
{
if(p != NULL)
{
*p3 = 100;
}
return 0;
}
指针加减整数运算
#define N_VALUE 5
float values[N_VALUES];
float *vp
for(vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0;
}
int main(void)
{
int arr[10] = { 0 };
int i = 0;
int sz = sizeof(arr)/sizeof(arr[0]);
for(i = 0; i < sz; i++)
{
// 把数组里面的值全部赋值为 1
arr[i] = 1;
}
// int 类型的指针变量p,数组名等于数组首元素的地址
int *p = arr;
for(i = 0; i < sz; i++)
{
// *P 解引用:也就是取值的意思
*p = 1;
p++;
}
// 使用指针步长的方式访问数组,进行数组操作
for(i = 0; i < sz; i++)
{
*(p + 1) = 1;
}
return 0;
}
5.0 指针加减指针
int main(void)
{
// 指针减去指针得到的绝对值得到的是指针之间元素的个数
// 不是所有的指针都能相减,指向同一块空间的指针才能相减
int arr[10] = { 0 };
printf("%d\n",&arr[9] - &arr[0]); // 9
}
字符串长度:使用指针减去指针的方式
int my_strlen(char *str)
{*
// str 刚开始起始地址是a
int count = 0;
while(*str != '\0')
{
count++;
str++;
}
return count;
}
// 指针 - 指针
int my_strlen(char *str)
{
char *start = str;
while(*str != '\0')
{
str++;
}
return (str - start);
}
int main(void)
{
int len = my_strlen("abcdef");
printf("%d\n",len);
}
指针的关系运算
#define N_VALUES 5
float values[N_VALUES]
float *vp;
for(vp = &values[N_VALUES]; vp > &values[0];)
{
*--p = 0;
}
C 语言标准规定指向指针的元素与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。
指针遍历访问数组
数组的定义:数组是一组相同元素的集合,指针变量是一个变量,存放的是地址,数组的数组名是首元素的地址 。
int main(void)
{
int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
int *p = arr;
int len = sizeof(arr)/sizeof(arr[0]);
int i = 0
for(i = 0; i < len; i++)
{
printf("%d ",*(p + i));
}
for(i = 0; i < len; i++)
{
// 打印输出的地址结果是相同的
printf("%p-----------------%p\n",&arr[i],p + i);
}
return 0;
}
6.0 二级指针
引入一级指针的概念:
int a = 10 ; a 是一个int 类型的变量初始值为10
int * pa = &a; pa是一个指针,pa是一个指向int 类型的指针变量,
&a 把 变量a在内存中的地址赋值给指针变量pa ,pa可以通过这个地址找到a并获取a在变量当中的值并修改同时打印输出。
int main()
{
int a = 10;
// pa 是一个一级指针变量
int* pa = &a;
*pa = 20;
printf("%d ", a);
return 0;
}
二级指针变量:程序输出的值是 30 注意理解:int **ppa 中
int main()
{
int a = 10;
// pa 是一个一级指针变量
int* pa = &a;
int** ppa = &pa;
**ppa = 30;
printf("%d ", a);
return 0;
}
二级指针存放的是一级指针变量的地址
int main() { int a = 10; // pa 是一个一级指针变量 int* pa = &a; int** ppa = &pa; **ppa = 30; printf("%d ", a); return 0; }
7.0 指针数组
【指针数组:存放指针的数组就是指针数组】
int main()
{
int a = 10;
int b = 20;
int c = 30;
int arr[10];
int* pa = &a;
int* pb = &b;
int* pc = &c;
// parr 是存放指针的数组,称之为指针数组
int* parr[10] = { &a, &b, &c };
int i = 0;
int len = sizeof(parr) / sizeof(parr[0]);
for (i = 0; i < len; i++)
{
printf("%d ", *(parr[i]));
}
return 0;
}
【补充:二维数组创建与遍历】
/*
****************************
* DEF : 二级指针
* 参数 : 无参数
* 返回值 : 无返回值
* 时间 : 2024/7/15
* 作者 : _沧浪之水_
****************************
*/
int main()
{
int arr[3][4] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
int i = 0;
int j = 0;
for (i = 0; i < 3; i++)
{
for (j = 0; j < 4; j++)
{
printf("%d ", arr[i][j]);
}
printf("\n");
}
return 0;
}
指针模拟二维数组
int main()
{
int arrOne [4] = { 1, 2, 3, 4 };
int arrTwo [4] = { 2, 3, 4, 5 };
int arrThree[4] = { 6, 7, 8, 9 };
int* parr[3] = { arrOne, arrTwo, arrThree };
int i = 0;
for (i = 0; i < 3; i++)
{
int j = 0;
for (j = 0; j < 4; j++)
{
printf("%d ", parr[i][j]);
}
printf("\n");
}
return 0;
}
......