在C#中,格式化输出可以使用索引占位符以及复合格式的占位符,可以不用关心后面参数是什么类型,使用起来非常方便,如下简单的示例:

Console.WriteLine("{2} {1} {0} {{{2}}}", "Hello, World!", 1, 8.8);

会输出:

8.8 1 Hello, World! {8.8}

规范可以参见:复合格式设置

在C语言标准库中使用的printf系列函数中,需要使用格式化字符串明确指定后面参数的类型,如果指定错误,可能会引发灾难! 那C语言,可以实现上述功能吗 ? 在C11泛型的加持下,使用关键字_Generic也可以实现上述功能了,下面笔者就一步步来实现,并且支持可以不指定索引,支持C标准格式化字符串。

一、实现效果

我们假定要实现的是printxprint,其使用的方式如下:

main.c:

#include "printx.h"int main() {char a = 'A';unsigned char b = 130;int age = 25;unsigned int ui = 123456;long l = 12345678;unsigned ul = 123456789;long long ll = 98761234567890;unsigned long long ull = 9998761234567890;float f = 2.71828F;double pi = 3.14159;const char* name = "Witton";unsigned int hexv = 8192;auto x = '\n';printx("test\n");printx("Name: {{{}}}, Age: {}\n", name, age);printx("Age: {1}, Name: {}\n", name, age);printx("Name: {0}, Age: {1}\n", name, age);printx("Pi: {0:.2f}\n", pi);printx("Hex: {0:#x}, Again: {0:#08x}\n", hexv);printx("Swap order: {1}, {0}\n", "first", "second");printx("Char: {0}\n", (char)'A');printx("Repeat: {0} {0} {1:.1f} {}\n", age, pi, f);printx("1:{} 2:{} 3:{} 4:{} 5:{}, 6:{}, 7:{}, 8:{}, 9:{}, 10:{}\n", name, a,b, age, ui, l, ul, ll, ull, f);print(name, a, b, age, ui, l, ul, ll, ull, (char)'\n');return 0;
}

其运行结果为:

test
Name: {Witton}, Age: 25
Age: 25, Name: Witton  
Name: Witton, Age: 25  
Pi: 3.14
Hex: 0x2000, Again: 0x002000
Swap order: second, first
Char: A
Repeat: 25 25 3.1 25
1:Witton 2:A 3:130 4:25 5:123456, 6:12345678, 7:123456789, 8:98761234567890, 9:9998761234567890, 10:2.718280
Witton A 130 25 123456 12345678 123456789 98761234567890 9998761234567890

在这里插入图片描述

需要注意的是:C语言中字面字符的类型是int,而不是char,所以:

printx("Char: {0}\n", (char)'A');

中需要强制转换成char类型才能正常输出字母A。

在这里插入图片描述

二、统一类型

由于在调用的过程中传入的参数可能是各种数据类型,我们需要统一转换成一种自定义类型,在自定义类型中去标识实际的数据类型。

typedef enum {T_UNKNOWN,T_CHAR,T_BYTE,T_STRING,T_BYTES,T_INT,T_UINT,T_LONG,T_ULONG,T_LONGLONG,T_ULONGLONG,T_FLOAT,T_DOUBLE,
} EDataType;typedef struct {EDataType type;union {char c;unsigned char b;int i;unsigned int u;long l;unsigned long ul;long long ll;unsigned long long ull;float f;double d;const char* s;const unsigned char* bs;} v;
} FmtArg;static inline FmtArg make_char(char v) {return (FmtArg){T_CHAR, {.c = v}};
}static inline FmtArg make_uchar(unsigned char v) {return (FmtArg){T_BYTE, {.b = v}};
}static inline FmtArg make_string(const char* s) {return (FmtArg){T_STRING, {.s = s}};
}static inline FmtArg make_bytes(const unsigned char* s) {return (FmtArg){T_BYTES, {.bs = s}};
}static inline FmtArg make_int(int v) {return (FmtArg){T_INT, {.i = v}};
}
static inline FmtArg make_uint(unsigned int v) {return (FmtArg){T_UINT, {.u = v}};
}static inline FmtArg make_long(long v) {return (FmtArg){T_LONG, {.l = v}};
}static inline FmtArg make_ulong(unsigned long v) {return (FmtArg){T_ULONG, {.ul = v}};
}static inline FmtArg make_longlong(long long v) {return (FmtArg){T_LONGLONG, {.ll = v}};
}static inline FmtArg make_ulonglong(unsigned long long v) {return (FmtArg){T_ULONGLONG, {.ull = v}};
}static inline FmtArg make_float(float v) {return (FmtArg){T_FLOAT, {.f = v}};
}static inline FmtArg make_double(double v) {return (FmtArg){T_DOUBLE, {.d = v}};
}static inline FmtArg make_unknown(void) {return (FmtArg){T_UNKNOWN};
}

