求助:如何解决sprintf参数类型错误导致程序崩溃

w195438178 2013-11-28 10:40:45
我现在在项目记录日志使用sprintf。经常因为在记录日志的参数错误导致程序崩溃。很多日志记录在出错的地方,执行的可能性很小。加入万一这些日志参数错误,导致执行的时候就会导致程序崩溃。我想用异常机制捕获异常,从而让程序不崩溃,可是异常好像捕获不到这样的错误,sprintf能使用异常处理机制捕获异常吗?或者该如何处理这样的程序崩溃问题,求助大神帮忙!
...全文
909 31 打赏 收藏 转发到动态 举报
写回复
用AI写文章
31 条回复
切换为时间正序
请发表友善的回复…
发表回复
w195438178 2013-11-29
  • 打赏
  • 举报
回复
引用 30 楼 zhao4zhong1 的回复:
仅供参考
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
    #include <windows.h>
    #include <io.h>
#else
    #include <unistd.h>
    #include <sys/time.h>
    #include <pthread.h>
    #define  CRITICAL_SECTION   pthread_mutex_t
    #define  _vsnprintf         vsnprintf
#endif
//Log{
#define MAXLOGSIZE 20000000
#define MAXLINSIZE 16000
#include <time.h>
#include <sys/timeb.h>
#include <stdarg.h>
char logfilename1[]="MyLog1.log";
char logfilename2[]="MyLog2.log";
static char logstr[MAXLINSIZE+1];
char datestr[16];
char timestr[16];
char mss[4];
CRITICAL_SECTION cs_log;
FILE *flog;
#ifdef WIN32
void Lock(CRITICAL_SECTION *l) {
    EnterCriticalSection(l);
}
void Unlock(CRITICAL_SECTION *l) {
    LeaveCriticalSection(l);
}
#else
void Lock(CRITICAL_SECTION *l) {
    pthread_mutex_lock(l);
}
void Unlock(CRITICAL_SECTION *l) {
    pthread_mutex_unlock(l);
}
#endif
void LogV(const char *pszFmt,va_list argp) {
    struct tm *now;
    struct timeb tb;

    if (NULL==pszFmt||0==pszFmt[0]) return;
    _vsnprintf(logstr,MAXLINSIZE,pszFmt,argp);
    ftime(&tb);
    now=localtime(&tb.time);
    sprintf(datestr,"%04d-%02d-%02d",now->tm_year+1900,now->tm_mon+1,now->tm_mday);
    sprintf(timestr,"%02d:%02d:%02d",now->tm_hour     ,now->tm_min  ,now->tm_sec );
    sprintf(mss,"%03d",tb.millitm);
    printf("%s %s.%s %s",datestr,timestr,mss,logstr);
    flog=fopen(logfilename1,"a");
    if (NULL!=flog) {
        fprintf(flog,"%s %s.%s %s",datestr,timestr,mss,logstr);
        if (ftell(flog)>MAXLOGSIZE) {
            fclose(flog);
            if (rename(logfilename1,logfilename2)) {
                remove(logfilename2);
                rename(logfilename1,logfilename2);
            }
        } else {
            fclose(flog);
        }
    }
}
void Log(const char *pszFmt,...) {
    va_list argp;

    Lock(&cs_log);
    va_start(argp,pszFmt);
    LogV(pszFmt,argp);
    va_end(argp);
    Unlock(&cs_log);
}
//Log}
int main(int argc,char * argv[]) {
    int i;
#ifdef WIN32
    InitializeCriticalSection(&cs_log);
#else
    pthread_mutex_init(&cs_log,NULL);
#endif
    for (i=0;i<10000;i++) {
        Log("This is a Log %04d from FILE:%s LINE:%d\n",i, __FILE__, __LINE__);
    }
#ifdef WIN32
    DeleteCriticalSection(&cs_log);
#else
    pthread_mutex_destroy(&cs_log);
#endif
    return 0;
}
//1-78行添加到你带main的.c或.cpp的那个文件的最前面
//81-85行添加到你的main函数开头
//89-93行添加到你的main函数结束前
//在要写LOG的地方仿照第87行的写法写LOG到文件MyLog1.log中
看出来了,赵老师用这行记录用户传递的参数防止参数传递错误: _vsnprintf(logstr,MAXLINSIZE,pszFmt,argp); 我看了我们记录日志的类,也用到了这行: vsnprintf(msg, sizeof(msg), pFormat, ap); 但是仍然不能避免程序downn掉。 不过我现在觉得,这样参数传递错误的问题在编译的时候就应该被发现而不是等到执行的时候, gcc在编译的时候能发现这些错误。
赵4老师 2013-11-29
  • 打赏
  • 举报
