深入理解指针2,数组名理解,指针访问数组,冒泡排序,二级指针,指针数组模拟二位数组

本文主要是介绍深入理解指针2,数组名理解,指针访问数组,冒泡排序,二级指针,指针数组模拟二位数组,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

1.数组名理解

  1. 数组名就是首元素(第一个元素)地址,和&arr[0] 意思一样。
  2. 首元素的意思是第一个元素的地址。
  3. 看这样图都是4个字节,他俩一样的
int main()
{int arr[10] = { 0 };int arr2[10] = { 0 };printf("arr = %p\n", arr);printf("arr +1= %p\n", arr + 1);printf("&arr[0] = %p\n", &arr[0]);printf("&arr[0] + 1 = %p\n", &arr[0] + 1);return 0;
}

1.2&arr

  1. 两个特殊情况
    1. sizeof(数组名),sizeof中单独放数组名,这里表示整个数组,计算的是整个数组的大小,单位字节
    2. &数组名,这里的数组名表示整个数组,取出的是整个数组的地址
  2. 注意:整个数组的地址和数组首元素地址是有区别的,虽然地址都是一样的,但是对其进行指针+-运算的时候就能看出差异
  3. 地址的大小取决于平台(看例子)
    1. 32位(x86)平台下为 4个字节
    2. 63位(x64)平台下为 8个字节
int main()
{int arr[4] = { 0 };printf("arr        = %p\n", arr);printf("arr +1    = %p\n", arr + 1);printf("&arr[0]    = %p\n", &arr[0]);printf("&arr[0] + 1= %p\n", &arr[0] + 1);printf("&arr       = %p\n", &arr);printf("&arr +1    = %p\n", &arr + 1);printf("%zd", sizeof(arr));//这里表示整个数组printf("%zd", sizeof(&arr));//计算的是地址的大小,拿到的是地址,4/8个字节return 0;
}

2. 使用指针访问数组

  1. [ ]数组下标访问符号,通过下标访问到对应的元素,但是arr的本质是数组的首元素地址
  2. 因为arr把地址给到了pr,pr也有访问权限了。arr + j 先访问到对应地址,然后 *(arr + j) 对其解引用,就能访问到对应元素
  3. pr[ j ] == *(pr + j) == arr[ j ] == *(arr + j)      *(j + arr)== j[arr]
  4. 我们知道arr[ j ] == *(arr + j)。arr[ j ]在访问的时候,编译器也会 转换成首元素的地址+偏移量求出元素的地址,然后解引用来访问的
  5.  如果还对 * (解引用)和 指针 +- 运算不了解,建议去看看我的这篇文章指针详解(1)

int main()
{int arr[10] = { 0 };int sz = sizeof(arr) / sizeof(arr[0]);int* pr = arr;//arr表示首元素地址for (int i = 0; i < sz; i++){scanf("%d", arr + i);//因为scanf函数需要的是一个地址}for (int j = 0; j < sz; j++){printf("%d ", pr[j]);// arr[j],[]数组下标访问符号,通过下标访问到对应的元素,但是arr的本质是数组的首元素地址return 0;
}

补充:

  1. 数组就是数组,是一块连续的空间(数组的大小和数组元素个数和元素类型都有关系)
  2. 指针(变量),就是指针,是一个变量大小是(4/8个字节),可以使用指针访问数组

3. 一维数组传参的本质

  1. 传参过来是地址,sizeof此时计算的是地址的大小
  2. arr[10],写成arr[],当然也没问题,本来就是用来声明有10个元素的,但是我通过首元素地址就能够访问数组的所有元素了
  3. 数组传参的本质是传递了数组首元素地址,所以形参和实参是同一个数组
//错误写法
void Print(int* arr1)//int arr[10],写成arr[]
{int sz = sizeof(arr1) / sizeof(arr1[0]);//传参过来是地址,sizeof此时计算的是地址的大小了for (int i = 0; i < 10; i++){printf("%d ", *(arr1 + i));}//printf("%d ", arr + 1);
}
//正确写法
void Print2(int* arr, int sz)
{for (int i = 0; i < 10; i++){printf("%d ", *(arr + i));}
}
int main()
{int arr[] = { 1,2,3,4,5,6,7,8,9,10 };Print(arr);//这里不能直接 &arr,拿不到整个数组地址Print2(arr, sz);//正确写法
}

补充

  1. sizeof本身是计算元素大小的,上面方法本质是先计算总数组大小,然后除以一个元素大小得出数组个数
  2. 假如传整个数组地址很尴尬,用起来麻烦,直接在外面算好传过去好很多

