6.指针emm

指针是什么?

指针是内存中一个最小单元的编号,也就是地址
内存单元编号 == 地址 == 指针
口语中说的指针指的是指针变量

11101111
10010000
10010001

内存

谈到指针就必须谈内存
内存被划分为一个个小的内存单元
一个基本的内存单元的大小是1个字节
给每一个内存单元进行编号,编号又被称为地址
通过内存编号快速定位到某一个内存单元

利用F10调试并查看a在内存中的地址:
按下F10->调试->窗口->内存->内存1
在地址中输入&a即可查看a的地址

一个小格子就是一个内存单元,1byte->1字节
给内存单元进行编号,把内存单元的编号–>地址
地址也叫指针,把地址存进变量p p就是指针变量

如何编址?

地址是如何产生的?地址是什么样的数据?
32位机器—32根地址线/数据线(物理的电线–通电–>高电平/低电平 即1/0)
32个地址线都是低电平的话

00000000000000000000000000000000
00000000000000000000000000000001
00000000000000000000000000000010
....
10000000000000000000000000000000
...
11111111111111111111111111111111

总共能产生2^32个编号
32个bit就可以存32个0/1组成的二进制序列
也就是4byte
所以指针变量大小也就是4个字节
同理64位机器,需要64bit也就是8byte存地址
地址不需要存起来,是由硬件电路直接产生

指针变量

#include<stdio.h>
int main()
{
	int a = 10;
	//4byte
	//& 取地址操作符
	//a占4个字节,每个字节都有编号
	//取出的是所占4个字节空间的第一个字节的地址(地址小的那个字节)
	printf("%p\n", &a);
	//%p 地址的打印格式
	int* p = &a;
	*p = 20;
	//* 解引用操作符,*p通过p中的值,找到p所指的对象
	//*表示p是指针变量
	//int 表示p指向的对象的类型是int类型
	//p是用来存地址的,所以把p称为指针变量
	printf("%p\n", p);
	printf("%d\n", a);
	return 0;
}
#include <stdio.h>
int main()
{
	char a = 10;
	//00000000 00000000 00000000 00001010
	//00 00 00 0a
	//倒着放进去,小端存储

	int* pa = &a;
	printf("%p\n", pa);//006FFDA7
	printf("%p\n", &a);//006FFDA7
	//取地址a,拿到的变量a所占空间起始地址
	return 0;
}
//*告诉我们p是指针变量
//int表示p是指向一个整型变量的

image-20220111233410993
存好了变量的地址是为了有一天通过指针来改变该变量的值
最底层的原理:就是为了通过地址改变值
&a a的地址
我们把地址也称为指针
地址放在指针变量中
在锤子的眼里,什么都是钉子
在指针变量眼里,什么都是地址

注意:
每次打印a的地址时,每次结果都不一样,
因为每次编译时会给变量重新分配内存空间

指针变量的大小

#include <stdio.h>
//指针变量的大小取决于地址的大小
//32位平台下地址是32个bit位(即4个字节)
//64位平台下地址是64个bit位(即8个字节)
int main()
{
   printf("%d\n", sizeof(char *));
   printf("%d\n", sizeof(short *));
   printf("%d\n", sizeof(int *));
   printf("%d\n", sizeof(double *));
   return 0;
}

4 4 4 4
要想知道指针变量有多大----地址的存放需要多大空间?---->
结论:

指针大小在32位平台是4个字节,64位平台是8个字节。

x86 代表32位机器
x64 表示641个二进制序列管理1个byte空间
则2^32二进制序列管理2^32byte空间
4,294,967,296-byte--->4GB

管理这个内存单元需要的地址的大小是4个字节
32位二进制序列组成一个地址,这个地址管理1个字节,
是一个字节的编号,这个地址本身大小是4个字节

指针类型

指针类型意义

1.

指针类型决定了解引用时一次能够访问几个字节

(指针的权限)

int* 4个字节
char* 1个字节
double* 8个字节
#include <stdio.h>
int main()
{
	int a = 0x11223344; //44 33 22 11
	//int* pa = &a;
	//*pa = 0; //改变了4个字节的值00 00 00 00

	char* pc = &a;
	*pc = 0; //00 33 22 11只改变了一个字节
	return 0;
}

image-20220112124638168

image-20220112124752350

2.

指针类型决定了指针加一/减一的步长(单位是字节)

#include <stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;
	char* pc = &a;
	printf("%p\n", pa);//005CFCF0
	printf("%p\n", pc);//005CFCF0
	//存的地址一样
	printf("%p\n", pa + 1);//0113F9B8
	printf("%p\n", pc + 1);//0113F9B5
	//pc只加了1个字节,pa加了4个字节
	return 0;
}

