工 具 函 数

欢迎访问我的博客首页


1. 内存操作函数


  内存操作函数 memset、memcpy、memcmp、memmove、memchr 可以在静态、动态内存操作。

int a[10] = { 9,8,7,6,5,4,3,2,1 }, b[10];
memset(b, 0, sizeof(b));  // 按字节操作,int不是1字节所以只能置0。对char型数组或指针可以置任意字符。
int* c = (int*)malloc(sizeof(int) * 5);
memcpy(c + 3, a + 1, sizeof(int) * 2);  //  按字节操作。从a[1]开始复制2*sizeof(int)字节的内容放在c[3]开始的内存。
int d[2] = { 0,1 }, e[2] = { 0,2 };
cout << memcmp(d, e, sizeof(int) * 1) << endl;  // 按字节操作,以d、e开始,比较它们前n个字节。

2. 字符串操作函数


  strcpy、strncpy、strcmp、strcat。

3. 比较


  memcpy 与 strcpy:前者按指定字节长度复制内存。后者只能复制字符直到复制 ‘\0’ 后才结束。

4. 包含位置信息的日志


void print(const char* msg, const char* file, const char* func, const int line) {
	char tmpbuf[21];
	time_t tmpTime = time(0);
	strftime(tmpbuf, sizeof(tmpbuf) / sizeof(char) - 1, "%Y/%m/%d %H:%M:%S", localtime(&tmpTime));
	cout << "-- file " << file << ", function " << func << ", line " << line << ", " << tmpbuf << endl << "   " << msg << endl;
}
#define log(msg) (print(msg, __FILE__, __FUNCTION__, __LINE__))

   C 语言实现的日志:

