void 类型指针、const和指针、指针和数组,二级指针

一、void 类型指针

- void解释为“空虚,空隙”,而在C++语言中,void被翻译为“无类型”,相应的void*为“无类型指针”。
- void* 指针变量可以指向任意变量的内存空间,任何类型指针变量都可以转为void *- int num = 10;
- void* p = #
- int* p_num = #
- void* p1 = p_num; (也是可以这样写的)
- 一般在函数里面用到void* | memset()
- 对应void*指针变量,不要解除引用操作(因为不知道取几个字节),如果想要通过指针变量p获取num值,需要进行强制类型转换后才可以
- void* 可以保存任意类型的地址。(万能指针)。
- 主要作为函数的参数

二、const和指针

cosnt 表示常量,对const修饰的变量,我们是不能去修改他的值的
int const 和 const int没有什么区别,但是const和指针关联起来就有区别了

1. 第一种情况:const在 * 的左边 修饰的是 * (指针,不是指针变量) ( 地址指向内存数据 )(* p1 只读的,p1可读可写)

const int* p1 = #
- 指针 p1 指向的是一块内存,指向的这个内存是不可修改的 (因为const修饰的是指针,不是指针变量,注意了注意了注意了!!!);
- 这块地址的内存是不可以修改的,这一块地址的内存,我们是通过*p1解除引用去获取的,即*p1是不可以修改的;
- 但是本身的p1变量是可以修改的
- 也称常量指针(常量的指针)


const int a = 10;
因为a 是一个常量,所以我们要加上const修饰 (const 修饰的是这个类型)
const int* p = &a; 即表示这个地址里面的数据是常量,对应上; 
  • 对应一块内存,修饰的这一块内存是一个常量
  • 指针指向的(内存的)数据是一个常量,常量的指针,常指针

2. 第二种情况:cosnt 在 * 的右边,修饰的是p2,( * p2可读可写,p2只读)

int* const p2 = # 
 - 这时候const修饰的就是p2变量(p2是一个指针变量,p2保存的是一个地址)
 - p2变量的值不可以修改;
 - 但是p2所指向的那一块内存的数据就可以修改 (*p2 解除引用);
  • 指针变量p2是一个常量,称为指针(类型的)常量 | 【类比 int 类型的常量等等】

3. 第三种情况:const 在 * 的左右两边,既修饰 * (指针类型, * 代表地址)也修饰指针变量(即 * p3只读,p3只读)

const int* const p3 = #

三、指针和数组

1. 一维数组名

#include <bits/stdc++.h>
using namespace std;

int main() {
	// 创建一个数组  5 * 4字节 
	int arr[5] = {10, 20, 30, 40, 50};
	// 数组名arr当成类型,用在sizeof()函数中,表示的是数组的总大小(内存大小)
	cout << sizeof (arr) << endl; 
	
	// 数组名可以作为地址,代表的是首元素的地址
	cout << arr << endl; // int * 类型 
	cout << arr + 1 << endl; // 指针运算,类型是int *型的,跨度 4 个字节
	
	cout << "arr[1] = " << arr[1] << endl;  
	cout << "*(arr + 1) = " << *(arr + 1) << endl;
	
	// arr[1] 展开 *(arr + 1) : []外边的值(arr)在 + 号的左边,[]里面的值(arr)在 + 号的右边
	cout << "1[arr] = " << 1[arr] << endl;
	cout << "*(1 + arr) = " << *(1 + arr) << endl; 
	
	// []是对*()的缩写
	for (int i = 0; i < sizeof(arr) / sizeof(arr[0]); ++i) {
		cout << arr[i] << endl;
		cout << i[arr] << endl;
		cout << *(arr + i) << endl; // 解除引用的方式 
	} 
	
	// 推导,数组名作为地址,是首元素的首地址
	// 数组名是这个数组的首元素首地址
	// &arr[0] == &*(arr + 0) == arr + 0 == arr 
	
	return 0;
} 

2. 指针在数组中的操作(略)

3. 指针数组(int类型的数组,double类型的数组,指针类型的数组)

指针数组本质是数组,数组中的每个元素都是指针类型。

需求:定义一个数组,存放4个变量的地址。
int* arr[4];

arr[0] = &num1;
arr[1] = &num2;
arr[2] = &num3;
arr[3] = &num4;

int* arr[4] = {&num1, &num2, &num3, &num4};

4. 数组指针(本质是指针):指向数组的指针,这个指针指向一个数组的首地址

