天天看點

[2012山東ACM省賽] Fruit Ninja II (三重積分,橢球體積)

              大概去年夏天的時候,在《c和指針》裡面一個練習題要求實作一個很簡單的不包含列印浮點數功能的printf函數。但是很好奇,于是一直糾結下去,結果就是知道了printf的實作,自己也寫了一個簡單的。或許是夏天的原因吧,那時候暑假沒回去,淩晨四點興奮到不能睡覺。那時候剛開始寫blog。沒想整理一下,隻是簡單的把最重要的實作"工具"貼了一個blog在

那時候第一次看linux的核心代碼,熬過來的感覺總是很舒坦的。。。現在可能還是有很多看不懂,但是不會去畏懼那些長段長段的宏定義函數了。。。各種高上大的skill啊。。。。

言歸正傳,實作printf。

其實printf和getchar()類似,它們都是一個”外殼“,真正實作功能的不是它本身,而是通過調用别的函數。

getchar() is equivalent to getc(stdin).

printf有一家子print函數

       printf, fprintf, sprintf, snprintf, vprintf, vfprintf, vsprintf, vsnprintf - formatted output conversion

它們的聲明在不同的header file裡面

       snprintf(), vsnprintf():

           這兩個函數是c99新加的,編譯的時候 注意 -std=c99

實作之前還是“複習”一下printf比較好,就當是鋪墊

有意思的是printf的declaration。

傳回值是int,第一個參數是const字元串指針,第二個參數是個...

先看看傳回值int有哪些情況

return value

    upon successful return, these functions return the number of characters printed (excluding the  null  byte  used  to  end  output  to strings).

嗯哼。。。傳回的是成功列印的字元的個數,這裡不包括null

demo:

jasonleaster@ubuntu:~$ ./a.out

hello world! 10

counter is 16

接着,第一個參數是一個指針,指向const字元串

format of the format string

       the format string is a character string, beginning and ending in its initial shift state, if any.  the format string is  composed  of

       zero or more directives: ordinary characters (not %), which are copied unchanged to the output stream; and conversion specifications,

       each of which results in fetching zero or more subsequent arguments.  each conversion specification is introduced by the character %,

       and  ends  with a conversion specifier.  in between there may be (in this order) zero or more flags, an optional minimum field width,

       an optional precision and an optional length modifier.

很少人會用下面這種用法

printf("%*d",10,5);

我第一次遇到的時候,可以說是“驚愕”,究竟會列印什麼東西。折騰了好久,最後搞定了。總結在這裡

       the format string is a character string, beginning and ending in its initial shift state, if any.  the format string is  composed  of zero or more directives: ordinary characters (not %), which are copied unchanged to the output stream; and conversion

specifications, each of which results in fetching zero or more subsequent arguments.  each conversion specification is introduced by the character %, and  ends  with a conversion specifier.  in between there may be (in this order) zero or more flags, an optional

minimum field width, an optional precision and an optional length modifier.

       the arguments must correspond properly (after type promotion) with the conversion specifier.  by default, the arguments are  used  in the  order  given, where each ‘*‘ and each conversion specifier asks for the next argument (and it is an error if insufficiently

many arguments are given).  one can also specify explicitly which argument is taken, at each place where an argument is required, by writing  "%m$"  instead  of  ‘%‘  and  "*m$" instead of ‘*‘, where the decimal integer m denotes the position in the argument

list of the

       desired argument, indexed starting from 1.  thus,

           printf("%*d", width, num);

       and

           printf("%2$*1$d", width, num);

       are equivalent.  the second style allows repeated references to the same argument.  the c99 standard does not include the style using ‘$‘,  which comes from the single unix specification.  if the style using ‘$‘ is used, it must be used throughout for

all conversions taking an argument and all width and precision arguments, but it may be mixed with "%%" formats which do  not  consume  an  argument.

       there  may  be  no  gaps in the numbers of arguments specified using ‘$‘; for example, if arguments 1 and 3 are specified, argument 2

       must also be specified somewhere in the format string.

第三個參數  ...

嗯,這家夥有點屌,叫做變長參數。把這個搞定,c總會有點長進的

           這個stdarg.h 我在現在的gcc和現在的linux 3.0版本的核心裡面找了好久,都木有,估計是封裝到被的地方了。。。。

__builtin_va_start(v,l) 線索就死在這個地方。。。之後就找不到__builtin_va_start的定義了

還是看早起核心的實作吧

0.12核心裡面的stdarg.h

va_list 是一個指向字元串的指針

分析上面的宏定義

這個用來得到type元素類型的位元組大小,若不足4位元組(例如short 和char),那麼認為這個元素的大小為4位元組,簡單的說就是檢測元素的大小,不足4位元組的當作4位元組看待。。。

ap一般都是va_list,lastarg則是指向參數變長函數的格式化字元串的指針.

va_start的作用就很明顯了。取得變長參數清單的第一個參數的位址。

va_end 則是把指針va_list 置0 (通過google知道的,這個va_end真沒找到定義,代碼裡面就一句#define 我無能為力啊。。。)

不過知道用va_start 和va_end 就ok啦

下面先來個變長參數的demo

寫這個demo的時候,稍微糾結了一下,還是留了個小陷阱,提示一下,最後foo的值不是15

[2012山東ACM省賽] Fruit Ninja II (三重積分,橢球體積)

這裡要特别注意變長參數函數的第一個值,必須是個指針,就是利用這個指針和棧的filo的性質來找到後面的元素的,這個是參數變長函數最根本的原理,利用了參數傳遞儲存在棧裡面,而且是連續的!

va_arg的作用很明顯

        把移動ap指針并取4位元組内容讀取出來。。。。我的語言表達隻能這樣了。。。t-t,看官自行領悟了。。。

        好吧,是時候貼出來我自己寫的一個鬧着玩的printf了,僅僅是好玩,實作一下很基本的功能而已,viewer有興趣的話可以去看vsprintf.c, 看源碼是個很爽的過程(如果看懂的話)。

真的很幼稚,建議去看vsprintf。很精彩

[2012山東ACM省賽] Fruit Ninja II (三重積分,橢球體積)