/***
我們提供的服務(wù)有:成都網(wǎng)站制作、成都網(wǎng)站建設(shè)、微信公眾號開發(fā)、網(wǎng)站優(yōu)化、網(wǎng)站認(rèn)證、朔城ssl等。為1000+企事業(yè)單位解決了網(wǎng)站和推廣的問題。提供周到的售前咨詢和貼心的售后服務(wù),是有科學(xué)管理、有技術(shù)的朔城網(wǎng)站制作公司
*printf.c - print formatted
*
* Copyright (c) 1985-1997, Microsoft Corporation. All rights reserved.
*
*Purpose:
* defines printf() - print formatted data
*
*******************************************************************************/
#include
#include
#include
#include
#include
#include
#include
/***
*int printf(format, ...) - print formatted data
*
*Purpose:
* Prints formatted data on stdout using the format string to
* format data and getting as many arguments as called for
* Uses temporary buffering to improve efficiency.
* _output does the real work here
*
*Entry:
* char *format - format string to control data format/number of arguments
* followed by list of arguments, number and type controlled by
* format string
*
*Exit:
* returns number of characters printed
*
*Exceptions:
*
*******************************************************************************/
int __cdecl printf (
const char *format,
...
)
/*
* stdout ''PRINT'', ''F''ormatted
*/
{
va_list arglist;
int buffing;
int retval;
va_start(arglist, format);
_ASSERTE(format != NULL);//斷言宏。如果輸出格式字符串指針為空,則在DEBUG版下斷言,報告錯誤。
_lock_str2(1, stdout);
buffing = _stbuf(stdout);//stdout:指定輸出到屏幕
retval = _output(stdout,format,arglist);
_ftbuf(buffing, stdout);
_unlock_str2(1, stdout);
return(retval);
}
以上為printf()的源代碼
1、從含有可選參數(shù)函數(shù)中獲得可選參數(shù),以及操作這些參數(shù)
typedef char *va_list;
void va_start( va_list arg_ptr, prev_param );
type va_arg( va_list arg_ptr, type );
void va_end( va_list arg_ptr );
假定函數(shù)含有一個必選參數(shù)和多個可選參數(shù),必選參數(shù)聲明為普通數(shù)據(jù)類型,且能通過參數(shù)名來獲得該變量的值??蛇x參數(shù)通過宏va_start、va_arg和va_end(定義在stdarg.h或varargs.h中)來進行操作,即通過設(shè)置指向第一個可選參數(shù)指針、返回當(dāng)前參數(shù)、在返回參數(shù)后重新設(shè)置指針來操作所有的可選參數(shù)。
va_start:為獲取可變數(shù)目參數(shù)的函數(shù)的參數(shù)提供一種便捷手段。設(shè)置arg_ptr為指向傳給函數(shù)參數(shù)列表中的第一個可選參數(shù)的指針,且該參數(shù)必須是va_list類型。prev_param是在參數(shù)列表中第一個可選參數(shù)前的必選參數(shù)。
va_arg:返回由arg_ptr所指向的參數(shù)的值,且自增指向下一個參數(shù)的地址。type為當(dāng)前參數(shù)的類型,用來計算該參數(shù)的長度,確定下一個參數(shù)的起始位置。它可以在函數(shù)中應(yīng)用多次,直到得到函數(shù)的所有參數(shù)為止,但必須在宏va_start后面調(diào)用。
va_end:在獲取所有的參數(shù)后,設(shè)置指針arg_ptr為NULL。
下面舉例說明:
#include
#include
int average( int first, ... );
void main( void )
{
/* Call with 3 integers (-1 is used as terminator). */
printf( "Average is: %d\n", average( 2, 3, 4, -1 ) );
/* Call with 4 integers. */
printf( "Average is: %d\n", average( 5, 7, 9, 11, -1 ) );
/* Call with just -1 terminator. */
printf( "Average is: %d\n", average( -1 ) );
}
int average( int first, ... )
{
int count = 0, sum = 0, i = first;
va_list marker;
va_start( marker, first ); /* Initialize variable arguments. */
while( i != -1 )
{
sum += i;
count++;
i = va_arg( marker, int);
}
va_end( marker ); /* Reset variable arguments. */
return( sum ? (sum / count) : 0 );
}
返回值為:
Average is: 3
Average is: 8
Average is: 0
綜上所述,在printf()函數(shù)中,可以只輸出一個字符串,也可按照一定的形式輸出含有多個可選參數(shù)的字符串信息。因此,首先就要通過這些宏來獲取所有的可選參數(shù)。在上面的源碼可以看出printf()中,只使用了宏at_start,將可選參數(shù)的首地址賦給了arglist。
2、鎖定字符串及輸出字符串到屏幕
#define _lock_str2(i,s) _lock_file2(i,s)
void __cdecl _lock_file2(int, void *);
#define _unlock_str2(i,s) _unlock_file2(i,s)
void __cdecl _unlock_file2(int, void *);
int __cdecl _stbuf(FILE *);
void __cdecl _ftbuf(int, FILE *);
int __cdecl _output(FILE *, const char *, va_list);
在output函數(shù)中,讀取格式字符串中的每一個字符,然后對其進行處理,處理方式根據(jù)每一個字符所代表的意義來進行,如:普通字符直接利用函數(shù)WRITE_CHAR(ch, charsout);輸出到控制臺。
其中的主要部分是對轉(zhuǎn)換說明符(d,c,s,f)的處理,現(xiàn)在將對其中的部分代碼進行詳細說明,這里只說明最基本的轉(zhuǎn)換說明符,對這些須基本的轉(zhuǎn)換說明符進行修飾的修飾符,程序中單獨進行處理。下面是函數(shù)output()(output.c)部分源代碼:
case ST_TYPE:
//表示當(dāng)前處理的字符的類型為轉(zhuǎn)換說明符。
...
switch (ch) {
//下面對參數(shù)的獲取都是利用宏va_arg( va_list arg_ptr, type );來進行的。
case ''c'': {
//從參數(shù)表中獲取單個字符,輸出到緩沖字符串中,此時,type=int
buffer[0] = (char) get_int_arg(argptr); /* get char to print */
text = buffer;
textlen = 1; /* print just a single character */
}
break;
case ''s'': {
//從參數(shù)表中獲取字符串,輸出到緩沖字符串中,此時,type=char*
int i;
char *p; /* temps */
text = get_ptr_arg(argptr);
...
}
break;
case ''w'': {
//對寬字符進行處理
...
} /* case ''w'' */
break;
...
case ''e'':
case ''f'':
case ''g'': {
//對浮點數(shù)進行操作
...
#if !LONGDOUBLE_IS_DOUBLE
/* do the conversion */
if (flags FL_LONGDOUBLE) {
_cldcvt((LONGDOUBLE*)argptr, text, ch, precision, capexp);
va_arg(argptr, LONGDOUBLE);
//對長雙精度型進行處理,此時,type=long double
}
else
#endif /* !LONGDOUBLE_IS_DOUBLE */
{
//對雙精度型進行處理,此時,type=double
_cfltcvt((DOUBLE*)argptr, text, ch, precision, capexp);
va_arg(argptr, DOUBLE);
}
...
break;
//對整型變量處理
case ''d'':
case ''i'':
...
goto COMMON_INT;
case ''u'':
radix = 10;
goto COMMON_INT;
case ''p'':
...
goto COMMON_INT;
case ''o'':
...
注:對于浮點型double和long double,有相應(yīng)的轉(zhuǎn)換說明符(%f表示雙精度型,%lf表示長雙精度型),而float卻沒有。其中的原因是,在KRC下,float值用于表達式或用作參數(shù)前,會自動轉(zhuǎn)換成double類型。而ANSI C一般不會自動把float轉(zhuǎn)換成double。有些程序已假定其中的float參數(shù)會被轉(zhuǎn)換成double,為了保護大量這樣的程序,所有printf()函數(shù)的float參數(shù)還是被自動轉(zhuǎn)換成double型。因此,在KRC或ANSI C下,都無需用特定的轉(zhuǎn)換說明符來顯示float型。
綜上所述,轉(zhuǎn)換說明符必須與待打印字符的類型。通常,用戶有種選擇。例如,如要打印一個int類型的值。則只可以使用%d,%x或%o。所有這些說明符都表示要打印一個int類型的值;它們只不過提供了一個數(shù)值的幾種不同表示。類似一,可以用%f、%g和%e來表示double類型的值。但如果轉(zhuǎn)換說明與類型不匹配,將會出現(xiàn)意想不到的結(jié)果。為什么呢?問題就在于C向函數(shù)傳遞信息的方式。
這個失敗的根本細節(jié)與具體實現(xiàn)相關(guān)。它決定了系統(tǒng)中的參數(shù)以何方式傳遞。函數(shù)調(diào)用如下:
float n1;
double n2;
long n3;
long n4;
...
printf("%ld,%ld,%ld,%ld",n1,n2,n3,n4);
這個調(diào)用告訴計算機,要把變量n1,n2,n3和n4的值交給計算機,它把這些變量放進稱作棧(stack)的內(nèi)存區(qū)域中,來完成這一任務(wù)。計算機把這些值放進棧中,其根據(jù)是變量的類型而不是轉(zhuǎn)換說明符,比如n1,把8個字節(jié)放入棧中(float被轉(zhuǎn)換成double),類似地,為n2放了8字節(jié),其后給n3和n4各放了4個字節(jié)。接著,控制的對象轉(zhuǎn)移到printf();此函數(shù)從棧中讀數(shù),不過在這一過程中,它是在轉(zhuǎn)換說明符的指導(dǎo)下,讀取數(shù)值的。說明符%ld指定printf()應(yīng)讀4個字節(jié)(va_arg( va_list arg_ptr, type )中type=long),因此printf()讀入棧中的4個字節(jié),作為它的第一個值。但是這只是n1的前半部分,這個值被看成一個long整數(shù)。下一個說明符%ld讀入4個字節(jié),這正是n1的后半部分,這個值被看成第二個long整數(shù)。類似地,第三、第四次又讀入n2的前后兩部分。因此,盡管我們對n3和n4使用了正確的說明符,printf()仍然會產(chǎn)生錯誤。
math.h
函數(shù)名:abs
功能:求整數(shù)的絕對值
用法:int abs(int i)
函數(shù)名: sqrt
功 能: 計算平方根
用 法: double sqrt(double x);
assert.h
函數(shù)名: assert
功 能: 測試一個條件并可能使程序終止
用 法: void assert(int test);
stdio.h
函數(shù)名: fopen
功 能:打開用filename指定的文件,并使其與一個流相聯(lián)。
用 法: FILE *fopen(const char *filename,const char *mode)
函數(shù)名: fclose
功 能: 關(guān)閉一個流
用 法: int fclose(FILE *stream);
函數(shù)名: fread
功 能: 從一個流中讀數(shù)據(jù)
用 法: int fread(void *ptr, int size, int nitems, FILE *stream);
string.h
函數(shù)名: strcpy
功 能: 拷貝一個字符串到另一個
用 法: char *strcpy(char *destin, char *source);
函數(shù)名: strcmp
功 能: 串比較
用 法: int strcmp(char *str1, char *str2);
函數(shù)名: strcpy
功 能: 串拷貝
用 法: char *strcpy(char *str1, char *str2);
malloc.h
函數(shù)名: malloc
功 能: 內(nèi)存分配函數(shù)
用 法: void *malloc(unsigned size);
函數(shù)名: free
功 能: 釋放已分配的塊
用 法: void free(void *ptr);
stdlib.h
函數(shù)名: atoi
功 能: 把字符串轉(zhuǎn)換成整型數(shù),并返回這個數(shù),錯誤返回0;
用 法: int atoi(const char *nptr);
函數(shù)名: rand
功 能: 隨機數(shù)發(fā)生器
用 法: void rand(void);
我不能保證你所有的函數(shù)都用到,但你要求的是函數(shù)庫,所以我給你把列表寫出來,用到那方面的就用那個函數(shù)庫就行!
字符函數(shù),所在函數(shù)庫為ctype.h
數(shù)學(xué)函數(shù),所在函數(shù)庫為math.h、stdlib.h、string.h、float.h
目錄函數(shù),所在函數(shù)庫為dir.h、dos.h
進程函數(shù),所在函數(shù)庫為stdlib.h、process.h
轉(zhuǎn)換子程序,函數(shù)庫為math.h、stdlib.h、ctype.h、float.h
診斷函數(shù),所在函數(shù)庫為assert.h、math.h
輸入輸出子程序,函數(shù)庫為io.h、conio.h、stat.h、dos.h、stdio.h、signal.h
接口子程序,所在函數(shù)庫為:dos.h、bios.h
操作函數(shù),所在函數(shù)庫為string.h、mem.h。
存貯分配子程序,所在函數(shù)庫為dos.h、alloc.h、malloc.h、stdlib.h、process.h
時間日期函數(shù),函數(shù)庫為time.h、dos.h
學(xué)以致用,用什么就看什么!
C語言的庫函數(shù)很多
大概幾百個,舉幾個常用的
比如
printf()、scanf(),等等
庫函數(shù)是系統(tǒng)內(nèi)部定義好的,不需要定義,但在使用前
需要把它所在的函數(shù)庫的頭文件進來
,
比如你要使用
sin()這個函數(shù)
,這個函數(shù)在math.h
這個頭文件里
,
就需要在程序開始
加入
#include
“math.h”
然后你就可以在程序里直接使用了
文件stddef.h里包含了標(biāo)準(zhǔn)庫的一些常用定義,無論我們包含哪個標(biāo)準(zhǔn)頭文件,stddef.h都會被自動包含進來。
這個文件里定義:
類型size_t (sizeof運算符的結(jié)果類型,是某個無符號整型);
類型ptrdiff_t(兩個指針相減運算的結(jié)果類型,是某個有符號整型);
類型wchar_t (寬字符類型,是一個整型,其中足以存放本系統(tǒng)所支持的所有本地環(huán)境中的字符集的所有編碼值。這里還保證空字符的編碼值為0);
符號常量NULL (空指針值);
宏offsetor (這是一個帶參數(shù)的宏,第一個參數(shù)應(yīng)是一個結(jié)構(gòu)類型,第二個參數(shù)應(yīng)是結(jié)構(gòu)成員名。 offsetor(s,m)求出成員m在結(jié)構(gòu)類型t的變量里的偏移量)。