【宝藏系列】一文讲透C语言数组与指针的关系

【宝藏系列】嵌入式 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);

输出结果:

p=0x1004,str=0x1001

可以看到,p 指向 int 型数据,p++ 就相当于 p+sizeof(int),而 str++ 就相当于 str+sizeof(char).

🌸🌸🌸🌷🌷🌷💐💐💐🌷🌷🌷🌸🌸🌸

1️⃣2️⃣关于指针定义的争议


怎么样定义一个指针大家都知道,在编程时通常有两种写法:

int* ptr;
int *ptr;

乍一看,这俩不是一样吗?如果你仔细观察就可以发现其中的不同,第一种定义方法中靠近类型,而第二种靠近变量,看到这里,有些朋友就要说了,你个杠精!这不就是个写法问题吗,至于这么纠结吗!

这还真不仅仅是个写法问题。这两种写法背后代表着不同的逻辑:

  • 第一种写法的背后的逻辑是,将 int* 作为一个整体,将其视为一个类型,即 int*、char*int、char 这些一样,都是一种独立的类型,再用这些类型来定义指针变量,从这个角度来看,指针是比较好理解的,而且看起来更能解释得通。
  • 第二种写法的背后逻辑是,在指针的定义中,* 仅仅是一个标识符,如 int *p,表明 * 后面所接的变量 p 是一个指针变量,指向数据类型为int型。

其实在早期,大家一直都更倾向于通过第一种去理解指针,后来又有第二种看起来比较生涩的理解,为什么会这样呢?我们来看下面的例子:

int* p1,p2;
p2=p1;

我们来编译这个例子,结果是这样:

warning: assignment makes integer from pointer without a cast [-Wint-conversion]

编译信息显示,p2 为普通 int 型变量,而 p1是 int 型指针变量,这明显违背我们的初衷。如果要定义两个指针变量,我们应该这么做:

int *p1,*p2;
p2=p1;

相信到这里,大家能够看出来了,第一种写法背后逻辑的缺陷所在。

所以现在越来越多的专业书籍都推荐第二种写法,毕竟作为一门底层语言,严谨性比易读性要重要。

🌸🌸🌸🌷🌷🌷💐💐💐🌷🌷🌷🌸🌸🌸

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来访问第一个元素,也可以使用 *(buf+N) 来访问第 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",&buf+1);

输出结果:

address of origin buf = de157880
address of changed buf = de157885

我们先定义一个长度为 5 的 bufbuf 中首元素地址为 0xde157880 ,然后再打印 &buf+1 的值,显示为0xde157885,那么问题就来了,为什么明明只是 +1,而地址却加了 5,5 正好是 sizeof(buf)。我们再来看看下面的例子:

char buf[5]={0};
printf("address of changed buf = %x\r\n",(&buf+1)-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] 类型,而 pchar 型二级指针,参数自然不匹配。

即使是参数不匹配,但是编译只是警告,而非报错,我们仍然可以执行它。那么执行这个程序的时候又发生了什么呢?我们根据"指针与数组数据访问的本质区别"小节部分来分析:

  • 首先,p 的地址是在编译时已知的,程序运行时,通过指针 p 的地址得到 p 的值,经过上面的分析,此时 p = &buf[0], 虽然 &buf[0] 是数组指针,但是 p 为 char** 类型,所以&buf[0]被强制转换成char** 型指针。
  • printf 函数中访问 p[0][0],事实上访问 P[0][0] 就先得访问 p[0] , 那么就先找到 *p 的值,那么 *p 的值又是多少呢?答案是 *p=buf[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*COLUMN+y]
printf("buf = %d,%d,%d,%d\r\n",p[COLUMN*0+0],p[COLUMN*0+1],p[COLUMN*1+0],p[COLUMN*1+1]);

如果你看懂了之前博主介绍的内容,理解这一份代码是非常简单的。

文章来源网络,如有侵权,请联系作者删除,谢谢!
来源:https://www.cnblogs.com/downey-blog/p/10469906.html

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/96763.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

