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陷阱与缺陷》阅读笔记1

第一章  词法“陷阱” 1.当需要对变量进行赋值并检查该变量的新值是否为0时,为了避免编译器的警告     需要这样写:(加括号是考虑运算符的优先级) if((x = y) != 0) { f...
  • jw903
  • jw903
  • 2015-05-21 21:39
  • 294

《C陷阱与缺陷》读书笔记

——导读—— 词法分析:将程序分解成符号(token)的过程。 ——第一章 词法陷阱—— library routine 库函数 Pascal和Ada使用:=做赋值运算符,=做比较运算符 C语言中,...

我是如何成为一名python大咖的?

人生苦短,都说必须python,那么我分享下我是如何从小白成为Python资深开发者的吧。2014年我大学刚毕业..

《C陷阱与缺陷》读书笔记2

第二章   语法陷阱  2.1  理解函数声明         任何C变量的声明都由两部分组成:类型以及一组类似表达式的声明符(declarator)。 ...

《C陷阱与缺陷》读书笔记

本文是自己读《C陷阱与缺陷》过程中的笔记,内容主要摘自原书 1. 当两个有符号整数运算时,“溢出”有可能发生。 例如:a和b是两个非负整型变量,检查a+b是否会溢出,可以采用如下方式: ...

《c陷阱与缺陷》4-6章笔记

连接1、extern 关键字表示对外部对象的引用,其定义只能在程序的某个地方进行,且只能定义一次。另外尽量少使用extern多处引用外部变量,如若干个函数需要共享一组外部对象,可以将这些函数放到一个源...

【读书笔记】C陷阱与缺陷

词法陷阱 = 不同于 == &和|不同于&&和|| 词法分析的“贪心法”:从左到右一个字符一个字符地读入,如果该字符能组成一个符号,那么再读入下一个字符,判断这两个字符组成的字符串是否可能是一个符号的...

《C陷阱与缺陷》学习笔记

单引号本质代表引用一个数字,而双引号本质上代表一个指针。 printf(‘\n’)在当今大多数编译器非法。  a+++++b的解法:     这种写法应当有一个最合理的解释:     ...

《C陷阱与缺陷》读书笔记

第一章《词法“陷阱”》1,=和==,一个容易疏忽的地方2,词法分析中的“贪心法”,诸如i++++++++j的理解,以前我总认为这是很无聊的问题,现在也是,呵呵,其实对这种问题的解决方案可以归纳为一个很...

《C陷阱与缺陷》读书笔记-Part1

第一章 词法“陷阱” 导言:字C语言中术语“符号”(token)指的是程序的一个基本组成单位,在边编译器中将语句分解成一个一个token的部分称为“词法分析器”。 看例子: if( x > big...

C陷阱与缺陷 读后笔记

C陷阱与缺陷第三章 这是笔者第一次读完著作后发表博客如有错误请及时告知笔者! 2017/2/28 (3.1-3.3)章 3.1 指针与数组 C语言中指针与数组这两个概念之间的联系是如此密不可分...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

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