当前位置: 首页 > news >正文

深入理解指针2

深入理解指针2

数组名的理解

数组名就是首元素的地址

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

但是有两种情况除外,

1.sizeof(数组名),sizeof操作符统计的是整个数组的大小,并不是第一个元素的大小, 单位是字节

2.&数组名,它取的整个数组的地址,整个数组的地址其实就是首元素的地址,数组地址+1,跳过的是整个数组

除此之外,你遇到的任何数组名都表示首元素的地址

int arr[]={1,2,34};
printf("%d",sizeof(arr));
//如果是一维数组,打印数组的地址其实就是首元素的地址;
int arr[]={1,3,2};
printf("%p\n",arr);
printf("%p\n",&arr[0]);
printf("%p\n",&arr);//三者打印结果一样


printf("%p\n",arr+1);//int类型的指针+1,跳过4个字节
printf("%p\n",&arr[0]+1);//跳过4个字节
printf("%p\n",&arr+1);//并不是跳过4个字节,跳过整个数组



//如果是二维数组,打印出来的地址是第一行的地址,也就是第一个一维数组的地址,而第一个一维数组的地址也就是首元素的地址
int main()
{

	int stp[2][3] = { {1,2,3},{4,5,6} };
	printf("%p\n", stp);
	printf("%p\n",&stp[0]);
	printf("%p\n",&stp);//三者打印结果一样
    
	printf("%p\n", &stp[0] + 1); 
	printf("%p\n", stp+1);
	printf("%p\n", &stp + 1);

	return 0;

}

指针访问数组

代码示例1
int main()
{
    int arr[10]={0};
    int*p=arr;
    int sz=sizeof(arr)/sizeof(arr[0]);
    int i=0;
    for(i=0;i<sz;i++)
    {
     scanf("%d",p+i);
    
    }
    //循环结束后,p里面存的还是arr,i已经变成10了
    for(i=0;i<sz;i++)
    {
        printf("%d ",arr[i]);
        //printf("%d ",*(p+i));//这样写也是可以的
        
    }
return 0;
}
代码示例2
int main()
{
    int arr[10]={0};
    int*p=arr;
    int sz=sizeof(arr)/sizeof(arr[0]);
    int i=0;
    for(i=0;i<sz;i++)
    {
     scanf("%d",p++);//通过指针加减也就是指针偏移的方式,让指针移动
    
    }
    //循环结束后,指针p指向下标为10的元素的地址了,i已经变成10了
    //p++ 的方式会使 p 的值发生改变,p + i 的方式本身不会改变指针 p 的值,所以这里p要重新赋值
    p=arr;
    for(i=0;i<sz;i++)
    {
        printf("%d ",*(p+i));
   
    }
return 0;
}

为了更透彻的理解指针,再举一个例子

//打印结果都是一样的
int main()
{
    int arr[10]={1,2,3,4,5,6,7,8,9};
    int*p=arr;
    int sz=sizeof(arr)/sizeof(arr[0]);
    int i=0;

    for(i=0;i<sz;i++)
    {
        printf("%d ",arr[i]);
        printf("%d ",*(p+i));
        printf("%d ",*(arr+i));//因为p=arr,p里面存放的就是arr的地址,所以还可以写成下面的样子
        printf("%d ",p[i]);
         printf("%d ",i[arr]);
        arr[i]=*(arr+i)//arr[i]这样的写法只是一种形式,即使这样写,编译器处理的时候也会转成后面的写法,转换成指针偏移的方式
      
        p[i]=*(p+i);//同理
        //我们知道加法是支持交换律的,所以下面的写法也是对的,不管怎么写,编译器最终都会转成指针偏移
        arr[i]=*(arr+i)=*(i+arr)=i[arr];
  
    }
return 0;
}

p+i到底是不是下标为i的地址呢?我们可以验证一下
int main()
{
    int arr[10]={1,2,3,4,5,6,7,8,9};
    int*p=arr;
    int sz=sizeof(arr)/sizeof(arr[0]);
    int i=0;

    for(i=0;i<sz;i++)
    {
      printf("%p========%p\n",p+i,&arr[i]);
    }
return 0;
}

一维数组传参的本质

想一下下面的代码为什么会出错?

void print(int arr[])//形参写成数组的形式,一维数组传参,形参的大小可以省略
{

int i=0;
int sz=sizeof(arr)/sizeof(arr[0]);//其实是得不到数组元素的个数的,统计的其实是指针变量的大小,也就是地址的大小
for(i=0;i<sz;i++)
{
    printf("%d ",arr[i]);
}

}

int main()
{
    
    int arr[]={1,2,3,4,5};
    print(arr);//print函数负责把数组里的元素都打印出来,传给形参的其实是地址
   
     return 0;
    
}