整型指针+1跳过4个字节

创建一个整型数组,10个元素
1.初始化数组内容1-10
2.打印数组

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	//正着赋值
	for (int i = 0; i < 10; i++)
	{
		*(p + i) = i + 1;
	}

	//倒着打印
	int* q = &arr[9];
	for (int i = 0; i < 10; i++)
	{
		printf("%d ", *q--);
        //整型指针一次跳过4个字节
	}
	return 0;
}
#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	char* p = (char*)arr;//需要强制类型转换
	return 0;
}

野指针

指针指向的位置是不可知的
(随机的、不正确的、没有明确限制的)
全局变量默认是0
局部变量默认是随机值

野指针成因

1.指针未初始化

#include <stdio.h>
int main()
{
       int* p;//野指针
       *p = 20;
       return 0;
}

野指针就像野狗一样,非常危险

2.指针越界访问

#include <stdio.h>
int main()
{
   int arr[10] = {0};
   int *p = arr;
   int i = 0;
   for(i=0; i<=10; i++)
  {
       //当指针指向的范围超出数组arr的范围时,p就是野指针
       *(p++) = i;
  }
   return 0;
}

3.指针指向的空间释放

#include <stdio.h>
int* test()
{
       int a = 100;
       return &a;
}
//a有它的生命周期,test结束时,a销毁了,权限被收回了
//a原来的空间还在,只是a没有权限访问原来的空间了
int main()
{
       int* p = test();
       printf("%d\n", *p);//100
       printf("%d\n", *p);//随机值
       return 0;
}

错误代码
第一次打印出100完全是巧合

image-20220112130002931

如何规避野指针

1.指针初始化

#include <stdio.h>
int main()
{
       int a = 10;
       int* pa = &a;
       int* p = NULL;//不知道指向谁时,让他指向空指针
       return 0;
}

把指针初始化为空指针后,不要再去挑逗它
就好比把野狗栓到树上后不要再取戏耍野狗

2.小心指针越界

3.使用指针之前检测指针有效性

#include <stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;
	int* p = NULL;
	if (pa != NULL)
	{
		*pa = 20;
	}
	if (p != NULL)
	{
		*p = 20;
	}
	return 0;
}

如果指针为空,就不要再去使用它
当一个指针不想用它的时候,也要把它赋为空指针

4.指针指向空间释放及时置NULL

int* p = NULL;

p的地址就是 00 00 00 00
image-20220112130516209

5.避免返回局部变量地址

#include <stdio.h>
int* test()
{
       int a = 100;
       return &a;
}
//a有它的生命周期,test结束时,a销毁了,
//a原来的空间还在,只是a没有权限访问原来的空间了
int main()
{
       int* p = test();
       printf("%d\n", *p);//100
       printf("%d\n", *p);//随机值
       return 0;
}

指针运算

指针±整数

#include <stdio.h>
int main()
{
	int arr[10] = { 0 };
	int* p = arr;
	int i = 0;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (i = 0; i < sz; i++)
	{
		*(p + i) = i;
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));//0 1 2 3 4 5 6 7 8 9
	}
	printf("\n");
    
	int* q = arr + sz - 1;
	//int* q = &arr[sz-1];
	
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *q--);//9 8 7 6 5 4 3 2 1 0
	}
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(q - i));//9 8 7 6 5 4 3 2 1 0
	}
	//q - i 不改变q的值
	return 0;
}
*(q - i) < == >*q--

指针-指针

指针+指针没有意义
地址-地址的绝对值 是2者之间元素的个数
指针-指针的前提是:两个指针指向同一块空间

#include <stdio.h>
int main()
{
	int a[10] = { 0 };
	//随着数组下标增长,地址由低到高增长
	printf("%d\n", &a[9] - &a[0]);//9
	printf("%d\n", &a[0] - &a[9]);//-9
	return 0;
}
//不能这么写:
int a = 10;
char c = 'w';
&a - &c;

image-20220112131500930

指针关系运算

#include <stdio.h>
#include<string.h>
//求字符串长度的函数
//1.计数器
int my_strlen1(char* s)
{
	int count = 0;
	while(*s != '\0')
	{
		count++;
		s++;
	}
	return count;
}

//2.递归
int my_strlen2(char* s)
{
	int count = 0;
	if (*s == '\0')
	{
		return 0;
	}
	else
	{
		return 1 + my_strlen2(++s);
	}
}
//3.指针-指针
int my_strlen3(char* s)
{
	char* start = s;
	while (*s != '\0')
	{
		s++;
	}
	return s - start;
}

