Linux:sprintf、snprintf、vsprintf、asprintf、vasprintf比较
这些函数都在stdio.h里,不过不同系统不同库,有些函数不一定提供。
1. sprintf
函数原型:
int sprintf (char *str, const char *format, ...);
extern int sprintf (char *__restrict __s, const char *__restrict __format, ...);
功能是将格式化输出,打印到str所指向的字符串内存里边,参数str是一已分配好的内存,后面跟随格式化输出。使用和printf类似,只是sprintf输出到字符串内。
例子:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>int test_sprintf()
{char *s1 = malloc(16 * sizeof(char));memset(s1, 16, 0);int ret = sprintf(s1, "sprintf %s %d", "test1", 7); // 测试正常用法printf("ret=%d, s1=%s,\n", ret, s1);memset(s1, 16, 0);ret = sprintf(s1, "0123456789 %s %d", "123456", 7); //测试超过长度printf("ret=%d, s1_strlen=%d, s1=%s,\n", ret, strlen(s1), s1);free(s1);char s2[16] = {0};ret = sprintf(s2, "sprintf %s, %d", "t2", 8); // 测试正常用法printf("ret=%d, s2=%s\n", ret, s2);ret = sprintf(s2, "0123456789 %s %d", "123456", 7); // 测试超过长度printf("ret=%d, s2=%s\n", ret, s2);char *s3 = NULL;ret = sprintf(s3, "sprintf %s, %d", "test3", 8); // 测试空指针printf("ret=%d, s2=%s", ret, s3);
}int main()
{test_sprintf();return 0;
}
输出结果:
ret=15, s1=sprintf test1 7, # 返回值为实际字符串的长度,不含'\0'
# 当格式化字符串长度超过已申请的内存大小,依然会执行,不报错,发生内存踩踏,
# 这将导致概率性运行内存错误(访问到不可访问的内存时会奔溃)
# 返回值依然是实际打印的格式化字符串的长度
ret=19, s1_strlen=19, s1=0123456789 123456 7,
ret=13, s2=sprintf t2, 8
ret=19, s2=0123456789 123456 7 # 也一样的
Segmentation fault # 目标指针是空指针时,编译不报错,运行奔溃
可见sprintf不会对内存长度进行检查,导致内存越界访问,踩踏后程序可能依然正常运行,很不容易发现问题,建议使用安全函数。
2. snprintf
函数原型
int snprintf (char *str, size_t maxlen, const char *format, ...);
/* Maximum chars of output to write in MAXLEN. */
extern int snprintf (char *__restrict __s, size_t __maxlen, const char *__restrict __format, ...);
用法也与printf类似,也是把格式化字符串输出给一块str所指向的char类型的内存数组(空间),maxlen是要写入的最大数目,超过n会被截断,后面则与printf一样的格式化字符串。
返回值: 成功则返回参数str 字符串长度,失败则返回-1,错误原因存于errno 中。需要注意的是snprintf的返回值是欲写入的字符串(即源字符串)长度,而不是实际写入的字符串度。
#include <stdio.h>
#include <string.h>
#include <stdlib.h>int test_snprintf()
{char *s1 = malloc(16 * sizeof(char));memset(s1, 16, 0);int ret = snprintf(s1, 16, "sprintf %s %d", "test1", 7); // 测试正常用法printf("ret=%d, s1=%s,\n", ret, s1);memset(s1, 16, 0);ret = snprintf(s1, 16, "0123456789 %s %d", "123456", 7); //测试超过长度printf("ret=%d, s1_strlen=%d, s1=%s,\n", ret, strlen(s1), s1);free(s1);char s2[16] = {0};ret = snprintf(s2, sizeof(s2), "sprintf %s, %d", "t2", 8); // 测试正常用法printf("ret=%d, s2=%s\n", ret, s2);ret = snprintf(s2, sizeof(s2), "0123456789 %s %d", "123456", 7); // 测试超过长度printf("ret=%d, s2=%s\n", ret, s2);char *s3 = NULL;ret = snprintf(s3, 0, "sprintf %s, %d", "test3", 8); // 测试空指针printf("ret=%d, s2=%s\n", ret, s3);
}int main()
{test_snprintf();return 0;
}
输出结果:
ret=15, s1=sprintf test1 7,
# 返回值是字符串实际长度,但未发生内存越界,字符串截断成内存大小,且留有'\0'
ret=19, s1_strlen=15, s1=0123456789 1234,
ret=13, s2=sprintf t2, 8
ret=19, s2=0123456789 1234 # 栈上内存也一样
ret=16, s2=(null) # 当传入空指针时,可获得存放格式化输出需要多少字节内存
snprintf通过限定maxlen(通常是申请的内存大小),保证内存不被越界访问。传入空指针的情况,可用于获取存放格式化字符串需要多少内存,用于malloc。
3. vprintf
使用参数列表发送格式化输出到标准输出 stdout,函数定义:
int vprintf(const char *format, va_list arg);
/* Write formatted output to stdout from argument list ARG.
This function is a possible cancellation point and therefore not
marked with __THROW. */
extern int vprintf (const char *__restrict __format, _G_va_list __arg);
参数arg: 一个表示可变参数列表的对象。这应被 中定义的 va_start 宏初始化。
返回值:如果成功,则返回写入的字符总数,否则返回一个负数。
例子:
#include <stdio.h>
#include <stdarg.h>void WriteFrmtd(char *format, ...)
{va_list args;va_start(args, format);vprintf(format, args); // 传入参数列表va_end(args);
}int main ()
{WriteFrmtd("%d variable argument\n", 1);WriteFrmtd("%d variable %s\n", 2, "arguments");return(0);
}
// 输出结果
/*
1 variable argument
2 variable arguments
*/
与printf类似,能够用于构建出自己的不定参数的函数。
4. vsprintf
有了vprintf理解,也就很好理解vsprintf了,vsprintf只是输出目标换成一char内存区域(或叫字符串)。
int vsprintf(char *str, const char *format, va_list arg);
/* Write formatted output to S from argument list ARG. */
extern int vsprintf (char *__restrict __s, const char *__restrict __format, _G_va_list __arg) __THROWNL;
vsprintf将格式化后的字符串输出到一个已经分配好的缓冲区中,需要手动指定缓冲区的大小。
返回值:如果成功,则返回写入的字符总数,否则返回一个负数。
#include <stdio.h>
#include <stdarg.h>char buffer[80];
int vspfunc(char *format, ...)
{va_list aptr;int ret;va_start(aptr, format);ret = vsprintf(buffer, format, aptr); // 传入参数列表va_end(aptr);return(ret);
}int main()
{int i = 5;float f = 27.0;char str[50] = "runoob.com";vspfunc("%d %f %s", i, f, str);printf("%s\n", buffer);return(0);
}
// 输出结果:5 27.000000 runoob.com
可见写入str时也有安全风险。
5. vsnprintf
与第二个snprintf函数类似,多了个限定内存maxlen,更加安全,其他用法一致。
int vsnprintf (char *str, size_t maxlen, const char *format, va_list arg);// 简化声明
int vsnprintf (char *__restrict __s, size_t __maxlen, const char *__restrict __format, _G_va_list __arg);
6. asprintf
vasprintf会自动分配足够大的缓冲区来存储格式化后的字符串,并将指向该缓冲区的指针作为返回值。ptr传入已空指针即可。
int asprintf (char **ptr, const char *fmt, ...)
int asprintf (char **__restrict __ptr, const char *__restrict __fmt, ...)
asprintf在有些系统库里没有提供,可用以下方法替换asprintf函数:
方法一
#include <stdio.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int test_asprintf(char **buf, const char *fmt, ...)
{va_list ap;va_start(ap, fmt);int ret = vasprintf(buf, fmt, ap);if (ret < 0) {*buf = NULL;}va_end(ap);return ret;
}
但很多系统同样也没提供vasprintf函数,可使用方法二替换asprintf函数,功能一致:
#include <stdio.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int test_asprintf(char **buf, const char *fmt, ...)
{*buf = NULL;va_list ap;va_start(ap, fmt);int count = vsnprintf(NULL, 0, fmt, ap);va_end(ap);if (count < 0) {return count;}char *buffer = malloc(count + 1);if (buffer == NULL) {return -1;}buffer[count] = 0;va_start(ap, fmt);count = vsnprintf(buffer, count + 1, fmt, ap);if (count < 0) {free(buffer);} else {*buf = buffer;}va_end(ap);return count;
}
使用参数列表过程中,这里发现过一个问题
#include <stdio.h>
#include <stdarg.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int dsl_append_sprintf(char **buf, const char *fmt, ...)
{//错误写法va_list ap;va_start(ap, fmt);*buf = NULL;int count = vsnprintf(NULL, 0, fmt, ap);if (count >= 0) {char *buffer = malloc(count + 1);if (buffer != NULL) {buffer[count] = 0;count = vsnprintf(buffer, count + 1, fmt, ap);if (count < 0) {free(buffer);} else {*buf = buffer;}}}va_end(ap);return count;
}
int main()
{char *buffer;dsl_append_sprintf(&buffer, "str= %s, data= %d\n", "test", 25);printf("%s", buffer);return 0;
}
// 输出 str= , data= 2564972
这里的写法是只用了一次va_start,被vsnprintf两次使用,导致第二次使用时,拿到的是错误的内存值。
7.vasprintf
这个和asprintf类似,只是参数是va_list
/* Write formatted output to a string dynamically allocated with `malloc'.
Store the address of the string in *PTR. */
extern int vasprintf (char **__restrict __ptr, const char *__restrict __f, _G_va_list __arg)
8. fprintf
/* Write formatted output to STREAM.
This function is a possible cancellation point and therefore not
marked with __THROW. */
extern int fprintf (FILE *__restrict __stream, const char *__restrict __format, ...);
9. vfprintf
/* Write formatted output to S from argument list ARG.
This function is a possible cancellation point and therefore not
marked with __THROW. */
extern int vfprintf (FILE *__restrict __s, const char *__restrict __format, _G_va_list __arg);
10. vdprintf
/* Write formatted output to a file descriptor. */
extern int vdprintf (int __fd, const char *__restrict __fmt, _G_va_list __arg)
相关文章:
Linux:sprintf、snprintf、vsprintf、asprintf、vasprintf比较
这些函数都在stdio.h里,不过不同系统不同库,有些函数不一定提供。 1. sprintf 函数原型: int sprintf (char *str, const char *format, ...); extern int sprintf (char *__restrict __s, const char *__restrict __format, ...); 功能是将…...
Github远程仓库改名字之后,本地git如何配置?
文章目录 缘由解决方案 缘由 今天在github创建一个仓库,备份一下本地电脑上的资料。起初随便起一个仓库名字,后来修改之。既然远程仓库改名,那么本地仓库需要更新地址。这里采用SSH格式。 解决方案 如果你的GitHub仓库改名了,你…...
Objective-C学习笔记(ARC,分类,延展)4.10
1.自动释放池autoreleasepool:存入到自动释放池的对象,在自动释放池销毁时,会自动调用池内所有对象的release方法。调用autorelease方法将对象放入自动释放池。 Person *p1 [ [ [ Person alloc ] init ] autorelease]; 2.在类方法里写一个…...

