C++/C++11中变长参数的使用

2023-11-20

C++/C++11中的变长参数可以应用在宏、函数、模板中。

1. 宏:在C99标准中,程序员可以使用变长参数的宏定义。变长参数的宏定义是指在宏定义中参数列表的最后一个参数为省略号,而预定义宏__VA_ARGS__则可以在宏定义的实现部分替换省略号所代表的字符串。

printf函数使用了C语言的函数变长参数特性,通过使用变长函数(variadic function),printf的实现能够接受任何长度的参数列表,如下:

int printf ( const char * format, ... );
可变参数宏是C语言与C++语言的函数宏的参数个数可以是0个或多个。这一语言特性由C99引入,C++11也支持。

可变参数宏的声明语法类似于可变参数函数:逗号后面三个句点”…”,表示一个或多个参数。但常见编译器也允许传递0个参数。宏扩展时使用特殊标识符__VA__ARGS__表示所传递的参数的替换。

对于可变参数为空情形,Visual Studio直接去掉可变参数前面的逗号,GCC需要在__VA__ARGS__前面放上##以去除逗号。

2. 函数:C/C++支持函数的可变参数列表,这个可变参数列表是通过宏来实现的,这些宏定义在头文件stdarg.h中,它是标准库的一部分。这个头文件声明了一个类型va_list和三个宏---- va_start, va_arg, va_end。我们可以声明一个类型为va_list的变量,与这几个宏配合使用,访问参数的值。函数内部必须定义一个类型为va_list的变量vl。然后使用宏va_start、va_arg和va_end来读取,使用va_start宏对vl进行初始化,使得vl成为被传递的变长参数的一个”句柄”(handler)。而后再使用va_arg宏从vl中将参数一一取出用于运算。va_end宏用于结束vl。由于将va_start,va_arg,va_end定义成了宏,可变参数的类型和个数在该函数中完全由程序代码控制,并不能智能地进行识别,所以导致编译器对可变参数的函数原型检查不够严格,难于查错,不利于写出高质量的代码。要创建一个可变参数函数,必须把省略号(…)放到参数列表后面。

一个可变参数函数是指一个函数拥有不定参数,即是它接受一个可变数目的参数。可变参数函数在C语言存在安全问题,如C语言在没有长度检查和类型检查,在传入过少的参数或不符的类型时可能会出现溢位的情况,更可能会被利用为攻击目标。所以,在设计函数时可以先考虑其它替补方案,例如以类型安全的方式----重载。

为了编写处理不同数量实参的函数,C++11新标准提供了两种主要的方法:如果所有的实参类型相同,可以传递一个名为std::initializer_list的标准库类型;如果实参的类型不同,我们可以编写一种特殊的函数,也就是所谓的可变参数模板。

C++还有一种特殊的形参类型(即省略符),这种功能一般只用于与C函数交互的接口程序。省略符形参是为了便于C++程序访问某些特殊的C代码而设置的,这些代码使用了名为varargs的C标准库功能。通常,省略符形参不应用于其它目的。省略符形参应该仅仅用于C和C++通用的类型,特别应该注意的是,大多数类型的对象在传递给省略符形参时都无法正确拷贝。省略符形参只能出现在形参列表的最后一个位置

关于std::initializer_list的使用可以参考: http://blog.csdn.net/fengbingchun/article/details/77938005 

3. 模板:C++11支持C99的变长宏。从C++11开始支持变长模板(variadic template)。在C++11中,”模板参数包”(template parameter pack),是一种新的模板参数类型。有了这样的参数包,类模板就可以接受任意多个参数作为模板参数。编译器可以将多个模板参数打包成为”单个的”模板参数包。与普通的模板参数类似,模板参数包也可以是非类型的。除了类型的模板参数包和非类型的模板参数包,模板参数包还可以是模板类型的。

一个模板参数包在模板推导时会被认为是模板的单个参数(虽然实际上它将会打包任意数量的实参)。为了使用模板参数包,我们总是需要将其解包(unpack)。在C++11中,这通常是通过一个名为包扩展(pack expansion)的表达式来完成。

除了变长的模板类,在C++11中,我们还可以声明变长模板的函数。对于变长模板函数而言,除了声明可以容纳变长个模板参数的模板参数包之外,相应地,变长的函数参数也可以声明成函数参数包(function parameter pack)。在C++11中,标准要求函数参数包必须唯一,且是函数的最后一个参数(模板参数包没有这样的要求)。