因为数组传参的本质其实是把数组的首元素的地址传给形参,所以形参接收到的其实是地址,也就是指针,所以统计 sizeof (arr)其实是统计的地址大小,如果平台是32位的,那么它的大小就是4个字节,而sizeof(arr[0])的大小是4 ,所以sz=4/4=1,所以打印在屏幕上只打印了1一个数字

//那既然函数内部不能统计数组元素的个数,只能在main函数内部统计数组元素个数我们就在main函数内部计算,然后作为实参传给形参。
//因为实参传给形参的数组名其实是首元素地址,那么用sizeof统计的其实是指针变量的大小

void print(int*arr,int sz)//形参写成指针的形式
{

int i=0;

for(i=0;i<sz;i++)
{
    printf("%d ",arr[i]);
}

}


int main()
{
    int arr[]={1,2,3,4,5};
    int sz=sizeof(arr)/sizeof(arr[0]);
    print(arr,sz);//print函数负责把数组里的元素都打印出来,传给形参的其实是地址
     return 0;
    
}
//我们用下面的代码验证下就知道了,数组名其实就是地址

void print(int arr[],int sz)
{
printf("%d\n",sizeof(arr));
}

void print(int*arr,int sz)
{
printf("%d\n",sizeof(arr));
}



int main()
{
 int arr[]={1,2,3,4,5};
 int sz=sizeof(arr)/sizeof(arr[0]);
 print1(arr,sz);
 print2(arr,sz);
return 0;
}

总结:

数组传参,无论形参是写成数组的形式还是指针的形式,本质上都是一样的,只是形式不同而已。

编译器最终都会转成指针的方式处理。

冒泡排序

方法1
void double_paixu(int * arr,int sz)
{
	int i = 0;
	//排序第一步根据元素个数确定排序的轮数
	for (i=0;i<sz-1;i++)//i的最大取值为3
	{

		//排序第二步,在每一轮排序中确定要比较的元素对数,随着轮数的增加,要排序的元素对数依次递减,所以比较的次数
		//根i有关系,因为变量i代表的是比较的轮数


		int j = 0;//j表示元素的下标,又表示元素比较的对数
		//内层for循环用于比较元素大小和交换,如果是降序,发现前一个元素小于后一个元素,就交换
		for (j = 0; j <sz-1-i ;j++)//
		{
			if (arr[j] < arr[j + 1])//一共有5个元素,但是j最大的取值为3,当j=3时,arr[3]和arr[4]进行比较
			//如果j=4,那就数组越界了,因为arr[4]和arr[5]进行比较,但是数组只有5个元素
			{
				int d = 0;
				d = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = d;
			}
		}
	}
}
int main()
{

	int arr[] = { 12,21,2,45,1};
	int sz = sizeof(arr) / sizeof(arr[0]);
	double_paixu(arr,sz);
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}
//优化版:如果要排序的序列本身是有序的,那么第一轮比较后发现没有交换,就证明是有序的,就不用进入下一轮的比较了
void double_arr(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz - 1; i++)//i的最大值为2
	{
		int j = 0;
		int flag = 1;//假设是有序的
		//如果是降序的话,就是找出最小值,如果a<b就交换
		for (j = 0; j < sz - 1 - i; j++)//j的最大值为2a
		{
			if (arr[j] > arr[j + 1])
			{
				int temp = 0;
				temp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = temp;
				//如果进来了,就说明是无序的
				flag = 0;
			}

		}
		//如果第一轮循环结束后,flag还是等于1,说明是有序的,就不用进入下一轮了
		if (flag == 1)
		{
			break;
		}
	}
	//跳出循环来到这
}
void print(int* arr, int sz)
{
	int i = 0;
	for (i = 0; i < sz; i++)
	{
		printf("%d ", arr[i]);
	}
}
int main()
{
	int arr[] = { 1,2,3,4};
	int sz = sizeof(arr) / sizeof(arr[0]);
	double_arr(arr, sz);
	print(arr, sz);
	return;
}

方法2:用qsort函数的方法











二级指针

指针变量也是变量,既然是变量就要像内存申请一块空间,既然有自己的内存空间,那肯定就有地址,那么指针变量的地址存放在哪里呢?这就是二级指针

1.二级指针变量创建


int a=10;
int*p=&a;//p存放的是a的地址,*本身p是一个指针变量,int表示p指向一个整型数据,int*是变量p的类型,整型指针

int* *pa=&p;//pa存放的是一级指针变量p的地址,*表示pa是一个指针变量,int*表示pa指向一个一级指针地址,int**是变量pa的类型,是一个二级指针

