【1】.关于sprintf和snprintf的正确使用

考虑以下有缺陷的例子:
void f(const char *p)
{
char buf[11]={0};
sprintf(buf,"%10s",p); // very dangerous
printf("%sn",buf);
}

不要让格式标记“%10s”误导你。如果p的长度大于10个字符,那么sprintf() 的写操作就会越过buf的边界,从而产生一个缓冲区溢出。
检测这类缺陷并不容易,因为它们只在 p 的长度大于10个字符的时候才会发生。黑客通常利用这类脆弱的代码来入侵看上去安全的系统。

要修正这一缺陷,可以使用函数snprintf()代替函数sprintf()。

函数原型:int snprintf(char *dest, size_t n, const char *fmt, ...);
函数说明: 最多从源串中拷贝n-1个字符到目标串中,然后再在后面加一个0。所以如果目标串的大小为n的话,将不会溢出。
函数返回值: 若成功则返回存入数组的字符数,若编码出错则返回负值。

推荐的用法:
void f(const char *p)
{
char buf[11]={0};
snprintf(buf, sizeof(buf), "%10s", p); // 注意:这里第2个参数应当用sizeof(str),而不要使用硬编码11,也不应当使用sizeof(str)-1或10
printf("%sn",buf);
}

【转载】http://kapok.blog.51cto.com/517862/113471


【2】.返回值、缓冲区问题

snprintf函数的返回值
sprintf函数返回的是实际输出到字符串缓冲中的字符个数,包括null结束符。而snprintf函数返回的是应该输出到字符串缓冲的字符个数,所以snprintf的返回值可能大于给定的可用缓冲大小以及最终得到的字符串长度。看代码最清楚不过了:

char tlist_3[10] = {0};
int len_3 = 0;
len_3 = snprintf(tlist_3,10,"this is a overflow test!\n");
printf("len_3 = %d,tlist_3 = %s\n",len_3,tlist_3);
上述代码段的输出结果如下:

//////////////////////////////////////////////
// 输出的数据:"25", "this is a".
len_3 = 25, tlist_3 = this is a
所以在使用snprintf函数的返回值时,需要小心慎重,避免人为造成的缓冲区溢出,不然得不偿失。

snprintf函数的字符串缓冲
int sprintf(char *str, const char *format, ...);
int snprintf(char *str, size_t size, const char *format, ...);
上面的函数原型大家都非常熟悉,我一直以为snprintf除了多一个缓冲区大小参数外,表现行为都和sprintf一致,直到今天遇上的bug。在此之前我把下面的代码段的两个输出视为一致。

char tlist_1[1024] = {0},tlist_2[1024]={0};
char fname[7][8] = {"a1","b1","c1","d1","e1","f1","g1"};
int i = 0, len_1,len_2 = 0;

len_1 = snprintf(tlist_1,1024,"%s;",fname[0]);
len_2 = snprintf(tlist_2,1024,"%s;",fname[0]);

for(i=1;i<7;i++)
{
len_1 = snprintf(tlist_1,1024,"%s%s;",tlist_1,fname[i]);
len_2 = sprintf(tlist_2,"%s%s;",tlist_2,fname[i]);
}

printf("tlist_1: %s\n",tlist_1);
printf("tlist_2: %s\n",tlist_2);
可实际上得到的输出结果却是:

////////////////////////////////////////////
// snprintf()会清除缓冲区,sprintf()不会清除缓冲区;
tlist_1: g1;
tlist_2: a1;b1;c1;d1;e1;f1;g1;

【转载】http://my.oschina.net/shelllife/blog/177279


【3】.三个打印函数printf()/sprintf()/snprintf()区别

先贴上其函数原型
printf( const char *format, ...) 格式化输出字符串,默认输出到终端-----stdout
sprintf(char *dest, const char *format,...) 格式化输出字符串到指定的缓冲区
snprintf(char *dest, size_t size,const char *format,...) 按指定的SIZE格式化输出字符串到指定的缓冲区

printf()函数在这就不再讨论,这里主要讨论sprintf()与snprintf()的用法及区别,

#include "stdafx.h"
#include <stdio.h>
using namespace std;
int _tmain(int argc, _TCHAR* argv[])
{
char *p1="China";
char a[20];
sprintf(a,"%s",p1);
printf("%s\n",a);
//////////////////////////
memset(a,0,sizeof(a));

_snprintf(a,3,"%s",p1);
printf("%s\n",a);
printf("%d\n",strlen(a));
return 0;
}

结果输出:
China
Chi
3

