c++的指针学习(长期更新,诚求指点)

1.第一部分,关于指针的基础

指针的定义语法

int * a=a;

(int*是一种数据类型)

指针内部储存着的是一块地址

可以借用指针的解引用操作,通过指针直接去访问一个变量

指针指向变量(存入地址)的时候,要在前面加入& 表示这个变量的地址,不加则代表直接对对应地址所在的变量进行修改(通过指针访问地址空间)

指针在输出的时候,*p代表解引用,直接代表那个被指向的空间

而单纯输出名字,只是输出指针下面储存的十六进制数字(地址字符串)

//1,关于指针的基础
	typedef  int* pointNeedle;
	//pointNeedle a = &2;指针类型存的是地址,你不能拿没有开辟空间的东西来调戏他..
	int a = 12;
	pointNeedle a1 = &a;//往指针类型里面存入数据的时候,注意村也是只能存地址
	//在变量前面加上一个&就代表这个玩应的地址
	//而输出的时候有两种情况
	//第一个,就是单纯输出指针里面的数值,也就是存在的地址
	// 
	//第二个,前面加上指针进行解引用,输出会直接按着地址导向指针指着的东西
	//其中第一个应该叫做解引用操作,*p直接导向p中所储存的地址,操作*p就可以顺着地址操作a;
	//(指针解引用对于地址对应空间的数据的修改)
	cout << "a=" << a << endl;
	*a1 = 123;
	cout << "a=" << a << endl;
	cout << "*ai=" << *a1 << endl;

2.关于空指针和野指针


	//2.关于空指针
	//空指针指的是仅仅初始化没有赋值,null的指针
	//实际上底层指向的是为0的空间,但是无权进行访问,必须再付给指针名称一个可以操控的地址
	int* assss;
	//3.关于野指针
	//野指针指的是指向了一块非程序员申请的,而且无权操控的空间
	//语法是不会出任何问题的,但是就不行.....
	int* asssss = (int*)0x1234;

(对了强调一个事情,包括空指针指向的0地址,0-255地址中都代表系统的空间,除非砸开硬盘,无权修改)

空指针和野指针的共同特点就素,都不是程序员申请的空间,无法调用,因此尽量不要出现这东西

另外,如果是new创建出来的堆空间元素,必须进行手动删除,在Java里面可以使用jvm自动删除,但是cpp不行,所以程序员必须手动删除,另外新建出来的对象可以使用指针进行接收,释放的时候不需要解引用

int * arr=new int(6);
delete arr;

另外注意一点,删除的时候如果是数组,那么一定要全部删除!\

比如这种方式  ---delete p[];

不然只会删除首地址

3.关于指针和数组;

指针可以用来遍历数组,其中指针每次自增(++操作),都是偏移和数据类型一致的字节,宏观来看,无论是啥数据类型都是跳一个位置;

int arr[10] = { 1,2,3,4,5,6,7,8,9,10 };
	int* point = arr;//数组名其实就是第一个的地址
	for (int i = 1; i <= 10; i++) {
		cout << *point << endl;
		//因为这里是整形,所以指针自动移动四个字节
		//和迭代器Iteritor还不一样
		point++;
	}

(1)关于这里和迭代器的区别,迭代器是插在两个数据中间的,判断方法是hasnext.

而指针在这里直接锁定再地址上,可以理解为直接定位再数据自己的头上

(2)关于遍历数组的时候发生越界,不会报错但是会输出这个数字

//-858993460//这个其实不是地址,也不是null,是被自动填充成的数字

(3)指针指向数组的时候有个建议就是,要么直接指向数组的第一个元素,要摸作为参数的时候直接传入地址名字(因为多维数组是不能与int*这种指针类型相互匹配的)

4.关于指针/地址作为参数传入,然后再方法体内部操控数据

void swap(int* p, int* b) {
	int temp = *p;
	*p = *b;
	*b = temp;
}
    int a2 = 12;
	int b2 = 13;
	swap(&a2, &b2);
	cout << "a=" << a2 << endl;
	cout << "b=" << b2 << endl;
//输出为13和12,顺序已经颠倒了

方法传入的是指针/地址,把ab两个数据的地址传入进去,就可以在sawp内部修改数值了