int** *paa=&pa;//paa存放的是二级指针变量pa的地址,*表示paa是一个指针变量,int**表示paa指向一个二级指针地址,int***是变量paa的类型,是一个三级指针


2.二级指针解引用

int main()
{
	int a = 10;
	int* p = &a;
	int* * pa = &p;
	int** * paa = &pa;
    printf("%d\n",*p);//p解引用后,打印a的值
    printf("%d\n",*pa)//pa解引用后,打印一级指针变量p的地址
    printf("%d\n",*paa);//paa解引用后,打印二级指针变量pa的地址
    
    //如果想打印a的值
    printf("%d\n",*p);//对一级指针变量解引用
    printf("%d\n",*(*pa));//对二级指针变量解引用
    printf("%d\n",*(*(*paa)))//对三级指针变量解引用
    
    
	printf("%d\n", **pa);//这样写也是对的,不带括号也是可以的
	printf("%d\n", ***paa);//对三级指针变量接
	return 0;
}

总结:

1.二级指针变量没有什么牛逼的,和一级指针变量一样,都是指针变量,用来存放地址

2.二级指针变量用来存放一级指针变量的地址,同理,三级指针变量用来存放二级指针变量的地

指针数组

数组有很多种类型,我们之前已经学过的有字符数组,整型数组,字符数组是用来存放字符串的,整型数组用来存放整型。

今天我们学习的指针数组,就是用来存放指针的,即地址。指针数组里的每一个元素都是指针。当然指针数组也有很多种类型,有int*,char*, long*, double*等等

//定义一个指针数组,然后把指针数组里每一个指针指向的元素打印出来

int main()
{
    int a=10;
    int b=20;
    int c=40;
    int*arr[]={&a,&b,&c};//arr[]表示数组,int*表示数组里的每一个元素都是int*类型的指针,即都是指向int类型的数据
    
    int i=0;
    for(i=0;i<3;i++)
    {
        
        printf("%d ",*(arr[i]));
        
    }
    
    return 0;
}
//上面的代码没什么问题,但是我们很少这样去用,一般都指针数组模拟二维数组
int main()
{
    int arr1[]={1,2,3,4};
    int arr2[]={5,6,7,8};
    int arr3[]={9,10,11,12};
    int*p[]={arr1,arr2,arr3};
    int i=0;
    //外层循环控制指针数组arr1  arr2  arr3里的每一个元素
    for(i=0;i<3;i++)
    {
        int j=0;
        //内层循环控制指针数组里的每一个指针指向的整型
        for(j=0;j<4;j++)
        {
            printf("%d ",*(p[i]+j));//p[i]是数组p里下标为i的元素,当i=0时,拿到的是arr1,也就是数组名,然后再加j,就是让指针指向arr1数组里下标为j的地址,然后解引用,就拿到arr1数组里的元素
            
           // printf("%d ",p[i][j]);
           // printf("%d ",*(*(p+i)+j));
          //这几种写法都可以
        }
        printf("\n");
      
    }

    return 0;
}

相关文章:

  • 【一条龙教程】用AI DS+创作原创音乐 (配合Midjourney漫画)制作原创MTV
  • vue3:vue3项目安装并引入Element-plus
  • 深入探讨分布式事务解决方案:从二阶段提交到现代模式
  • Github项目管理之 其余分支同步main分支
  • pip太慢了怎么办 换源下载
  • 【Uniapp-Vue3】导入uni-id用户体系
  • Linux中文件目录类指令
  • [杂学笔记]OSI七层模型作用、HTTP协议中的各种方法、HTTP的头部字段、TLS握手、指针与引用的使用场景、零拷贝技术
  • Python 批量横屏转竖屏视频处理工具
  • 一文掌握python中正则表达式的各种使用
  • Zetero导出文献附件和题录到Endnote
  • Imagination 最新的D系列GPU IP 为智能手机和其他电力受限设备上图形和计算工作负载的高效加速设定了新的标准
  • 【视频2 - 4】初识操作系统,Linux,虚拟机
  • git基本用法
  • 基于Qlearning强化学习的2DoF机械臂运动控制系统matlab仿真
  • 集成shardingSphere实现读写分离
  • 谷云科技iPaaS×DeepSeek:构建企业智能集成的核心底座
  • sql server笔记
  • 15KM无线数字图传,双路全高清视频无人机遥控器技术详解
  • C++之string类的模拟实现(超详细)
  • 网站ui界面设计模板/百度网站排名优化
  • 同城网站开发/广州网络推广定制
  • 网站里 动效是用什么做的/免费自己制作网站
  • wordpress默认模板/网络关键词优化方法
  • 制作网站建设的公司/seo关键词快速排名软件
  • 玉溪网站建设制作/如何做好网络营销?