首页 > 其他分享 >指针初阶

指针初阶

时间:2023-05-26 11:36:59浏览次数:30  
标签:arr 初阶 字节 int 地址 数组 指针

一、指针

1、什么是指针

      指针是一个变量,是用来存放某个数据或元素在内存(存储空间)中的地址的。通过这个指针可以间接的访问指针指向的数据或元素。什么类型的数据就用什么类型的指针,如:int 类型的数据,就用【int* 指针变量名】去存储。

2、指针所指向的空间的大小是多大?

      在内存中指针指向的地址的大小是一个字节,因为系统为每个地址分配的大小就都是一个字节,char类型占1个字节,int类型占4个字节,double类型占8个字节,等等。

指针初阶_数组

3、32位/64位指针所占内存的大小是多少?

      32位机器,有32根地址线,每条地址线可以传送0/1的电信号,每个电信号转变成内存地址时所占的空间就是1bit,所以它能传送的二进制序列就有2的32次方中可能。

指针初阶_野指针_02

      每种结果都是32个比特位的空间,1字节(byte) = 8比特位(bit),所以32个比特位就等于4个字节。所以:

      32位操作系统中指针所占内存的大小是4个byte。

      64位操作系统中指针所占内存的大小是8个byte。

4、指针和指针类型

      不同类型的元素或变量在取地址后要有有一个指针变量来存放这个地址,而这个指针变量的类型就是看所指空间的元素或变量的类型来决定的。

如:【char*】类型的指针是为了存放 char 类型变量的地址。 【short*】 类型的指针是为了存放 short 类型变量的地址。【int*】类型的指针是为了存放 int 类型变量的地址。

(1)、指针 +- 整数

      指针+-一个整数,得到的结果是不一样的,【char*】指针+1,是跳过一个字节指向下一个字节。

指针初阶_数组_03

      因为char类型只占1个字节,所以它只移动一位。

      【int*】指针+1,是跳过4个字节,指向第五个字节。

指针初阶_野指针_04

因为int类型占4个字节,所以pa+1,移动4个字节。

总结:指针类型决定了,指针+-一个整数,移动的距离有多大。

            int* +1 —>   +1*sizeof(int)  ==  +4

            char* +1 —>  +1*sizeof(char) ==  +1

            int* +n —>   +n*sizeof(int)  ==  +n*4

            char* +n —>  +n*sizeof(char) ==  +n*1

(2)、指针的解引用

      指针通过对取地址&操作得到元素或变量的地址,而通过解引用操作符*可以间接访问指针所指地址的元素或变量。

      不同类型的指针的变量所能访问的内存空间是不同的。

指针初阶_野指针_05

      这里的a是int类型,取出a的地址是用int接收,对pa这个指针变量进行修改时,int*指针可以修改内存中的4个字节。

指针初阶_指针变量_06

而这里是将a取地址后存放在char*指针中,对*pa这个指针变量进行修改时,char*指针可以修改内存中的1个字节。

总结: 指针的类型决定了,对指针解引用的时候有多大的权限(能操作几个字节)。 比如: char* 的指针解引用就只能访问一个字节,而 int* 的指针的解引用就能访问四个字节。


二、野指针

1、什么是野指针?

      野指针是指,指针指向的位置不可知、指向的位置随机、指向的位置不正确,或没有明确限制的指针,就叫野指针。

2、野指针的成因

      a.指针未初始化

指针初阶_数组_07

      因为这里的指针变量p没有初始化,也就是它没有一个指定的,唯一的地址,所以他有可能选择任意一个地址,但是那个任意的地址又不属于这个指针变量,所以会出现报错。

      b.指针越界访问

指针初阶_指针变量_08

      因为创建的数组只有5个元素即{1,2,3,4,5},而for循环中指针parr要访问到数组的第10个元素。所以出现了5个随机值,这随机值不是我们能料到的,所以这就是指针越界访问的后果。

      c.指针指向的空间被释放

指针初阶_数组_09

      这里在函数中创建了一个局部变量a,返回变量的地址给主函数中的指针变量pa,函数test结束,栈区中局部变量a创建的空间也被释放了,空间还给操作系统了,这时返回的a的地址不再指向a这个局部变量了,当还用*pa修改这个空间的值,就有可能会出现问题。


3、如何避免野指针?

      (1)对指针进行初始化