在swap方法内,对两个传入的地址进行解引用,就可以操作数值了

将变量的地址传入方法,让方法中和外面共用一个地址进行数据操作.java中只能通过封装,这里可以用指针调用基本数据类型,java基本做不到这一点.

(深刻理解一下解引用)!!!!!!

5.关于const修饰指针的几种情况

//1.先构建一个常量指针
	//常量指针的特点,可以修改指向,但是不能修改指向的数值
	//记忆方法:const修饰的是*,对于解引用进行限制,地址可以随意更换
	//但是无法利用例如"*a=12"的解引用操作进行修改
	const int* p1 = &a;
	//2.再构建出一个指针常量
	//指针常量的特点是,可以对于数值进行修改,但是不能修改这个指针的指向
	//记忆方法为:const修饰的是p2,直接限制了里面储存的十六位地址不能动,但是可以按照这个死路进行修改
	int* const p2 = &a;
	//第三种,同时对解引用和内部地址进行限制的方法
	//现在好了,指向和地址都不能修改了
	const int* const p3 = &a;

(一个使用示例:利用指针传输数组头位的地址,在方法中对数组进行修改)

(注:只要得到数组头位置的地址,就可以对数组整体进行修改,数组名其实就是数组第零位的地址,所以这里传入的只是数组名,方法里面使用的时候也是直接用名字,无需解引用即可需改)