前面说到数组名是地址,数组名是数组首元素的首地址
而数组指针,存放的是数组首地址(这就是不一样的点,注意了!!!)
虽然数组的首地址和首元素首地址的值是相同的,但是二者的操作是不同的(值一样,类型不一样 char *char **;


- 对元素操作与对整个数组操作区分一下,想一想二维数组


例如:char a[4]char (*pa)[4];
a是char 类型,a+1,a的值会实实在在的加1,而pa是char[4]类型的,pa+1,pa则会加4(整个数组整个数组的跳,[6]的话,pa + 1就会 + 6(数组大小));
pa = &a,pa相当与二维数组的行指针,现在它指向a[4]的地址。;
不用想的太复杂,可以认为pa(数组指针)是二维数组的行指针,执行一维数组(列)的地址

char (*q)[4];
这是一个指针,指向长度为4的一维数组,即指向一个char[4]
存放的是一个数组的地址
大小为4字节

5. 二级指针:一个指针变量保存的是另外一个指针变量的地址值

int num = 10;
int* p = &num;

定义一个二级指针
int** q = &p; 

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
指针数组数组指针的区别 数组指针(也称行指针) 定义 int (*p)[n]; ()优先级高,首先说明p是一个指针,指向一个整型的一维数组,这个一维数组的长度是n,也可以说是p的步长。也就是说执行p+1时,p要跨过n个整型数据的长度。 如要将二维数组赋给一指针,应这样赋值: int a[3][4]; int (*p)[4]; //该语句是定义一个数组指针,指向含4个元素的一维数组。 p=a; //将该二维数组的首地址赋给p,也就是a[0]或&a[0][0] p++; //该语句执行过后,也就是p=p+1;p跨过行a[0][]指向了行a[1][] 所以数组指针也称指向一维数组指针,亦称行指针指针数组 定义 int *p[n]; []优先级高,先与p结合成为一个数组,再由int*说明这是一个整型指针数组,它有n个指针类型数组元素。这里执行p+1是错误的,这样赋值也是错误的:p=a;因为p是个不可知的表示,只存在p[0]、p[1]、p[2]...p[n-1],而且它们分别是指针变量可以用来存放变量地址。但可以这样 *p=a; 这里*p表示指针数组第一个元素的值,a的首地址的值。 如要将二维数组赋给一指针数组: int *p[3]; int a[3][4]; for(i=0;i<3;i++) p[i]=a[i]; 这里int *p[3] 表示一个一维数组内存放着三个指针变量,分别是p[0]、p[1]、p[2] 所以要分别赋值。 这样两者的区别就豁然开朗了,数组指针只是一个指针变量,似乎是C语言里专门用来指向二维数组的,它占有内存中一个指针的存储空间。指针数组是多个指针变量,以数组形式存在内存当中,占有多个指针的存储空间。 还需要说明的一点就是,同时用来指向二维数组时,其引用和用数组名引用都是一样的。 比如要表示数组中i行j列一个元素: *(p[i]+j)、*(*(p+i)+j)、(*(p+i))[j]、p[i][j] 就指向指针指针,很早以前在说指针的时候说过,但后来发现很多人还是比较难以理解,这一次我们再次仔细说一说指向指针指针。  先看下面的代码,注意看代码中的注解: #include <iostream>  #include <string>  using namespace std;    void print_char(char* array[],int len);//函数原形声明    void main(void)    {  //-----------------------------段1-----------------------------------------      char *a[]={"abc","cde","fgh"};//字符指针数组      char* *b=a;//定义一个指向指针指针,并赋予指针数组首地址所指向的第一个字符串的地址也就是abc\0字符串的首地址      cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)<<endl;  //-------------------------------------------------------------------------    //-----------------------------段2-----------------------------------------      char* test[]={"abc","cde","fgh"};//注意这里是引号,表示是字符串,以后的地址每加1就是加4位(在32位系统上)      int num=sizeof(test)/sizeof(char*);//计算字符串个数      print_char(test,num);      cin.get();  //-------------------------------------------------------------------------  }    void print_char(char* array[],int len)//当调用的时候传递进来的不是数组,而是字符指针他每加1也就是加上sizeof(char*)的长度  {      for(int i=0;i<len;i++)      {          cout<<*array++<<endl;      }  }   下面我们来仔细说明一下字符指针数组和指向指针指针,段1中的程序是下面的样子: char *a[]={"abc","cde","fgh"};  char* *b=a;  cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)<<endl;   char *a[]定义了一个指针数组,注意不是char[], char[]是不能同时初始化为三个字符的,定义以后的a[]其实内部有三个内存位置,分别存储了abc\0,cde\0,fgh\0,三个字符串的起始地址,而这三个位置的内存地址却不是这三个字符串的起始地址,在这个例子中a[]是存储在栈空间内的,而三个字符串却是存储在静态内存空间内的const区域中的,接下去我们看到了char* *b=a;这里是定义了一个指向指针指针,如果你写成char *b=a;那么是错误的,因为编译器会返回一个无法将char* *[3]转换给char *的错误,b=a的赋值,实际上是把a的首地址赋给了b,由于b是一个指向指针指针,程序的输出cout<<*b<<"|"<<*(b+1)<<"|"<<*(b+2)<<endl;   结果是 abc cde fgh   可以看出每一次内存地址的+1操作事实上是一次加sizeof(char*)的操作,我们在32位的系统中sizeof(char*)的长度是4,所以每加1也就是+4,实际上是*a[]内部三个位置的+1,所以*(b+1)的结果自然就是cde了,我们这时候可能会问,为什么输出是cde而不是c一个呢?答案是这样的,在c++中,输出字符指针就是输出字符串,程序会自动在遇到\0后停止.   我们最后分析一下段2中的代码,段2中我们调用了print_array()这个函数,这个函数中形式参数是char *array[]和代码中的char *test[]一样,同为字符指针,当你把参数传递过来的时候,事实上不是把数组内容传递过来,test的首地址传递了进来,由于array是指针,所以在内存中它在栈区,具有变量一样的性质,可以为左值,所以我们输出写成了,cout<<*array++<<endl;当然我们也可以改写为cout<<array[i]<<endl,这里在循环中的每次加1操作和段1代码总的道理是一样的,注意看下面的图!   到这里这两个非常重要的知识点我们都说完了,说归说,要想透彻理解希望读者多动手,多观察,熟能生巧。   下面是内存结构示意图:

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值