高质量编程指南-笔记

  • 一、文件结构
    • 1.1 版权和版本的声明
    • 1.2 头文件的结构
    • 1.3 定义(.cpp) 文件的结构
    • 1.4 头文件的作用
    • 1.5 目录结构
  • 二、程序的版式
    • 2.1 长行拆分
    • 2.2类的版式
    • 三、命名规则
  • 四、表达式和基本语句
  • 五、常量
    • 5.1 const常量与#define的比较
    • 5.2 常量定义规则
    • 5.3 类中的常量
  • 六、函数设计
    • 6.1 参数的规则
    • 6.2 返回值的规则
    • 6.2 函数内部实现的规则
    • 6.4 其它建议
    • 6.5 使用断言
    • 6.6 引用与指针的区别
  • 七、内存管理
    • 7.1 指针参数是如何传递内存的?
  • 八、 C++函数的高级特性
    • 8.1 函数重载的概念
    • 8.2 运算符重载
    • 8.3 内联函数
  • 九、类的构造函数、析构函数与赋值函数

一、文件结构

1.1 版权和版本的声明

(1)版权信息。
(2)文件名称,标识符,摘要。
(3)当前版本号,作者/修改者,完成日期。
(4)版本历史信息。

/*1. Copyright (c) 2001,上海贝尔有限公司网络应用事业部2. All rights reserved.3.  4. 文件名称:filename.h4. 文件标识:见配置管理计划书5. 摘 要:简要描述本文件的内容6. 7. 当前版本:1.18. 作 者:输入作者(或修改者)名字9. 完成日期:2001年7月20日10.  12. 取代版本:1.0 11. 原作者 :输入原作者(或修改者)名字12. 完成日期:2001年5月10日
*/

1.2 头文件的结构

规则
1)防止头文件重复引用,用 #ifndef/#define/#endif 预处理
2)用 #include <filename.h> 引用标准库的头文件(编译器将从标准库目录开始搜索)。
3)用 #include “filename.h” 引用非标准库的头文件(编译器将从用户的工作目录开始搜索)。
建议
1)头文件只声明不定义
== C++允许在声明时直接定义,会自动转成内联函数,书写风格不一致,不建议。==
2)尽量不使用全局变量,不在头文件出现extern int value 。

// 版权和版本声明,此处省略。
#ifndef GRAPHICS_H // 防止 graphics.h 被重复引用
#define GRAPHICS_H
#include <math.h> // 引用标准库的头文件
…
#include “myheader.h” // 引用非标准库的头文件
…
void Function1(…); // 全局函数声明
…
class Box // 类结构声明
{…
};
#endif

1.3 定义(.cpp) 文件的结构

定义文件有三部分内容:

(1) 定义文件开头处的版权和版本声明。
(2) 对一些头文件的引用。
(3) 程序的实现体(包括数据和代码)。

// 版权和版本声明,此处省略。
#include “person.h”// 引用头文件
…
// 全局函数的实现体
void Function1(…)
{…
}
// 类成员函数的实现体
void Person::Draw(…)
{…
}

1.4 头文件的作用

(1)通过头文件来调用库功能。

源代码不便(或不准)向用户公布时,只要向用户提供头文件二进制的库即可。用户只需要按照头文件中的接口声明来调用库功能,而不必关心接口怎么实现的。编译器会从库中提取相应的代码。

(2)头文件能加强类型安全检查。

如果某个接口被实现或被使用时,其方式与头文件中的声明不一致,编译器就会指出错误,这一简单的规则能大大减轻程序员调试、改错的负担。

1.5 目录结构

如果项目的头文件比较多(超过10个) 可以将头文件和定义文件分开,例如将头文件放入include文件夹,源文件放入source文件夹.

如果某些头文件是私有的,它不会被用户的程序直接引用,则没有必要公开其“声明”。为了加强信息隐藏,这些私有的头文件可以和定义文件存放于同一个目录。

