IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> C++知识库 -> 数组与指针 -> 正文阅读

[C++知识库]数组与指针

目录

前言

一、指针和一维数组

二、指针和二维数组

三、数组指针

四、指针数组

六、二级指针

总结


前言

? ? ? ? 指针在C语言中是很重要的一部分,不会指针的不能说自己学过C语言,相信很多朋友都有学过了,现在我们一起进阶一下,学习指针数组,相信经过这次学习,同学们对指针和数组将会有更深刻的理解(当然了,大佬除外)。


一、指针和一维数组

?????? ??????? 数组名就是一个指针常量,可以读其值,而不能改其值。数组的内存存储方式为连续存储,各个成员地址是一个挨一个的,数组名就是这一块内存的首地址。

  • 对于一个数组s,我们不仅可以通过s[i],访问他的成员,我们还可以以指针的方式访问其成员,如*(s+i)
  • 数组名就是一个指针常量,可以直接把其值赋给指针,所以对于一个数组s,一个指针*p,可以p = s;可以p = s+n;此时可以p++,但不能s++,因为s是一个常量。

??????? 通过指针定义字符串有两个方法。

方法一:直接定义

char *p = "hello work";

方法二:借助数组定义

char str[1024];
char *p = str;
scanf("%s",p);

不能这样定义:

char *p;
scanf("%s",p);

解析:指针指向的内存地址必须是确定的,并且访问内存时不能越界。

下面是指针和数组的一些使用实例。

#include <stdio.h>


int main()
{
	int s[5] = {10,20,30,40,50};

    //对比s地址和s[0]地址,看看结果是不是一样的
	printf("s = %p\n",s);  //打印数组首地址
	printf("&s[0] = %p\n",&s[0]);//打印s[0]地址

	//s[1] s[2]
	//通过数组和下标的形式访问数组成员的本质
	//是指针“偏移“取值
	printf("s[0] = %d,*(s+0) = %d\n",s[0],*(s+0));
	printf("s[1] = %d,*(s+1) = %d\n",s[1],*(s+1));
	printf("s[2] = %d,*(s+2) = %d\n",s[2],*(s+2));
	printf("s[3] = %d,*(s+3) = %d\n",s[3],*(s+3));
	printf("s[4] = %d,*(s+4) = %d\n",s[4],*(s+4));

	//将数组的首地址赋值给指针P
	int *p = s;
	//赋值之后,下面的操作是等价的
	//s[3] <==> *(s+3) <==> p[3] <==> *(p+3)
	int i = 0;
	for(i = 0;i < 5;i++){
		printf("%d ",*(p+i)); //通过定义的指针访问数组成员
	}

	//指针是变量,可以被重新赋值
	int *q = s;
	for(i = 0;i < 5;i++){
		printf("%d ",*q);
		q++; //指针区别于数组,指针可以赋值,数组不能
	}
	printf("\n");
	return 0;
}

二、指针和二维数组

?????? 二维数组数组名也是指针,但不能直接给一维指针赋值,因为对于一个二维数组ss+1操作,地址偏移的不是一个数据类型,而是一行的长度乘一个数据类型。正确的赋值方式是指针p = *s或者p=**s,前者是行地址,后者是某个元素地址。

下面是使用例子及细节解析。

#include <stdio.h>

