初阶指针Part(含指针比较大小)

只要你愿意 开始总比放弃好。 Roman.

愿我们都有自己的目标并正在为其不懈努力。

-----------------------------------------------------------------------

0. 补充:

寄存器(读取较快):ebp 、 esp (前两个存放的是地址) 、eax 、ebx 、ecx 、edx

****************************************

一、指针是什么

一)指针理解的两个要点

1. 指针是内存中一个最小单元的编号,也就是地址

2. 平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量

二)补充

 1. 内存中最小单元是一字节

2.

  •    32位虚拟空间:

CPU生成32位地址(即:32bit == 4byte)-- 通过地址线传输 -- 定位到内存空间

  • 64位虚拟空间:

CPU生成64位地址(即:64bit == 8byte)-- 通过地址线传输 -- 定位到内存空间

  • 注:每根地址线在寻址的时候产生高电平(高电压)和低电平(低电压),即 1 或者 0
  • 每个地址(32 or 64 根地址线组成一个地址)标识一个字节,共有 (2^32 or 2^64)个地址

三)指针变量

 1. 我们可以通过 &(取地址操作符)取出变量的内存起始地址,把地址存放到一个变量中去,这个变量就是指针变量

 2. 总结:指针变量,用来存放地址的变量。(存放在指针中的值都被当做地址来处理)

四)小结

 1. 指针就是地址,口语中的指针一般就是指指针变量

 2. 指针是用来存放地址的,地址是唯一标识一块地址空间的

 3. 指针大小:在32位平台上--4字节    64位平台上--8字节

****************************************

二、指针和指针类型

 1. 指针类型 决定了 指针在被解引用的时候访问的权限。(即:操作的大小)

     如:① 整型指针解引用访问 4字节

            ②字符型指针解引用访问 1字节

 2. 指针类型决定了指针向前 or 向后 走一步走多大距离

   如:① int* + n  --> 跳过 n * sizeof (int) ==  n *4 个字节

          ② char * + n  --> 跳过 n * sizeof (char) ==  n *1 个字节

 3. 补充1: sizeof(long) >= sizeof(int)

      32 位环境下: sizeof(long) == 4 byte

      64 位环境下: sizeof(long) == 8 byte

 4. 注: sizeof(float) == 4 字节

 5. 补充:

  • 不管是二维数组还是一维数组,在传参的时候都不会去创建数组
  • 一维数组传参的时候,形参的数组大小可以省略
  • 二维数组传参的时候,形参的数组中,行可以省略,列不可以省略
  • 在声明时,二维数组如果要省略行,则一定要进行初始化
  • 整型和浮点型数据,在内存中存储方式不同 

如:

float n = 3.14f ;

int * p = &n;

*p ;  // ???未知

****************************************

三、野指针

 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)

一)野指针的成因

 1. 指针变量未初始化

  1)补充:局部变量未初始化则自动初始化为随机值

  2)如何规避:

  • 如果已知变量,则取地址
  • 若未知,则赋值  NULL(本质是0),0是坚决不允许被访问的,所以访问指针是有条件的:指针变量不等于NULL

  3)注:为什么0不能被访问?

    内存中有些空间是不能被用户访问的,只是供操作系统(内核)访问,该空间包括0

 2. 指针越界访问

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

 3. 指针指向的空间释放

  如:

int* test()
{
 int a = 10;
 printf("%d\n",a);
 return &a;
}

int main()
{
 int* p = test();
 *p = 100; 
 return 0;
}

 注:局部变量出了函数后被销毁,空间释放,找不到该地址指向的变量进行赋值操作

二)如何规避野指针

 1. 指针初始化

 2. 小心指针越界

 3. 指针指向的空间释放,及时置 NULL :不想使用时也置NULL

 4. 避免返回局部变量的地址

 5. 指针使用之前检查有效应:即-判断指针变量不等于NULL

****************************************

