跳至主要内容

特色视频

记录 sprintf 使用

在dol的代码里看到有使用sprintf的系统函数,之前不是很了解,这里记录下使用过程; 首先还是例子: #include int main(void) { char out[20]; char in[20]="www.lvdou.co.nz"; sprintf(out,"绿豆网%s", in); printf("%s",out); return 0; } 运行结果:绿豆网www.lvdou.co.nz 首先“sprintf”函数是在标准输入输出库里的,所以要包含"stdio.h"; 这个还是的作用就是一组数据格式化输出到另外一个字符串中, 第一个参数就是目的字符类型的变量; 第二个参数是格式化公式,和printf的格式化方式是一样的; 最后的就是输入变量; 需要提到的一点是根据编译器的不同,目标变量有时候会有溢出的风险;有些编译器会自动优化,有些则不会;可以用两点保证,第一自己一定要确定目标变量的空间是够的;第二就是可以尝试“snprintf”函数; 结束。

函数/宏的可变长参数

阅读tek-dor的代码时看到来很多函数和宏使用可变长参数,在这里做下总结。

使用详情在<C Primer Plus 6th edit>里的16.14章节有详细介绍的内容,这是权威,下面是个人理解和使用;
1. 首先要包含头文件 #include <stdarg.h>;
2. 使用 [va_list] [va_start()] [va_arg()] [va_end()]来操作读取函数的输入参数;

例子:

int myprint(int fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    printf("have %d items\n", fmt);
    printf("0. %s\n", va_arg(args,char*));
    printf("1. %s\n", va_arg(args,char*));
    printf("2. %s\n", va_arg(args,char*));
    
    va_end(args);
}
解释:
对于可变长输入参数的使用,规定至少要有一个形参和“...”符号;
va_list args;
"va_list"是声明一个专门的变量"args"用来存放各个参数;后续会把“...”省略的参数通过函数一个个提取到这个变量里来;
va_start(args, fmt);
"va_start"函数用来初始化上面声明的变量;所以va_start函数的第一个输入参数就是刚才声明的变量;而第二个输入参数就是书写函数的第一个参数“fmt”;其实它就是用这个fmt的位置来定位后续的所有输入参数的位置,所以第一个参数是必须要给定的;
有个博客说来输入参数在编译时的执行过程:http://www.cnblogs.com/hanyonglu/archive/2011/05/07/2039916.html
这样就可以比较清晰的了解变长输入参数是如何被一个个提取出来的;
printf("0. %s\n", va_arg(args,char*));
"va_arg"函数是来顺序的提取变长输入参数的函数;这里提取的是"..."代表的输入参数,不包括第一个fmt输入参数,fmt可以直接调用;相当于从第二个输入参数开始到第n个;还有一点是顺序读取,无法选择次序;
va_end(args);
"va_end"函数顾名思义就是释放掉上面系统函数使用的资源;
上面函数的调用和运行结果如下:
函数调用:myprint(3,"douzi","douer","www.lvdou.co.nz");

函数结果:
have 3 items
0. douzi
1. douer
2. www.lvdou.co.nz
还有一个例子:
int myprint2(char* fmt, ...)
{
    va_list args;
    va_start(args, fmt);
    printf("start sentence -> %s\n", fmt);
    printf("next sentence  -> %s\n", va_arg(args,char*));
    
    va_end(args);
}
函数调用:myprint2("绿豆网","www.lvdou.co.nz");
函数输出:
start sentence -> 绿豆网
next sentence  -> www.lvdou.co.nz
这个例子就是在输出字符时经常使用的格式;

=================== 我是分割线 ==================
下面来说下对于可变长输出参数宏的使用;在<C Primer Plus 6th edit>里的13.3.3章节有详细介绍的内容;
还是先来一个例子:
宏:
    #define P(...) printf(__VA_ARGS__)
    #define P2(arge...) printf(arge)
宏调用:
    P("%s%s%s\n","www.","lvdou.","co.nz");
    P2("www.lvdou.co.nz\n");
输出:
    www.lvdou.co.nz
    www.lvdou.co.nz
首先这里两个写法的最终作用是完全一样的,"__VA_ARGS__"是系统变量,可以使用自己定义的变量"arge"进行代替;
然后在看使用,这里的宏其实就相当于一个代码生成器,把"..."的内容完全替换到"__VA_ARGS__"上去,不管你有几个参数,只是替换内容;
再看另外一个例子,可以在宏里做一点自己的操作:
宏:
    #define PP(X, ...) printf(#X" is %s\n",__VA_ARGS__); 
    #define PP2(X, arge...) printf(#X" is %s\n",arge); 
宏调用:
    PP(绿豆网,"www.lvdou.com");
    PP2(绿豆网2,"www.lvdou.com");
输出:
    绿豆网 is www.lvdou.com
    绿豆网2 is www.lvdou.com
首先这两个宏都是一样的只是写法不同,和上面一个例子是一样的;然后这次是在宏里有了自己的操作;
结合这两个例子可以发现,宏的作用还是整体的替换,虽然我们可以输入不同个输入参数,但是在宏里还是把这些个输入参数整体替换到"__VA_ARGS__"上;

================= 我是分割线 ===============
在<C Primer Plus 6th edit>里的13.3.1~13.3.2章节里有说宏操作的"#"运算和"##"运算;
总的来说,
#号运算就是字符串化,把变量名给格式化成同样的字符串,可以用来打印输出;
##运算就是组合功能,把两段记号给组合成一个记号;但是只是记号不是变量;
这里需要有个例子后续补上。
例子来了
宏:
    #define ccc(n) x##n
    #define PRINTF(n) printf("x"#n" = %d\n",x##n);
宏调用:
    int ccc(1) = 10;
    PRINTF(1);
输出:
    x1 = 10


最后贴一个在线的编译用来方便调试http://www.dooccn.com/c/

############ 2018/10/18 ############
有的时候会有如下代码,
#define debug(format, ...) fprintf (stderr, format, ## __VA_ARGS__)
在__VA_ARGS__可变参数前用了##符号,这个目前没有完全搞明白,只是了解到不同的编译器可能操作是不同的; 目前这个对使用可变参数没有什么影响,具体的细节后续再体会补充吧。
有个帖子对这个情况给了说明,这里把链接给下:
http://www.cnblogs.com/alexshi/archive/2012/03/09/2388453.html

结束

评论