有了自定义类型了,就可以声明具体实现的C函数了:

void printx_impl(const char* fmt, int arg_count, FmtArg* argv);

前面的fmt就是类似C#的格式符,也可以是nullptr,表明不需要格式字符串,自动依次使用参数;arg_count表明有几个参数,决定着后面参数argv的个数;argv为实际的参数信息。

而给用户调用的API,printxprint其实是一个宏,它类似如下声明:

#define printx(fmt, ...)
#define print(...)

我们需要在宏中调用实际工作的C函数printx_impl,在调用前需要将传入的参数转换成自定义数据类型FmtArg,并且准备好printx_impl函数需要的参数。

三、计算参数个数

如何计算宏参数...中包含的参数个数?
在C/C++中参数... 可能包含0个到多个参数,我们假定最多支持10个参数。为了计算宏参数个数,需要定义一个匹配或者说是取宏参数的宏:

#define GET_MACRO(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, n, ...) n

GET_MACRO的参数列表是:_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, n, ...,这里的...表示剩余参数,但在宏定义中,我们只取n

然后定义计算宏参数个数的宏:

#define COUNT_ARGS(...) \GET_MACRO(0, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

__VA_ARGS__中可能是0个参数,C/C++编译器有一种写法:##__VA_ARGS__,当是0个时,会把前面的逗号去掉,变为:

GET_MACRO(0, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

GET_MACRO第12个参数,结果为0

如果有多个参数,则依次填充。比如,参数1,2,3,变为:

GET_MACRO(0, 1, 2, 3, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)

GET_MACRO第12个参数,结果为3

四、转换参数

通过前面的方法,我们知道了参数个数,接下来就是实现参数类型的转换了,要实现类型转换就需要知道是什么类型,C11提供了_Generic关键字,只有它才能识别类型,实现泛型。其语法参见:https://cppreference.cn/w/c/language/generic

定义如下宏来将参数转换成自定义类型FmtArg

#define MAKE_FMTARG(x)                    \_Generic((x),                           \char: make_char,                    \unsigned char: make_uchar,          \const char*: make_string,           \char*: make_string,                 \const unsigned char*: make_bytes,   \unsigned char*: make_bytes,         \int: make_int,                      \unsigned int: make_uint,            \long: make_long,                    \unsigned long: make_ulong,          \long long: make_longlong,           \unsigned long long: make_ulonglong, \float: make_float,                  \double: make_double,                \default: make_unknown)(x)

使用获取参数个数一样的方法,我们也可以逐个取参数,然后调用MAKE_FMTARG进行转换,先定义一组宏:

#define APPLY0(m, a) 0 // 这里定义为0,避免编译器警告
#define APPLY1(m, a) m(a)
#define APPLY2(m, a, ...) m(a), APPLY1(m, __VA_ARGS__)
#define APPLY3(m, a, ...) m(a), APPLY2(m, __VA_ARGS__)
#define APPLY4(m, a, ...) m(a), APPLY3(m, __VA_ARGS__)
#define APPLY5(m, a, ...) m(a), APPLY4(m, __VA_ARGS__)
#define APPLY6(m, a, ...) m(a), APPLY5(m, __VA_ARGS__)
#define APPLY7(m, a, ...) m(a), APPLY6(m, __VA_ARGS__)
#define APPLY8(m, a, ...) m(a), APPLY7(m, __VA_ARGS__)
#define APPLY9(m, a, ...) m(a), APPLY8(m, __VA_ARGS__)
#define APPLY10(m, a, ...) m(a), APPLY9(m, __VA_ARGS__)#define APPLY(m, ...)                                                          \GET_MACRO(0, ##__VA_ARGS__, APPLY10, APPLY9, APPLY8, APPLY7, APPLY6, APPLY5, \APPLY4, APPLY3, APPLY2, APPLY1, APPLY0)(m, __VA_ARGS__)

这组宏支持0~10个参数,对每个参数调用mm可以是宏,也可以是函数。我们这里需要调用的是MAKE_FMTARG宏。

五、实现printxprint

现在可以写出printxprint宏的实现了:

#define printx(fmt, ...)                            \printx_impl(fmt, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})#define print(...)                                \printx_impl(0, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})

(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)}构建了一个FmtArg的数组,如果参数个数为0,则是(FmtArg[]){0}