在C++11中,标准定义了7种参数包可以展开的位置:表达式、初始化列表、基类描述列表、类成员初始化列表、模板参数列表、通用属性列表、lambda函数的捕捉列表。其它”地方”则无法展开参数包。

在C++11中,标准还引入了新操作符”sizeof…”,其作用是计算参数包中的参数个数。

一个可变参数模板(variadic template)就是一个接受可变数目参数的模板函数或模板类。可变数目的参数被称为参数包(parameter packet)。存在两种参数包:模板参数包(template parameter packet),表示零个或多个模板参数;函数参数包(function parameter packet),表示零个或多个函数参数。

我们用一个省略号来指出一个模板参数或函数参数表示一个包。在一个模板参数列表中,class…或typename…指出接下来的参数表示零个或多个类型的列表;一个类型名后面跟一个省略号表示零个或多个给定类型的非类型参数的列表。在函数参数列表中,如果一个参数的类型是一个模板参数包,则此参数也是一个函数参数包。

编译器从函数的实参推断模板参数类型。对于一个可变参数模板,编译器还会推断包中参数的数目。

sizeof…运算符:当我们需要知道包中有多少元素时,可以使用sizeof…运算符。类似sizeof,sizeof…也返回一个常量表达式,而且不会对其实参求值。

我们可以使用一个std::initializer_list来定义一个可接受可变数目实参的函数。但是,所有实参必须具有相同的类型(或它们的类型可以转换为同一个公共类型)。当我们既不知道想要处理的实参的数目也不知道它们的类型时,可变参数函数是很有用的。

可变参数函数通常是递归的。第一步调用处理包中的第一个实参,然后用剩余实参调用自身

包扩展:对于一个参数包,除了获取其大小外,我们能对它做的唯一的事情就是扩展(expand)它。当扩展一个包时,我们还要提供用于每个扩展元素的模式(pattern)。扩展一个包就是将它分解为构成的元素,对每个元素应用模式,获得扩展后的列表。我们通过在模式右边放一个省略号(…)来触发扩展操作。

在新标准下,我们可以组合使用可变参数模板与forward机制来编写函数,实现将其实参不变地传递给其它函数。

可变参数模板是模板编程时,模板参数(template parameter)的个数可变的情形。C++11之前,模板(类模板与函数模板)在声明时必须有固定数量的模板参数。C++11允许模板定义有任意类型任意数量的模板参数。

省略号(…)在可变参数模板中有两种用途:(1)、省略号出现在形参名字左侧,声明了一个参数包(parameter pack)。使用这个参数包,可以绑定0个或多个模板实参给这个可变模板形参参数包。参数包也可以用于非类型的模板参数。(2)、省略号出现在包含参数包的表达式的右侧,则把这个参数包解开为一组实参,使得在省略号前的整个表达式使用每个被解开的实参完成求值,所有表达式求值结果被逗号分开。注意这里的逗号不是作为逗号运算符,而是用作:A、被逗号分隔开的一组函数调用实参列表(该函数必须是可变参数函数,而不能是固定参数个数的函数);B、被逗号分隔开的一组初始化列表(initializer list);C、被逗号分隔开的一组基类列表(base class list)与构造函数初始化列表(constructor’s initialization list);D、被逗号分隔开的一组函数的可抛出的异常规范(exception specification)的声明列表。实际上,能够接受可变参数个数的参数包展开的场合,必须是能接受任意个数的逗号分隔开的表达式列表。

我们平时经常使用的std::tuple就是一个变长模板类,如下:

template <class... Types> class tuple;
下面是从其他文章中copy的测试代码,详细内容介绍可以参考对应的reference:

#include "variable_arguments.hpp"
#include <stdarg.h>
#include <cassert>
#include <iostream>
#include <stdexcept>
#include <string>
#include <initializer_list>

namespace variable_arguments_ {
///
#define LOG(...) { \
	fprintf(stderr, "%s: Line %d:\t", __FILE__, __LINE__); \
	fprintf(stderr, __VA_ARGS__); \
	fprintf(stderr, "\n"); \
}

int test_variable_arguments_macro_1()
{
	int value{ 5 };
	LOG("value = %d", value);

	return 0;
}