int main()
{
	char arr[] = "abcefg";
	int len = my_strlen2(arr);
	printf("%d\n", len);
	return 0;
}

指针比较大小

#define N_VALUES 5
float values[N_VALUES];
float *vp;

for(vp = &values[N_VALUES]; vp > &values[0];)
{
   *--vp = 0;//先--再解引用
}

只是指向它,没有使用就不算越界
image-20220112132050401

倒着赋值为0
尝试简化一下:

for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--)
{
   *vp = 0;
}

即一开始vp指向下标为4的地址
vp指向下标为0的地址时,vp–指向下标为0前面的那块空间的地址
尽量不要这么写,虽然大多数编译器都能通过

但标准规定:

允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较
但是不允许与指向第一个元素之前的那个内存位置的指针进行比较

也就是不允许P1和P3进行比较

image-20220112132429178

指针与数组

指针 - 地址
指针变量

数组:一组相同类型的数据
联系:通过指针访问数组

#include <stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (size_t i = 0; i < sz; i++)
	{
		printf("%p\n", p + i);
	}
	// p + i 等价于&arr[i]

	/*
	00BBFC28
	00BBFC2C
	00BBFC30
	00BBFC34
	00BBFC38
	00BBFC3C
	00BBFC40
	00BBFC44
	00BBFC48
	00BBFC4C
	*/

	return 0;
}
#include <stdio.h>
int main()
{
	int arr[] = { 1,2,3,4,5,6,7,8,9,10 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	for (size_t i = 0; i < sz; i++)
	{
		printf("%d ", p[i]);//1 2 3 4 5 6 7 8 9 10
	}
	//p[i] 被编译器翻译成*(p+i)
	return 0;
}

二级指针

int *p1 p2;

此时p1是指针,p2是整型变量

指针变量也是变量,是变量就有地址
取指针变量的地址就是二级指针

#include <stdio.h>
int main()
{
	int a = 10;
	int* pa = &a;
	int** ppa = &pa;
	//int* 是pa的类型 *ppa表示 ppa是指针
    //前面的int*表示ppa指向的东西的类型是int*
    //ppa的类型是int** 
	int*** pppa = &ppa;//三级指针
    //*pppa表面pppa是指针	int**表示pppa指向的东西的类型是int**类型也就是ppa
	//...
	return 0;
}

image-20220112133502731

pa有自己的地址,pa存的是a的地址

**ppa = 20;
a的值就变成了20

指针数组

#include <stdio.h>
int main()
{
	//整型数组-存放整型的数组
	int arr1[10] = { 0 };
	char ch[5] = { 0 };//字符数组 - 存放字符的数组
	int a = 10;
	int b = 20;
	int c = 30;
	int i = 0;
	int* arr2[5] = { &a,&b,&c };//指针数组,存放z指针的数组
	//不完全初始化,剩下2个为NULL空指针
	for (i = 0; i < 3; i++)
	{
		printf("%d ", *(arr2[i]));//10 20 30
	}
	return 0;
}

练习

野指针也能正常使用×
32位机器 操作系统可以使用的最大内存空间为2^32bit
64位机器 可以管理2^64bit位空间 16G

1.大小端

#include <stdio.h>
int main()
{
  int arr[] = {1,2,3,4,5};
  short *p = (short*)arr;
  int i = 0;
  for(i=0; i<4; i++)
  {
    *(p+i) = 0;
  }
   
  for(i=0; i<5; i++)
  {
    printf("%d ", arr[i]);//0 0 3 4 5
  }
  return 0;
}

01 00 00 00 02 00 00 00 03 00 00 00 04 00 00 00
05 00 00 00
小端存储
1是0x 00 00 00 01 倒着存变成01 00 00 00
short* 解引用只能访问2个字节
p+1跳过2个字节
第一次 把01 00 赋值成00 00
第二次 把00 00 赋值给00 00

结果00345

image-20220114124735517

2.指针运算

unsigned long pulArray[] = {6,7,8,9,10};
unsigned long *pulPtr;
pulPtr = pulArray;
*(pulPtr + 3) += 3;
printf(%d,%d\n”,*pulPtr, *(pulPtr + 3));//6 12

数组里面的值变了

image-20220114125028367

3.大小端

#include <stdio.h>
int main()
{
    int a = 0x11223344;
    char *pc = (char*)&a;
    *pc = 0;
    printf("%x\n", a);
    return 0;
}

小端存储
44 33 22 11
char* 访问一个字节
00 33 22 11
打印出来 11223300

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值