指针初阶_指针变量_10

      (2)小心/防止指针越界

指针初阶_指针变量_11

      (3)指针指向的空间被释放后,即使将指针置为NULL(空指针)

指针初阶_数组_12

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

      避免2.(c)的情况。

      (5)指针在使用前要检查指针的有效性

指针初阶_野指针_13


三、指针运算

1、指针 +/- 整数

      指针 +/- 一个整数,实现的是对这个指针位置的移动。

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* parr = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(parr + i));
	}
	return 0;
}

指针初阶_野指针_14

      开始指针(parr+0)指向数组中的1,当i+1之后,parr的指向也向后移动了一位指向了数组中的2。所以,对指针+/-一个整数实现的是对指针位置的移动。

2、指针 - 指针

      指针-指针得到的是两个指针之间元素的个数,前提是:两个指针必须指向同一块空间。

int test(char* arr)
{
	char* start = arr;
	while (*arr)
	{
		arr++;
	}
	return arr - start;
}

int main()
{
	char arr[] = "abcdefg";
	int ret = test(arr);
	printf("ret = %d\n", ret);
}

指针初阶_指针变量_15

这里实现的是使用指针的方法实现strlen函数,求一个字符串中字符的个数。test中,将arr数组的首地址送给了指针start,然后通过while循环找到\0前的元素,用这个元素的地址 - start的首地址得到的就是两个地址之间元素的个数。

3、指针的关系运算

      C语言标准规定:允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与指向第一个元素之前的那个内存位置的指针进行比较。

指针初阶_指针变量_16

允许和数组最后一个元素后面的位置地址进行比较,不允许和数组第一个元素前面的位置地址进行比较。


四、指针和数组

int main()
{
	int arr[5] = { 1,2,3,4,5 };
	printf("%p\n", arr);
	printf("%p\n", &arr[0]);
	return 0;
}

指针初阶_数组_17

由此程序可以得出结论:数组名是首元素地址。

但是有两个例外:

     (1)sizeof(数组名) ——> 得出的是整个数组的大小。

      (2)&数组名 ——> 取出的是整个数组的地址。

int main()
{
	int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	printf("%p\n", arr);
	printf("%p\n", arr+1);

	printf("%p\n", &arr[0]);
	printf("%p\n", &arr[0]+1);

	printf("%p\n", &arr);
	printf("%p\n", &arr+1);

	printf("%d\n", sizeof(arr));
	return 0;
}

指针初阶_指针变量_18

由此图可以得出,数组名是数组首元素的地址,+1跳过的是一个元素,4个字节。

&数组首元素,+1跳过的也是一个元素,4个字节。

&数组名,取出的是整个数组的地址,+1跳过的是整个数组,从884到8AC,中间差了28(十六进制),转化为10进制得到的是40,每个元素占4个字节,40个字节就是10个元素,所以是跳过了一个数组。

sizeof数组名,求得出的值是整个数组的大小——40字节。


五、通过指针可以定位到数组中的任何一个元素

int main()
{
	int arr[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 };
	int* p = arr;
	int sz = sizeof(arr) / sizeof(arr[0]);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", *(p + i));
	}
	return 0;
}

指针初阶_指针变量_19

指针变量p被赋值为数组首元素的地址,再利用p+i可以找到数组中任何一个元素的地址,在对其解引用即可获得该元素的值。


六、二级指针

      指针变量也是变量,是变量就有地址,那指针变量的地址存放在哪里呢?

      指针变量的地址也要存放在指针中,这个存放指针变量地址的指针就叫:二级指针。

指针初阶_野指针_20

二级指针如何理解?

      一级指针int* pa = &a; 可以理解为:int *pa = &a;*pa结合说明pa是个指针,而pa指向的地址中元素的类型是int类型的元素。

      二级指针int** ppa = &pa; 可以理解为:int* *ppa = &pa; 结合说明ppa是个指针,而int* 代表的是这个指针指向的地址中元素的类型是int*的。

所以对ppa解引用(*ppa)得到的就是pa这个指针,再对pa进行解引用(*pa)得到的就是变量a。所以**ppa可以直接得到变量a。



七、指针数组

      整形数组中存放的是整形变量,字符数组中存放的是字符变量,那指针数组中存放的就是指针变量。

      如:

指针初阶_野指针_21