六、扩展实现fprintxfprint

目前的API还只支持输出到标准输出设备stdout,无法输出到文件,比如想输出到日志文件,只需要添加一个FILE* fp参数即可,相应修改如下:

void printx_impl(FILE* fp, const char* fmt, int arg_count, FmtArg* argv);#define printx(fmt, ...)                            \printx_impl(stdout, fmt, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})#define print(...)                                \printx_impl(stdout, 0, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})

实现fprintxfprint宏:

#define fprintx(fp, fmt, ...)                   \printx_impl(fp, fmt, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})#define fprint(fp, ...)                       \printx_impl(fp, 0, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})

七、源码

printx.h:

// copyright(C), author: Witton
// email: witton@163.com#ifndef _PRINT_X_H_INCLUDE_
#define _PRINT_X_H_INCLUDE_#include <stdio.h>typedef enum {T_UNKNOWN,T_CHAR,T_BYTE,T_STRING,T_BYTES,T_INT,T_UINT,T_LONG,T_ULONG,T_LONGLONG,T_ULONGLONG,T_FLOAT,T_DOUBLE,
} EDataType;typedef struct {EDataType type;union {char c;unsigned char b;int i;unsigned int u;long l;unsigned long ul;long long ll;unsigned long long ull;float f;double d;const char* s;const unsigned char* bs;} v;
} FmtArg;static inline FmtArg make_char(char v) {return (FmtArg){T_CHAR, {.c = v}};
}static inline FmtArg make_byte(unsigned char v) {return (FmtArg){T_BYTE, {.b = v}};
}static inline FmtArg make_string(const char* s) {return (FmtArg){T_STRING, {.s = s}};
}static inline FmtArg make_bytes(const unsigned char* s) {return (FmtArg){T_BYTES, {.bs = s}};
}static inline FmtArg make_int(int v) {return (FmtArg){T_INT, {.i = v}};
}
static inline FmtArg make_uint(unsigned int v) {return (FmtArg){T_UINT, {.u = v}};
}static inline FmtArg make_long(long v) {return (FmtArg){T_LONG, {.l = v}};
}static inline FmtArg make_ulong(unsigned long v) {return (FmtArg){T_ULONG, {.ul = v}};
}static inline FmtArg make_longlong(long long v) {return (FmtArg){T_LONGLONG, {.ll = v}};
}static inline FmtArg make_ulonglong(unsigned long long v) {return (FmtArg){T_ULONGLONG, {.ull = v}};
}static inline FmtArg make_float(float v) {return (FmtArg){T_FLOAT, {.f = v}};
}static inline FmtArg make_double(double v) {return (FmtArg){T_DOUBLE, {.d = v}};
}static inline FmtArg make_unknown(void) {return (FmtArg){T_UNKNOWN};
}#define MAKE_FMTARG(x)                    \_Generic((x),                           \char: make_char,                    \unsigned char: make_byte,          \const char*: make_string,           \char*: make_string,                 \const unsigned char*: make_bytes,   \unsigned char*: make_bytes,         \int: make_int,                      \unsigned int: make_uint,            \long: make_long,                    \unsigned long: make_ulong,          \long long: make_longlong,           \unsigned long long: make_ulonglong, \float: make_float,                  \double: make_double,                \default: make_unknown)(x)#define GET_MACRO(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, n, ...) n#define COUNT_ARGS(...) \GET_MACRO(0, ##__VA_ARGS__, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)#define APPLY0(m, a) 0  // 这里定义为0,避免编译器警告
#define APPLY1(m, a) m(a)
#define APPLY2(m, a, ...) m(a), APPLY1(m, __VA_ARGS__)
#define APPLY3(m, a, ...) m(a), APPLY2(m, __VA_ARGS__)
#define APPLY4(m, a, ...) m(a), APPLY3(m, __VA_ARGS__)
#define APPLY5(m, a, ...) m(a), APPLY4(m, __VA_ARGS__)
#define APPLY6(m, a, ...) m(a), APPLY5(m, __VA_ARGS__)
#define APPLY7(m, a, ...) m(a), APPLY6(m, __VA_ARGS__)
#define APPLY8(m, a, ...) m(a), APPLY7(m, __VA_ARGS__)
#define APPLY9(m, a, ...) m(a), APPLY8(m, __VA_ARGS__)
#define APPLY10(m, a, ...) m(a), APPLY9(m, __VA_ARGS__)#define APPLY(m, ...)                                                          \GET_MACRO(0, ##__VA_ARGS__, APPLY10, APPLY9, APPLY8, APPLY7, APPLY6, APPLY5, \APPLY4, APPLY3, APPLY2, APPLY1, APPLY0)(m, __VA_ARGS__)#define printx(fmt, ...)                            \printx_impl(stdout, fmt, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})#define print(...)                                \printx_impl(stdout, 0, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})#define fprintx(fp, fmt, ...)                   \printx_impl(fp, fmt, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})#define fprint(fp, ...)                       \printx_impl(fp, 0, COUNT_ARGS(__VA_ARGS__), \(FmtArg[]){APPLY(MAKE_FMTARG, ##__VA_ARGS__)})void printx_impl(FILE* fp, const char* fmt, int arg_count, FmtArg* argv);#endif