02 Git 之IDEA 集成使用 GitHub(Git同时管理本地仓库和远程仓库)
2 .IDEA 集成使用 GitHub(Git同时管理本地仓库和远程仓库) 首先在 IDEA 的设置中绑定 GitHub 的账号 先创建一个 test1.txt 文件,内容为 aaa. 最上一栏 VCS, SHARE ON GitHub,然后选择要发送到远程仓库的文件即可。…...

CSS滚动条样式修改
前言 目前我们可以通过 CSS伪类 来实现滚动条的样式修改,以下为修改滚动条样式用到的CSS伪类: ::-webkit-scrollbar — 整个滚动条 ::-webkit-scrollbar-button — 滚动条上的按钮 (上下箭头) ::-webkit-scrollbar-thumb — 滚动条上的滚动滑块 ::-web…...

《零秒思考》像麦肯锡精英一样思考 - 三余书屋 3ysw.net
零秒思考:像麦肯锡精英一样思考 大家好,今天我们要深入探讨的著作是《零秒思考》。在领导提出问题时,我们常常会陷入沉思,却依然难以有所进展,仿佛原地踏步,但是身边的同事却能够立即给出清晰的回答。这种…...

使用docker制作Android镜像(实操可用)
一、安装包准备 1、准备jdk 下载地址:Java Downloads | Oracle 注意版本!!!!!! 我下载的jdk17,不然后面构建镜像报错,就是版本不对 2、准备安装的工具包 ttps://dev…...