//
// reference: http://www.geeksforgeeks.org/variable-length-arguments-for-macros/
#define INFO 1
#define ERR 2
#define STD_OUT stdout
#define STD_ERR stderr

#define LOG_MESSAGE(prio, stream, msg, ...) do { \
	char *str; \
	if (prio == INFO) \
		str = "INFO"; \
	else if (prio == ERR) \
		str = "ERR"; \
	fprintf(stream, "[%s] : %s : %d : "msg" \n", \
			str, __FILE__, __LINE__, ##__VA_ARGS__); \
} while (0)

int test_variable_arguments_macro_2()
{
	char *s = "Hello";

	/* display normal message */
	LOG_MESSAGE(ERR, STD_ERR, "Failed to open file");

	/* provide string as argument */
	LOG_MESSAGE(INFO, STD_OUT, "%s Geeks for Geeks", s);

	/* provide integer as arguments */
	LOG_MESSAGE(INFO, STD_OUT, "%d + %d = %d", 10, 20, (10 + 20));

	return 0;
}

///
// reference: https://msdn.microsoft.com/en-us/library/ms177415.aspx
#define EMPTY

#define CHECK1(x, ...) if (!(x)) { printf(__VA_ARGS__); }
#define CHECK2(x, ...) if ((x)) { printf(__VA_ARGS__); }
#define CHECK3(...) { printf(__VA_ARGS__); }
#define MACRO(s, ...) printf(s, __VA_ARGS__)

int test_variable_arguments_macro_3()
{
	CHECK1(0, "here %s %s %s", "are", "some", "varargs1(1)\n");
	CHECK1(1, "here %s %s %s", "are", "some", "varargs1(2)\n");

	CHECK2(0, "here %s %s %s", "are", "some", "varargs2(3)\n");
	CHECK2(1, "here %s %s %s", "are", "some", "varargs2(4)\n");

	// always invokes printf in the macro
	CHECK3("here %s %s %s", "are", "some", "varargs3(5)\n");

	MACRO("hello, world\n");

	MACRO("error\n", EMPTY);

	return 0;
}

///
static double SumOfFloat(int count, ...)
{
	va_list ap;
	double sum{ 0.f };
	va_start(ap, count);

	for (int i = 0; i < count; ++i) {
		sum += va_arg(ap, double);
	}

	va_end(ap);

	return sum;
}

int test_variable_arguments_function_1()
{
	fprintf(stdout, "sum: %f\n", SumOfFloat(3, 1.2f, -2.3f, 5.8f));

	return 0;
}


template<typename T>
T sum(std::initializer_list<T> il)
{
	T data(0);
	for (T i : il)
		data += i;
	return data;
}

int test_variable_arguments_function_2()
{
	std::cout << sum({ 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 }) << std::endl;
	return 0;
}

//
// reference: https://stackoverflow.com/questions/41400/how-to-wrap-a-function-with-variable-length-arguments
void myprintf(char* fmt, ...)
{
	va_list args;
	va_start(args, fmt);
	vprintf(fmt, args); //  vprint, vsprintf, vfprintf: there are also 'safe' versions in Microsoft's C runtime that will prevent buffer overruns, etc.
	va_end(args);
}

int test_variable_arguments_function_3()
{
	int a = 9;
	int b = 10;
	char v = 'C';
	myprintf("This is a number: %d and \nthis is a character: %c and \n another number: %d\n", a, v, b);

	return 0;
}

//
// reference: https://msdn.microsoft.com/en-us/library/fxhdxye9.aspx
//  ShowVar takes a format string of the form
//   "ifcs", where each character specifies the
//   type of the argument in that position.
//
//  i = int
//  f = float
//  c = char
//  s = string (char *)
//
//  Following the format specification is a variable
//  list of arguments. Each argument corresponds to
//  a format character in the format string to which
// the szTypes parameter points
void ShowVar(char* szTypes, ...)
{
	va_list vl;
	int i;

	//  szTypes is the last argument specified; you must access   
	//  all others using the variable-argument macros.  
	va_start(vl, szTypes);

	// Step through the list.  
	for (i = 0; szTypes[i] != '\0'; ++i) {
		union Printable_t {
			int     i;
			float   f;
			char    c;
			char   *s;
		} Printable;

		switch (szTypes[i]) {   // Type to expect.  
		case 'i':
			Printable.i = va_arg(vl, int);
			printf_s("%i\n", Printable.i);
			break;

		case 'f':
			Printable.f = va_arg(vl, double);
			printf_s("%f\n", Printable.f);
			break;

		case 'c':
			Printable.c = va_arg(vl, char);
			printf_s("%c\n", Printable.c);
			break;

		case 's':
			Printable.s = va_arg(vl, char *);
			printf_s("%s\n", Printable.s);
			break;

		default:
			break;
		}
	}
	va_end(vl);
}