回复
仅供参考
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#ifdef WIN32
    #include <windows.h>
    #include <io.h>
#else
    #include <unistd.h>
    #include <sys/time.h>
    #include <pthread.h>
    #define  CRITICAL_SECTION   pthread_mutex_t
    #define  _vsnprintf         vsnprintf
#endif
//Log{
#define MAXLOGSIZE 20000000
#define MAXLINSIZE 16000
#include <time.h>
#include <sys/timeb.h>
#include <stdarg.h>
char logfilename1[]="MyLog1.log";
char logfilename2[]="MyLog2.log";
static char logstr[MAXLINSIZE+1];
char datestr[16];
char timestr[16];
char mss[4];
CRITICAL_SECTION cs_log;
FILE *flog;
#ifdef WIN32
void Lock(CRITICAL_SECTION *l) {
    EnterCriticalSection(l);
}
void Unlock(CRITICAL_SECTION *l) {
    LeaveCriticalSection(l);
}
#else
void Lock(CRITICAL_SECTION *l) {
    pthread_mutex_lock(l);
}
void Unlock(CRITICAL_SECTION *l) {
    pthread_mutex_unlock(l);
}
#endif
void LogV(const char *pszFmt,va_list argp) {
    struct tm *now;
    struct timeb tb;

    if (NULL==pszFmt||0==pszFmt[0]) return;
    _vsnprintf(logstr,MAXLINSIZE,pszFmt,argp);
    ftime(&tb);
    now=localtime(&tb.time);
    sprintf(datestr,"%04d-%02d-%02d",now->tm_year+1900,now->tm_mon+1,now->tm_mday);
    sprintf(timestr,"%02d:%02d:%02d",now->tm_hour     ,now->tm_min  ,now->tm_sec );
    sprintf(mss,"%03d",tb.millitm);
    printf("%s %s.%s %s",datestr,timestr,mss,logstr);
    flog=fopen(logfilename1,"a");
    if (NULL!=flog) {
        fprintf(flog,"%s %s.%s %s",datestr,timestr,mss,logstr);
        if (ftell(flog)>MAXLOGSIZE) {
            fclose(flog);
            if (rename(logfilename1,logfilename2)) {
                remove(logfilename2);
                rename(logfilename1,logfilename2);
            }
        } else {
            fclose(flog);
        }
    }
}
void Log(const char *pszFmt,...) {
    va_list argp;

    Lock(&cs_log);
    va_start(argp,pszFmt);
    LogV(pszFmt,argp);
    va_end(argp);
    Unlock(&cs_log);
}
//Log}
int main(int argc,char * argv[]) {
    int i;
#ifdef WIN32
    InitializeCriticalSection(&cs_log);
#else
    pthread_mutex_init(&cs_log,NULL);
#endif
    for (i=0;i<10000;i++) {
        Log("This is a Log %04d from FILE:%s LINE:%d\n",i, __FILE__, __LINE__);
    }
#ifdef WIN32
    DeleteCriticalSection(&cs_log);
#else
    pthread_mutex_destroy(&cs_log);
#endif
    return 0;
}
//1-78行添加到你带main的.c或.cpp的那个文件的最前面
//81-85行添加到你的main函数开头
//89-93行添加到你的main函数结束前
//在要写LOG的地方仿照第87行的写法写LOG到文件MyLog1.log中
赵4老师 2013-11-29
  • 打赏
  • 举报