分析:
sprintf(a,"%s",p1) 把p1字符串拷贝到数组a中('\0'也拷贝过去了)。
snprintf(a,3,"%s",p1) 拷贝P1中前3个字符到数组a中,并在末尾自动添加'\0'。
sprintf属于I/O库函数,snprintf函数并不是标准c/c++中规定的函数,但是在许多编译器中,厂商提供了其实现的版本。在gcc中,该函数名称就snprintf,而在VC中称为_snprintf。 如果你在VC中使用snprintf(),会提示此函数未声明,改成_snprintf()即可。

注意点:
1 sprintf是一个不安全函数,src串的长度应该小于dest缓冲区的大小,(如果src串的长度大于或等于dest缓冲区的大小,将会出现内存溢出。)
2 snprintf中源串长度应该小于目标dest缓冲区的大小,且size等于目标dest缓冲区的大小。(如果源串长度大于或等于目标dest缓冲区的大小,且size等于目标dest缓冲区的大小,则只会拷贝目标dest缓冲区的大小减1个字符,后加'\0';该情况下,如果size大于目标dest缓冲区的大小则溢出。)
3 snprintf ()函数返回值问题, 如果输出因为size的限制而被截断,返回值将是“如果有足够空间存储,所应能输出的字符数(不包括字符串结尾的'\0')”,这个值和size相等或者比size大!也就是说,如果可以写入的字符串是"0123456789ABCDEF"共16位,但是size限制了是10,这样 snprintf() 的返回值将会是16 而不是10!

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

【转载】http://www.cnblogs.com/ningskyer/articles/4037964.html


【4】. <string.h> 、<string>

<string.h> 是C语言的一个【头文件】,里
面有 定义了 很多操作字符串的【方法】,例如:
strcpy(), strncpy(),
strcat(), stncat(),
strcmp(), strncmp(),
// 找子串
strstr()
strlen()
memset()

<string> 里面定义了一个 string 类,其功能和一容器类似。
string类的方法有:
at(), append(), assign(), begin(), end(),
earse(), find(), insert(),
clear(), length(), c_str(),
// Returns whether the string is empty
empty(),
push_back(),
size(),
string 类定义的对象,可以直接加上字符串。
要访问string类对象中保存的字符串,需要调用类的c_str()方法。

----------------------------------------------------------------------------

-----------------------------------------------------
strlen,sizeof()
char mystr[100]="test string";
defines an array of characters with a size of 100 chars, but the C string with which mystr has ben initialized has a length of only 11 characters.
Therefore, while sizeof(mystr) evaluates to 100,
strlen(mystr) returns 11.


【5】.C里操作字符串很高效,但也很麻烦。

1. char * strcpy ( char * destination, const char * source );

最常用的函数,但是却不安全,原因在于,一是要destination有足够的空间,二是要保证source和destination指向的空间没有overlap。

2. int sprintf ( char * str, const char * format, ... );

也许要问,这个怎么用于字符串拷贝呢?可以这么用 sprintf(dest, "%s", src); 但是要调用者保证dest有足够的内存存放src。

3. char * strncpy ( char * destination, const char * source, size_t num );

比起strcpy,多了个长度的控制。从source拷贝num个字符到destination。如果source里不够num字符怎么办呢?会补充0。

一个典型的用法是:

char buf[MAX];
strncpy(buf, src, MAX-1);

这段代码的本意是,一个长为MAX的buf,最多也就放MAX-1个字符,最后一个位置放‘\0'。因此最多能从src里拷贝MAX-1个字符,如果src里没这么多,剩余的填充0就是了。

但是这样做就安全了么?不是,如果src刚好MAX-1个字符。注意到strncpy只复制了MAX-1个字符,最后一个位置未知,有潜在的隐患。下段代码可以诠释:

#define MAX 4
char buf[MAX];
char* src="123";
memset(buf, 'x', MAX);

// solution 1. memset(buf, 0, MAX);
strncpy(buf, src, MAX-1);
// solution 2. buf[MAX-1] = '\0';

printf("%s\n", buf);

有两个办法可以解决:
1.调用strncpy之前memset为0,有点浪费。
2.在strncpy之后对最后一个字符赋值为0。

都可以,但不够优雅。

4. int snprintf( char *buffer, int buff_size, const char *format, ... );

用作字符串拷贝的用法:

char buf[MAX];
snprintf(buf, sizeof(buf), "%s", src);

即安全,又简洁。

你可能会关心:如果src的长度大于dest(buf)呢?这个是另外一个问题,这里需要的是安全的字符串拷贝,在C语言里,如果一个字符串指针指向的内存没有结尾字符'\0',是非常危险的。

snprintf会把buf的最后一个位置保留为'\0'。

关于返回值:如果当前buf够用,返回实际写入的字符数;如果不够用,返回将要写入的字符数。换句话说,返回值就是传入的字符数目。