int test_variable_arguments_function_4()
{
	ShowVar("fcsi", 32.4f, 'a', "Test string", 4);

	return 0;
}

//
// reference: http://www.cplusplus.com/reference/cstdarg/va_arg/
int FindMax(int n, ...)
{
	int i, val, largest;
	va_list vl;
	va_start(vl, n);
	largest = va_arg(vl, int);
	for (i = 1; i<n; i++) {
		val = va_arg(vl, int);
		largest = (largest>val) ? largest : val;
	}
	va_end(vl);

	return largest;
}

int test_variable_arguments_function_5()
{
	int m;
	m = FindMax(7, 702, 422, 631, 834, 892, 104, 772);
	fprintf(stdout, "The largest value is: %d\n", m);

	return 0;
}

///
// reference: 《深入理解C++11----C++11新特性解析与应用》 6.2
template <long... nums> struct Multiply;

template<long first, long... last>
struct Multiply<first, last...> {
	static const long val = first * Multiply<last...>::val;
};

template<>
struct Multiply<> {
	static const long val = 1;
};

int test_variable_arguments_template_class_1()
{
	fprintf(stdout, "%ld\n", Multiply<2, 3, 4, 5>::val); // 120
	fprintf(stdout, "%ld\n", Multiply<22, 44, 66, 88, 9>::val); // 50599296

	return 0;
}

///
void Printf(const char* s)
{
	while (*s) {
		if (*s == '%' && *++s != '%')
			throw std::runtime_error("invalid format string: missing arguments");
		std::cout << *s++;
	}
}

template<typename T, typename... Args>
void Printf(const char* s, T value, Args... args)
{
	while (*s) {
		if (*s == '%' && *++s != '%') {
			std::cout << value;
			return Printf(++s, args...);
		}

		std::cout << *s++;
	}

	throw std::runtime_error("extra arguments provided to Printf");
}

int test_variable_arguments_template_function_1()
{
	Printf("hello %s\n", std::string("world")); // hello world

	return 0;
}

/
template<typename... T>
void DummyWrapper(T... t) {}

template<typename T>
T pr(T t)
{
	std::cout << t;
	return t;
}

template<typename... A>
void VTPrint(A... a)
{
	DummyWrapper(pr(a)...);
}

int test_variable_arguments_template_function_2()
{
	VTPrint(1, ", ", 1.2, ", abc\n");

	return 0;
}

/
// 用来终止递归并打印最后一个元素的函数,此函数必须在可变参数版本的print定义之前声明
template<typename T>
std::ostream& print(std::ostream& os, const T& t)
{
	return os << t; // 包中最后一个元素之后不打印分隔符
}

// 包中除了最后一个元素之外的其它元素都会调用这个版本的print
template<typename T, typename... Args>
std::ostream& print(std::ostream& os, const T& t, const Args&... rest)
{
	os << t << ", "; // 打印第一个字符
	return print(os, rest...); // 递归调用,打印其它实参
}

int test_variable_arguments_template_function_3()
{
	// 对于最后一个调用,两个函数提供同样好的匹配。但是,非可变参数模板比可变参数模板更
	// 特例化,因此编译器选择非可变参数版本
	char i{ 'A' }, s{ 'b' };
	print(std::cout, i, s, 42);

	// 当定义可变参数版本的print时,非可变参数版本的声明必须在作用域中,否则,可变参数版本会无限递归。

	return 0;
}

///
template<class... A>
void Print(A... arg)
{
	assert(false);
}

void Print(int a1, int a2, int a3, int a4, int a5, int a6)
{
	fprintf(stdout, "%d, %d, %d, %d, %d, %d\n", a1, a2, a3, a4, a5, a6);
}