int main(int argc, const char *argv[])
{
    	int str[3][4] = {{1,2,3,4},
    					{5,6,7,8},
    					{9,10,11,12}};
    	
    //二维数组的数组名也是一个指针  是一个 行指针
	//每次加1  相当于 偏移一行  ====== 1 * 列数 * 数据类型
	//是地址偏移  也就是说  *(str+1)  相当于取了 第二行 首元素的地址
	//对二维数组名 取 * 相当于给指针进行降维 降维成一维指针
	//降维之后的地址每次 +1 相当于偏移一个数据类型
	//如果想取到第二行的首元素   *(*(str+1)+0)
	
	//str[0]  <==> *(str+0)
	//str[1]  <==> *(str+1)
	printf("%p  %p\n",*(str+0), str[0]);
	printf("%p  %p\n",*(str+1), str[1]);
	printf("%p  %p\n",*(str+2), str[2]);

	//str[i][j] <==> *(str[i]+j) <==> *(*(str+i)+j)
	//二维数组的遍历
	int i = 0;
	int j = 0;
	for(i = 0; i < 3; i++){
		for(j = 0; j < 4; j++){
			//printf("%d  ",str[i][j]);
			//printf("%d  ",*(str[i]+j));
			printf("%d  ",*(*(str+i)+j));
		}
		printf("\n");
	}

	//二维数组的数组名 也是一个地址常量 是不能 赋值的
	//str++; //错误的
	
	//因为二维数组的数组名是个行指针 每次偏移 是偏移一整行
	//已经超过了基本类型的长度
	//所以不可以用普通的一级指针去保存二维数组的地址
	//	(保存是可以保存,但是不能按照二维数组一样操作)
	//原因是,p+1 时,操作系统会认为你是想偏移一行,但是 int * 指针只能
	//偏移4个字节,所以会有冲突,会报警告
	//并且 二维数组名  str 可以做 **str  ,而指针p 是不可以 **p 的
	int *p = str;//可以保存  但是有警告
	printf("%p\n",p);
	printf("%d\n",*p);
	printf("%d\n",*(p+1));//打印的不是 第二行的首地址 而值 第一行的第二个元素 !!!
	//printf("%d\n",**p);//直接错误
 
    printf("%p\n",str+1);
    printf("%p\n",*(str+1));
    //上面两种写法都是地址,且地址的值是一样的
    //但是表达的含义是不一样的
    //str+1  还是一个行指针(偏移是偏移一行的)
    //*(str+1) 就是一个指针了(偏移只偏移一个数据类型)
	
	return 0;
}

上面代码中这两行是要特别注意的,理解这两个,指针和二维数组的操作就很简单了。printf("%p\n",str+1);
printf("%p\n",*(str+1));

三、数组指针

??????? 上面说过,二维数组是不能直接通过数组名向一维指针赋值的,但是可以直接给数组指针赋值的,数组指针本质上是一个指针,他指向一个数组(一般是二维的)。上面说到的赋值问题,本质上其实是数组和指针的维度问题。

  • 我们可以通过 *& 运算符处理维度问题,*表示降维,&表示升维,但&不能作用于指针。
  • 二维数组不能通过数组名直接给一维指针赋值,主要是因为二维数组,数组名的维度比一维指针维度高,需要用*降维。
  • 同样,一维数组也是不能通过数组名向数组指针赋值的,赋值前需要用&对数组名进行升维操作。

??????? 使用格式:

int (*p)[4] = NULL;
p = str;

或者
int (*p)[4] = str;

注意:

  • ()是必须加的,否则变成指针数组了,[4]表示数组的列宽为4.
  • 数组指针就是一个数组的指针,性质与数组名类型,对于指向一个二维数组a[m][n]的数组指针p,p++表示下一行首地址,指向一维数组的数组指针不允许这么操作,*(p+i)+j表示a[i][j]的地址。

操作实例

#include <stdio.h>

int main()
{
	char str1[4] = "abcd";
	char str2[3] [4] ={{'a','b','c','d'},
									{'e','f','g','h'},
									{'i','j','k','l'}, };
	char *p;
	char (*p2)[4] = NULL;

	//1、str1 和p维度一致,可以直接赋值
	p = str1;

	//2、str2比p高一维,在赋值前需要进行降维操作
	p = *str2;

	//3、p2比str高一维,在赋值前需要进行升维操作
	p2 = &str1;
	//打印str1
	printf("%s\n",*p2);
	//4、str12和p2维度一致,可以直接赋值
	p2 = str2;

	//下面重点来讲一下数组指针
	for(int i = 0;i <3;i++){
		//注意,数组指针打印二维数组字符串时
		//不会因为,某一行结束而停止打印
		//而是一直打印到该字符串结束
		//所以下面代码不可取
		//printf("%s\n",*p2);
		for ( int j = 0; j < 4; j++)
		{
			//遍历二维数组
			printf("%c\n",*(*(p2 + i) + j));		
		}
	}
	return 0;
}

四、指针数组

??????? 指针数组本质上是一个数组,其中的元素都是指针,可以保存数组的地址,也可以是二维数组。

#include <stdio.h>