二、程序的版式

2.1 长行拆分

1)代码行最长控制在 70 ~ 80 个字符以内。
2)长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。

if ((very_longer_variable1 >= very_longer_variable12)
&& (very_longer_variable3 <= very_longer_variable14)
&& (very_longer_variable5 <= very_longer_variable16))
{dosomething();
}
virtual CMatrix CMultiplyMatrix (CMatrix leftMatrix,CMatrix rightMatrix);
for (very_longer_initialization;very_longer_condition;very_longer_update)
{dosomething();
}

2.2类的版式

建议:
将 public 类型的函数写在前面,而将 private 类型的数据写在后面,采用这种版式的程序员主张类的设计“以行为为中心”,重点关注的是类应该提供什么样的接口(或服务)
因为用户最关心的是接口,谁愿意先看到一堆私有数据成员!

三、命名规则

命名规范:

  1. windows:驼峰命名,Linux:下划线;
  2. 用英文别用拼音;
  3. 长度应当符合“min-length && max-information”原则;
  4. 不要出现标识符完全相同的局部变量和全局变量(虽然作用域不同,容易混淆);
  5. 变量的名字应当使用“名词”或者“形容词+名词”;如oldValue;
  6. 全局函数的名字应当使用“动词”或者“动词+名词”(动宾词组)。类的成员函数应当只使用“动词”,被省略掉的名词就是对象本身。
DrawBox(); // 全局函数
box->Draw(); // 类的成员函数
  1. 尽量避免名字中出现数字编号,如 Value1,Value2 等,除非逻辑上的确需要编号;
  2. 类名和函数名用大写字母开头的单词组合而成;
class Node; // 类名
class LeafNode; // 类名
void Draw(void); // 函数名
void SetValue(int value); // 函数名

9.变量和参数用小写字母开头的单词组合而成;

BOOL flag;
int drawMode;

10.常量全用大写的字母,用下划线分割单词;

const int MAX = 100;
const int MAX_LENGTH = 100;

11.如果不得已需要全局变量,则使全局变量加前缀 g_(表示 global)。

int g_howManyPeople; // 全局变量
int g_howMuchMoney; // 全局变

12.类的数据成员加前缀 m_(表示 member),这样可以避免数据成员与
成员函数的参数同名。

void Object::SetValue(int width, int height)
{m_width = width;
m_height = height;
}

13.库函数可能会使用统一标识前缀,如QMap。

四、表达式和基本语句

  1. 记不住优先级,用括号!
  2. 不要编写太复杂的复合表达式。
  3. 不要有多用途的复合表达式;

例如: d = (a = b + c) + r ;
该表达式既求 a 值又求 d 值。应该拆分为两个独立的语句:
a = b + c;
d = a + r;

  1. bool类型判断真假
if (flag) // 表示 flag 为真
if (!flag) // 表示 flag 为假
其它的用法都属于不良风格,例如:
if (flag == TRUE)
if (flag == 1 )
if (flag == FALSE)
if (flag == 0)

5.整型变量与零值比较
应当将整型变量用“==”或“!=”直接与 0 比较。

if (value == 0)
if (value != 0)

不可模仿布尔变量的风格而写成:

if (value) // 会让人误解 value 是布尔变量
if (!value)

6.浮点变量与零值比较
不可将浮点变量用“==” 或 “!=”与任何数字比较。注意精度,应该设法转化成“>=”或“<=”形式。
假设浮点变量的名字为 x,

if (x == 0.0) // 隐含错误的比较
转化为
if ((x>=-EPSINON) && (x<=EPSINON))
其中 EPSINON 是允许的误差(即精度)。

7.指针变量与零值比较
应当将指针变量用“==”或“!=”与 NULL 比较。
指针变量的零值是“空”(记为 NULL)。尽管 NULL 的值与 0 相同,但是两者意义不
同。假设指针变量的名字为 p,它与零值比较的标准 if 语句如下:

if (p == NULL) // p 与 NULL 显式比较,强调 p 是指针变量
if (p != NULL)

不要写成

if (p == 0) // 容易让人误解 p 是整型变量
if (p != 0)

或者

if (p) // 容易让人误解 p 是布尔变量
if (!p)

五、常量

C语言里用#define定义常量,C++除了#define还可以用const。

5.1 const常量与#define的比较

const常量的优点:

(1) const 常量有数据类型,而宏常量没有数据类型。编译器可以对前者进行类型安
全检查。而对后者只进行字符替换,没有类型安全检查,并且在字符替换可能会 产生意料不到的错误(边际效应)。
(2) 有些集成化的调试工具可以对const 常量进行调试,但是不能对宏常量进行调试。

C++可以完全用const取代#define宏。

5.2 常量定义规则

需要对外公开的常量放在头文件中,不需要对外公开的常量放在定义文件的头部。为便于管理,可以把不同模块的常量集中存放在一个公共的头文件中。
如果某一常量与其它常量密切相关,应在定义中包含这种关系,而不应给出一些孤立的值。
例如:

const float RADIUS = 100;
const float DIAMETER = RADIUS * 2;

5.3 类中的常量

有时我们希望某些常量只在类中有效。由于#define 定义的宏常量是全局的,不能达到目的,于是想当然地觉得应该用 const 修饰数据成员来实现。const 数据成员的确是存在的,但其含义却不是我们所期望的。const 数据成员只在某个对象生存期内是常量,而对于整个类而言却是可变的,因为类可以创建多个对象,不同的对象其 const 数据成员的值可以不同。
不能在类声明中初始化 const 数据成员。以下用法是错误的,因为类的对象未被创
建时,编译器不知道 SIZE 的值是什么。

class A
{…
const int SIZE = 100; // 错误,企图在类声明中初始化 const 数据成员
int array[SIZE]; // 错误,未知的 SIZE
};
const 数据成员的初始化只能在类构造函数的初始化表中进行,例如
class A
{…
A(int size); // 构造函数
const int SIZE ;
};
A::A(int size) : SIZE(size) // 构造函数的初始化表
{…
}
A a(100); // 对象 a 的 SIZE 值为 100
A b(200); // 对象 b 的 SIZE 值为 200

怎样才能建立在整个类中都恒定的常量呢?别指望 const 数据成员了,应该用类中
的枚举常量来实现。例如

class A
{…
enum { SIZE1 = 100, SIZE2 = 200}; // 枚举常量
int array1[SIZE1];
int array2[SIZE2];
};

枚举常量不会占用对象的存储空间,它们在编译时被全部求值。枚举常量的缺点是:
它的隐含数据类型是整数,其最大值有限,且不能表示浮点数(如 PI=3.14159)。

六、函数设计

6.1 参数的规则

  1. 一般地,应将目的参数放在前面,源参数放在后面。
    如果将函数声明为:
void StringCopy(char *strDestination, char *strSource);
  1. 如果参数是指针,且仅作输入用,则应在类型前加 const,以防止该
    指针在函数体内被意外修改。
    例如:
void StringCopy(char *strDestination,const char *strSource);
  1. 如果输入参数以值传递的方式传递对象,则宜改用“const &”方式来传递,这样可以省去临时对象的构造和析构过程,从而提高效率。
  2. 函数参数尽量控制5个以内。

6.2 返回值的规则

  1. 不要将正常值和错误标志混在一起返回。正常值用输出参数获得,而错误标志用 return 语句返回。
  2. 有时候函数原本不需要返回值,但为了增加灵活性如支持链式表达,可以附加返回值。
  3. 不要返回局部对象的引用或指针。如果函数的返回值是一个对象,有些场合用“引用传递”替换“值传
    递”可以提高效率。而有些场合只能用“值传递”而不能用“引用传递”,否则会出错。
    例如:
class String
{…
// 赋值函数
String & operate=(const String &other);
// 相加函数,如果没有 friend 修饰则只许有一个右侧参数
friend String operate+( const String &s1, const String &s2);
private:
char *m_data;
}

String 的赋值函数 operate = 的实现如下:

String & String::operate=(const String &other)
{if (this == &other)
return *this;
delete m_data;
m_data = new char[strlen(other.data)+1];
strcpy(m_data, other.data);
return *this; // 返回的是 *this 的引用,无需拷贝过程
}

6.2 函数内部实现的规则

在函数的入口处和出口处严格把关。

  1. 在入口处检查参数是否非法,我们应该充分理解并正确使用“断言”(assert)。
  2. 在函数体的“出口处”,对 return 语句的正确性和效率进行检查。
    a) return 语句不可返回指向“栈内存”的“指针”或者“引用”,因为该内存在函数
    体结束时被自动销毁。
    b) 要搞清楚返回的究竟是“值”、“指针”还是“引用”。
    c) 如果函数返回值是一个对象,要考虑 return 语句的效率。
return String(s1 + s2);

这是临时对象的语法,表示“创建一个临时对象并返回它”。不要以为它与“先创建
一个局部对象 temp 并返回它的结果”是等价的,如

String temp(s1 + s2);
return temp;

实质不然,上述代码将发生三件事。首先,temp 对象被创建,同时完成初始化;然
后拷贝构造函数把 temp 拷贝到保存返回值的外部存储单元中;最后,temp 在函数结束
时被销毁(调用析构函数)。然而“创建一个临时对象并返回它”的过程是不同的,编译
器直接把临时对象创建并初始化在外部存储单元中,省去了拷贝和析构的化费,提高了
效率。
类似地,我们不要将

return int(x + y); // 创建一个临时变量并返回它

写成

int temp = x + y;
return temp;

由于内部数据类型如 int,float,double 的变量不存在构造函数与析构函数,虽然该“临
时变量的语法”不会提高多少效率,但是程序更加简洁易读。

6.4 其它建议

  1. 函数的功能要单一,不要设计多用途的函数。
  2. 函数体的规模要小,尽量控制在 50 行代码之内。
  3. 尽量避免函数带有“记忆”功能。相同的输入应当产生相同的输出。带有“记忆”功能的函数,其行为可能是不可预测的,因为它的行为可能取决于某种“记忆状态”。这样的函数既不易理解又不利于测试和维护。在 C/C++语言中,函数的 static 局部变量是函数的“记忆”存储器。建议尽量少用 static 局部变量,除非必需。
  4. 不仅要检查输入参数的有效性,还要检查通过其它途径进入函数体内的变量的有效性,例如全局变量、文件句柄等。
  5. 用于出错处理的返回值一定要清楚,让使用者不容易忽视或误解错误情况。

6.5 使用断言

断言宏(assert)只在Debug模式起作用,用来检查**“不应该”**发生的情况。

void *memcpy(void *pvTo, const void *pvFrom, size_t size)
{assert((pvTo != NULL) && (pvFrom != NULL)); // 使用断言
byte *pbTo = (byte *) pvTo; // 防止改变 pvTo 的地址
byte *pbFrom = (byte *) pvFrom; // 防止改变 pvFrom 的地址
while(size -- > 0 )
*pbTo ++ = *pbFrom ++ ;
return pvTo;
}
  1. 使用断言捕捉不应该发生的非法情况。不要混淆非法情况与错误情况 之间的区别,后者是必然存在的并且是一定要作出处理的。
  2. 在函数的入口处,使用断言检查参数的有效性(合法性)。
  3. 在编写函数时,要进行反复的考查,并且自问:“我打算做哪些假定?” 一旦确定了的假定,就要使用断言对假定进行检查。
  4. 一般教科书都鼓励程序员们进行防错设计,但要记住这种编程风格可 能会隐瞒错误。当进行防错设计时,如果“不可能发生”的事情的确发生了,则要 使用断言进行报警。

