C陷阱与缺陷阅读笔记(下)

原创 2013年12月06日 10:41:36

关于printf函数

printf函数将数据写到标准输出,fprintf函数将数据写到任何文件,sprintf函数格式化字符串。这三个函数的返回值都是已经处理的字符数。

sprintf函数作为输出数据结束标志的空字符不计入返回值,printf和fprintf函数在试图写入时出现的I/O错误,将返回一个负值,此时我们无法得出究竟有多少字符已经被写出。sprintf不进行I/O操作,因此不会返回负值。当然也不排除因为C语言实现的某种原因返回负值。

对printf所输出的字符串,遵循通常的C语言习惯,即以空字符结束,因此,要用双引号括起来,把这个字符串写成字符串常量的形式,能够自动保证它以空字符结尾。该函数把格式说明字符串中的字符逐一复制到标准输出中,直到格式字符串结束或者遇到%字符,此时printf查看%后紧跟的字符而不打印%字符,以获得有关如何转换其下一个参数的指示。

sprintf和printf的第一个参数不一样,其他都一样。

关于printf函数的参数,有如下说明:

%o,%x,%X都是以无符号数据打印的。

%s,格式项对于的输出字符串必须以空字符结束,舍此别无他法。如果不是以空字符结束,则printf函数将不断打印其后的字符,直到内存某处找到空字符,这种情况下,最终打印的数据将会是乱码,且可能会很长很长。

%g,用来打印那些不需要按列对齐的浮点数。即会舍弃数值尾的零,保留6位有效数字。如果实际数据超过6位有效数字,则用科学计数法表示。对于较小的数字,只有该数的指数小于等于5时,采用科学计数法处理。

   printf("%g\n", 3.14159e-4);0.000314159
   printf("%g\n", 3.14159e-5);3.14159e-005

%e,打印小数点后6位有效数字,用0填充。%f也是 如此。

修饰符

长度修饰符l只对用于整型的格式码有效。

宽度修饰符对所有的格式码都有效,甚至%%也不例外。如果不能填满设置的宽度域,左边用空格字符填充,超过则挤占同一行右侧紧邻数值的位置。

精度修饰符与格式码有关,对于整型,指定了打印的数字的最少位数。不够则补0,对于%e,%E,%f,表示小数点后的位数,%.0f,除非标志另有说明,否则不打印小数点。%g,%G精度修饰符指定了打印数值中的有效数字位数,除非标志另有说明,非有效数字的0被去掉,如果小数点后不跟数字,则小数点被删除。对于%s,精度修饰符指定将要从相应的字符串中打印的字符数。

标志符:

在显示宽度大于被显示的位数时,数据尾部都是以显示区的右端对齐的,标识符-的作用是将其改为左端对齐。因此,仅当域宽修饰符存在时,标识符-才有意义。

标识符+的作用是,规定每个待打印的数值在输出时都应该以它的符号(正负)作为第一个字符。

+和空白字符一起出现时,以+的作用为主。

标示符#的作用与具体的格式项有关:

给%o加上#的作用是,当有必要时增加数值的输出精度。

%#x,%#X,输出0xXX,,,0XXX,,,,

%#o,输出0XXX

%#f,要求小数点必须被打印出来,即使小数点后没有数字。

%#g,打印出的数值尾缀不会被去掉,共6位有效数字。

可变域宽与精度

printf("%*.*s\n", 12, 5, str);

%p,打印指针地址

%n,打印打印出的字符数。


可变参数列表设计

类似于printf函数的可变参数设计,其机制是类似的,只需要知道第一个参数的类型,就可以对其进行存取,一旦第n个参数被成功的存取,第n+1个参数就可以在仅知道类型的情况下进行存取,并且按这种方式存取一个参数的时间不应该太多。需要注意的是,对于参数的存取顺序,都没有必要,另外参数列表是否结束通常也不必要。

大多数C语言实现上述机制,都是通过一组varargs的宏定义。虽然这些宏的实现与特定的C语言实现有关,但是只要运用得到,还是能够在大多数机器上使用可变参数列表。

首先,包含这组宏:

#include <varargs.h>

这组宏包含:

va_list :该宏是用来定义一个数据类型,我们用该宏申明一个对象ap,则只需要给定ap与第1个参数的类型就可以确定第1个参数的值。实际上,在任何一个C实现中,对于可变参数列表的第n个参数,在已知其类型的情况下,要对其进行存取还需要知道一些信息,这些信息通过已经可以存取的第一个参数到第n-1个参数而间接得到,因此可以把ap看成是指向参数列表内部的指针。通过va_list存取一个参数后,va_list被更新,指向下一个参数。

va_start:初始化ap,即指向参数列表头

va_end:清空ap

va_arg:获取参数

va_alist:该宏将扩展为特定C实现所要求的参数列表,这样函数就能够处理变长参数。

va_dcl:va_alist 的声明(UNIX version only) ,即将扩展为与参数列表对应的申明。

也就是说:

如果要定义一个可变参数的函数,则定义如下:

int fun(int first, ...); ANSI 版本

int fun(va_alist) va_dcl ;UNIX版本

这里举例说明:

#define END -1