回复
改用snprintf Windows: _snprintf, _snwprintf Write formatted data to a string. int _snprintf( char *buffer, size_t count, const char *format [, argument] ... ); int _snwprintf( wchar_t *buffer, size_t count, const wchar_t *format [, argument] ... ); Routine Required Header Compatibility _snprintf <stdio.h> Win 95, Win NT _snwprintf <stdio.h> or <wchar.h> Win 95, Win NT For additional compatibility information, see Compatibility in the Introduction. Libraries LIBC.LIB Single thread static library, retail version LIBCMT.LIB Multithread static library, retail version MSVCRT.LIB Import library for MSVCRT.DLL, retail version Return Value _snprintf returns the number of bytes stored in buffer, not counting the terminating null character. If the number of bytes required to store the data exceeds count, then count bytes of data are stored in buffer and a negative value is returned. _snwprintf returns the number of wide characters stored in buffer, not counting the terminating null wide character. If the storage required to store the data exceeds count wide characters, then count wide characters are stored in buffer and a negative value is returned. Parameters buffer Storage location for output count Maximum number of characters to store format Format-control string argument Optional arguments Remarks The _snprintf function formats and stores count or fewer characters and values (including a terminating null character that is always appended unless count is zero or the formatted string length is greater than or equal to count characters) in buffer. Each argument (if any) is converted and output according to the corresponding format specification in format. The format consists of ordinary characters and has the same form and function as the format argument for printf. If copying occurs between strings that overlap, the behavior is undefined. _snwprintf is a wide-character version of _snprintf; the pointer arguments to _snwprintf are wide-character strings. Detection of encoding errors in _snwprintf may differ from that in _snprintf. _snwprintf, like swprintf, writes output to a string rather than to a destination of type FILE. Generic-Text Routine Mappings TCHAR.H Routine _UNICODE & _MBCS Not Defined _MBCS Defined _UNICODE Defined _sntprintf _snprintf _snprintf _snwprintf Example /* SPRINTF.C: This program uses sprintf to format various * data and place them in the string named buffer. */ #include <stdio.h> void main( void ) { char buffer[200], s[] = "computer", c = 'l'; int i = 35, j; float fp = 1.7320534f; /* Format and print various data: */ j = sprintf( buffer, "\tString: %s\n", s ); j += sprintf( buffer + j, "\tCharacter: %c\n", c ); j += sprintf( buffer + j, "\tInteger: %d\n", i ); j += sprintf( buffer + j, "\tReal: %f\n", fp ); printf( "Output:\n%s\ncharacter count = %d\n", buffer, j ); } Output Output: String: computer Character: l Integer: 35 Real: 1.732053 character count = 71 Stream I/O Routines See Also sprintf, fprintf, printf, scanf, sscanf, vprintf Functions Linux: Section: POSIX Programmer's Manual (P) Updated: 2003 -------------------------------------------------------------------------------- NAME snprintf - print formatted output SYNOPSIS #include <stdio.h> int snprintf(char *restrict s, size_t n, const char *restrict format, ...); DESCRIPTION The snprintf() function shall be equivalent to sprintf(), with the addition of the n argument which states the size of the buffer referred to by s. If n is zero, nothing shall be written and s may be a null pointer. Otherwise, output bytes beyond the n-1st shall be discarded instead of being written to the array, and a null byte is written at the end of the bytes actually written into the array. If copying takes place between objects that overlap as a result of a call to sprintf() or snprintf(), the results are undefined. APPLICATION USAGE If the application calling fprintf() has any objects of type wint_t or wchar_t, it must also include the <wchar.h> header to have these objects defined.
unituniverse2 2013-11-29
  • 打赏
  • 举报