6.6 引用与指针的区别

“引用传递”的性质像“指针传递”,而书写方式像“值传递”。实际上“引用”可以做的任何事情“指针”也都能够做,为什么还要“引用”这东西?
答案是“用适当的工具做恰如其分的工作”。
指针能够毫无约束地操作内存中的如何东西,尽管指针功能强大,但是非常危险。就象一把刀,它可以用来砍树、裁纸、修指甲、理发等等,谁敢这样用?如果的确只需要借用一下某个对象的“别名”,那么就用“引用”,而不要用“指针”,以免发生意外。比如说,某人需要一份证明,本来在文件上盖上公章的印子就行了,如果把取公章的钥匙交给他,那么他就获得了不该有的权利。

七、内存管理

7.1 指针参数是如何传递内存的?

如果函数的参数是一个指针,不要指望用该指针去申请动态内存。示例 7-4-1 中,
Test 函数的语句 GetMemory(str, 200)并没有使 str 获得期望的内存,str 依旧是 NULL,
为什么?

void GetMemory(char *p, int num)
{p = (char *)malloc(sizeof(char) * num);
}
void Test(void)
{char *str = NULL;
GetMemory(str, 100); // str 仍然为 NULL
strcpy(str, "hello"); // 运行错误
}

毛病出在函数 GetMemory 中。编译器总是要为函数的每个参数制作临时副本,指针
参数 p 的副本是 _p,编译器使 _p = p 。如果函数体内的程序修改了_p 的内容,就导致
参数 p 的内容作相应的修改。这就是指针可以用作输出参数的原因。在本例中,_p 申请
了新的内存,只是把_p 所指的内存地址改变了,但是 p 丝毫未变。所以函数 GetMemory
并不能输出任何东西。事实上,每执行一次 GetMemory 就会泄露一块内存,因为没有用
free 释放内存。
如果非得要用指针参数去申请内存,那么应该改用“指向指针的指针”:

void GetMemory2(char **p, int num)
{*p = (char *)malloc(sizeof(char) * num);
}
void Test2(void)
{char *str = NULL;
GetMemory2(&str, 100); // 注意参数是 &str,而不是 str
strcpy(str, "hello");
cout<< str << endl;
free(str);
}

由于“指向指针的指针”这个概念不容易理解,我们可以用函数返回值来传递动态
内存。这种方法更加简单,

char *GetMemory3(int num)
{char *p = (char *)malloc(sizeof(char) * num);
return p;
}
void Test3(void)
{char *str = NULL;
str = GetMemory3(100);
strcpy(str, "hello");
cout<< str << endl;
free(str);
}

用函数返回值来传递动态内存这种方法虽然好用,但是常常有人把 return 语句用错
了。这里强调不要用 return 语句返回指向“栈内存”的指针,因为该内存在函数结束时
自动消亡.
为什么 free 函数不象 malloc 函数那样复杂呢?这是因为指针 p 的类型以及它所指
的内存的容量事先都是知道的,语句 free§能正确地释放内存。如果 p 是 NULL 指针,
那么 free 对 p 无论操作多少次都不会出问题。如果 p 不是 NULL 指针,那么 free 对 p
连续操作两次就会导致程序运行错误。

八、 C++函数的高级特性

对比于 C 语言的函数,C++增加了重载(overloaded)、内联(inline)、const 和 virtual
四种新机制。其中重载和内联机制既可用于全局函数也可用于类的成员函数,const 与 virtual 机制仅用于类的成员函数。

8.1 函数重载的概念