假设当前的buf[4].

待写入 实际写入 返回值
12 12 2 够用
123 123 3 够用
1234 123 4 不够用
12345 123 5 不够用

sprintf/snprintf的另外一个用法:

itoa不是ANSI C或C++的一部分,可以变相的用sprintf来代替:

sprintf(str,"%d",value) 转换为十进制数值。
sprintf(str,"%x",value) 转换为十六进制数值。
sprintf(str,"%o",value) 转换为八进制数值。

【转载】http://blog.csdn.net/kadwf123/article/details/7819439


【6】.C++危险的函数 strcpy strncpy

1.strcpy

函数原型为char *strcpy(char *dest,const char *src);

函数说明:strcpy函数会将参数src字符串拷贝至参数dest所指的地址。

参数说明:dest,我们说的出参,最终得到的字符串。src,入参,因为其有const修饰。表示在此函数中不会也不能修改src的值。

返回值:返回dest字符串的起始地址。

附加说明:如果参数dest所指的内存空间不够大,可能会造成缓冲溢出的错误情况。

特别强调:此函数很好用,可是它也很危险。如果在用的时候加上相关的长度判断,则会大大降低出此错误的危险。此函数还有一个特点,就是它在把字符串b拷贝到字符串a的时候,会在拷贝的a字符串的末尾加上一个\0结束标志。这个不同于strncpy()函数。

例如:

#include<string.h>

main(){
char a[30]="string(1)";
char b[]="string(2)";
printf("before strcpy():%s\n",a);
if(strlen(b)<strlen(a))
{
printf("after strcpy():%s\n",strcpy(a,b));
}
}

2.strncpy
函数原型为:char *strncpy(char *dest,const char *src ,size_t n);

函数说明:strncpy会将参数src字符串拷贝前n个字符至参数dest所指的地址。

返回值:返回参数dest的字符串起始地址。

特别强调:不要以为这个函数是个好东西,往往在定位问题时,它是罪魁祸首,到顶了,它是静态的容值函数,程序跑起来你就等着dbug吧。

strncpy的正确用法:

strncpy(dest, src, sizeof(dest));
dest[sizeof(dest)-1] = ‘\0’;

size一定要用sizeof(dest)或sizeof(dest)-1,不可误用sizeof(src).
手工填0. 务必要把dest的最后一个字节手工设置为0. 因为strncpy仅在src的长度小于dest时,对剩余的字节填0.
性能问题。当dest长度远大于src时,由于strncpy会对多余的每个字节填0,会有很大的性能损失。
返回值。strncpy返回dest,因而无法知道拷贝了多少个字节。

【转载】http://blog.csdn.net/kadwf123/article/details/7819052


strcpy
char * strcpy ( char * destination, const char * source );
Copy string
Copies the C string pointed by source into the array pointed by destination, including the terminating null character (and stopping at that point).

To avoid overflows, the size of the array pointed by destination shall be long enough to contain the same C string as source (including the terminating null character), and should not overlap in memory with source.

/* strcpy example */
#include <stdio.h>
#include <string.h>

int main ()
{
char str1[]="Sample string";
char str2[40];
char str3[40];
// 拷贝的时候,包含了最后的字符串结束符
strcpy (str2,str1);
strcpy (str3,"copy successful");
printf ("str1: %s\nstr2: %s\nstr3: %s\n",str1,str2,str3);
return 0;
}

--------------------------------------------------------------------------------
strncpy
char * strncpy ( char * destination, const char * source, size_t num );
Copy characters from string
Copies the first num characters of source to destination. If the end of the source C string (which is signaled by a null-character) is found before num characters have been copied, destination is padded with zeros until a total of num characters have been written to it.

No null-character is implicitly appended at the end of destination if source is longer than num. Thus, in this case, destination shall not be considered a null terminated C string (reading it as such would overflow).

destination and source shall not overlap (see memmove for a safer alternative when overlapping).
/* strncpy example */
#include <stdio.h>
#include <string.h>

int main ()
{
char str1[]= "To be or not to be";
char str2[40];
char str3[40];

/* copy to sized buffer (overflow safe): */
strncpy ( str2, str1, sizeof(str2) );

/* partial copy (only 5 chars): */
strncpy ( str3, str2, 5 );
// 需要你手动添加字符串结束符!!!!!
str3[5] = '\0'; /* null character manually added */

puts (str1);
puts (str2);
puts (str3);

return 0;
}

Output:
To be or not to be
To be or not to be
To be