内网穿透——使用Windows自带的网站程序建立网站

文章目录 1.前言2.Windows网页设置2.1 Windows IIS功能设置2.2 IIS网页访问测试 3. Cpolar内网穿透3.1 下载安装Cpolar3.2 Cpolar云端设置3.3 Cpolar本地设置 4.公网访问测试5.结语 1.前言 在网上各种教程和介绍中,搭建网页都会借助各种软件的帮助,比如…

java 使用log4j显示到界面和文件 并格式化

1.下载log4j jar包https://dlcdn.apache.org/logging/log4j/2.20.0/apache-log4j-2.20.0-bin.zip 2. 我只要到核心包 ,看需要 sources是源码包,可以看到说明。在IDEA里先加入class jar后,再双击这个class jar包或或右键选Navigate ,Add ,…

Xxl-job安装部署以及SpringBoot集成Xxl-job使用

1、安装Xxl-job: 可以使用docker拉取镜像部署和源码编译两种方式,这里选择源码编译安装。 代码拉取地址: https://github.com/xuxueli/xxl-job/tree/2.1.2 官方开发文档: https://www.xuxueli.com/xxl-job/#%E3%80%8A%E5%88%…

代码随想录算法训练营day38 | 70. 爬楼梯,509. 斐波那契数,746. 使用最小花费爬楼梯

目录 动态规划五部曲: 确定dp数组(dp table)以及下标的含义确定递推公式dp数组如何初始化确定遍历顺序举例推导dp数组 509. 斐波那契数 类型:动态规划 难度:easy 思路: f(n) f&am…

redis十种数据类型及底层原理