函数的参数不同(个数,类型)的函数互相重载。
编译器会通过参数修改函数的名字来区分不同的重载函数。例如:foo(int,int);会转换成像_foo_int_int
的函数。
关于C函数foo() 的调用吗,告诉 C++编译译器,函数 foo 是个 C 连接,应该到库中找名字_foo 而不是找
_foo_int_int。C++编译器开发商已经对 C 标准库的头文件作了 extern“C”处理,所以我们可以用#include 直接引用这些头文件。

8.2 运算符重载

在 C++语言中,可以用关键字 operator 加上运算符来表示函数,叫做运算符重载。例如两个复数相加函数:

Complex Add(const Complex &a, const Complex &b);

可以用运算符重载来表示:

Complex operator +(const Complex &a, const Complex &b);

运算符与普通函数在调用时的不同之处是:对于普通函数,参数出现在圆括号内;而对于运算符,参数出现在其左、右侧。例如

Complex a, b, c;
…
c = Add(a, b); // 用普通函数
c = a + b; // 用运算符 +

如果运算符被重载为全局函数,那么只有一个参数的运算符叫做一元运算符,有两个参数的运算符叫做二元运算符。
如果运算符被重载为类的成员函数,那么一元运算符没有参数,二元运算符只有一个右侧参数,因为对象自己成了左侧参数。

8.3 内联函数

C++ 语言的函数内联机制既具备宏代码的效率,又增加了安全性,而且可以自由操
作类的数据成员。所以在 C++ 程序中,应该用内联函数取代所有宏代码,“断言 assert”
恐怕是唯一的例外。assert 是仅在 Debug 版本起作用的宏,它用于检查“不应该”发生
的情况。为了不在程序的 Debug 版本和 Release 版本引起差别,assert 不应该产生任何
副作用。如果 assert 是函数,由于函数调用会引起内存、代码的变动,那么将导致 Debug
版本与 Release 版本存在差异。所以 assert 不是函数,而是宏。

内联函数不应该定义在声明文件中。
将成员函数的定义体放在类声明之中虽然能带来书写上的方便,但不是一种良好的编程
风格,下面是良好的风格:

// 头文件
class A
{public:
void Foo(int x, int y);
}
// 定义文件
inline void A::Foo(int x, int y)
{…
}

以下情况不宜使用内联:
(1)如果函数体内的代码比较长,使用内联将导致内存消耗代价较高。
(2)如果函数体内出现循环,那么执行函数体内代码的时间要比函数调用的开销大。

九、类的构造函数、析构函数与赋值函数

既然能自动生成函数,为什么还要程序员编写?
原因如下:
(1)如果使用“缺省的无参数构造函数”和“缺省的析构函数”,等于放弃了自主“初始化”和“清除”的机会,C++发明人 Stroustrup 的好心好意白费了。
(2)“缺省的拷贝构造函数”和“缺省的赋值函数”均采用“位拷贝”而非“值拷贝”的方式来实现,倘若类中含有指针变量,这两个函数注定将出错。

