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

网站厨师短期培训班任务平台网站建设

网站厨师短期培训班,任务平台网站建设,福州网站设计大概多少钱,美食网页设计作品【宝藏系列】嵌入式 C 语言代码优化技巧【超详细版】 文章目录 【宝藏系列】嵌入式 C 语言代码优化技巧【超详细版】#x1f468;‍#x1f3eb;前言1️⃣指针1️⃣1️⃣指针的操作1️⃣2️⃣关于指针定义的争议1️⃣3️⃣对教材错误写法的小看法 2️⃣指针和数组的区别2️⃣… 【宝藏系列】嵌入式 C 语言代码优化技巧【超详细版】 文章目录 【宝藏系列】嵌入式 C 语言代码优化技巧【超详细版】‍前言1️⃣指针1️⃣1️⃣指针的操作1️⃣2️⃣关于指针定义的争议1️⃣3️⃣对教材错误写法的小看法 2️⃣指针和数组的区别2️⃣1️⃣数据访问的本质区别 3️⃣数组指针和数组元素指针4️⃣指针数组和二维数组5️⃣二维数组和二级指针 ‍前言 在 C 语言中要说到哪一部分最难搞首当其冲就是指针指针永远是个让人又爱又恨的东西用好了可以事半功倍用不好就会有改不完的 bug 和通不完的宵。但是程序员一般都有一种迷之自信总认为自己是天选之人明知山有虎偏向虎山行直到最后用 C 的人都要被指针虐一遍。 1️⃣指针 首先明确一个概念指针是什么一旦提到这个老生常谈且富有争议性的话题那真是 1000 个人有 1000 种看法。 在国内的很多教材中给出的定义一般就是指针就是地址从初步理解指针的角度来说这种说法是最容易理解的但是这种说法明显有它的缺陷所在。“指针就是地址这种说法相当于指针字面值地址(或者说一个具体的右值)”这种说法的错误所在就是弄错了指针的本质属性指针是变量 试想一下如果指针是地址成立那么二级指针怎么理解呢地址的地址吗这明显是错误的。 下面我们从指针是变量这个原则出发来分析什么是指针 作为一个变量肯定有自己的地址作为一个变量肯定有自己的值和普通变量的区别就是指针变量的值是地址。从第二点延伸过来既然指针变量的值是地址那么那个地址上的内容就是指针变量指向的数据指针的类型就是指针变量指向数据的类型。指针有本身的类型这个本身的类型区别于指向对象的类型。 在这里最容易弄混的就是指针本身的类型和指针的类型指针本身的类型是 int 型一般情况下同一平台上所有类型指针都是一样的长度则是平台相关一般情况下 32 位机中为 4 字节64 位机中为 8 字节事实上指针的大小由处理器中所使用的地址总线宽度决定指针本身的类型有什么意义呢(为什么说一般情况下同一平台上所有类型指针都是一样而不是所有情况呢事实上在某些地址总线宽度与数据总线宽度不同的特殊机器上指针类型可能不一致) 内存的访问是以字节为单位的同时指针的值为一个地址指针的类型就直接决定了指针的所能表示地址的上界和下界32 位指针访问范围为 0~2^32 字节所以是 4GB。注以下讨论中对于指针指向数据的类型统一称为指针的类型这篇博客主要讨论指针的类型而非指针本身的类型 而指针指向数据的类型则是在定义时指定的比如 int *ptr, char *str, 在这里ptr 指针的数据类型就是 int 型而str指针指向的类型是 char 型区分指针指向数据的类型主要是用在对指针解引用时的不同指针的值是具体的某一个位置指向数据的不同则代表解引用的时候所取数据的不同当 ptr 为 int* 类型时表示在ptr表示的地址处取 sizeof(int) 个数据依此类推。 指针的地址如果一个指针变量存储的值是另一个指针的地址那这个指针就是二级指针同样的定义可以递推到多级指针。 1️⃣1️⃣指针的操作 解引用用 * 来获取指针指向的数据这个不用多说。指针的运算加减运算需要注意的是指针的加减运算的粒度是基于指针类型的长度在下例中 int *p (int*)0x1000; char *str (char*)0x1000; p; str; print(p%d,str%d\r\n,p,str);输出结果 p0x1004,str0x1001可以看到p 指向 int 型数据p 就相当于 psizeof(int)而 str 就相当于 strsizeof(char). 1️⃣2️⃣关于指针定义的争议 怎么样定义一个指针大家都知道在编程时通常有两种写法 int* ptr; int *ptr;乍一看这俩不是一样吗如果你仔细观察就可以发现其中的不同第一种定义方法中靠近类型而第二种靠近变量看到这里有些朋友就要说了你个杠精这不就是个写法问题吗至于这么纠结吗 这还真不仅仅是个写法问题。这两种写法背后代表着不同的逻辑 第一种写法的背后的逻辑是将 int* 作为一个整体将其视为一个类型即 int*、char* 与 int、char 这些一样都是一种独立的类型再用这些类型来定义指针变量从这个角度来看指针是比较好理解的而且看起来更能解释得通。第二种写法的背后逻辑是在指针的定义中* 仅仅是一个标识符如 int *p表明 * 后面所接的变量 p 是一个指针变量指向数据类型为int型。 其实在早期大家一直都更倾向于通过第一种去理解指针后来又有第二种看起来比较生涩的理解为什么会这样呢我们来看下面的例子 int* p1,p2; p2p1;我们来编译这个例子结果是这样 warning: assignment makes integer from pointer without a cast [-Wint-conversion]编译信息显示p2 为普通 int 型变量而 p1是 int 型指针变量这明显违背我们的初衷。如果要定义两个指针变量我们应该这么做 int *p1,*p2; p2p1;相信到这里大家能够看出来了第一种写法背后逻辑的缺陷所在。 所以现在越来越多的专业书籍都推荐第二种写法毕竟作为一门底层语言严谨性比易读性要重要。 1️⃣3️⃣对教材错误写法的小看法 说实话博主学习 C 语言也是从国内教材开始一开始接触到的也是“指针就是地址”的概念其实于我而言这种说法让我快速地理解了指针后来慢慢接触到复杂的逻辑看了一些更好的教材慢慢地才开始有了更深入的理解。 其实博主更倾向于这样去理解这个事情就像小学老师会告诉我们 0 是最小的数这个概念当然是错的但是这种教法正是可以剥去语言的外壳让我们避免陷入繁杂的分支和细节中快速地理解使用和培养兴趣至于后面的进阶自然会有进阶的书籍来纠正就像高中或者大学以至于更高的平台总会告诉你你之前建立的部分概念并不完全正确关键是重新建立这个概念并不会太难因为需要重新建立的时候往往是初级到中级的进阶过程。 至于网络上的一些比较过激的言论我是不抱以支持态度的无论如何在我们没有能力接触国外教材且资源缺乏的时候是这些不完美的教材使我们踏入了计算机的世界。 2️⃣指针和数组的区别 废话说了那么多我们来回到正题看看指针和数组。不得不说指针和数组就像孪生兄弟有时候让人分不清楚这种情况主要发生在函数参数传递的时候当一个函数需要一个数组作为一个参数时我们并不会将整个数组作为参数传递给函数而是传入一个同类型指针p然后在函数中就可以使用p[N]来访问数组中元素(这个大家都懂就不放示例了)。 那么指针和数组到底是不是同一个东西呢我们来看看下面的例子 file1.c:int buf[10]; file2.c:extern int *buf;编译结果 error: conflicting types for ‘buf’。从这里可以看出数组和指针并不相等。至于具体的区别且听我细细道来。 2️⃣1️⃣数据访问的本质区别 毫无疑问我们经常使用指针的数组也经常混用。但是我们有没有关注过它们背后的执行原理呢我们看下面的代码 int buf[10] {5}; int *p buf; *p 10;首先有必要来讲讲数组的初始化在定义时如果我们不对数组进行初始化操作有两种情况 数组为全局变量或者静态变量时在程序加载阶段默认所有元素都被初始化为0。数组为局部变量因为数组数据在栈上分配就延续了栈上上一次的值所以这个值是不确定的。 同时我们可以对其进行初始化可以全部初始化或者部分初始化部分初始化时未被初始化部分全部默认被初始化为 0。所以我们常用 buf[N]{0} 来在定义时初始化一个数组。 根据 C 语言的规定数组名数组首元素指针所以直接可以用数组名的解引用*buf来访问第一个元素也可以使用 *(bufN) 来访问第 N 个元素。 我们需要知道的是在程序编译的时候会对所有的变量分配一个地址这个地址和变量的对应在符号表中被呈现数组和指针在符号表中的区别就体现在这里 对于数组而言符号表中存在的地址为数组首元素地址所以当我们使用数组下标访问元素 N 时它执行的是这样的操作 – 先取出数组首元素地址 – 目标地址首地址sizeof(type)*N得到被访问元素的地址type 是指针指向数据类型指针加法参考上面。 – 解引用(相当于在变量前加 * )从地址上取出被访问元素。对于指针变量而言符号表中存储的是指针变量的地址它访问元素时这样的过程 – 取出指针变量的地址解引用以获取指针变量 – 继续对指针变量进行解引用获取目标元素的值。 看到这里我想你已经知道了指针和数组访问数据的本质区别但是我们在这里需要讨论的情况并非这两种. 而是参数定义为指针但是以数组的方式引用。这个在函数调用时才是发生得最频繁的那这时候会发生什么呢? 这个时候其实就是两种访问方式的结合了假设定义了指针 buf 那么在符号表中存在的就是buf指针的地址(注意是 buf 的地址而且 buf 本身是个指针)参考上述指针的访问方式.以获取 buf中第二个元素为例 首先根据 buf 变量的地址获取 buf 指针。使用第一步中获取的地址进行偏移得到目标数组元素的地址此时目标地址为 (buf[0]2)解引用(相当于在变量前加 * )从地址上取出被访问元素,相当于执行 *(buf[0]2)。 到这里我想你已经大概清楚了数组和指针的区别以及参数传递时指针的下标引用背后的原理。 3️⃣数组指针和数组元素指针 在上一小节中我指出了数组名数组首元素指针的概念如果朋友们不仔细看或者自己不去写代码尝试很容易把它记成了数组名数组的指针 这个概念请特别注意数组名数组的指针这个概念是完全错误的这也是数组中非常容易混淆和犯错的地方我们不妨来看下面的例子 char buf[5]{0}; printf(address of origin buf %x\r\n,buf); printf(address of changed buf %x\r\n,buf1);输出结果 address of origin buf de157880 address of changed buf de157885我们先定义一个长度为 5 的 bufbuf 中首元素地址为 0xde157880 然后再打印 buf1 的值显示为0xde157885那么问题就来了为什么明明只是 1而地址却加了 55 正好是 sizeof(buf)。我们再来看看下面的例子 char buf[5]{0}; printf(address of changed buf %x\r\n,(buf1)-buf);编译时信息如下 error: invalid operands to binary - (have ‘char (*)[5]’ and ‘char *’)从这个报错信息我们可以看出buf 的类型为 char (*)[5]为数组指针类型而 buf 类型为 char *字符指针类型。 看到这里问题也就慢慢地清晰了。在 C 语言中数组名是一个特殊的存在与我们惯有的思维相反数组名代表数组首元素的指针而不是数组指针如果要声明一个数组指针我们可以这样来声明 char (*p)[5] buf;说了这么多那么区分数组指针和数组元素指针的意义在哪里呢参考上面所说的指针的加减运算即指针的加减运算的粒度是基于指针类型的长度数组指针的长度为 sizeof(数组)而数组元素指针是 sizeof(单个元素)(再啰嗦一次数组名为数组元素指针而不是数组指针)。 4️⃣指针数组和二维数组 数组指针是一个指针类型为数组的指针比如定义一个带有 5 个 char 元素数组的指针 char (*buf)[5]那么指针数组又是什么东西呢其实指针数组要比数组指针容易理解它就是一个普通数组只不过特殊的是数组内所有元素都是指针比如定义一个字符指针数组 char *buf[5]注意它们之间的区别数组指针是一个指针指针数组是一个数组。 二维数组大家可能没有使用过但是一定听过二维数组的定义 char buf[x][y]其中 x 可缺省y 不能缺省。 对于二维数组我们可以这样理解二维数组是一维数组的嵌套即一维数组中所有元素为同类型数组。 例如 char array[3][3]我们可以将其理解成 array 数组是一个一维数组数组的元素分别是array[0] , array[1] , array[2]三个char[3] 型数组这种理解可以递推到多维数组从而来理解二维数组的内存模型。 下面详细说说为什么需要将多维数组看成一维数组。 5️⃣二维数组和二级指针 既然一维数组和指针在一定程度上可以混合使用,那么二维数组肯定也是可以使用二维指针来访问了 —— 某不知名程序员语录 问上面这句话有没有什么问题 答大错特错 很惭愧博主曾经也是这么认为的二维数组肯定是可以像一维数组那样使用指针访问只不过要用二级指针(二维嘛)。 话不多说我们先看下面代码 char buf[2][2]{{1,2},{3,4}}; char **p buf; printf(buf[] %d,%d,%d,%d\r\n,p[0][0],p[0][1],p[1][1],p[1][2]);输出结果 Segmentation fault (core dumped)在这个示例中博主的本意是使用二级指针 p 赋值为二维数组名然后使用 p 访问数组中元素但是结果明显跑偏了这是为什么? 有些朋友可能在学习上面的数组和指针数据访问的本质区别的时候会想我只要会用就行了我要去关注这些底层细节有什么作用在简单的应用中当然没什么作用但是在这种时刻就需要对底层扎实的理解了。 我们来详细分析一下上面代码中的背后访问逻辑 第一点我们需要确认的是二维数组的数组名到底是什么类型的指针。是二维数组中第一个char型元素的指针吗?还是按照上一节指针数组和二维数组中说的那样将二维数组看成一个一维数组从一维数组的角度看首元素为 buf[0](注意 buf[0] 是一个数组)那二维数组名就是一个数组指针类型为 char (*)[2]。要验证这个很简单我们分别编译两份代码 代码1 char buf[2][2]{{1,2},{3,4}}; char *p buf;编译结果 warning: initialization from incompatible pointer type [-Wincompatible-pointer-types]代码2 char buf[2][2]{{1,2},{3,4}}; char (*p)[2] buf;编译结果 无警告信息 所谓实践出真知结果很显然答案是第二种我们应该将二维数组当成嵌套的一维数组而数组名为首元素地址注意这里的首元素是从一维数组的角度出发这个首元素的类型可能是普通变量数组甚至是多维数组。 第二点char **p buf ; 这一条怎么去理解呢根据上面的结论二维数组名 buf 是 char (*)[2] 类型而 p 是 char 型二级指针参数自然不匹配。 即使是参数不匹配但是编译只是警告而非报错我们仍然可以执行它。那么执行这个程序的时候又发生了什么呢我们根据指针与数组数据访问的本质区别小节部分来分析 首先p 的地址是在编译时已知的程序运行时通过指针 p 的地址得到 p 的值经过上面的分析此时 p buf[0], 虽然 buf[0] 是数组指针但是 p 为 char** 类型所以buf[0]被强制转换成char** 型指针。在 printf 函数中访问 p[0][0]事实上访问 P[0][0] 就先得访问 p[0] , 那么就先找到 *p 的值那么 *p 的值又是多少呢答案是 *pbuf[0][0] ,*p 不是一个地址而是一个字面值 1所以此时 p[0] 1 , 访问 *p[0] 自然会导致 Segmentation fault (core dumped)。 鉴于上面的解析部分非常难以理解而且仅仅是字面讲解几乎无法讲清楚博主就尝试通过几个示例来进行讲解 示例1 char buf[2][2]{{1,2},{3,4}}; char **p buf; printf(array name--buf address %x\r\n,buf); printf(buf[0] address %x\r\n,buf[0]); printf(Secondary pointer address %x\r\n,p);输出 array name--buf address a836a2c0 buf[0] address a836a2c0 buf[0][0] address a836a2c0 Secondary pointer address a836a2c0尽管编译过程有好几个 Warning暂时不去理会结果显示至少从数值上来说 p buf buf[0] buf[0][0]示例2 char buf[2][2]{{1,2},{3,4}}; char **p buf; printf(p[0] %x\r\n,p[0]);输出 p[0] 04030201这个结果就非常有意思了可以看到指针 p[0] 的值正好是数组 buf 的四个元素的值(内存中存储顺序将01020304 反序存储这里涉及到大小端的存储问题不过多赘述)。可想而知访问 p[0][0] 的时候会发生什么按照之前的讲解我们先将 p[0] 做相应位移即 p[0]p[0]sizeof(char)*0 , 然后再解引用获取地址上的值那就是直接取0x04030201地址上的值结果当然不会是我们所期待的 再回到示例为什么 p[0] 的值会是 0x04030201? 首先我们要知道p[0] 是什么类型p[0] 即为*p , p是二级指针*p 也是一个指针所以*p的本身的类型为 int*所以它的值为 4 个字节。根据前面的分析p buf buf[0] buf[0][0]对 p 解引用(即 *p )相当于取出 p 地址处的数据根据int*类型取四个字节数据而这四个字节正好就是 buf 中四个元素。 那如果我们要使用指针来访问二维数组中的元素该怎么做呢 看下面的代码 #define ROW 2 #define COLUMN 2 char buf[ROW][COLUMN]{{1,2},{3,4}}; char *p (char*)buf; //访问buf[x][y],即访问p[x*COLUMNy] printf(buf %d,%d,%d,%d\r\n,p[COLUMN*00],p[COLUMN*01],p[COLUMN*10],p[COLUMN*11]);如果你看懂了之前博主介绍的内容理解这一份代码是非常简单的。 文章来源网络如有侵权请联系作者删除谢谢 来源https://www.cnblogs.com/downey-blog/p/10469906.html
http://www.pierceye.com/news/480699/