int main(int argc, const char *argv[])
{
    	//想存储一些字符串 "abcd"  "abcdfr"  "hello" "https://mp.csdn.net/mp_blog/creation/editor/120120753"
    	//存储在二维数组中的字符串行地址可以通过指针数组保存
    	char str[4][1024] = {"abcd","hello","world","https://mp.csdn.net/mp_blog/creation/editor/120120753"};
        char *p[4];//定义一个指针数组,数组中每个元素都是一个 char * 指针
    	int i = 0;
    	for(i = 0; i < 4; i++){
            p[i] = str[i];
	}

    for(i = 0; i < 4; i++){
		printf("%s\n",p[i]);
	}

	//我们可以将字符串 单独存储,并也用指针数组访问
	char s1[] = "hello";
	char s2[] = "world";
	char s3[] = "abcd";
	char s4[] = "https://mp.csdn.net/mp_blog/creation/editor/120120753";

	//printf("sizeof(p) = %ld\n",sizeof(p));//32 =  sizeof(char *) * 4
	p[0] = s1;
	p[1] = s2;
	p[2] = s3;
	p[3] = s4;

	printf("--------------------------\n");
	for(i = 0; i < 4; i++){
		printf("%s\n",p[i]);
	}
	return 0;
}

六、二级指针

??????? 指向指针的指针叫二维指针,一维指针不能指向一维指针,所以以下操作是错误的。

int *p,*q;
p = &q;

??????? 但可以这样操作

int **p,*q,arr[5];
p = &q;

??????? 注意:二级指针也不能这样操作

char str[10];
char **p;
p = &str;

七、练习

/***************************************************************
 * 编码: 自己实现 atoi() 函数的功能
 *   atoi()函数的功能:
 * 				将字符串转换成整型数;
 * 				atoi()会扫描参数nptr字符串,跳过前面的空格字符,直到遇上数字或正负号才开始做转换,
 * 				而再遇到非数字或字符串时('\0')才结束转化,并将结果返回(返回转换后的整型数)。
 * 	要求,运行时命令行直接传参
 *  			./a.out  string
 * *************************************************************/

#include <stdio.h>
#include <stdbool.h>

//注意:const类型给给非const类型赋值会报警告
//而非const类型给const类型赋值则不会
//所以为了迎合const char *argv[],把参数定义为const char *类型
int atoi(const char *p);

int main(int argc,const char *argv[])
{
//	char arr[1024];
	int add;

	//const char *argv[]是一个数组指针,保存的是传入的参数
	add = atoi(argv[1]);
	printf("number = %d\n",add);
	return 0;
}

int atoi(const char *p){
	bool a = false,b = true,c = true,d[2] = {false,false},e = false;
	int i = 0,p1=0;
	while(1){
		if(b){
			if(*p == 43 || *p==45||(*p>47&&*p<58)){
				b = false;
				a = true;
				c = false;
				if(*p == 45) d[1] = true;
			}
			if(c) p++;
		}
		if(a){
			if(*p == 43 || *p==45||(*p>47&&*p<58)){
				if(*p == 45){
					if(d[0]){
						if(d[1]) return -p1;
						else return p1;
					}
					p++;
				}
				if(*p>47&&*p<58){
					p1 = p1*10 + *p - 48;
					p++;
				}
				d[0] = true;
			}
			else{
				if(d[1]) return -p1;
				else return p1;
			}
		}
	}
	return p1;
}

总结

????????数组名就是一个指针常量,可以赋值给指针,但不能修改其地址,不同维度的指针不能互相赋值,但我们可以进行升降维操作,*表示降维,&表示升维,对于数组而言,str2、*str 和&str2 的值是一样的,但维度却是不一样的,使用时要注意。

  C++知识库 最新文章
【C++】友元、嵌套类、异常、RTTI、类型转换
通讯录的思路与实现(C语言)
C++PrimerPlus 第七章 函数-C++的编程模块(
Problem C: 算法9-9~9-12:平衡二叉树的基本
MSVC C++ UTF-8编程
C++进阶 多态原理
简单string类c++实现
我的年度总结
【C语言】以深厚地基筑伟岸高楼-基础篇(六
c语言常见错误合集
上一篇文章      下一篇文章      查看所有文章
加:2021-09-08 10:29:29  更:2021-09-08 10:32:02 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/23 19:41:44-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码