大厂MVP技术JAVA架构师培养
课程介绍 这是一个很强悍的架构师涨薪计划课程,课程由专家级MVP讲师进行教学,分为是一个章节进行分解式面试及讲解,不仅仅是面试,更像是一个专业的架构师研讨会课程。课程内容从数据结构与算法、Spring Framwork、JVM原理、 JUC并…...
uniapp实现文件和图片选择上传功能实现
主要介绍了uni-file-picker文件选择上传,本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下 上传一张: <template><view class="container example"><uni-forms ref="baseForm" …...

2024认证杯数学建模C题思路模型代码
目录 2024认证杯数学建模C题思路模型代码:4.11开赛后第一时间更新,获取见文末名片 以下为2023年认证杯C题: 2024年认证杯数学建模C题思路模型代码见此 2024认证杯数学建模C题思路模型代码:4.11开赛后第一时间更新,获…...

springcloud项目中,nacos远程的坑
我将允许重写放在了远程nacos的注册中心,还是无法启动。这个bug,想想确实也可以解决。 解决方案 1.配置到bootstrap.yml或者application.yml中 2.实现EnvironmentPostProcessor并设置值,并在META-INF中注入我们的类 org.springframework.boot…...

南京航空航天大学-考研科目-513测试技术综合 高分整理内容资料-01-单片机原理及应用分层教程-单片机有关常识部分
系列文章目录 高分整理内容资料-01-单片机原理及应用分层教程-单片机有关常识部分 文章目录 系列文章目录前言总结 前言 单片机的基础内容繁杂,有很多同学基础不是很好,对一些细节也没有很好的把握。非常推荐大家去学习一下b站上的哈工大 单片机原理及…...
【python】Flask Web框架
文章目录 WSGI(Web服务器网关接口)示例Web应用程序Web框架Flask框架创建项目安装Flask创建一个基本的 Flask 应用程序调试模式路由添加变量构造URLHTTP方法静态文件模板—— Jinja2模板文件(Template File)<...