template<class... A>
int Vaargs(A... args)
{
	int size = sizeof...(A);

	switch (size) {
	case 0: Print(99, 99, 99, 99, 99, 99);
		break;
	case 1: Print(99, 99, args..., 99, 99, 99);
		break;
	case 2: Print(99, 99, args..., 99, 99);
		break;
	case 3: Print(args..., 99, 99, 99);
		break;
	case 4: Print(99, args..., 99);
		break;
	case 5: Print(99, args...);
		break;
	case 6: Print(args...);
		break;
	default:
		Print(0, 0, 0, 0, 0, 0);
	}

	return size;
}

int test_variable_arguments_sizeof_1()
{
	Vaargs();
	Vaargs(1);
	Vaargs(1, 2);
	Vaargs(1, 2, 3);
	Vaargs(1, 2, 3, 4);
	Vaargs(1, 2, 3, 4, 5);
	Vaargs(1, 2, 3, 4, 5, 6);
	Vaargs(1, 2, 3, 4, 5, 6, 7);

	return 0;
}

} // namespace variable_arguments_

GitHub:  https://github.com/fengbingchun/Messy_Test 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

C++/C++11中变长参数的使用 的相关文章

  • C++11中std::condition_variable的使用

  • C++11中enum class的使用

    枚举类型 enumeration 使我们可以将一组整型常量组织在一起 和类一样 每个枚举类型定义了一种新的类型 枚举属于字面值常量类型 C 包含两种枚举 限定作用域的和不限定作用域的 这里主要介绍限定作用域的 不限定作用域的使用可以参考 h
  • C和C++安全编码笔记:整数安全

    5 1 整数安全导论 整数由包括0的自然数 0 1 2 3 和非零自然数的负数 1 2 3 构成 5 2 整数数据类型 整数类型提供了整数数学集合的一个有限子集的模型 一个具有整数类型的对象的值是附着在这个对象上的数学值 一个具有整数类型的
  • C语言中的弱符号与强符号介绍

    弱符号 Weak symbol 是链接器 ld 在生成ELF Executable and Linkable Format 缩写为ELF 可执行和可链接格式 是一种用于可执行文件 目标文件 共享库和核心转储的标准文件格式 ELF文件有两种索
  • C++/C++11中头文件algorithm的使用

  • C++11中std::function的使用

    类模版std function是一种通用 多态的函数封装 std function的实例可以对任何可以调用的目标实体进行存储 复制 和调用操作 这些目标实体包括普通函数 Lambda表达式 函数指针 以及其它函数对象等 通过std func
  • C++/C++11中引用的使用

    引用 reference 是一种复合类型 compound type 引用为对象起了另外一个名字 引用类型引用 refer to 另外一种类型 通过将声明符写成 d的形式来定义引用类型 其中d是声明的变量名 一 一般引用 一般在初始化变量时
  • C和C++安全编码笔记:文件I/O

    C和C 程序通常会对文件进行读写 并将此作为它们正常操作的一部分 不计其数的漏洞正是由这些程序与文件系统 其操作由底层操作系统定义 交互方式的不规则性而产生的 这些漏洞最常由文件的识别问题 特权管理不善 以及竞争条件导致 8 1 文件I O
  • log库spdlog简介及使用

    spdlog是一个开源的 快速的 仅有头文件的C 11 日志库 code地址在 https github com gabime spdlog 目前最新的发布版本为0 14 0 它提供了向流 标准输出 文件 系统日志 调试器等目标输出日志的能
  • 提高C++性能的编程技术笔记:多线程内存池+测试代码

    为了使多个线程并发地分配和释放内存 必须在分配器方法中添加互斥锁 全局内存管理器 通过new 和delete 实现 是通用的 因此它的开销也非常大 因为单线程内存管理器要比多线程内存管理器快的多 所以如果要分配的大多数内存块限于单线程中使用
  • 提高C++性能的编程技术笔记:引用计数+测试代码

    引用计数 reference counting 基本思想是将销毁对象的职责从客户端代码转移到对象本身 对象跟踪记录自身当前被引用的数目 在引用计数达到零时自行销毁 换句话说 对象不再被使用时自行销毁 引用计数和执行速度之间的关系是与上下文紧
  • C++中的内存对齐介绍

    网上有很多介绍字节对齐或数据对齐或内存对齐的文章 虽然名字不一样 但是介绍的内容大致都是相同的 这里以内存对齐相称 注 以下内容主要来自网络 内存对齐 通常也称为数据对齐 是计算机对数据类型合法地址做出了一些限制 要求某种类型对象的地址必须
  • 程序员的自我修养--链接、装载与库笔记:Linux共享库的组织

    共享库 Shared Library 概念 其实从文件结构上来讲 共享库和共享对象没什么区别 Linux下的共享库就是普通的ELF共享对象 由于共享对象可以被各个程序之间共享 所以它也就成为了库的很好的存在形式 很多库的开发者都以共享对象的
  • 内存检测工具Dr. Memory的使用

    Dr Memory是一个内存调试工具 它是一个开源免费的内存检测工具 它能够及时发现内存相关的编程错误 比如未初始化访问 内存非法访问 数组越界读 写 以及内存泄露等 它可以在Linux Windows Mac OS和Android操作系统
  • C++中插件使用举例

    插件并不是在构建时链接的 而是在运行时发现并加载的 因此 用户可以利用你定义好的插件API来编写自己的插件 这样他们就能以指定方式扩展API的功能 插件库是一个动态库 它可以独立于核心API编译 在运行时根据需要显示加载 不过插件也可以使用
  • C++中的封装、继承、多态

    封装 encapsulation 就是将抽象得到的数据和行为 或功能 相结合 形成一个有机的整体 也就是将数据与操作数据的源代码进行有机的结合 形成 类 其中数据和函数都是类的成员 封装的目的是增强安全性和简化编程 使用者不必了解具体的实现
  • C++11中std::shared_future的使用

    C 11中的std shared future是个模板类 与std future类似 std shared future提供了一种访问异步操作结果的机制 不同于std future std shared future允许多个线程等待同一个共
  • C++17中utf-8 character literal的使用

    一个形如42的值被称作字面值常量 literal 这样的值一望而知 每个字面值常量都对应一种数据类型 字面值常量的形式和值决定了它的数据类型 由单引号括起来的一个字符称为char型字面值 双引号括起来的零个或多个字符则构成字符串型字面值 字
  • Linux下getopt函数的使用

    getopt为解析命令行参数函数 它是Linux C库函数 使用此函数需要包含系统头文件unistd h getopt函数声明如下 int getopt int argc char const argv const char optstri
  • 提高C++性能的编程技术笔记:单线程内存池+测试代码

    频繁地分配和回收内存会严重地降低程序的性能 性能降低的原因在于默认的内存管理是通用的 应用程序可能会以某种特定的方式使用内存 并且为不需要的功能付出性能上的代价 通过开发专用的内存管理器可以解决这个问题 对专用内存管理器的设计可以从多个角度