printx.c:

// copyright(C), author: Witton
// email: witton@163.com#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>#include "printx.h"#if __STDC_VERSION__ >= 202311L
#ifdef NULL
#undef NULL
#define NULL nullptr
#endif
#endifstatic void print_one_arg(FILE* fp, const char* fmt_spec, FmtArg arg) {if (!fmt_spec[0]) {switch (arg.type) {case T_CHAR:(void)fprintf(fp,"%c", arg.v.c);break;case T_BYTE:(void)fprintf(fp,"%hhu", arg.v.b);break;case T_INT:(void)fprintf(fp,"%d", arg.v.i);break;case T_UINT:(void)fprintf(fp,"%u", arg.v.u);break;case T_LONG:(void)fprintf(fp,"%ld", arg.v.l);break;case T_LONGLONG:(void)fprintf(fp,"%lld", arg.v.ll);break;case T_ULONGLONG:(void)fprintf(fp,"%llu", arg.v.ull);break;case T_FLOAT:(void)fprintf(fp,"%f", arg.v.f);break;case T_DOUBLE:(void)fprintf(fp,"%lf", arg.v.d);break;case T_STRING:(void)fprintf(fp,"%s", arg.v.s);break;default:(void)fprintf(fp,"<?(by witton)>");break;}return;}char real_fmt[64];(void)snprintf(real_fmt, sizeof(real_fmt), "%%%s", fmt_spec);switch (arg.type) {case T_CHAR:(void)fprintf(fp,real_fmt, arg.v.c);break;case T_BYTE:(void)fprintf(fp,real_fmt, arg.v.b);break;case T_INT:(void)fprintf(fp,real_fmt, arg.v.i);break;case T_UINT:(void)fprintf(fp,real_fmt, arg.v.u);break;case T_LONG:(void)fprintf(fp,real_fmt, arg.v.l);break;case T_LONGLONG:(void)fprintf(fp,real_fmt, arg.v.ll);break;case T_ULONGLONG:(void)fprintf(fp,real_fmt, arg.v.ull);break;case T_FLOAT:(void)fprintf(fp,real_fmt, arg.v.f);break;case T_DOUBLE:(void)fprintf(fp,real_fmt, arg.v.d);break;case T_STRING:(void)fprintf(fp,real_fmt, arg.v.s);break;default:(void)fprintf(fp,"<?(by witton)>");break;}
}static inline void handle_printx_fmt(FILE* fp, const char* p,const char* end,int arg_count,FmtArg* argv) {char index_str[16] = {0};char spec[32] = {0};int idx = 0;const char* colon = memchr(p + 1, ':', end - (p + 1));if (colon) {size_t len_idx = colon - (p + 1);strncpy(index_str, p + 1, len_idx);strncpy(spec, colon + 1, end - (colon + 1));} else {strncpy(index_str, p + 1, end - (p + 1));}if (isdigit((unsigned char)index_str[0])) {idx = strtol(index_str, NULL, 10);if (idx >= 0 && idx < arg_count) {print_one_arg(fp, spec, argv[idx]);} else {(void)fprintf(fp, "<BAD_INDEX(by witton)>");}} else {(void)fprintf(fp, "<BAD_FORMAT(by witton)>");}
}void printx_impl(FILE* fp, const char* fmt, int arg_count, FmtArg* argv) {if (NULL == fmt) {for (int i = 0; i < arg_count; ++i) {print_one_arg(fp, "", argv[i]);(void)putc(' ', fp);}return;}int used_args = 0;const char* p = fmt;while (*p) {if (p[0] == '{' && p[1] == '}' && used_args < arg_count) {print_one_arg(fp, "", argv[used_args++]);p += 2;  // 跳过 '}'} else if (*p == '{' && *(p + 1) != '{') {const char* end = strchr(p, '}');if (!end) {(void)putc(*p++, fp);continue;}handle_printx_fmt(fp, p, end, arg_count, argv);p = end + 1;} else if (*p == '{' && *(p + 1) == '{') {(void)putc('{', fp);p += 2;} else if (*p == '}' && *(p + 1) == '}') {(void)putc('}', fp);p += 2;} else {(void)putc(*p++, fp);}}
}

至此,我们可以像C#一样写格式化输出代码了,可以不担心格式符写错了。但是如果了使用自定义格式符,即类似{1:.1f}中有冒号后面标准C格式符,则依旧需要小心格式符是否写正确!

如果本文对你有帮助,欢迎点赞收藏!

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

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

相关文章

一人公司方法论

** 一人公司方法论 ** 那什么是一人公司&#xff1f; 字面的理解就是一个人运营的公司&#xff0c;但实际上它指代的是比较少的人运营的小公司&#xff0c;一般来说 1 ~ 3 个人运营的公司&#xff0c;也可以把它放到一人公司的范围以内。其他一些形式&#xff0c;比如说一个人再…

Ceph CSI 镜像删除流程与 Trash 机制失效问题分析文档

#作者&#xff1a;闫乾苓 文章目录一、问题背景二、实际行为三、源码分析四、分析与推论五、期望行为与建议优化六、结论一、问题背景 在生产环境中&#xff0c;为避免因误操作导致的永久数据丢失&#xff0c;Ceph RBD 提供了 Trash 功能&#xff0c;允许将镜像“软删除”至回…

.NET Framework 3.5 不原生支持PreApplicationStartMethod特性

.NET Framework 3.5 不原生支持PreApplicationStartMethod特性。这个特性是在 .NET Framework 4.0 中引入的&#xff0c;用于在应用程序启动早期执行初始化逻辑。 在.NET 3.5 中&#xff0c;如果你需要实现类似的 “应用启动时自动注册模块” 功能&#xff0c;需要通过手动配置…

智能巡检技术浅析

从机载智能硬件到深度学习算法&#xff0c;从实时边缘计算到数字孪生平台&#xff0c;无人机AI智能巡检通过多模态感知、自主决策和持续进化&#xff0c;实现从"被动检查"到"主动预防"的跨越式发展。机载智能硬件边缘计算与机载AI芯片当代先进巡检无人机已…

【图像算法 - 11】基于深度学习 YOLO 与 ByteTrack 的目标检测与多目标跟踪系统(系统设计 + 算法实现 + 代码详解 + 扩展调优)

前言 详细视频介绍 【图像算法 - 11】基于深度学习 YOLO 与 ByteTrack 的目标检测与多目标跟踪系统&#xff08;系统设计 算法实现 代码详解 扩展调优&#xff09;在计算机视觉应用中&#xff0c;目标检测与多目标跟踪的结合是实现智能视频分析的关键。本文基于 YOLO 检测模…

AI加持下的智能路由监控:Amazon VPC Direct Connect实战指南

> 一次流量突增引发的生产事故,如何催生出融合流日志、机器学习与自动化告警的智能监控体系 深夜2点,电商平台运维负责人李明的手机疯狂报警——北美用户下单量断崖式下跌。他紧急登录系统,发现跨境专线延迟飙升至2000ms。**经过3小时的排查**,罪魁祸首竟是新部署的CDN…

具身智能竞速时刻,百度百舸提供全栈加速方案

2025年&#xff0c;全球具身智能赛道迎来快速发展期&#xff0c;技术方向日益清晰。每一家企业都面临着同样的核心命题&#xff1a;如何将前沿的模型能力&#xff0c;转化为在真实世界各类场景中可规模化应用落地的机器人产品&#xff1f;这背后&#xff0c;是研发团队对模型迭…

JavaScript 压缩与混淆实战:Terser 命令行详解

使用 Terser 压缩 JavaScript 文件&#xff08;基础 现代语法问题解决&#xff09; 在前端开发中&#xff0c;随着业务复杂度增加&#xff0c;JavaScript 文件体积越来越大。 文件大带来的问题&#xff1a; 加载慢&#xff1a;文件越大&#xff0c;浏览器下载和解析时间越长…

【数据结构初阶】--排序(三):冒泡排序、快速排序

&#x1f618;个人主页&#xff1a;Cx330❀ &#x1f440;个人简介&#xff1a;一个正在努力奋斗逆天改命的二本觉悟生 &#x1f4d6;个人专栏&#xff1a;《C语言》《LeetCode刷题集》《数据结构-初阶》 前言&#xff1a;在上篇博客的学习中&#xff0c;我们掌握了直接选择排序…

名词概念:什么是尾部误差?

“尾部误差”就是指误差分布在两端的那一小撮、但数值特别大的误差——也就是离中心&#xff08;均值/中位数&#xff09;很远的“极端样本”的误差。对应统计学里的“分布尾部”&#xff08;tails&#xff09;。通俗点&#xff1a;大多数样本误差都很小&#xff0c;但总会有少…

记对外国某服务器的内网渗透

本专栏是笔者的网络安全学习笔记&#xff0c;一面分享&#xff0c;同时作为笔记 文章目录前文链接前言上线CS上线rdp后渗透信息收集SMB Pth攻击权限维持魔幻上线提权关Windows Defenderend前文链接 WAMP/DVWA/sqli-labs 搭建burpsuite工具抓包及Intruder暴力破解的使用目录扫描…

速卖通平台关键字搜索商品列表列表接口实现指南:从接口分析到代码落地

在跨境电商开发中&#xff0c;速卖通平台的商品数据获取是许多开发者关注的焦点。本文将详细介绍如何实现速卖通关键字搜索商品列表接口&#xff0c;涵盖接口请求参数分析、签名机制、分页处理及完整代码实现&#xff0c;帮助开发者快速对接速卖通开放平台。一、接口基本信息速…

UE UDP通信

1.确保工程为C工程&#xff0c;在项目工程的xx.Build.cs中加入Networking和Sockets模块。PublicDependencyModuleNames.AddRange(new string[] { "Core", "CoreUObject", "Engine", "InputCore", "Networking", "Socke…

JavaScript 逻辑运算符与实战案例:从原理到落地

JavaScript 中的逻辑运算符不仅是条件判断的核心&#xff0c;还能通过“短路特性”简化代码&#xff1b;结合 DOM 操作的实战案例&#xff0c;更能体现其灵活性。本文整理了逻辑运算符的个人理解、优先级规则&#xff0c;以及 4 个高频实战需求的实现方案&#xff0c;附个人思路…

Android RxJava 过滤与条件操作详解

RxJava 是一个基于观察者模式的响应式编程库&#xff0c;在 Android 开发中被广泛使用。其中&#xff0c;过滤和条件操作是 RxJava 中非常重要的一部分&#xff0c;它们允许我们对数据流进行精细控制。本文将详细介绍 RxJava 中常用的过滤与条件操作符及其使用场景。一、过滤操…

云手机都具有哪些特点?

云手机拥有着便捷的远程操作功能&#xff0c;让用户无论身处何地&#xff0c;只要能连接网络&#xff0c;就能通过手机、电脑等终端设备远程操控云手机&#xff0c;无需受限于物理位置&#xff0c;大大提升了工作的灵活性与便捷性。云手机主要是依赖于云计算技术&#xff0c;能…

Sparse-ICP—(4) 加权稀疏迭代最近点算法(matlab版)

目录 一、算法原理 1、原理概述 2、参考文献 二、代码实现 三、结果展示 一、算法原理 1、原理概述 见:Sparse-ICP—(1)稀疏迭代最近点算法 2、参考文献 二、代码实现 SparseWeightedDistance.m function [move_points,T] =

统信UOS安装NFS共享文件夹

在 UOS ARM 架构系统上安装和配置 NFS 服务&#xff0c;实现与局域网中其他服务器共享文件夹的步骤如下&#xff1a;1. 安装 NFS 服务首先更新系统并安装 NFS 服务器组件&#xff1a;bash# 更新软件包列表 sudo apt update# 安装NFS服务器 sudo apt install nfs-kernel-server …

【完整源码+数据集+部署教程】孔洞检测系统源码和数据集:改进yolo11-RetBlock

背景意义 研究背景与意义 随着工业自动化和智能制造的快速发展&#xff0c;孔洞检测作为关键的质量控制环节&#xff0c;受到了广泛关注。孔洞的存在可能会影响产品的强度、密封性和整体性能&#xff0c;因此&#xff0c;准确、快速地检测孔洞对于保障产品质量至关重要。传统的…

k8s环境使用Operator部署Seaweedfs集群(一)

#作者&#xff1a;闫乾苓 文章目录4.1 前置条件4.2 部署seaweedfs-operator4.3 准备operator镜像SeaweedFS Operator是一个Kubernetes Operator&#xff0c;用于自动化部署和管理SeaweedFS集群 README.md:6-8 。部署分为两个阶段&#xff1a;首先部署Operator本身&#xff0c;然…