第十章 数组和指针
本章介绍以下内容:
关键字:static
运算符:&、*(一元)
如何创建并初始化数组
指针(在已学过的基础上)、指针和数组的关系
编写处理数组的函数
二维数组
人们通常借助计算机完成统计每月的支出、日降雨量、季度销售额等任务。企业借助计算机管理薪资、库存和客户交易记录等。作为程序员,不可避免地要处理大量相关数据。通常,数组能高效便捷地处理这种数据。第 6 章简单地介绍了数组,本章将进一步地学习如何使用数组,着重分析如何编写处理数组的函数。这种函数把模块化编程的优势应用到数组。通过本章的学习,你将明白数组和指针关系密切。
10.1 数组
数组由数据类型相同的一系列元素组成
方括号中的数字表明数组中的元素个数。
要访问数组中的元素,通过使用数组下标数(也称为索引)表示数组中的各元素。数组元素的编号从0开始,所以candy[0]表示candy数组的第1个元素
10.1.1 初始化数组
如上所示,用以逗号分隔的值列表(用花括号括起来)来初始化数组,各值之间用逗号分隔。在逗号和值之间可以使用空格。
使用数组前必须先初始化它。与普通变量类似,在使用数组元素之前,必须先给它们赋初值。编译器使用的值是内存相应位置上的现有值,
当初始化列表中的值少于数组元素个数时,编译器会把剩余的元素都初始化为0。也就是说,如果不初始化数组,数组元素和未初始化的普通变量一样,其中储存的都是垃圾值;但是,如果部分初始化数组,剩余的元素就会被初始化为0。
如果初始化数组时省略方括号中的数字,编译器会根据初始化列表中的项数来确定数组的大小。
整个数组的大小除以单个元素的大小就是数组元素的个数
10.1.2 指定初始化器(C99)
而C99规定,可以在初始化列表中使用带方括号的下标指明待初始化的元素:
int arr[6] = {[5] = 212}; // 把arr[5]初始化为212
对于一般的初始化,在初始化一个元素后,未初始化的元素都会被设置为0。
第一,如果指定初始化器后面有更多的值,如该例中的初始化列表中的片段:[4] = 31,30,31,那么后面这些值将被用于初始化指定元素后面的元素。也就是说,在days[4]被初始化为31后,days[5]和days[6]将分别被初始化为30和31。第二,如果再次初始化指定的元素,那么最后的初始化将会取代之前的初始化。
编译器会把数组的大小设置为足够装得下初始化的值。
10.1.3 给数组元素赋值
声明数组后,可以借助数组下标(或索引)给数组元素赋值。
C 不允许把数组作为一个单元赋给另一个数组,除初始化以外也不允许使用花括号列表的形式赋值。
10.1.4 数组边界
10.1.5 指定数组的大小
在C99标准之前,声明数组时只能在方括号中使用整型常量表达式。所谓整型常量表达式,是由整型常量构成的表达式。sizeof表达式被视为整型常量,但是(与C++不同)const值不是。另外,表达式的值必须大于0:
10.2 多维数组
在计算机内部,这样的数组是按顺序储存的,从第1个内含12个元素的数组开始,然后是第2个内含12个元素的数组,以此类推。
10.2.1 初始化二维数组
如果第1个列表中只有10个数,则只会初始化数组第1行的前10个元素,而最后两个元素将被默认初始化为0。如果某列表中的数值个数超出了数组每行的元素个数,则会出错,但是这并不会影响其他行的初始化。
初始化时也可省略内部的花括号,只保留最外面的一对花括号。只要保证初始化的数值个数正确,初始化的效果与上面相同。但是如果初始化的数值不够,则按照先后顺序逐行初始化,直到用完所有的值。后面没有值初始化的元素被统一初始化为0。
10.2.2 其他多维数组
通常,处理三维数组要使用3重嵌套循环,处理四维数组要使用4重嵌套循环。对于其他多维数组,以此类推。
10.3 指针和数组
数组名是数组首元素的地址。也就是说,如果flizny是一个数组,下面的语句成立:
flizny == &flizny[0]; // 数组名是该数组首元素的地址
flizny 和&flizny[0]都表示数组首元素的内存地址(&是地址运算符)。两者都是常量,在程序的运行过程中,不会改变。但是,可以把它们赋值给指针变量,然后可以修改指针变量的值,
我们的系统中,地址按字节编址,short类型占用2字节,double类型占用8字节。在C中,指针加1指的是增加一个存储单元。对数组而言,这意味着把加1后的地址是下一个元素的地址,而不是下一个字节的地址(见图10.3)。这是为什么必须声明指针所指向对象类型的原因之一。只知道地址不够,因为计算机要知道储存对象需要多少字节(即使指针指向的是标量变量,也要知道变量的类型,否则*pt 就无法正确地取回地址上的值)。
在指针前面使用*运算符可以得到该指针所指向对象的值。
指针加1,指针的值递增它所指向类型的大小(以字节为单位)。
也就是说,定义ar[n]的意思是*(ar + n)。可以认为*(ar + n)的意思是“到内存的ar位置,然后移动n个单元,检索储存在那里的值”。
10.4 函数、数组和指针
关于函数的形参,还有一点要注意。只有在函数原型或函数定义头中,才可以用int ar[]代替int * ar:
int sum (int ar[], int n);
int *ar形式和int ar[]形式都表示ar是一个指向int的指针。但是,int ar[]只能用于声明形式参数。第2种形式(int ar[])提醒读者指针ar指向的不仅仅一个int类型值,还是一个int类型数组的元素。
由于函数原型可以省略参数名,所以下面4种原型都是等价的:
int sum(int *ar, int n);
int sum(int *, int);
int sum(int ar[], int n);
int sum(int [], int);
但是,在函数定义中不能省略参数名。下面两种形式的函数定义等价:
int sum(int *ar, int n)
{
// 其他代码已省略
}
int sum(int ar[], int n);
{
//其他代码已省略
}
10.4.1 使用指针形参
指针start开始指向marbles数组的首元素,所以赋值表达式total += *start把首元素(20)加给total。然后,表达式start++递增指针变量start,使其指向数组的下一个元素。因为start是指向int的指针,start递增1相当于其值递增int类型的大小。
而sump()函数则使用第2个指针来结束循环:
while (start < end)
因为while循环的测试条件是一个不相等的关系,所以循环最后处理的一个元素是end所指向位置的前一个元素。这意味着end指向的位置实际上在数组最后一个元素的后面
10.4.2 指针表示法和数组表示法
使用数组表示法,让函数是处理数组的这一意图更加明显。
但是,只有当ar是指针变量时,才能使用ar++这样的表达式。
指针表示法(尤其与递增运算符一起使用时)更接近机器语言,因此一些编译器在编译时能生成效率更高的代码。然而,许多程序员认为他们的主要任务是确保代码正确、逻辑清晰,而代码优化应该留给编译器去做。
10.5 指针操作
如果编译器不支持%p 转换说明,可以用%u 或%lu 代替%p;如果编译器不支持用%td转换说明打印地址的差值,可以用%d或%ld来代替。
下面分别描述了指针变量的基本操作。
赋值:可以把地址赋给指针。例如,用数组名、带地址运算符(&)的变量名、另一个指针进行赋值。在该例中,把urn数组的首地址赋给了ptr1,该地址的编号恰好是0x7fff5fbff8d0。变量ptr2获得数组urn的第3个元素(urn[2])的地址。注意,地址应该和指针类型兼容。也就是说,不能把double类型的地址赋给指向int的指针,至少要避免不明智的类型转换。C99/C11已经强制不允许这样做。
解引用:*运算符给出指针指向地址上储存的值。因此,*ptr1的初值是100,该值储存在编号为0x7fff5fbff8d0的地址上。
取址:和所有变量一样,指针变量也有自己的地址和值。对指针而言,&运算符给出指针本身的地址。本例中,ptr1 储存在内存编号为 0x7fff5fbff8c8 的地址上,该存储单元储存的内容是0x7fff5fbff8d0,即urn的地址。因此&ptr1是指向ptr1的指针,而ptr1是指向utn[0]的指针。
指针与整数相加:可以使用+运算符把指针与整数相加,或整数与指针相加。无论哪种情况,整数都会和指针所指向类型的大小(以字节为单位)相乘,然后把结果与初始地址相加。因此ptr1 +4与&urn[4]等价。如果相加的结果超出了初始指针指向的数组范围,计算结果则是未定义的。除非正好超过数组末尾第一个位置,C保证该指针有效。
递增指针:递增指向数组元素的指针可以让该指针移动至数组的下一个元素。因此,ptr1++相当于把ptr1的值加上4(我们的系统中int为4字节),ptr1指向urn[1](见图10.4,该图中使用了简化的地址)。现在ptr1的值是0x7fff5fbff8d4(数组的下一个元素的地址),*ptr的值为200(即urn[1]的值)。注意,ptr1本身的地址仍是 0x7fff5fbff8c8。毕竟,变量不会因为值发生变化就移动位置。
图10.4 递增指向int的指针
指针减去一个整数:可以使用-运算符从一个指针中减去一个整数。指针必须是第1个运算对象,整数是第 2 个运算对象。该整数将乘以指针指向类型的大小(以字节为单位),然后用初始地址减去乘积。所以ptr3 - 2与&urn[2]等价,因为ptr3指向的是&arn[4]。如果相减的结果超出了初始指针所指向数组的范围,计算结果则是未定义的。除非正好超过数组末尾第一个位置,C保证该指针有效。
递减指针:当然,除了递增指针还可以递减指针。在本例中,递减ptr3使其指向数组的第2个元素而不是第3个元素。前缀或后缀的递增和递减运算符都可以使用。注意,在重置ptr1和ptr2前,它们都指向相同的元素urn[1]。
指针求差:可以计算两个指针的差值。通常,求差的两个指针分别指向同一个数组的不同元素,通过计算求出两元素之间的距离。差值的单位与数组类型的单位相同。例如,程序清单10.13的输出中,ptr2 - ptr1得2,意思是这两个指针所指向的两个元素相隔两个int,而不是2字节。只要两个指针都指向相同的数组(或者其中一个指针指向数组后面的第 1 个地址),C 都能保证相减运算有效。如果指向两个不同数组的指针进行求差运算可能会得出一个值,或者导致运行时错误。
比较:使用关系运算符可以比较两个指针的值,前提是两个指针都指向相同类型的对象。
注意,这里的减法有两种。可以用一个指针减去另一个指针得到一个整数,或者用一个指针减去一个整数得到另一个指针。
在递增或递减指针时还要注意一些问题。编译器不会检查指针是否仍指向数组元素。C 只能保证指向数组任意元素的指针和指向数组后面第 1 个位置的指针有效。但是,如果递增或递减一个指针后超出了这个范围,则是未定义的。另外,可以解引用指向数组任意元素的指针。但是,即使指针指向数组后面一个位置是有效的,也能解引用这样的越界指针。
10.6 保护数组中的数据
10.6.1 对形式参数使用const
如果函数的意图不是修改数组中的数据内容,那么在函数原型和函数定义中声明形式参数时应使用关键字const。例如,sum()函数的原型和定义如下:
int sum(const int ar[], int n); /* 函数原型 */
int sum(const int ar[], int n) /* 函数定义 */
{
int i;
int total = 0;
for( i = 0; i < n; i++)
total += ar[i];
return total;
}
以上代码中的const告诉编译器,该函数不能修改ar指向的数组中的内容。如果在函数中不小心使用类似ar[i]++的表达式,编译器会捕获这个错误,并生成一条错误信息。
一般而言,如果编写的函数需要修改数组,在声明数组形参时则不使用const;如果编写的函数不用修改数组,那么在声明数组形参时最好使用const。
10.6.2 const的其他内容
如果程序稍后尝试改变数组元素的值,编译器将生成一个编译期错误消息:
无论是使用指针表示法还是数组表示法,都不允许使用pd修改它所指向数据的值。但是要注意,因为rates并未被声明为const,所以仍然可以通过rates修改元素的值。另外,可以让pd指向别处:
关于指针赋值和const需要注意一些规则。首先,把const数据或非const数据的地址初始化为指向const的指针或为其赋值是合法的
然而,只能把非const数据的地址赋给普通指针
const还有其他的用法。例如,可以声明并初始化一个不能指向别处的指针,关键是const的位置:
double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
double * const pc = rates; // pc指向数组的开始
pc = &rates[2]; // 不允许,因为该指针不能指向别处
*pc = 92.99; // 没问题 -- 更改rates[0]的值
可以用这种指针修改它所指向的值,但是它只能指向初始化时设置的地址。
最后,在创建指针时还可以使用const两次,该指针既不能更改它所指向的地址,也不能修改指向地址上的值:double rates[5] = {88.99, 100.12, 59.45, 183.11, 340.5};
const double * const pc = rates;
pc = &rates[2]; //不允许
*pc = 92.99; //不允许
10.7 指针和多维数组
因为zippo[0]是该数组首元素(zippo[0][0])的地址,所以*(zippo[0])表示储存在zippo[0][0]上的值(即一个int类型的值)。与此类似,*zippo代表该数组首元素(zippo[0])的值,但是zippo[0]本身是一个int类型值的地址。该值的地址是&zippo[0][0],所以*zippo就是&zippo[0][0]。对两个表达式应用解引用运算符表明,**zippo与*&zippo[0][0]等价,这相当于zippo[0][0],即一个int类型的值。简而言之,zippo是地址的地址,必须解引用两次才能获得原始值。地址的地址或指针的指针是就是双重间接(double indirection)的例子
要特别注意,与 zippo[2][1]等价的指针表示法是*(*(zippo+2) + 1)。看上去比较复杂,应最好能理解。下面列出了理解该表达式的思路:
图10.5 数组的数组
10.7.1 指向多维数组的指针
int (* pz)[2]; // pz指向一个内含两个int类型值的数组
int * pax[2]; // pax是一个内含两个指针元素的数组,每个元素都指向int的指针
系统不同,输出的地址可能不同,但是地址之间的关系相同。如前所述,虽然pz是一个指针,不是数组名,但是也可以使用 pz[2][1]这样的写法。可以用数组表示法或指针表示法来表示一个数组元素,既可以使用数组名,也可以使用指针名:
zippo[m][n] == *(*(zippo + m) + n)
pz[m][n] == *(*(pz + m) + n)
10.7.2 指针的兼容性
指针之间的赋值比数值类型之间的赋值要严格。例如,不用类型转换就可以把 int 类型的值赋给double类型的变量,但是两个类型的指针不能这样做
10.7.3 函数和多维数组
一种方法是,利用for循环把处理一维数组的函数应用到二维数组的每一行。
可以这样声明函数的形参:
void somefunction( int (* pt)[4] );
另外,如果当且仅当pt是一个函数的形式参数时,可以这样声明:
void somefunction( int pt[][4] );
注意,下面的声明不正确:
int sum2(int ar[][], int rows); // 错误的声明
int sum2(int ar[][4], int rows); // 有效声明
int sum2(int ar[3][4], int rows); // 有效声明,但是3将被忽略
int sum2(arr3x4 ar, int rows); // 与下面的声明相同
int sum2(int ar[3][4], int rows); // 与下面的声明相同
int sum2(int ar[][4], int rows); // 标准形式
一般而言,声明一个指向N维数组的指针时,只能省略最左边方括号中的值:
int sum4d(int ar[][12][20][30], int rows);
10.8 变长数组(VLA)
鉴于此,C99新增了变长数组(variable-length array,VLA),允许使用变量表示数组的维度。如下所示:
int quarters = 4;
int regions = 5;
double sales[regions][quarters]; // 一个变长数组(VLA)
前面提到过,变长数组有一些限制。变长数组必须是自动存储类别,这意味着无论在函数中声明还是作为函数形参声明,都不能使用static或extern存储类别说明符(第12章介绍)。而且,不能在声明中初始化它们。最终,C11把变长数组作为一个可选特性,而不是必须强制实现的特性。
注意前两个形参(rows和cols)用作第3个形参二维数组ar的两个维度。因为ar的声明要使用rows和cols,所以在形参列表中必须在声明ar之前先声明这两个形参。因此,下面的原型是错误的:
int sum2d(int ar[rows][cols], int rows, int cols); // 无效的顺序
C99/C11标准规定,可以省略原型中的形参名,但是在这种情况下,必须用星号来代替省略的维度:
int sum2d(int, int, int ar[*][*]); // ar是一个变长数组(VLA),省略了维度形参名
需要注意的是,在函数定义的形参列表中声明的变长数组并未实际创建数组。和传统的语法类似,变长数组名实际上是一个指针。这说明带变长数组形参的函数实际上是在原始数组中处理数组,因此可以修改传入的数组。
C90标准不允许(也可能允许)。数组的大小必须是给定的整型常量表达式,可以是整型常量组合,如20、sizeof表达式或其他不是const的内容。由于C实现可以扩大整型常量表达式的范围,所以可能会允许使用const,但是这种代码可能无法移植。
C99/C11 标准允许在声明变长数组时使用 const 变量。所以该数组的定义必须是声明在块中的自动存储类别数组。
变长数组还允许动态内存分配,这说明可以在程序运行时指定数组的大小。普通 C数组都是静态内存分配,即在编译时确定数组的大小。由于数组大小是常量,所以编译器在编译时就知道了。第12章将详细介绍动态内存分配
10.9 复合字面量
字面量是除符号常量外的常量。例如,5是int类型字面量, 81.3是double类型的字面量,'Y'是char类型的字面量,"elephant"是字符串字面量。
下面的复合字面量创建了一个和diva数组相同的匿名数组,也有两个int类型的值:
(int [2]){10, 20} // 复合字面量
注意,去掉声明中的数组名,留下的int [2]即是复合字面量的类型名。
初始化有数组名的数组时可以省略数组大小,复合字面量也可以省略大小,编译器会自动计算数组当前的元素个数:
(int []){50, 20, 90} // 内含3个元素的复合字面量
因为复合字面量是匿名的,所以不能先创建然后再使用它,必须在创建的同时使用它。使用指针记录地址就是一种用法。也就是说,可以这样用:
int * pt1;
pt1 = (int [2]) {10, 20};
与有数组名的数组类似,复合字面量的类型名也代表首元素的地址,所以可以把它赋给指向int的指针。然后便可使用这个指针。例如,本例中*pt1是10,pt1[1]是20。
还可以把复合字面量作为实际参数传递给带有匹配形式参数的函数
记住,复合字面量是提供只临时需要的值的一种手段。复合字面量具有块作用域(第12章将介绍相关内容),这意味着一旦离开定义复合字面量的块,程序将无法保证该字面量是否存在。也就是说,复合字面量的定义在最内层的花括号中。
10.10 关键概念
10.11 本章小结
数组是一组数据类型相同的元素。数组元素按顺序储存在内存中,通过整数下标(或索引)可以访问各元素。在C中,数组首元素的下标是0,所以对于内含n个元素的数组,其最后一个元素的下标是n-1。作为程序员,要确保使用有效的数组下标,因为编译器和运行的程序都不会检查下标的有效性。
声明一个简单的一维数组形式如下:
type name [ size ];
这里,type是数组中每个元素的数据类型,name是数组名,size是数组元素的个数。对于传统的C数组,要求size是整型常量表达式。但是C99/C11允许使用整型非常量表达式。这种情况下的数组被称为变长数组。
C把数组名解释为该数组首元素的地址。换言之,数组名与指向该数组首元素的指针等价。概括地说,数组和指针的关系十分密切。如果ar是一个数组,那么表达式ar[i]和*(ar+i)等价。
对于 C 语言而言,不能把整个数组作为参数传递给函数,但是可以传递数组的地址。然后函数可以使用传入的地址操控原始数组。如果函数没有修改原始数组的意图,应在声明函数的形式参数时使用关键字const。在被调函数中可以使用数组表示法或指针表示法,无论用哪种表示法,实际上使用的都是指针变量。
指针加上一个整数或递增指针,指针的值以所指向对象的大小为单位改变。也就是说,如果pd指向一个数组的8字节double类型值,那么pd加1意味着其值加8,以便它指向该数组的下一个元素。
二维数组即是数组的数组。例如,下面声明了一个二维数组:
double sales[5][12];
该数组名为sales,有5个元素(一维数组),每个元素都是一个内含12个double类型值的数组。第1个一维数组是sales[0],第2个一维数组是sales[1],以此类推,每个元素都是内含12个double类型值的数组。使用第2个下标可以访问这些一维数组中的特定元素。例如,sales[2][5]是slaes[2]的第6个元素,而sales[2]是sales的第3个元素。
C 语言传递多维数组的传统方法是把数组名(即数组的地址)传递给类型匹配的指针形参。声明这样的指针形参要指定所有的数组维度,除了第1个维度。传递的第1个维度通常作为第2个参数。例如,为了处理前面声明的sales数组,函数原型和函数调用如下:
void display(double ar[][12], int rows);
...
display(sales, 5);
变长数组提供第2种语法,把数组维度作为参数传递。在这种情况下,对应函数原型和函数调用如下:
void display(int rows, int cols, double ar[rows][cols]);
...
display(5, 12, sales);
虽然上述讨论中使用的是int类型的数组和double类型的数组,其他类型的数组也是如此。然而,字符串有一些特殊的规则,这是由于其末尾的空字符所致。有了这个空字符,不用传递数组的大小,函数通过检测字符串的末尾也知道在何处停止。我们将在第11章中详细介绍。
相关文章:
第十章 数组和指针
本章介绍以下内容: 关键字:static 运算符:&、*(一元) 如何创建并初始化数组 指针(在已学过的基础上)、指针和数组的关系 编写处理数组的函数 二维数组 人们通常借助计算机完成统计每月的支出…...
JVM系列 运行时数据区
系列文章目录 第一章 运行区实验 文章目录 系列文章目录前言一、堆(Heap)1.1、新生代/Young区1.1.1、Eden区1.1.2、Survival区 1.2、年老代(old区) 二、虚拟机栈(Stack)2.1、栈顶缓存技术2.2、溢出2.3、栈…...
软件测试/测试开发丨突破传统,革新测试:ChatGpt指引下的测试方案编写
点此获取更多相关资料 简介 测试方案是指描述需要被测产品的特性、测试的方法、测试环境的规划、测试工具的设计和选择、测试用例的设计方法、测试代码的设计方案。 我们常常需要根据产品的特性、测试策略等几个方向输出对应的测试方案。在写测试方案的过程中,常…...
JVM-垃圾回收器详解、参数配置
相关概念 并行和并发 并行(Parallel) 指多条垃圾收集线程并行工作,但此时用户线程仍然处于等待状态。 并发(Concurrent) 指用户线程与垃圾收集线程同时执行(但不一定是并行的,可能会交替执行…...
计算机算法分析与设计(1)---求算法时间复杂性(手写例题)
文章目录 一、主定理求解二、递归树求解三、递归树求解含O的递归方程 一、主定理求解 二、递归树求解 三、递归树求解含O的递归方程...
MyBatisPlus 分页查询
首先要定义一个配置类 MybatisConfig 放在 config 类下 他的生效是通过拦截生效的 所以是要写拦截器的 (这段拦截器的配置是固定的 CV 也可以) Configuration public class MybatisConfig{Beanpublic MybatisPlusInterceptor mybatisPlusInterceptor(){// 1.定义MybatisPlu…...
Kafka3.1部署和Topic主题数据生产与消费
文章目录 前言一、Kafka3.1X版本在Windows11主机部署二、Kafk生产Topic主题数据1.kafka生产数据2.JAVA kafka客户端消费数据 总结 前言 本章节主要讲述Kafka3.1X版本在Windows11主机下部署以及JAVA对Kafka应用: 一、Kafka3.1X版本在Windows11主机部署 1.安装JDK配…...
ICIF2023化工展首亮相,宏工科技解决方案助力制造升级
ICIF China 2023中国国际化工展览会于9月4日-6日在上海新国际博览中心举办。宏工科技携化工物料处理一站式解决方案首次亮相,同化工行业全产业链共叙物料处理自动化未来。 宏工科技是一家提供物料处理自动化设备、系统与服务的国家级高新技术企业,业务覆…...
本地部署kubesphere集群
本地部署kubesphere集群 本文采用一主两从结构 1.前置硬件准备 准备最少3台机器,本人分配如下 IP:192.168.58.10 (主) 192.168.58.11 (节点1) 192.168.58.12 (节点2) 系统镜像…...
HNU小学期工训-STC15单片机模型大作业实验报告
STC15单片机模型大作业实验报告 全称:基于STC15单片机与OLED显示模块&PC端演示的多功能声光温振时钟智能手表模型 计科210X 甘晴void 202108010XXX 【请注意:本作业入选优秀范例,直接照抄源码有很大风险】 【建议理解原理之后作改动】 …...
【计算机网络】 TCP协议头相关知识点
文章目录 TCP协议头 TCP协议头 我们来看一下TCP协议头里都有什么东西,研究一下为什么TCP协议是可靠的呢 TCP协议可靠是因为在协议头里带着一些校验的数据 首先是源端口和目的端口,这两个是UDP中也有的,但是UDP中只有这两个,没有…...
深度学习相关VO梳理
相关论文 基于学习的VO 相关: DeepVO Towards End-to-End Visual Odometry with Deep Recurrent Convolutional Neural Networks(ICRA,2017) TartanVO: A Generalizable Learning-based VO(CoRL2021) SimVODIS: Simultaneous Vis…...
SpringMVC---CRUD实现
思路分析 搭建环境逆向生层对应的类(model、mapper.xml、mapper.java)编写业务逻辑层编写web层(控制器)前端页面 一、环境搭建 1.1、导入项目所需依赖(pom.xml) <project xmlns"http://maven.apache.org/POM/4.0.0"…...
vue+elementUI el-select 自定义搜索逻辑(filter-method)
下拉列表的默认搜索是搜索label显示label,我司要求输入id显示label名称 <el-form-item label"部门:"><el-select v-model"form.region1" placeholder"请选择部门" filterable clearable:filter-method"dataFilter&qu…...
数据库——事务
事务是指作为一个整体被执行的一系列操作。在数据库管理系统中,事务是指一组数据库操作(如插入、更新、删除等)的逻辑单元,也就是说事务的本质是把多个操作打包成一个操作,并且它要么完全执行,要么完全不执…...
echarts折线图每段显示不同的颜色
效果图 配置项: zqChartFour: {title: {text: "一天用电量分布",subtext: "纯属虚构",},tooltip: {trigger: "axis",axisPointer: {type: "cross",},},toolbox: {show: true,feature: {saveAsImage: {},},},xAxis: {type:…...
设计模式-单例模式(Singleton)
文章目录 前言一、单例模式的概念二、单例模式的实现三、单例模式的应用场景四、单例模式优缺点优点:缺点:总结 前言 单例模式(Singleton Pattern)是一种创建型设计模式,它确保一个类只有一个实例,并提供一…...
优漫动游 常见的AI视频生成网站的官方网站:
1、Lumen5 Lumen5是一款在线视频制作工具,利用人工智能技术能够迅速将文本、和音乐转换为视频。它可以帮助你把博客文章、社交媒体内容等转化为吸引人的视频,从而提高你的品牌曝光率和社交媒体的参与度。 2.Animoto Animoto是一个视频制作平台&…...
Vue中数据可视化关系图展示与关系图分析
Vue中数据可视化关系图展示与关系图分析 数据可视化是现代Web应用程序的重要组成部分之一,它可以帮助我们以图形的方式呈现和分析复杂的数据关系。Vue.js是一个流行的JavaScript框架,它提供了强大的工具来构建数据可视化应用。本文将介绍如何使用Vue.js…...
【启扬方案】基于启扬安卓屏一体机的医疗手推车解决方案
医疗手推车作为医院基础设施的一部分,被广泛应用于医院内部,包括急诊室、手术室、病房和其他临床部门。伴随着互联网技术的发展和行业的渗透,智慧医疗受到越来越多的青睐,这也使得很多医疗设施得到了改进,医疗手推车也…...
JavaScript实现MD5加密的6种方式
关于MD5: MD5.js是通过前台js加密的方式对用户信息,密码等私密信息进行加密处理的工具,也可称为插件。 在本案例中 可以看到MD5共有6种加密方法: 1, hex_md5(value) 2, b64_md5(value) 3, …...
腾讯云和阿里云2核2G服务器租用价格表对比
2核2G云服务器可以选择阿里云服务器或腾讯云服务器,腾讯云轻量2核2G3M带宽服务器95元一年,阿里云轻量2核2G3M带宽优惠价108元一年,不只是轻量应用服务器,阿里云还可以选择ECS云服务器u1,腾讯云也可以选择CVM标准型S5云…...
抖音无需API开发连接Stable Diffusion,实现自动根据评论区的指令生成图像并返回
抖音用户使用场景: 随着AI绘图的热度不断升高,许多抖音达人通过录制视频介绍不同的AI工具,包括产品背景、使用方法以及价格等,以吸引更多的用户。其中,Stable Diffusion这款产品受到了许多博主达人的青睐。在介绍这款产…...
MySQL(三)
DDL(数据定义语言) 库 /* 创建数据库testone */ create database testone; /* 查询数据库testone */ show databases; /* 选择数据库testone */ use testone; /* 删除数据库testone */ drop database testone; 表 创建表 create table table_name (…...
汽车级肖特基二极管DSS220-Q 200V 2A
DSS220-Q是什么二极管?贵司有生产吗? 肖特基二极管DSS220-Q符合汽车级AEC Q101标准吗? DSS220-Q贴片肖特基二极管参数是什么封装?正向电流和反向电压是多大? DSS220-Q肖特基二极管需要100KK,有现货吗&#…...
maven jetty post 上传长度设置
maven jetty post 上传长度设置 <plugin><groupId>org.eclipse.jetty</groupId><artifactId>jetty-maven-plugin</artifactId><version>9.4.8.v20171121</version><configuration><scanIntervalSeconds>1</scanInter…...
LeetCode 面试题 03.03. 堆盘子
文章目录 一、题目二、C# 题解 一、题目 堆盘子。设想有一堆盘子,堆太高可能会倒下来。因此,在现实生活中,盘子堆到一定高度时,我们就会另外堆一堆盘子。请实现数据结构 SetOfStacks,模拟这种行为。SetOfStacks 应该由…...
Python-函数进阶
函数的多返回值 按照返回值的顺序, 写对应顺序的多个变量接受即可, 变量之间用逗号隔开,支持不同类型的数据return def test_return():return 1, 2, 3x, y, z test_return()print(x) print(y) print(z)函数参数种类 使用方式上的不同&am…...
实操Hadoop大数据高可用集群搭建(hadoop3.1.3+zookeeper3.5.7+hbase3.1.3+kafka2.12)
前言 纯实操,无理论,本文是给公司搭建测试环境时记录的,已经按照这一套搭了四五遍大数据集群了,目前使用还未发现问题。 有问题麻烦指出,万分感谢! PS:Centos7.9、Rocky9.1可用 集群配置 iph…...
如何在 Ubuntu 上安装和使用 Nginx?
ginx(发音为“engine-x”)是一种流行的 Web 服务器软件,以其高性能和可靠性而闻名。它是许多流行网站使用的开源软件,包括 Netflix、GitHub 和 WordPress。Nginx 可以用作 Web 服务器、负载均衡器、反向代理和 HTTP 缓存等。 它以…...
做p2p理财网站/友情链接发布网
字符/意义:对于字符,通常表示按字面意义,指出接着的字符为特殊字符,不作解释。例如:/b/匹配字符’b’,通过在b 前面加一个反斜杠,也就是/b/,则该字符变成特殊字符,表示匹配一个单词的…...
网易邮箱网页版/上海外包seo
这里只写cacti监控lvs的部分,前提是cacti安装完成。服务端(下载2个附件)1.服务端添加模板cacti_data_query_snmp_lvs.xml2.将 snmp-lvs.xml 拷贝到source 目录下客户端(lvs机器-这里针对5和4的32位系统,el5_64位不适用)A 安装 net…...
如何优化网站内容/营销平台有哪些
浪花是如何形成的?如果把浪花拆解开来,它只剩下一个个水分子。如果再把这些水分子聚集起来,在潮汐力的作用下,浪花才能“涌现”出来。涌现,也是智慧诞生的方式。由简单的元素和简单的联接,构成一个足够复杂…...
棋牌游戏在哪做网站/推广方法有哪几种
上次我们并没有实现Excel中的数据与数据库中的数据进行整合,存在即更新,不存在即插入.这次主要介绍几种方法来实现: 1.使用Lookup 2.使用execute SQL task调用存储过程 3.使用script component脚本实现 4.使用MERGE 语句(SQL SERVER 2008) 5.使用…...
购物网站设计流程图/广州百度提升优化
VRP基础 文件系统基础 VRP系统管理...
怎么用vs2017做asp网站/推广app大全
关注菜鸟软件下载『软件名称』:地质勘察CAD(工勘版) 9.0PB3『软件大小』:279.37MB『软件语言』:简体中文『安装时间』:20-25分钟『安装环境』:Win10/Win8/Win7『支持版本』:AutoCAD2010-2014『下载链接/64位…...