void bub(int * arr) {
	//整个好像是做不到的    int arr2[] = * arr;
	//这里好像传入首位的地址就能使用整个数组
	//大概是数组的特性?只要得到第一位的地址就可以改动整个数组
	//而且我是试了一下,做不到打开整个数组指针类型好像........
	for (int i = 0; i < 9; i++) {
		for (int j = 0; j< 10 - 1 - i; j++) {
			if (arr[j] >= arr[j + 1]) {
				int temp = arr[j];
				arr[j] = arr[j+1];
				arr[j+1] = temp;
			}
		}
	}
int arr2[10] = {2,3,4,5,6,7,8,9,10,1};
	bub(arr2);//在传入地址的时候直接传入数组第一位的地址就行
	//不用传入整个数组的地址...............................
	int* point2 = arr2;
	for (int i = 1; i <= 10; i++) {
		cout <<* point2 << endl;//这里记得要在读入数据的时候进行解引用
		point2++;
	}

6.关于结构体指针;

利用结构体指针访问结构体中的某个属性的时候,可以不解引用,直接用指针名字->属性名字

就可以调用了.(->,,,,貌似vs里面会自己跳出来,作用类似.)

struct student {
		string name;
	};
	struct student arry[3] = {
		{"dijia"},
		{"daina"},
		{"gaiya"}
	};
	student* point12 = arry;
------------------------------------------------------------
-	//cpp没有提供查询数组长度的方法,所以我们应该是用的是       |
-	int longlonglong = sizeof(arry) / sizeof(arry[0]);     |
------------------------------------------------------------
	for (int i = 1; i <= longlonglong; i++) {
		//使用  指针名字->属性名字  就可以利用指针调用相应的属性了
		cout << point12->name << endl;
		point12++;
	}

(其中这里还有一个查询数组的方法,总字节数除以每个元素的字节数,不过对于太复杂的数组不一定管用)

7.关于指针类型的强制转化

空类型指针void*,可以指向任何的数据类型

void* s;
	//但是使用并不是无限制的
	s = (int*) & a;
	//这个让指针可以理解整数,这里要强转,不然s作为void指针是无法识别地址的

(类型 *)起到的是对地址/指针进行类型强转的作用

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

比较常用的就是"(void *)"代表转化成一个纯地址的类型

效果类似取地址符号(&);

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

再举个例子,野指针进行赋值的时候

int* s=(int*)0x22 ;//野指针也要指明是地址,不能用数值赋值

不能给指针输入一个野地址,这里一开始会默认是一个十六进制数字,必须指明这是一个int*类型的指针

8.关于解引用和自增运算符号的先后和连用

(1)先说结论,运算顺序为"前后分开看"

                                   (一)  (前自增/解引用)

     前自增和解引用,谁先接触point谁先起作用,依次向外发挥作用

     如果没有解引用,那么效果就是地址偏移,如果解引用已经发生,那么效果就是数值变化

                                     (二)关于(后自增)

    如果没有括号保护,都是对地址进行偏移

 (2)

//其实一共只有这三种情况-------------------------------------------------
    // =*p++                  赋值,然后地址发生偏移,
    // =++*p                  先解引用,再进行自增(数值)
    // =*++p                  先自增(地址),再进行解引用

    // =(++p)*  =p++*没有这种乱七八糟的东西orz;            

(3)关于多重连用的一个举例

int a1[] = {3,4};
    int* ass = &a1[0];
	int bb = *ass++;//举个例子这样的,输出结果应该是五
	//先进行地址自增,然后解引用,再对对应地址的数值进行自增
	//先偏移到4,然后4自增输出5
	cout << "结果就是" <

  
9.关于三种特殊的指针迭代方式

char arr[] = {'1','2'};
	char* pp= arr;

(1)关于*(p+i)的形式

for (int i = 0; i <= 1; i++) {
		cout << *(pp + i) << endl;
	}

(2)关于指针直接自增p++

for (int i = 0; i <= 1;i++ ) {
		cout << *(pp ) << endl;
		pp++;
	}

另一种友好写法*p++(先输出数值,再地址偏移)(实用)

for (int i = 0; i <= 1;i++ ) {
		cout << *pp++ << endl;
	}

(3)使用指针作为角标,这个

for (int i = 0; i <= 1;i++ ) {
		cout << pp[i] << endl;
	}

这里要注意一件事情,指针作为角标的时候

例如arr= 1 2 3 4 5 6 7

       *point=arr[1]

  则point[1]其实就是arr[2];

结构如下

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

arr[0]                                                     |

1          2              3             4         5

       point          point[1]                        |

      (point[0])

---------------------------------------------------|

10,关于字符串使用指针

字符串使用指针的方法为,直接指向字符串的第一个字符

char* point = &str[0];//好家伙能用这种方法进行指向一个字符串

至少我的编译器做不到直接指向字符串名字,一般直接指向首地址

另外补充一个重点,一旦输出的是数组首地址的解引用,那么就会输出整体的字符串/数组全部元素

11.关于指向指针的指针

定义方式

//指向指针的指针
	int a = 1;
	//int** p = && a;这种操作是错的,不能连续取地址
	int* i =&a;
	//只能接受一个指针的地址,也要加上 &
	int** p = &i;
	//p--->i--->a

储存的是另一个指针所占用的地址

通过连续解引用,可以访问最下面的东西

举个例子,访问指针数组的时候

*(* (point + 1) + 2)--------其实就是point[1][2]

文字解读:首先point偏移一位,再解引用,就是第一行的首地址

第一行的首地址再偏移两位,再解引用,就是第一行第二个的东西

13.关于数组和指针有四种形式

int point[2][3];//单纯的,长度为12的一个连续的数组
	int(*point)[3];//为指向数组的指针,n行3列的数组,内存必然连续
	int* point [3];//为存了地址的数组,3行n列的数组,内存可以不连续
	int **point;//指向指针的的指针,完全可以不连续,
阅读方法为*(*(point+1)+2) 其实就是point[1][2]

前面两个是内存连续的,但是后面两个是内存连续的

关于

    int(*point)[3];//为指向数组的指针,n行3列的数组,内存必然连续
    int* point [3];//为存了地址的数组,3行n列的数组,内存可以不连续

看似效果差不多但本质上完全不一样

前者是指向数组首地址的指针,

比如指向一个数组{123456}的时候,指针一开始指向的是1,但是一旦偏移一下指针,就指向4了

而后者则是一个储存地址的数组

见下表就算两者的区别

c++的指针学习(长期更新,诚求指点)_第1张图片

 指针指针常见用法是这样的

string a1 = "jdjd";
	string a2 = "jdjd";
	string a3 = "jdjd";
	char * ass[] = { &a1[0],&a2[0],&a3[0]};
	char** as = ass;//这样存入的就是a1[0]这个地址

输出就是j

你可能感兴趣的:(s,spring)