【高质量编程指南笔记】相关推荐

  1. C语言书籍阅读-读书笔记--高质量程序设计指南》--C/C++,林锐

    最近闲下来想找机会参加一些编程比赛锻炼锻炼,可没有一个相关网站可以搜集这些信息,因而自己记录下来: 百度之星,http://star.baidu.com/index/developer.时间07.25 ...

  2. 高质量程序设计指南C/C++阅读笔记

    阅读书籍:<高质量程序设计指南C/C++语言>林锐.韩勇泉著 说明: 本文档为阅读笔记,要求有一定的C/C++基础,属于提高篇 一.软件质量基本概念 功能属性: 1.正确性Correcte ...

  3. 字节青训营第三课之高质量编程与性能调优实战的笔记和总结

    这是字节青训营第三课:高质量编程与性能调优实战的笔记和总结 概要 准备 尝试使用 test 命令,编写并运行简单测试 尝试使用 -bench参数,对函数进行性能测试 推荐阅读Go代码Review建议. ...

  4. 《高质量程序设计指南——C/C++语言》之开篇记

    <高质量程序设计指南--C/C++语言>是林锐博士和韩永泉先生撰写的著作,记得刚毕业那会读过了.应该说,这本书是对学校教科书的一个很好补充和提高.关于C/C++的经典书籍,国外已经有很多很 ...

  5. Spark编程指南笔记

    Spark编程指南笔记 标签: spark 编程 笔记 | 发表时间:2015-02-02 16:00 | 作者: 分享到: 出处:http://blog.javachen.com/rss.xml 本 ...

  6. 高质量程序设计指南:C++/C语言

    <高质量程序设计指南:C++/C语言> 基本信息 作者: 林锐 韩永泉 [作译者介绍] 出版社:电子工业出版社 ISBN:9787121186172 上架时间:2012-10-24 出版日 ...

  7. 立足自主创新,编程猫助推国内高质量编程教育普及

    来源:金融界百家 2017年,国务院印发<新一代人工智能发展规划>,要求在中小学阶段设置人工智能相关课程,逐步推广编程教育.编程教育逐渐受到广泛重视,作为国内少儿编程赛道先行者,编程猫始终 ...

  8. 《高质量程序设计指南——C++/C》第三版勘误

    <高质量程序设计指南--C++/C>第三版勘误 1.Page IX:1.5 "关于软件开发..."存在印刷问题: 2.Page 7:倒数第13行,"啰里啰唆& ...

  9. 【转贴】对《高质量程序设计指南--C++/C第二版》的探讨

    <高质量程序设计指南--C++/C(第二版)>的读书心得              作者:fang_rk     写这篇文章是出于一个偶尔的原因:读计算机系的女友即将升入研究生三年级,她说 ...

最新文章

  1. IBM人工智能将“掌舵”全球首个跨大西洋自主驾驶船舶
  2. pmp考试用计算机,PMP是计算机类考试吗
  3. Minimizing Difference CodeForces - 1244E(贪心题)
  4. 算法题——投篮比赛获胜的概率
  5. oem718d 基准站设置_诺瓦泰NovAtel OEM718D全系统多频单机测向板卡
  6. for 循环中实现多个点击事件
  7. 安卓手机反应慢又卡怎么办_安卓手机出现卡顿反应慢的具体处理方法
  8. linux ssh freeradius,配置FreeRADIUS提供AAA服务
  9. matlab sil,丰田使用高精度发动机模型和SIL+M前置开发发动机控制系统
  10. MacOS 开发 — Dock 显示网速/消息
  11. c语言求解线性方程组ax=b,用C语言求解N阶线性矩阵方程Ax=b的简单解法
  12. 阿里云VPC网络内网实例通过SNAT连接外网
  13. Android Jetpack之DataBinding+ViewModel+LiveData+Room
  14. 基于linux用C语言编写的局域网通信软件(在ubuntu上正常运行)
  15. Arping命令手册
  16. 大物复习整理(自用)
  17. matlab添加旁白,在MATLAB中向已知信号添加高斯白噪声 (转载)
  18. window游戏编程1
  19. 银联开放平台操作指南合辑
  20. matlab怎么画碎石图,成分分析中biplot函数不理解_主成分分析

热门文章

  1. 都2022年了,还在争论编程语言?
  2. 字符串右移n位,例如 “hello world“ 右移两位 后ldhello wor 要求写一个方法实现此功能,方法的格式是 String moveToRight(String str,int po
  3. 搭建CTF-AWD训练平台
  4. ViewPager中ViewPager的使用
  5. 都要2023年了,Android开发是否还值得入场?
  6. 区块链技术在三角债清收领域的应用思考
  7. 一文科普区块链技术:未来注定将颠…
  8. Franka Emika Panda连接真实机械臂(二)
  9. 论文笔记 :Multi30K: Multilingual English-German Image Descriptions
  10. FusionCharts Free