Electron+React 搭建桌面应用
创建应用程序 创建 Electron 应用 使用 Webpack 创建新的 Electron 应用程序: npm init electron-applatest my-new-app -- --templatewebpack 启动应用 npm start 设置 Webpack 配置 添加依赖包,确保可以正确使用 JSX 和其他 React 功能ÿ…...

基于Android的记单词App系统的设计与实现
博主介绍:✌IT徐师兄、7年大厂程序员经历。全网粉丝15W、csdn博客专家、掘金/华为云//InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取源码联系🍅 👇🏻 精彩专栏推荐订阅👇dz…...

ELK 企业级日志分析系统 简单介绍
目录 一 ELK 简介 1, elk 是什么 2,elk 架构图 3,elk 日志处理步骤 二 Elasticsearch 简介 1, Elasticsearch 是什么 2, Elasticsearch 的核心概念 3, Elasticsearch 的原理 三 Logstas…...

GET与POST:详述HTTP两大请求方法的语义、数据处理机制、安全特性与适用场景
GET和POST方法在HTTP请求中具有明确的角色分工和特性差异。GET适用于读取操作和不敏感数据的传递,强调可缓存性和安全性,而POST适用于写入操作和敏感数据的提交,提供了更大的数据承载能力和更强的隐私保护。本文详细介绍了GET与POST请求方法的…...

Unity Pro 2019 for Mac:专业级游戏引擎,助力创意无限延伸!
Unity Pro 2019是一款功能强大的游戏开发引擎,其特点主要体现在以下几个方面: 强大的渲染技术:Unity Pro 2019采用了新的渲染技术,包括脚本化渲染流水线,能够轻松自定义渲染管线,通过C#代码和材料材质&…...