指针初阶_野指针_22


指针数组中存放的都是指针变量。

指针初阶_数组_23

而指针数组是指针还是数组?

答案:是数组。是存放指针的数组。

int main()
{
	int a = 10;
	int b = 20;
	int c = 30;
	int d = 40;
	int* arr[] = { &a,&b,&c,&d };
	return 0;
}

指针初阶_指针变量_24

      如图,将a,b,c,d四个变量取地址后存放在数组arr中,这个arr的类型是int*的,所以这个数组arr就是指针数组。




结尾:感谢你能看到这里,如果有写的不对的地方请给位不吝赐教,谢谢啦!

指针初阶_数组_25


标签:arr,初阶,字节,int,地址,数组,指针
From: https://blog.51cto.com/u_15865089/6354485

相关文章

  • C指针细节补充
    C指针细节补充void*变量可以赋值给任何指针变量,反之依然成立void*当其类型指针存放的是struct地址时,不能用指针->成员变量,可以赋值给相应的指针变量后在进行操作......
  • 指向常对象的指针变量和指向对象的常指针
    1,指向常对象的指针变量指向常对象的指针变量可以指向一个已经申明为常对象的变量,此时只能用指向常对象的指针变量指向它;也可以指向一个非常对象的变量,而此时可通过指针访问该对象,但是不能通过指针改变该对象的值。下面给出一个简单程序:#include <iostream>using namespace std;......
  • C数组和指针
    C数组和指针关键字->static运算符->&、*创建并初始化数组指针、指针和数组的关系编写处理数组的函数二维数组数组什么是数组?数据类型相同的一系列元素声明数组的方式:多少个元素->数组大小元素的类型示例代码:/***@Author:Lucifer*@Date:5/6/2023......
  • 字符串原地修改双指针经典实现
    字符串原地修改经常遇到的一类题,双指针一个用于写入,一个用于扫描,互不干扰,各司其职。题目:https://leetcode.cn/problems/reverse-words-in-a-string/stringreverseWords(strings){reverse(s.begin(),s.end());intwrite=0,scan=0;while......
  • C++中const和constexpr关键字解析:常量、函数和指针
    C++中const和constexpr的作用很多C++的初学者看到const这个关键字的第一反应都是一头雾水,主要是因为const可以出现在很多的位置,以及后面加入的constexpr更是常常感到困惑,今天就为大家一一解释出现它们的含义和以及作用const关键字const修饰变量这是最基本的一种用法,顾名思义,就是......
  • 代码随想录算法训练营第9天 | ●28. 实现 strStr() ●459.重复的子字符串 ●字符串总
     第四章 字符串part02今日任务  ● 28. 实现 strStr()● 459.重复的子字符串● 字符串总结 ● 双指针回顾   详细布置  28. 实现 strStr()  (本题可以跳过) 因为KMP算法很难,大家别奢求 一次就把kmp全理解了,大家刚学KMP一定会有各种各样的疑问......
  • 指向对象数组的对象指针
    #include<iostream>usingnamespacestd;classstudent{public:student(intn,floats):num(n),score(s){}voiddisplay(void);private:intnum;floatscore;};voidstudent::display(void){cout<<num<<":"......
  • strtok() 函数_2种方法的指针实现
    //Lvxin4-1strtok.cpp//strtok()函数的实现2种方法//下面的函数实现考虑一下3种极端情况://"-This,asamplestring"无行尾标志//"-This,asamplestring-"有一个行尾标志//"-This,asamplestring------”有多个行尾标志#define_CRT_SECURE_NO_WAR......
  • strtok() 函数 2种方法的指针实现
    //Lvxin4-1strtok.cpp//strtok()函数的实现2种方法//下面的函数实现考虑一下3种极端情况://"-This,asamplestring"无行尾标志//"-This,asamplestring-"有一个行尾标志//"-This,asamplestring------”有多个行尾标志#define_CRT_SECURE_NO_WAR......
  • strtok() 函数 2种方法的指针实现
    //Lvxin4-1 strtok.cpp //strtok()函数的实现 2种方法//下面的函数实现考虑一下3种极端情况://"-This,asamplestring"无行尾标志//"-This,asamplestring-"有一个行尾标志//"-This,asamplestring------” 有多个行尾标志#define_CRT_SECURE_NO_WARNI......