talk about string,char的更多相关文章

  1. C++中int,float,string,char*的转换(待续)

    //float转string char a[100]; float b = 1.234; sprintf(a, "%f", b); string result(a); //int转 ...

  2. C#入门篇6-6:字符串操作 StringBiulder string char[]之间的转化

    //StringBiulder string char[]之间的转化 public static void Fun3() { StringBuilder sb = new StringBuilder( ...

  3. CString string char* char 之间的字符转换(多种方法)

    在写程序的时候,我们经常遇到各种各样的类型转换,比如 char* CString string 之间的互相转换.首先解释下三者的含义. CString 是一种很有用的数据类型.它们很大程度上简化了MF ...

  4. C++ wstring string char* wchar_t相互转换

    标签: stringwstringwchar_tcharc++2013-12-19 00:29 3721人阅读 评论(0) 收藏 举报本文章已收录于: C++知识库 分类: C/C++(50) 1. ...

  5. string,char*,int 之间的转化

    c++中经常遇到string,char*,int之间的相互转化,今天就来整理一下. 以下是转载并修改的内容: 以下是常用的几种类型互相之间的转换 string 转 int先转换为char*,再使用at ...

  6. 【转】CString,string,char*综合比较

    (一)  概述 1.string和CString均是字符串模a板类: 2.string为标准模板类(STL)定义的字符串类,已经纳入C++标准之中: 3.CString(typedef CString ...

  7. mfc CString,string,char* 之间的转换

    知识点: CString转char*,string string转char*,CString char* 转CString,string 一.CString转char*,string //字串转换测试 ...

  8. (c++) int 转 string,char*,const char*和string的相互转换

    一.int 和string的相互转换 1 int 转化为 string c++ //char *itoa( int value, char *string,int radix); // 原型说明: / ...

  9. (C/C++) string / *char / int 基本轉換

    網路上有許 string / *char / integer 基本轉換方式 string 與 *char 互相轉換的方法 /* string to *char */ string ssbuf1 = & ...

  10. string char * const char *之间的互相转换

    string  ->   const char * 用str的c_str()方法或者data()方法均可,这个两个方法返回值为cong char * string str = "hel ...

随机推荐

  1. Spring-AOP源码分析随手记(二)

    这次来分析下切面的执行过程. 1.怎么看? 怎么开始看源码呢?就直接从被增强的方法调用那里打断点,看看怎么执行的: 然后就来到了这: 2.初步分析 里面有段: if (this.advised.exp ...

  2. 关于如何提高缓存命中率(redis)

    一.缓存命中率的介绍 命中:可以直接通过缓存获取到需要的数据. 不命中:无法直接通过缓存获取到想要的数据,需要再次查询数据库或者执行其它的操作.原因可能是由于缓存中根本不存在,或者缓存已经过期. 通常 ...

  3. 调用SqlCommand或SqlDataAdapter的Dispose方法,是否会关闭绑定的SqlConnection?(转载)

    1. Does SqlCommand.Dispose close the connection? 问 Can I use this approach efficiently? using(SqlCom ...

  4. JVM指令手册

    JVM指令大全 常量入栈指令 指令码 操作码(助记符) 操作数 描述(栈指操作数栈) 0x01 aconst_null null值入栈. 0x02 iconst_m1 -1(int)值入栈. 0x03 ...

  5. (九)分布式服务----Zookeeper注册中心

    ==>>点击查看本系列文章目录 首先看一下几种注册中心: 最老的就是Zookeeper了, 比较新的有Eureka,Consul 都可以做注册中心.可以自行搜索对比三者的优缺点. Zook ...

  6. 学习shiro第三天

    今天比较晚,所以只看了shiro的认证策略Authentication Strategy,下面讲讲shiro的三种认证策略. 1.AtLeastOneSuccessfulStrategy:这个是shi ...

  7. 深入理解JVM虚拟机(一):JVM运行时数据区

    概述: JVM将内存的管理进行封装,使得开发人员不必关心内存申请.释放操作.但是在高级程序开发.复杂业务场景开发的时候,如果出现内存溢出的情况,对于开发人员而言就很难去分析出原因.所以还是很有必要去了 ...

  8. 5.InfluxDB-InfluxQL基础语法教程--WHERE子句

    本文翻译自官网,官网地址:(https://docs.influxdata.com/influxdb/v1.7/query_language/data_exploration/) WHERE子句 语法 ...

  9. composer基本操作详解

    原文转自微信公众号:qq1005349393 Composer介绍 Composer 是 PHP 的一个包依赖管理工具.我们可以在项目中声明所依赖的外部工具库,Composer 会帮你安装这些依赖的库 ...

  10. Java数据库连接组件C3P0和DBCP

    C3P0连接池组件 开源数据库连接池组件 jar包:c3p0-0.9.2.jar和mchange-commons-java-0.2.2.3.jar 支持JDBC3规范和JDBC2的标准扩展 使用项目H ...