4. 冒泡排序

  1. 冒泡排序的核心思想就是:两两相邻的元素进行比较。 建议直接看图更加便于理解
    1. 最后一趟已然有序不用交换
    2. 该个数字交换最后一次时,两个交换都会到对应位置
    3. 每交换完一次,趟数都会逐渐减少

//以升序为例
void BubblSort(int* arr ,int sz)
{for (int i = 0; i < sz - 1; i++){for (int j = 0; j < sz - i - 1;j++){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;}}}
}
int main()
{int arr[6] = { 6,5,4,3,2,1 };int sz = sizeof(arr) / sizeof(arr[0]);BubblSort(arr, sz);//输出for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

4.1稍微优化一下

  1. 因为通过观察可以发现,趟数和交换次数都是固定的,如图上假如经过第一趟后,已经有序,后面无需交换
  2. 此时可以给代码稍微优化下,在趟数循化内弄一个变量flag = 1;
  3. 在交换次数的循环里,如果元素一次都没有交换,说明数字已然有序,则flag == 1;即可跳出趟数循环

//以升序为例
void BubblSort(int* arr ,int sz)
{//趟数for (int i = 0; i < sz - 1; i++){int flag = 1;//假设有序情况//一趟过程交换for (int j = 0; j < sz - i - 1;j++){if (arr[j] > arr[j + 1]){int tmp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tmp;flag = 0;}}if (flag == 1)//如果这一趟有序,后面不用排序了break;}
}
int main()
{int arr[6] = { 6,5,4,3,2,1 };int sz = sizeof(arr) / sizeof(arr[0]);BubblSort(arr, sz);for (int i = 0; i < sz; i++){printf("%d ", arr[i]);}return 0;
}

5. 二级指针

  1. 我们可以发现,对pp解引用(*pp),就是拿到a的地址,和&a一样
  2. 可以看下面的图以便理解
int main()
{int a = 20;int* p = &a;//p是一级指针int** pp = &p;//pp是二级指针printf("%p\n", *pp);printf("%p\n", &a);printf("%d\n", **pp);printf("%d\n", *&a);//先取地址然后对其解引用,相当于脱裤子放屁,结果还是20return 0;
}

6. 指针数组 存放指针的数组

  1. 数组的每个元素是指针类型的时候这个时候就是指针数组。
  2. 指针数组:存放指针的数组
int main()
{int va = 10;int* pva = &va;int* arr[5] = { 0 };//指针数组char* arr2[5] = { 0 };//字符指针数组double* arr3[5] = { 0 };//浮点指针数组arr[0] = pva;//把va的地址放到了数组的第一个元素return 0;
}

  1. 数组名表示首元素的地址,通过首元素的地址可以访问到其他成员
  2. 理解方式1arr 指针+-运算,访问到里面的成员,因为指针数组存储的是每个数组的首元素地址,再通过 首元素 + j,即可访问到对应元素
  3. 理解方式2先通过[ ],下标访问符号访问到对应下标元素,因为存储的是数组,所以也可以通过[ ] 下标访问,访问到对应元素
  4. *(rep1 + j) == rep[ j ]
  5. 补充:其本质都是指针+-运算,计算写成rep1[j]的形式,编译器也会转换成 (rep1 + j)的形式
  6. int main()
    {int rep1[5] = { 1,2,3,4,5 };int rep2[5] = { 2,3,4,5,6 };int rep3[5] = { 3,4,5,6,7 };//数组名表示首元素地址int* arr[3] = { rep1,rep2,rep3};//arr指针数组里存储的是每个数组的首元素地址通过数组的首元素地址可以访问到里面的其他成员for (int i = 0; i < 3; i++){for (int j = 0; j < 5; j++){printf("%d ", *(*(arr + i) + j));//先访问到对应下标然后再访问里面的数组,数组的访问也是[],所以就是这样}printf("\n");}return 0;
    }
  7. 我们只是通过指针数组模拟而已
  8. 模拟二维数组,不是真正的数组,真正的二维数组是连续存放的

总结:

  1. 最后重要的是要多去写写代码,看的懂地,不一定会敲代码的,脑子会了手不会
  2. 所以要把会的输出出来,通过写代码和写博客,讲给别人听

这篇关于深入理解指针2,数组名理解,指针访问数组,冒泡排序,二级指针,指针数组模拟二位数组的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/884441

相关文章

【前端学习】AntV G6-08 深入图形与图形分组、自定义节点、节点动画(下)

【课程链接】 AntV G6:深入图形与图形分组、自定义节点、节点动画(下)_哔哩哔哩_bilibili 本章十吾老师讲解了一个复杂的自定义节点中,应该怎样去计算和绘制图形,如何给一个图形制作不间断的动画,以及在鼠标事件之后产生动画。(有点难,需要好好理解) <!DOCTYPE html><html><head><meta charset="UTF-8"><title>06

认识、理解、分类——acm之搜索

普通搜索方法有两种:1、广度优先搜索;2、深度优先搜索; 更多搜索方法: 3、双向广度优先搜索; 4、启发式搜索(包括A*算法等); 搜索通常会用到的知识点:状态压缩(位压缩,利用hash思想压缩)。

深入探索协同过滤:从原理到推荐模块案例

文章目录 前言一、协同过滤1. 基于用户的协同过滤(UserCF)2. 基于物品的协同过滤(ItemCF)3. 相似度计算方法 二、相似度计算方法1. 欧氏距离2. 皮尔逊相关系数3. 杰卡德相似系数4. 余弦相似度 三、推荐模块案例1.基于文章的协同过滤推荐功能2.基于用户的协同过滤推荐功能 前言     在信息过载的时代,推荐系统成为连接用户与内容的桥梁。本文聚焦于

hdu2241(二分+合并数组)

题意:判断是否存在a+b+c = x,a,b,c分别属于集合A,B,C 如果用暴力会超时,所以这里用到了数组合并,将b,c数组合并成d,d数组存的是b,c数组元素的和,然后对d数组进行二分就可以了 代码如下(附注释): #include<iostream>#include<algorithm>#include<cstring>#include<stack>#include<que

【C++】_list常用方法解析及模拟实现

相信自己的力量,只要对自己始终保持信心,尽自己最大努力去完成任何事,就算事情最终结果是失败了,努力了也不留遗憾。💓💓💓 目录   ✨说在前面 🍋知识点一:什么是list? •🌰1.list的定义 •🌰2.list的基本特性 •🌰3.常用接口介绍 🍋知识点二:list常用接口 •🌰1.默认成员函数 🔥构造函数(⭐) 🔥析构函数 •🌰2.list对象

安卓链接正常显示,ios#符被转义%23导致链接访问404

原因分析: url中含有特殊字符 中文未编码 都有可能导致URL转换失败,所以需要对url编码处理  如下: guard let allowUrl = webUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed) else {return} 后面发现当url中有#号时,会被误伤转义为%23,导致链接无法访问