四、指针运算

 1. 补充:随数组下标增大,地址由低到高变化

 2. 指针运算是指: 指针可以进行大小比较

 3. 指针 + - 整数:  如 *vp++; // +1则跳过一个元素

 4. 指针 - 指针

  1)前提:两个指针必须指向同一块空间,即同一个数组

  2)结果:(指针 - 指针)的绝对值 得到的是指针和指针之间元素的个数

     (指针 - 指针 有正有负)

 5. 实例:my_strlen() -- 求数组长度(元素个数)

    // 注:三种方法:计数器、递归、指针-指针

   // 指针 - 指针 : 只适用于字符数组

//my_strlen() -- 求数组长度(元素个数)
//指针-指针

#include<stdio.h>

int my_strlen(char* arr)
{
	char* start = arr;  //首元素地址
	while (*arr)//寻找\0--末尾
	{
		arr++; //跳过一个元素
	}
	return arr - start;
}

int main()
{
	char arr[] = "abcdefghi";
	int len = my_strlen(arr);
	printf("长度:%d\n", len);
	return 0;
}

 6.  指针的关系运算

  1)即:比较指针之间的大小

  2) 实例

for ( vp = & values [ N_VALUES ]; vp > & values [ 0 ];)
{
    *-- vp = 0 ;  // 先减减,再使用(赋值)
}
代码简化 , 这将代码修改如下:
for ( vp = & values [ N_VALUES - 1 ]; vp >= & values [ 0 ]; vp -- )
{
    * vp = 0 ;
}
//实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证它可行。
  • 标准规定:
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与
指向第一个元素之前的那个内存位置的指针进行比较。

****************************************

五、指针和数组

 1. 数组可以通过指针来访问

 2. 数组名是首元素地址,但是有两个例外:

  • sizeof(数组名),数组名表示整个数组,计算的是整个数组的大小
  • &数组名,数组名表示整个数组,取出的是整个数组的地址

       +1 表示跳过整个数组, 如:&数组名+1 

  • 补充:

      ① 数组可以通过指针来访问,前提是:数组是连续存放的,是同一块空间

      ②数组和指针不是同一回事儿:

         数组是一块连续的空间, 指针是存放地址的变量

         可以通过指针访问数组

****************************************

六、二级指针

 1. 二级指针是 指针的指针

 2. 举例:

//二级指针
#include<stdio.h>
int main()
{
	int a = 0;
	int* pa = &a;
	int** ppa = &pa;
	return 0;
}
// int * pa : *表示pa是指针变量, int 说明 pa 指向的是 int类型的数据
// int* * ppa : *(ppa之前最接近ppa的*)表示ppa是指针变量, int* 说明 ppa指向的对象是 int* 类型的数据

// *ppa == pa ;
// *pa == a;
// **ppa == a;

****************************************

七、指针数组

 指针数组 是存放【指针类型】的数组

 1. 如: int* arr[5];  //指针数组,元素类型是 int*

 2. 模拟二维数组:指针模拟

//模拟二维数组:指针模拟

#include<stdio.h>
int main()
{
	int arr1[] = { 1,2,3,4,5 };
	int arr2[] = { 2,3,4,5,6 };
	int arr3[] = { 3,4,5,6,7 };
	//元素类型相同
	int** num[] = { &arr1,&arr2,&arr3 };//数组名即首元素地址,地址即指针
	// 二级指针是地址的地址
	int size1 = sizeof(arr1) / sizeof(arr1[0]);
	int size2 = sizeof(num) / sizeof(num[0]);
	int i = 0;
	for (i = 0; i < size2; i++) //决定行(把每个一维数组都看作一行)
	{
		int j = 0;
		for (j = 0; j < size1; j++) //决定列(每个一维数组中元素个数即列)
		{
			//printf("%d ", num[i][j]); //方式一
			printf("%d ", *(num[i]+j)); //方式二
			//num[i]找到行--即第i个一维数组首元素地址,加j--即第i行跳过j个元素,解引用后即得元素
		}
		printf("\n");
	}
	return 0;
}

------------------------一个人所有的愤怒都来源于对自己无能的痛苦。------------------------

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

'Dream是普通小孩耶

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值