相关文章:

  • 有效方法的小企业网站建设域名怎么拿来做网站
  • 网站版面如何布局做一个企业的网站怎么做的
  • 天门市城市建设管理局网站大连专业网页设计
  • 百度网站收录提交入口全攻略网站支付体现功能怎么做
  • 网站更改文章标题主流跨境电商平台有哪些
  • vue做直播网站上海最繁华的五个区
  • 做网站和微信小程序电子商务网站建设与管理的背景
  • 做网站国内阿里云虚拟主机多少钱东莞常平中学高中部
  • 用.net做购物网站灵山建设局网站
  • 烟台网站设计制作公司电话python购物网站开发流程
  • 医疗网站怎么做seo怎样通过网址浏览自己做的网站
  • 湖北现代城市建设集团网站wordpress用户头像插件
  • 徐州双语网站制作响应式网站开发pdf
  • 怎么做建设网站公司创建一个网站多少钱
  • 好看的扁平化网站wordpress插件编写
  • 深圳网站设计模板ps可以做网站动态图
  • 微信网站制作入门网站开发实施方案进度
  • 网站用户界面设计国内网站建设最好公司
  • 运城做网站费用高吗高端模板建站
  • 凡客诚品网站设计合肥网红打卡地
  • 淘宝网站代理怎么做的广西送变电建设公司铁塔厂网站
  • 自媒体网站开发网站的推广方式包括
  • 教育做的比较好的网站有哪些网站的建设及维护
  • dw设计做网站案例建设网站杭州
  • 做网站认证对网站有什么好处广西网站建设开发团队
  • 建一个网站需要哪些知识无锡大型互联网公司
  • 餐饮公司 网站建设做网站一年大概的盈利
  • 做金融怎么进基金公司网站免费行情软件网站游戏
  • 网站推广解释创立一个网站要多少钱
  • 绍兴专业网站建设公司大型网站建设哪家好