C++设计模式:单例模式(十)
1、单例设计模式 单例设计模式,使用的频率比较高,整个项目中某个特殊的类对象只能创建一个 并且该类只对外暴露一个public方法用来获得这个对象。 单例设计模式又分懒汉式和饿汉式,同时对于懒汉式在多线程并发的情况下存在线程安全问题 饿汉…...

openssl3.2 - exp - zlib
文章目录 openssl3.2 - exp - zlib概述笔记命令行实现程序实现备注 - 压缩时无法base64压缩时无法带口令压缩实现 - 对buffer进行压缩和解压缩测试效果工程实现main.cppCOsslZlibBuffer.hCOsslZlibBuffer.cpp总结END openssl3.2 - exp - zlib 概述 客户端和服务端进行数据交换…...

深度学习在微纳光子学中的应用
深度学习在微纳光子学中的主要应用方向 深度学习与微纳光子学的结合主要集中在以下几个方向: 逆向设计 通过神经网络快速预测微纳结构的光学响应,替代传统耗时的数值模拟方法。例如设计超表面、光子晶体等结构。 特征提取与优化 从复杂的光学数据中自…...
[2025CVPR]DeepVideo-R1:基于难度感知回归GRPO的视频强化微调框架详解
突破视频大语言模型推理瓶颈,在多个视频基准上实现SOTA性能 一、核心问题与创新亮点 1.1 GRPO在视频任务中的两大挑战 安全措施依赖问题 GRPO使用min和clip函数限制策略更新幅度,导致: 梯度抑制:当新旧策略差异过大时梯度消失收敛困难:策略无法充分优化# 传统GRPO的梯…...

51c自动驾驶~合集58
我自己的原文哦~ https://blog.51cto.com/whaosoft/13967107 #CCA-Attention 全局池化局部保留,CCA-Attention为LLM长文本建模带来突破性进展 琶洲实验室、华南理工大学联合推出关键上下文感知注意力机制(CCA-Attention),…...
电脑插入多块移动硬盘后经常出现卡顿和蓝屏
当电脑在插入多块移动硬盘后频繁出现卡顿和蓝屏问题时,可能涉及硬件资源冲突、驱动兼容性、供电不足或系统设置等多方面原因。以下是逐步排查和解决方案: 1. 检查电源供电问题 问题原因:多块移动硬盘同时运行可能导致USB接口供电不足&#x…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...

【配置 YOLOX 用于按目录分类的图片数据集】
现在的图标点选越来越多,如何一步解决,采用 YOLOX 目标检测模式则可以轻松解决 要在 YOLOX 中使用按目录分类的图片数据集(每个目录代表一个类别,目录下是该类别的所有图片),你需要进行以下配置步骤&#x…...
Unit 1 深度强化学习简介
Deep RL Course ——Unit 1 Introduction 从理论和实践层面深入学习深度强化学习。学会使用知名的深度强化学习库,例如 Stable Baselines3、RL Baselines3 Zoo、Sample Factory 和 CleanRL。在独特的环境中训练智能体,比如 SnowballFight、Huggy the Do…...
大数据学习(132)-HIve数据分析
🍋🍋大数据学习🍋🍋 🔥系列专栏: 👑哲学语录: 用力所能及,改变世界。 💖如果觉得博主的文章还不错的话,请点赞👍收藏⭐️留言Ǵ…...

深度学习习题2
1.如果增加神经网络的宽度,精确度会增加到一个特定阈值后,便开始降低。造成这一现象的可能原因是什么? A、即使增加卷积核的数量,只有少部分的核会被用作预测 B、当卷积核数量增加时,神经网络的预测能力会降低 C、当卷…...

【VLNs篇】07:NavRL—在动态环境中学习安全飞行
项目内容论文标题NavRL: 在动态环境中学习安全飞行 (NavRL: Learning Safe Flight in Dynamic Environments)核心问题解决无人机在包含静态和动态障碍物的复杂环境中进行安全、高效自主导航的挑战,克服传统方法和现有强化学习方法的局限性。核心算法基于近端策略优化…...