int va_sum (int first_num, ...)
{
    // (1) 定义参数列表
    va_list ap;
    // (2) 初始化参数列表
    va_start(ap, first_num);

    int result = first_num;
    int temp = 0;
    // 获取参数值
    while ((temp = va_arg(ap, int)) != END) //va_arg 用于对一个参数进行存取,它的两个参数为va_list变量名和希望存取的参数类型,va_arg将取得这个参数,并更新va_list指向下一个参数、
    {
        result += temp;
    }

    // 关闭参数列表,一定要调用该宏,在大多数C实现上,调用va_end与否并无区别,但某些机器上,为了方便va_list遍历,实际采用动态分配内存。
    va_end(ap);

    return result;
}

vprintf,vfprintf,vsprintf与相应的函数在使用方式上类似,只是用va_list替换了格式字符串后的参数序列。用法如下:

char *format;

va_start(ap);

format = va_arg(ap, char *);

fprintf(stderr, "error: ");

vfprintf(stderr, format, ap);

va_end(ap);


实现varargs.h

typedef char *va_list;

#define va_dcl int va_alist;

#define va_start(list) list = (char *)va_alist

#define va_end(list)

#define va_arg(list, mode) \

((mode *) (list += sizeof(mode)))[-1]

因此,

int fun(va_alist) va_dcl ;

被扩展为:

int fun(va_alist)  int va_alist ;

实际上这种实现方式隐含了要求函数参数在内存中连续储存。

va_arg有个陷阱要注意:

第2个参数不能用char ,short, float类型,因为char short类型被自动换成int类型,float会被换成double类型。


ANSI 版varargs.h

ANSIC对varargs.h 做了一种扩展,但前者能够运行的环境更多,可移植性更强,ANSIC的头文件为stdarg.h

其用法见例子,stdarg.h中并没有va_alist,va_dcl宏。




版权声明:本文为博主原创文章,未经博主允许不得转载。

《C陷阱与缺陷》总结

这本书不是对C语言的批判,而是列出了一些使用C语言常见的错误,以及一些看似简单的陷阱。对于提升对C语言的使用及认识有很大的作用!         该书主要讲解了词法分析、语法分析以及语义细...
  • u011795520
  • u011795520
  • 2016年06月14日 21:37
  • 527

C陷阱与缺陷学习笔记

N年读过这本书,当时读的时候囫囵吞枣,加上时间久远,90%的内容都忘记了。昨天在整理书籍的时候翻出来了,这本书短小精悍却不失为经典之作。抽出时间再拜读一遍,顺便做做笔记,记录精华。...
  • zfs2008zfs
  • zfs2008zfs
  • 2016年04月23日 09:38
  • 1554

C陷阱与缺陷阅读笔记(上)

转载自:http://blog.csdn.net/app_12062011/article/details/16951777 词法陷阱 1.贪心法 C编译器对C语言符号的识别,基于每一个符号...
  • yeyiliang
  • yeyiliang
  • 2017年04月15日 16:19
  • 122

C陷阱与缺陷阅读笔记(上)

1.贪心法 C编译器对C语言符号的识别,基于每一个符号应该包含尽可能多的字符原则。 如果输入流截止至某个字符之前都已经分解成为一个个符号,那么下一个符号将包括从该字符之后可能组成一个符号的最长字符串。...
  • App_12062011
  • App_12062011
  • 2013年11月25日 21:41
  • 986

《C缺陷与陷阱》阅读笔记-词法陷阱

《C缺陷与陷阱》阅读笔记-词法陷阱 1.1 = 不同于 == 1.2 & 不同于 && | 不同于 || 1.3 词法分析中的“贪心法” 编译器将程序分解成符号的方法是:每个符号应该包含尽可能多的字符...
  • u013178472
  • u013178472
  • 2016年11月28日 20:25
  • 127

C陷阱与缺陷--笔记

原文链接:http://codeshold.me/2017/01/c_trapsandpitfalls.html
  • wuzhimang
  • wuzhimang
  • 2017年01月28日 23:59
  • 964

《C陷阱与缺陷》学习笔记(一)

前言和导读   “得心应手的工具在初学时的困难程度往往超过那些容易上手的工具。”比较认同这句话。我至今觉得自己其实还是个刚入了门的初学者。   第一章  “词法”陷阱     由于之前学过编...
  • ghevinn
  • ghevinn
  • 2014年06月06日 16:12
  • 1458

《C陷阱与缺陷》&《C专家编程》4

2016年11月20日 22:40:09   ——————————————《C陷阱与缺陷》———————————————— 库函数这一章,讲的库函数基本没见过,不写笔记了 ——...
  • qq_36100960
  • qq_36100960
  • 2016年11月20日 22:40
  • 103

阅读笔记《c陷阱与缺陷》《c和指针》

《c陷阱与缺陷》笔记         ——————2011/12    1. 表达式的结合:依次尽可能多的读取字符,直到读取下一个不能组成完整意义的字符(贪心法),或遇到空白(空格,tab,回车...
  • songcf_faith
  • songcf_faith
  • 2015年04月29日 14:14
  • 227

《C陷阱与缺陷》阅读笔记1

第一章  词法“陷阱” 1.当需要对变量进行赋值并检查该变量的新值是否为0时,为了避免编译器的警告     需要这样写:(加括号是考虑运算符的优先级) if((x = y) != 0) { f...
  • jw903
  • jw903
  • 2015年05月21日 21:39
  • 359
内容举报
返回顶部
收藏助手
不良信息举报
您举报文章:C陷阱与缺陷阅读笔记(下)
举报原因:
原因补充:

(最多只允许输入30个字)