#define selfLog(fmt, ...) \
printf("-- %s, function %s, line %d, time %s: "fmt, __FILE__, __func__, __LINE__, __TIME__, ##__VA_ARGS__)

   把日志输入文件中:

FILE *handle;
#define selfLog(fmt, ...) \
handle = fopen("log.txt", "a+"); \
fprintf(handle, fmt, __VA_ARGS__); \
fclose(handle);

5. 在堆上申请二维数组


const int w = 3, h = 4;
int(*arr)[w] = (int(*)[w])malloc(sizeof(int) * w * h);
int(*arr)[w] = new int[h][w];
vector<vector<int>> arr(h, vector<int>(w));

  上面三种方法申请 4 行 3 列的数组。前两种方法可以很方便地初始化为 0,但它们的第 2 个纬度的长度 w 必须用常量指定。

6. linux 系统输出函数调用栈


  在 linux 系统上使用 C/C++ 时,可以借助 execinfo.h 输出函数调用栈。下面的代码来自 CSDN

#include <execinfo.h>
#include <stdio.h>
#include <stdlib.h>

void print_stacktrace() {
    int size = 16;
    void * array[16];
    int stack_num = backtrace(array, size);
    char ** stacktrace = backtrace_symbols(array, stack_num);
    for (int i = 0; i < stack_num; ++i) {
        printf("%s\n", stacktrace[i]);
    }
    free(stacktrace);
}

void fun1() {
    printf("stackstrace begin:\n");
    print_stacktrace();
}
 
void fun2() {
    fun1();
}
 
void fun3() {
    fun2();
}
 
int main() {
    fun3();
}

  编译这段代码,需要指定链接选项

cmake_minimum_required(VERSION 3.5.1)
project(demo)

set(LINK_FLAGS "-rdynamic -Wl,-z,noexecstack -Wl,-z,relro -Wl,-z,now")
set(CMAKE_SHARED_LINKER_FLAGS "${LINK_FLAGS}")
set(CMAKE_EXE_LINKER_FLAGS "${LINK_FLAGS}")

add_executable(main
    main.cc
)

0. 宏定义


1.3.1 宏函数

// 1.参与运算的参数要加括号。
#define f1(x) 2 * x // f1(1+2) = 2 * 1 + 2 = 4。
#define f2(x, y) x * y // f2(1+2, 3+4) = 1 + 2 * 3 + 4 = 11。
// 2.参与运算的宏定义要加括号。
#define f3(x, y) (x) < (y) ? (x) : (y) // f3(1, 2) * 10 = 1 < 2 ? 1 : 2 * 10 = 1。
// 3.宏定义与自增:表达式中不同位置的同一个自增变量的值总是相等的。
// 3.1 i从1开始。
#define f4(x) (x) * (x)
// f4(++i) // 用前加2,用时是3,用后不加。
// f4(i++) // 用前不加,用时是3,用后加2。
// 3.2 i从1开始。
#define f5(x, y) (x) * (y)
// f5(++i, ++i) // 用前加2,用时是3,用后不加。
// f5(++i, i++) // 用前加1,用时是4,用后加1。
// f5(i++, ++i) // 用前加1,用时是6,用后加1。
// f5(i++, i++) // 用前不加,用时是7,用后加2。
// 3.3 i从1开始。
#define f6_1(x) (++x) * (++x) // 用前加2,用时是3,用后不加。
#define f6_2(x) (++x) * (x++) // 用前加1,用时是4,用后加1。
#define f6_3(x) (x++) * (++x) // 用前加1,用时是6,用后加1。
#define f6_4(x) (x++) * (x++) // 用前不加,用时是7,用后加2。

3. 运算符

3.1 算术运算符

2.1.1 递增运算符

  下面每行语句开始前x的值都是1。

x = ++x; // 2。
x = x++; // 2。
x = (++x) * (++x); // (x+1+1) * (x+1+1)        = 9。
x = (++x) * (x++); //  (x+1)  *  (x+1) + 1     = 5。
x = (x++) * (++x); //  (x+1)  *  (x+1) + 1     = 5。
x = (x++) * (x++); //     x   *    x   + 1 + 1 = 3。

y = ++x; // 2。
y = x++; // 1。
y = (++x) * (++x); // (x+1+1) * (x+1+1) = 9。
y = (++x) * (x++); //  (x+1)  *  (x+1)  = 4。
y = (x++) * (++x); //  (x+1)  *  (x+1)  = 4。
y = (x++) * (x++); //    x    *    x    = 1。

3.2 关系运算符

  用C++判断两个以上变量的关系,可能不是你想要的结果:

int a = 1, b = 2, c = 1;
if (a <= b <= c) cout << "yes" << endl; else cout << "no" << endl;

  上面的程序运行结果是yes。因为a <= b <= c等价于(a <= b) <= c。a <= b为true,true的值是1,所以等价于1 <= c。于是表达为真。

3.3 逻辑运算符

  逻辑运算表达式化简。逻辑运算和算术运算一样有交换律、分配律、结合律。此外逻辑运算还有吸收律。

!a && (a || b) 等价于 (!a && a) || (!a && b) 等价于 !a && b

  高效判断奇偶的方法:奇数&1 = 1/true,偶数&1 = 0/false。任何数&0 = 0。

4. 拷贝控制


4.1 右值引用

3.5.1 右值引用与std::move

  右值引用int&&只能绑定/匹配右值。这是移动函数的参数匹配原理:右值引用的形参只能匹配右值。
  std::move实现从左值到右值的转换。

// 1.右值引用只能绑定右值。对于左值,需要使用std::move转换成右值才能绑定。
int a = 1;
const int b = 2;
int&& r1 = 1;
int&& r2 = std::move(a);
int&& r3 = a + 1;
int&& r4 = b + 1;
int&& r5 = std::move(r4);

// 2.移动函数的参数匹配。
void fun(int&) { cout << "实参是左值" << endl; }
void fun(int&&) { cout << "实参是右值" << endl; }
int main() {
	int a = 1;
	fun(a); // 左值。
	fun(a + 1); // 右值。
	fun(std::move(a)); // 右值。
}

3.5.2 完美转发与std::forward

void Call(int& e) { cout << "--1." << endl; }
void Call(int&& e) { cout << "--2." << endl; }
void Call(const int& e) { cout << "--3." << endl; }
void Call(const int&& e) { cout << "--4." << endl; }

template<typename T>
void notPerfectForward(T&& t) { Call(t); }

template<typename T>
void PerfectForward(T&& t) { Call(forward<T>(t)); }

int main() {
	int a = 1;
	const int b = 2;
	// 1.直接根据实参匹配重载函数。
	Call(a); // 匹配1,3。
	Call(move(a)); // 匹配2,4,3。
	Call(b); // 匹配3。
	Call(move(b)); // 匹配4,3。
	// 2.不使用std::forward。
	notPerfectForward(a); // 重载函数1。
	notPerfectForward(move(a)); // 重载函数1。
	notPerfectForward(b); // 重载函数3。
	notPerfectForward(move(b)); // 重载函数3。
	// 3.完美转发。
	PerfectForward(a); // 重载函数1。
	PerfectForward(move(a)); // 重载函数2。
	PerfectForward(b); // 重载函数3。
	PerfectForward(move(b)); // 重载函数4。
}

  第10行定义的函数,可以完美转发4种类型的实参匹配4个重载函数。

参考

5. 内存泄漏

5.3.1 内存泄露检测工具

  在windows上可以使用VLD检测内存泄漏。

5.3.2 基类虚析构函数防止内存泄露

  见虚函数与虚析构函数

6. 函数


6.1 重载、隐藏、覆盖

6.4.1 重载

  重载函数:同一作用域内的几个函数名字相同但形参列表不同。
  重载原理:使用命名倾轧技术,即编译器使用函数名和参数列表把重载函数编译成不同名字的函数。
  1. 使用底层const指针或引用可以定义重载函数。

// 下面2个函数都匹配int*和int* const的实参。它们不能重载。
void fun(int* x) { cout << "--fun(int*)." << endl; }
void fun(int* const x) { cout << "--fun(int* const)." << endl; } // 顶层const。
// 下面1个函数匹配const int*的实参。它可以和上面两个函数之一重载。
void fun(const int* x) { cout << "--fun(const int*)." << endl; } // 底层const。

  2. 重载函数参数数量相同且参数类型可以相互转换时的最佳匹配

void f(int, int) {}
void f(double, double) {}
f(42, 2.56); // 与上面两个函数各匹配一个参数。因具有二义性,无法匹配。

  实参类型转换。

6.4.2 隐藏

  派生类的函数会隐藏所有从基类继承来的同名函数。

6.4.3 覆盖/重写

  覆盖是隐藏的一种,除函数名相同外,还要:1.基类函数是虚函数;2.派生类函数与该虚函数形参相同;3.两个函数的返回值相同或协变。
协变和逆变

6.4.4 与访问控制的关系

  访问控制只限定访问不改变成员性质。访问控制不影响重载、隐藏、覆盖,即不同访问控制属性的函数依然可以重载、隐藏、覆盖。

  • 8
    点赞
  • 33
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: modbus是一种常用的通信协议,用于实现可编程逻辑控制器(PLC)和其他设备之间的通信。Modbus报文解析是一种用于解析modbus通信协议据报文的。它可以帮助用户更好地理解和分析modbus通信协议的据报文,从而更好地实现设备之间的通信。 Modbus报文解析可以帮助用户解析modbus通信协议中的据报文,包括读写寄存器、读取线圈、读取离散输入以及读取保持寄存器等操作。它能够对收到的modbus通信协议据报文进行自动解析,并将解析结果以易于阅读和理解的形式展示出来。 使用Modbus报文解析可以帮助用户更好地了解和掌握modbus通信协议的据结构和通信方法,提高分析和诊断modbus通信问题的能力。该还可以提高用户的作效率,节省了手动解析modbus据报文的时间和精力。 总之,Modbus报文解析是一种非常有用的,可以帮助用户更好地掌握和使用modbus通信协议,提高设备之间的通信效率和可靠性。 ### 回答2: Modbus报文解析是一种用于解析Modbus协议报文的软件。Modbus协议是一种用于业控制系统的通信协议,可用于连接不同设备之间的通信。 Modbus报文解析通常以下功能: 1. 可以解析Modbus协议的不同类型报文:如读取线圈、读取离散输入、读取保持寄存器、读取输入寄存器等。 2. 可以显示报文的内容和格式,包括报文头、功能码、据长度、据内容等。 3. 可以将报文转换为易于理解的格式,如16进制、ASCII码等。 4. 可以对报文进行编辑、存储和发送,方便用户进行Modbus通信协议的测试和调试。 5. 可以进行报文的自动化测试,方便用户根据要求建立测试用例,减少人操作。 总之,Modbus报文解析是一种重要的软件,可以方便地解析Modbus协议的报文,便于用户进行测试、调试和应用开发等作,同时可以提高作效率和减少出错率。 ### 回答3: Modbus报文解析,是一种用来解析和分析Modbus协议报文的软件。Modbus是一种通信协议,用来在不同的设备之间进行通信和据交换。Modbus协议常用于业自动化、据采集、控制系统和监测设备等领域。 Modbus报文解析可以帮助程师和技术人员开发和测试Modbus协议相关的应用程序。该能够识别Modbus报文,进行解析并显示其中的据内容。通过对Modbus报文的解析分析,可以帮助用户快速定位问题和故障,提高作效率和准确性。 Modbus报文解析通常有良好的可视化界面和多种操作选项。用户只需要输入Modbus协议标准的读取和写入函数码等参,即可使用进行解析和分析。还可以支持多种不同的Modbus协议类型和通信方式。 在业网络通信领域,Modbus报文解析是非常重要的。它可以帮助快速查找网络中的问题和故障,优化网络通信性能,提高作效率和有效性。因此,这个业自动化和控制系统领域被广泛应用。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值