回复
如果sprintf崩溃了,往往是字符串溢出,类似这种调用栈破坏的错误已经是无法恢复的了。强行的让他执行下去不是个好办法。 用snprintf防止类似的溢出。不过缓冲区长度必须写对,否则起不到保护作用。 要么专门写一个自定义转字符串的函数,那个字符串长度需要是动态的。 至于参数格式不匹配。。。还是多练练吧,连这种问题都经常看走眼的话,编程相关的事就没剩几件能做得好了。
ggglivw 2013-11-28
  • 打赏
  • 举报
回复
sprintf是只要你每条过一遍,肯定能解决的。这个没有上下文逻辑问题,很好解决,就是点体力活而已。
w195438178 2013-11-28
  • 打赏
  • 举报
回复
引用 8 楼 ggglivw 的回复:
打个比方,你做了一个功能,在崩溃的时候捕获异常,然后记录日志,然后抛消息给系统自动重启进程。 问题是你在记录日志的时候又崩溃了,你说这个时候系统应该咋给办?无限死循环直到死机吗? 像sprintf这种能完全能通过【肉眼解决】的都不是问题!!! 怕的是内存越界啊之类的,你某些地方少写了边界检查的,或者你用第三方库,里面有BUG造成的,这种才是应该重点考虑的
如果出错记录的日志突然大面积执行,导致程序一直down掉,会影响其他业务。记录日志是个辅助行为,不应该因为它导致程序崩溃。 至于你说的内存越界的问题,当然更是要提高警惕,肯定要重点考虑。
w195438178 2013-11-28
  • 打赏
  • 举报
回复
引用 7 楼 adlay 的回复:
[quote=引用 3 楼 w195438178 的回复:] [quote=引用 1 楼 adlay 的回复:] 用 __try { } __except(...) { } 试试
linux下无法使用这样的异常捕获啊[/quote] linux 可以注册自己的信号处理函数来捕获, 一般来说 sprintf 出错都是访问非法的内存, 这种错误需要捕捉 SIGSEGV 信号。[/quote] 需要用这么复杂的方法啊?看来改用其他方式记录日志感觉更靠谱些啊
ggglivw 2013-11-28
  • 打赏
  • 举报
回复
打个比方,你做了一个功能,在崩溃的时候捕获异常,然后记录日志,然后抛消息给系统自动重启进程。 问题是你在记录日志的时候又崩溃了,你说这个时候系统应该咋给办?无限死循环直到死机吗? 像sprintf这种能完全能通过【肉眼解决】的都不是问题!!! 怕的是内存越界啊之类的,你某些地方少写了边界检查的,或者你用第三方库,里面有BUG造成的,这种才是应该重点考虑的
www_adintr_com 2013-11-28
  • 打赏
  • 举报
回复
引用 3 楼 w195438178 的回复:
[quote=引用 1 楼 adlay 的回复:] 用 __try { } __except(...) { } 试试
linux下无法使用这样的异常捕获啊[/quote] linux 可以注册自己的信号处理函数来捕获, 一般来说 sprintf 出错都是访问非法的内存, 这种错误需要捕捉 SIGSEGV 信号。
w195438178 2013-11-28
  • 打赏
  • 举报
回复
引用 5 楼 ggglivw 的回复:
[quote=引用 4 楼 w195438178 的回复:] [quote=引用 2 楼 ggglivw 的回复:] sprintf本身不会崩溃,当然你犯2另说,比如%s的参数,你要传一个int. 我们的dump的sprintf从来都没有出过问题,检查下代码吧
当你有记录1w条日志,你能保证每次参数都传对?[/quote] 你自己代码要写错,能怪sprintf?[/quote] 我没怪它,我只是在寻找一种方法,在出错的时候能捕获到,保证程序不崩溃,之后把程序更正过来。
ggglivw 2013-11-28
  • 打赏
  • 举报