usaco 1.2 Transformations(模拟)

我的做法就是一个一个情况枚举出来 注意计算公式: ( 变换后的矩阵记为C) 顺时针旋转90°:C[i] [j]=A[n-j-1] [i] (旋转180°和270° 可以多转几个九十度来推) 对称:C[i] [n-j-1]=A[i] [j] 代码有点长 。。。 /*ID: who jayLANG: C++TASK: transform*/#include<

hdu 1166 敌兵布阵(树状数组 or 线段树)

题意是求一个线段的和,在线段上可以进行加减的修改。 树状数组的模板题。 代码: #include <stdio.h>#include <string.h>const int maxn = 50000 + 1;int c[maxn];int n;int lowbit(int x){return x & -x;}void add(int x, int num){while

【生成模型系列(初级)】嵌入(Embedding)方程——自然语言处理的数学灵魂【通俗理解】

【通俗理解】嵌入(Embedding)方程——自然语言处理的数学灵魂 关键词提炼 #嵌入方程 #自然语言处理 #词向量 #机器学习 #神经网络 #向量空间模型 #Siri #Google翻译 #AlexNet 第一节:嵌入方程的类比与核心概念【尽可能通俗】 嵌入方程可以被看作是自然语言处理中的“翻译机”,它将文本中的单词或短语转换成计算机能够理解的数学形式,即向量。 正如翻译机将一种语言

【C++高阶】C++类型转换全攻略:深入理解并高效应用

📝个人主页🌹:Eternity._ ⏩收录专栏⏪:C++ “ 登神长阶 ” 🤡往期回顾🤡:C++ 智能指针 🌹🌹期待您的关注 🌹🌹 ❀C++的类型转换 📒1. C语言中的类型转换📚2. C++强制类型转换⛰️static_cast🌞reinterpret_cast⭐const_cast🍁dynamic_cast 📜3. C++强制类型转换的原因📝