随机推荐

  • Notepad++ 列操作

    在网上找到一篇关于socket编程的文章 想把其中的代码直接拷贝下来运行测试 但是人家网站做的不够人性化 每行的开头都有行号 直接拷贝就要一行行的删除 甚是麻烦 想到linux下的vi编辑器可以完成列操作 windows下应该也有相关软件可
  • kali之vulhub,medium_socnet

    目录 一 部署环境 二 进行主机发现 arp scan进行扫描 三 使用nmap扫描发现的靶机IP 1 全端口扫描 看服务 2 查看端口所用服务版本 3 登录ip地址看一下 四 扫目录 dirsearch脚本 和windous御剑类似 1
  • Linux进程间通信--使用命名管道

    Linux进程间通信 使用命名管道 一 什么是命名管道 命名管道也被称为FIFO文件 它是一种特殊类型的文件 它在文件系统中以文件名的形式存在 但是它的行为却和之前所讲的没有名字的管道 匿名管道 类似 由于Linux中所有的事物都可被视为文
  • OpenCV-Python中的标量Scalar是什么

    前往老猿Python博客 https blog csdn net LaoYuanPython 一 标量的常规含义 在百度百科中标量是这样定义的 标量 scalar 亦称 无向量 有些物理量 只具有数值大小 而没有方向 部分有正负之分 物理学
  • Windows驱动开发(一)第一个驱动程序

    首先我们需要了解 在操作系统中 是分两种权限的 一种是内核态 我们也称为0环 一种是用户态 称之为3环 而在我们的电脑中 驱动程序是运行在内核态的 这意味着和操作系统内核是在同一权限的 而普通的应用程序的权限是最低的 高权限谁不想拥有呢 因
  • Qt:鼠标事件(鼠标移动事件、鼠标按下事件、鼠标释放事件、进入窗口区域、离开窗口区域)

    鼠标事件 本质是重写重写QWidgt中虚函数 以下程序是获取鼠标坐标 mylabel h ifndef MYLABEL H define MYLABEL H include
  • IntelliJ IDEA(Community版本)本地模式的下载、安装及其使用

    对于初学者来说可以先使用免费的社区版本练练手 ideaIC 2017 3 5 gt 社区版 ideaIU 2017 3 5 gt 旗舰版 一 IntelliJ IDEA Community版本 下载 下载链接 https www jetbr
  • parser解析中的-与_

    import argparse parser argparse ArgumentParser description 解析命令行参数 parser add argument echo s parser add argument echo d
  • [python] 使用Jieba工具中文分词及文本聚类概念

    前面讲述了很多关于Python爬取本体Ontology 消息盒InfoBox 虎扑图片等例子 同时讲述了VSM向量空间模型的应用 但是由于InfoBox没有前后文和语义概念 所以效果不是很好 这篇文章主要是爬取百度5A景区摘要信息 再利用J
  • Nginx 官方rpm包下载地址

    运维更换地方需求保存的地址 centos http nginx org packages rhel 7 x86 64 RPMS
  • 怎么用好免费的ChatGPT

    最近 ChatGPT因其出色的表现而备受关注 网友们纷纷赞叹不已 然而 在国内目前还没有相应的使用途径 本文旨在向大家介绍如何免费使用ChatGPT 并教你如何善用它来提升个人效率 在下文中 我们将提供详细指南 让您能够充分利用ChatGP
  • 利用正则表达式对手机号、身份证、姓名脱敏(**模糊化)

    var phone 13623021456 var idcode 440582199612056666 var name 陈妹 var name1 陈美美 var name2 欧阳美美 phone phone replace 3 4 1 2
  • 【无线电力传输】12 V 直流风扇无线电力传输系统的实现(Simulink)

    欢迎来到本博客 博主优势 博客内容尽量做到思维缜密 逻辑清晰 为了方便读者 座右铭 行百里者 半于九十 本文目录如下 目录 1 概述 2 运行结果 3 参考文献 4 Simulink仿真实现 1 概述 经过实现 我们成功地开发出了一种无线电
  • shell脚本,一次性启动kafka集群

    版本centos6 5 64位操作系统 已配置JDK1 8 三个节点 在s121节点上可以免密登录到另外两个节点 另外kafka0 9 0 1的安装目录相同 修改了主机名 并在每个节点的hosts文件中设置了映射 脚本内容 bin bash
  • 关于git存储空间的注意

    摘自 https blog csdn net weixin 30411819 article details 97716808 utm medium distribute pc relevant none task blog BlogCom
  • 解决vxe-table复选框翻页选中问题

    解决vxe table复选框翻页选中问题 根据vxe table官方文档 想要保留勾选中的数据 我们的代码中需要设置 row id 和 checkbox config中的 reserve 属性 vxe table官方文档 简单写下html部
  • thinkphp 6.x 任意文件写入漏洞

    目录 前言 一 复现 二 漏洞分析 前言 提示 这里可以添加本文要记录的大概内容 ThinkPHP v6 0 0 6 0 1 存在任意文件操作漏洞 主要原因是调用了session的进行了文件的写入 导致的漏洞 补丁对传入的 sessionI
  • API接口开发简述简单示例

    作为最流行的服务端语言PHP PHP Hypertext Preprocessor 在开发API方面 是很简单且极具优势的 API Application Programming Interface 应用程序接口 架构 已经成为目前互联网产
  • vue3.0删除node_modules 无用的依赖

    安装插件 npm i depcheck 查看无用的插件 npx depcheck 对应删除 npm uninstall kd layout
  • C++/C++11中变长参数的使用

    C C 11中的变长参数可以应用在宏 函数 模板中 1 宏 在C99标准中 程序员可以使用变长参数的宏定义 变长参数的宏定义是指在宏定义中参数列表的最后一个参数为省略号 而预定义宏 VA ARGS 则可以在宏定义的实现部分替换省略号所代表的