回复
引用 4 楼 w195438178 的回复:
[quote=引用 2 楼 ggglivw 的回复:] sprintf本身不会崩溃,当然你犯2另说,比如%s的参数,你要传一个int. 我们的dump的sprintf从来都没有出过问题,检查下代码吧
当你有记录1w条日志,你能保证每次参数都传对?[/quote] 你自己代码要写错,能怪sprintf?
w195438178 2013-11-28
  • 打赏
  • 举报
回复
引用 2 楼 ggglivw 的回复:
sprintf本身不会崩溃,当然你犯2另说,比如%s的参数,你要传一个int. 我们的dump的sprintf从来都没有出过问题,检查下代码吧
当你有记录1w条日志,你能保证每次参数都传对?
w195438178 2013-11-28
  • 打赏
  • 举报
回复
引用 1 楼 adlay 的回复:
用 __try { } __except(...) { } 试试
linux下无法使用这样的异常捕获啊
ggglivw 2013-11-28
  • 打赏
  • 举报
回复
sprintf本身不会崩溃,当然你犯2另说,比如%s的参数,你要传一个int. 我们的dump的sprintf从来都没有出过问题,检查下代码吧
www_adintr_com 2013-11-28
  • 打赏
  • 举报
回复
用 __try { } __except(...) { } 试试
w195438178 2013-11-28
  • 打赏
  • 举报
回复
引用 22 楼 akirya 的回复:
[quote=引用 20 楼 w195438178 的回复:] [quote=引用 14 楼 akirya 的回复:] NB一点的编译器就能检查出 格式信息和参数不匹配的情况。
对啊,这种问题应该在编译的时候就能检测了,不知道怎样的编译器能检测出这样的错误?[/quote] clang 可以[/quote] 我发现G++ -Wall就能检查
w195438178 2013-11-28
  • 打赏
  • 举报
回复
引用 23 楼 zhao4zhong1 的回复:
[quote=引用 19 楼 w195438178 的回复:] [quote=引用 18 楼 zhao4zhong1 的回复:] [quote=引用 12 楼 zhao4zhong1 的回复:] 为什么不用pc_lint呢?
为什么呢?[/quote] 我前面试了下pc_lint,它是对单个文件检测的,而且项目依赖的库多了,它检测起来总是报错,不是很方便啊[/quote] 我也没具体用过pc_lint,不过我听说过pc_lint可以配置检查哪些,不检查哪些的。[/quote] 恩,谢谢赵老师提醒,有时间我再了解下。
w195438178 2013-11-28
  • 打赏
  • 举报
回复
引用 24 楼 zhao4zhong1 的回复:
程序员要做的不是尽力避免错误,而是聚焦在快速发现并改正错误。真正以快速方式轻易解决错误,“快速的失败”远胜过“预防错误”。Fred George
我觉得这要分情况吧,如果在正常流程产生错误当然要立刻修复,就怕某些很难进入的异常流程,又不能100%覆盖代码流程,造成异常崩溃,然后影响其他功能。而且这是记录日志之类的辅助功能,不应该影响正常业务。
赵4老师 2013-11-28
  • 打赏
  • 举报
回复
程序员要做的不是尽力避免错误,而是聚焦在快速发现并改正错误。真正以快速方式轻易解决错误,“快速的失败”远胜过“预防错误”。Fred George
赵4老师 2013-11-28
  • 打赏
  • 举报
回复
引用 19 楼 w195438178 的回复:
[quote=引用 18 楼 zhao4zhong1 的回复:] [quote=引用 12 楼 zhao4zhong1 的回复:] 为什么不用pc_lint呢?
为什么呢?[/quote] 我前面试了下pc_lint,它是对单个文件检测的,而且项目依赖的库多了,它检测起来总是报错,不是很方便啊[/quote] 我也没具体用过pc_lint,不过我听说过pc_lint可以配置检查哪些,不检查哪些的。
加载更多回复(11)

64,685

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

试试用AI创作助手写篇文章吧