概述 Redis 是一个开源的高性能键值数据库,它支持多种数据类型,可以满足不同的业务需求。本文将介绍 Redis 的10种数据类型,分别是 string(字符串) hash(哈希) list(列表&#xf…

[Java优选系列第2弹]SpringMVC入门教程:从零开始搭建一个Web应用程序

想和你们分享我眼里的代码世界🗺️ 优选系列持续更新中💫 一直在等你,你终于来啦💖 绿色代表解释说明 黄色代表重点 红色代表精髓 SpringMVC是一个基于Java的Web框架,它使用了MVC&…

Linux 消息队列的创建与使用

消息队列的创建与使用 进程a发送一条消息,进程b读取消息。 a.c代码: b.c代码: 1.a进程创建向消息队列,并向消息队列中发送消息 运行a程序之前,当前系统中消息队列的数量为0: 运行一次a程序,消…

5、flink任务中可以使用哪些转换算子(Transformation)

1、什么是Flink中的转换算子 在使用 Flink DataStream API 开发流式计算任务时,可以将一个或多个 DataStream 转换成新的 DataStream,在应用程序中可以将多个数据转换算子合并成一个复杂的数据流拓扑图。 2、常用的转换算子 Flink提供了功能各异的转换算…

53.Linux day03 文件查看命令,vi/vim常用命令

今天进行了新的学习。 目录 1.cat a.查看单个文件的内容: b.查看多个文件的内容: c.将多个文件的内容连接并输出到一个新文件: d.显示带有行号的文件内容: 2.more 3.less 4.head 5.tail 6.命令模式 7.插入模式 8.图…

H3C交换机如何配置本地端口镜像并在PC上使用Wireshake抓包

环境: H3C S6520-26Q-SI version 7.1.070, Release 6326 Win 10 专业版 Wireshake Version 4.0.3 问题描述: H3C交换机如何配置本地端口镜像并在PC上使用Wireshake抓包 解决方案: 配置交换机本地端口镜像 1.进入系统视图,并创建本地镜像组1 <H3C>system-vie…

Python项目实战:基于napari的3D可视化(点云+slice)

文章目录 一、napari 简介二、napari 安装与更新三、napari【巨巨巨大的一个BUG】四、napari 使用指南4.1、菜单栏&#xff08;File View Plugins Window Help&#xff09;4.2、Window&#xff1a;layer list&#xff08;参数详解&#xff09;4.3、Window&#xff1a;layer…

Linux学习之Telnet明文漏洞

yum install telnet telnet-server xinetd -y安装软件。 systemctl start xinetd.service开启xinetd&#xff0c;systemctl start telnet.socket开启telnet。 xinetd来监控端口&#xff0c;然后把数据传给telnet。 ifconfig eth0看一下eth0网卡信息&#xff0c;。 iptable…

Linux Mint 21.3 计划于 2023 年圣诞节发布

Linux Mint 项目近日公布了基于 Ubuntu 的 Linux Mint 发行版下一个重要版本的一些初步细节&#xff0c;以及备受期待的基于 Debian 的 LMDE 6&#xff08;Linux Mint Debian Edition&#xff09;版本。 近日&#xff0c;Linux Mint 项目负责人克莱门特-勒菲弗&#xff08;Clem…

万字长文·通俗易懂·一篇包掌握——输入/输出·文件操作(c语言超详细系列)(二)

前言&#xff1a;Hello&#xff0c;大家好&#x1f618;&#xff0c;我是心跳sy&#xff0c;上一节我们主要学习了格式化输入输出的基本内容&#xff0c;这一节我们对格式化进行更加深入的了解&#xff0c;对文件概念进行介绍&#xff0c;并且对输入、输出与文件读写的基本概念…

基于 KubeSphere 的应用容器化在智能网联汽车领域的实践

公司简介 某国家级智能网联汽车研究中心成立于 2018 年&#xff0c;是担当产业发展咨询与建议、共性技术研发中心、创新成果转化的国家级创新平台&#xff0c;旨在提高我国在智能网联汽车及相关产业在全球价值链中的地位。 目前着力建设基于大数据与云计算的智能汽车云端运营…

[bug] 记录version `GLIBCXX_3.4.29‘ not found 解决方法

在使用mediapipe 这个库的时候&#xff0c;首次使用出现 GLIBCXX_3.4.29’ not found 错误&#xff0c; 看起来是安装mediapipe 的时候自动升级了 matplotlib 这个库&#xff0c;导致依赖的 libstd.so 版本不满足了&#xff0c;GLIBCXX_3.4.29 is an object from libstdc.so.…

Debian10: 安装nut服务器(UPS)

UPS说明&#xff1a; UPS的作用就不必讲了&#xff0c;我选择是SANTAKTGBOX-850&#xff0c;规格为 850VA/510W&#xff0c;可以满足所需&#xff0c;关键是Debian10自带了驱动可以支持&#xff0c;免去安装驱动&#xff0c;将UPS通过USB线连接服务器即可&#xff0c;如下图所示…

【数理知识】向量与基的内积,Matlab 代码验证

序号内容1【数理知识】向量的坐标基表示法&#xff0c;Matlab 代码验证2【数理知识】向量与基的内积&#xff0c;Matlab 代码验证 文章目录 1. 向量与基的内积2. 二维平面向量举例3. 代码验证Ref 1. 向量与基的内积 假设存在一个二维平面内的向量 a ⃗ \vec{a} a &#xff0c…

QT TLS initialization failed问题(已解决) QT基础入门【网络编程】openssl

问题: qt.network.ssl: QSslSocket::connectToHostEncrypted: TLS initialization failed 这个问题的出现主要是使用了https请求:HTTPS ≈ HTTP + SSL,即有了加密层的HTTP 所以Qt 组件库需要OpenSSL dll 文件支持HTTPS 解决: 1.加入以下两行代码获取QT是否支持opensll以…

【学习FreeRTOS】第8章——FreeRTOS列表和列表项

1.列表和列表项的简介 列表是 FreeRTOS 中的一个数据结构&#xff0c;概念上和链表有点类似&#xff0c;列表被用来跟踪 FreeRTOS中的任务。列表项就是存放在列表中的项目。 列表相当于链表&#xff0c;列表项相当于节点&#xff0c;FreeRTOS 中的列表是一个双向环形链表列表的…