您好,對于你的遇到的問題,我很高興能為你提供幫助,我之前也遇到過喲,以下是我的個人看法,希望能幫助到你,若有錯誤,還望見諒!。展開全部
創(chuàng)新互聯(lián)長期為近千家客戶提供的網(wǎng)站建設(shè)服務(wù),團隊從業(yè)經(jīng)驗10年,關(guān)注不同地域、不同群體,并針對不同對象提供差異化的產(chǎn)品和服務(wù);打造開放共贏平臺,與合作伙伴共同營造健康的互聯(lián)網(wǎng)生態(tài)環(huán)境。為陸港企業(yè)提供專業(yè)的成都網(wǎng)站建設(shè)、做網(wǎng)站,陸港網(wǎng)站改版等技術(shù)服務(wù)。擁有十余年豐富建站經(jīng)驗和眾多成功案例,為您定制開發(fā)。
用C語言的時候,您是否還在使用printf函數(shù)來輸出日志呢?您是否考慮過將printf函數(shù)打印的內(nèi)容存到文件中去呢?您是否想擁有一個可選擇的既支持輸出到屏幕又支持存儲到文件中的日志函數(shù)呢?很高興的告訴您,如果您愿意的話,歡迎使
一、定義宏變量BUF_SIZE
view plaincopy to clipboardprint?
#define BUF_SIZE 1024
二、定義log_st結(jié)構(gòu)體
view plaincopy to clipboardprint?
typedef struct _log_st log_st;
struct _log_st
{
char path[128];
int fd;
int size; 非常感謝您的耐心觀看,如有幫助請采納,祝生活愉快!謝謝!
用C語言的時候,您是否還在使用printf函數(shù)來輸出日志呢?您是否考慮過將printf函數(shù)打印的內(nèi)容存到文件中去呢?您是否想擁有一個可選擇的既支持輸出到屏幕又支持存儲到文件中的日志函數(shù)呢?很高興的告訴您,如果您愿意的話,歡迎使用本人編寫的一個一套日志函數(shù),該套函數(shù)由五部分組成,分別是宏變量BUF_SIZE、結(jié)構(gòu)體log_st、log_init函數(shù)、log_debug函數(shù)和log_checksize函數(shù)。其中宏變量BUF_SIZE用來限制每次輸出的日志的最大長度;結(jié)構(gòu)體用來存儲用戶需求,包括文件路徑、文件描述符號、單個文件最大大小、輸出方式標(biāo)志、文件命名標(biāo)志等;log_init函數(shù)用來完成用戶需求錄入、文件創(chuàng)建等功能,在mian函數(shù)的開始調(diào)用一次即可;log_debug函數(shù)的功能跟printf很類似,是在printf基礎(chǔ)上進行的擴充,實現(xiàn)將日志輸出到屏幕或者寫入到文件,在需要打印日志的地方調(diào)用該函數(shù);log_checksize函數(shù)用來檢測日志文件大小是否超過最大大小限制,它需要您定時或者定點調(diào)用它,如果一直不調(diào)用,則日志文件將不受指定的最大大小限制。
一、定義宏變量BUF_SIZE
view plaincopy to clipboardprint?
#define?BUF_SIZE?1024
二、定義log_st結(jié)構(gòu)體
view plaincopy to clipboardprint?
typedef?struct?_log_st?log_st;
struct?_log_st
{
char?path[128];
int?fd;
int?size;
int?level;
int?num;
};
三、定義log_init函數(shù)
參數(shù)說明:path——您要存儲的文件路徑;size——單個文件的最大大小,如果超過該大小則新建新的文件用來存儲;level——日志輸出方式,建議在上層限制其值的范圍為0到3,0表示日志既不輸出到屏幕也不創(chuàng)建文件和保存到文件,1表示日志保存到文件但不輸出到屏幕,2表示日志既輸出到屏幕也保存到文件,3表示日志只輸出到文件而不創(chuàng)建文件和存入文件;num——日志文件命名方式,非0表示以(int)time(NULL)作為文件名來保存文件,文件數(shù)量隨著日志量的遞增而遞增;0表示以“.new”和“.bak”為文件名來保存文件,文件數(shù)量不超過兩個,隨著日志量的遞增,舊的日志文件將被新的覆蓋,更直觀的說就是說.new”和“.bak”文件只保存最近的日志。
view plaincopy to clipboardprint?
log_st?*log_init(char?*path,?int?size,?int?level,?int?num)
{
char?new_path[128]?=?{0};
if?(NULL?==?path?||?0?==?level)?return?NULL;
log_st?*log?=?(log_st?*)malloc(sizeof(log_st));
memset(log,?0,?sizeof(log_st));
if?(level?!=?3)
{
//the?num?use?to?control?file?naming
log-num?=?num;
if(num)
snprintf(new_path,?128,?"%s%d",?path,?(int)time(NULL));
else
snprintf(new_path,?128,?"%s.new",?path);
if(-1?==?(log-fd?=?open(new_path,?O_RDWR|O_APPEND|O_CREAT|O_SYNC,?S_IRUSR|S_IWUSR|S_IROTH)))
{
free(log);
log?=?NULL;
return?NULL;
}
}
strncpy(log-path,?path,?128);
log-size?=?(size??0???size:0);
log-level?=?(level??0???level:0);
return?log;
}
四、定義log_debug函數(shù)
view plaincopy to clipboardprint?
void?log_debug(log_st?*log,?const?char?*msg,?...)
{
va_list?ap;
time_t?now;
char?*pos;
char?_n?=?'\n';
char?message[BUF_SIZE]?=?{0};
int?nMessageLen?=?0;
int?sz;
if(NULL?==?log?||?0?==?log-level)?return;
now?=?time(NULL);
pos?=?ctime(now);
sz?=?strlen(pos);
pos[sz-1]=']';
snprintf(message,?BUF_SIZE,?"[%s?",?pos);
for?(pos?=?message;?*pos;?pos++);
sz?=?pos?-?message;
va_start(ap,?msg);
nMessageLen?=?vsnprintf(pos,?BUF_SIZE?-?sz,?msg,?ap);
va_end(ap);
if?(nMessageLen?=?0)?return;
if?(3?==?log-level)
{
printf("%s\n",?message);
return;
}
if?(2?==?log-level)
printf("%s\n",?message);
write(log-fd,?message,?strlen(message));
write(log-fd,?_n,?1);
fsync(log-fd);
}
五、定義log_checksize函數(shù)
view plaincopy to clipboardprint?
void?log_checksize(log_st?*log)
{
struct?stat?stat_buf;
char?new_path[128]?=?{0};
char?bak_path[128]?=?{0};
if(NULL?==?log?||?3?==?log-level?||?'\0'?==?log-path[0])?return;
memset(stat_buf,?0,?sizeof(struct?stat));
fstat(log-fd,?stat_buf);
if(stat_buf.st_size??log-size)
{
close(log-fd);
if(log-num)
snprintf(new_path,?128,?"%s%d",?log-path,?(int)time(NULL));
else
{
snprintf(bak_path,?128,?"%s.bak",?log-path);
snprintf(new_path,?128,?"%s.new",?log-path);
remove(bak_path);?//delete?the?file?*.bak?first
rename(new_path,?bak_path);?//change?the?name?of?the?file?*.new?to?*.bak
}
//create?a?new?file
log-fd?=?open(new_path,?O_RDWR|O_APPEND|O_CREAT|O_SYNC,?S_IRUSR|S_IWUSR|S_IROTH);
}
}
你輸入6后回車,程序先讀入6,不是回車,輸出6,然后讀入回車結(jié)束循環(huán),在輸出yes,所以結(jié)果是6yes。
注意:while循環(huán)就只有一個語句:printf("%c",c);
printf("yes");語句是在while循環(huán)外的,所以只要while循環(huán)結(jié)束就會輸出yes。
C中的可變參數(shù)研究
一. 何謂可變參數(shù)
int printf( const char* format, ...);
這是使用過C語言的人所再熟悉不過的printf函數(shù)原型,它的參數(shù)中就有固定參數(shù)format和可變參數(shù)(用”…”表示). 而我們又可以用各種方式來調(diào)用printf,如:
printf("%d",value);
printf("%s",str);
printf("the number is %d ,string is:%s", value, str);
二.實現(xiàn)原理
C語言用宏來處理這些可變參數(shù)。這些宏看起來很復(fù)雜,其實原理挺簡單,就是根據(jù)參數(shù)入棧的特點從最靠近第一個可變參數(shù)的固定參數(shù)開始,依次獲取每個可變參數(shù)的地址。下面我們來分析這些宏。在VC中的stdarg.h頭文件中,針對不同平臺有不同的宏定義,我們選取X86平臺下的宏定義:
typedef char *va_list;
/*把va_list被定義成char*,這是因為在我們目前所用的PC機上,字符指針類型可以用來存儲內(nèi)存單元地址。而在有的機器上va_list是被定義成void*的*/
#define _INTSIZEOF(n) ( (sizeof(n) + sizeof(int) - 1) ~(sizeof(int) - 1) )
/*_INTSIZEOF(n)宏是為了考慮那些內(nèi)存地址需要對齊的系統(tǒng),從宏的名字來應(yīng)該是跟sizeof(int)對齊。一般的sizeof(int)=4,也就是參數(shù)在內(nèi)存中的地址都為4的倍數(shù)。比如,如果sizeof(n)在1-4之間,那么_INTSIZEOF(n)=4;如果sizeof(n)在5-8之間,那么_INTSIZEOF(n)=8。*/
#define va_start(ap,v)( ap = (va_list)v + _INTSIZEOF(v) )
/*va_start的定義為 v+_INTSIZEOF(v) ,這里v是最后一個固定參數(shù)的起始地址,再加上其實際占用大小后,就得到了第一個可變參數(shù)的起始內(nèi)存地址。所以我們運行va_start(ap, v)以后,ap指向第一個可變參數(shù)在的內(nèi)存地址*/
#define va_arg(ap,t) ( *(t *)((ap += _INTSIZEOF(t)) - _INTSIZEOF(t)) )
/*這個宏做了兩個事情,
①用用戶輸入的類型名對參數(shù)地址進行強制類型轉(zhuǎn)換,得到用戶所需要的值
②計算出本參數(shù)的實際大小,將指針調(diào)到本參數(shù)的結(jié)尾,也就是下一個參數(shù)的首地址,以便后續(xù)處理。*/
#define va_end(ap) ( ap = (va_list)0 )
/*x86平臺定義為ap=(char*)0;使ap不再 指向堆棧,而是跟NULL一樣.有些直接定義為((void*)0),這樣編譯器不會為va_end產(chǎn)生代碼,例如gcc在linux的x86平臺就是這樣定義的. 在這里大家要注意一個問題:由于參數(shù)的地址用于va_start宏,所以參數(shù)不能聲明為寄存器變量或作為函數(shù)或數(shù)組類型. */
以下再用圖來表示:
在VC等絕大多數(shù)C編譯器中,默認情況下,參數(shù)進棧的順序是由右向左的,因此,參數(shù)進棧以后的內(nèi)存模型如下圖所示:最后一個固定參數(shù)的地址位于第一個可變參數(shù)之下,并且是連續(xù)存儲的。
|——————————————————————————|
|最后一個可變參數(shù) | -高內(nèi)存地址處
|——————————————————————————|
...................
|——————————————————————————|
|第N個可變參數(shù) | -va_arg(arg_ptr,int)后arg_ptr所指的地方,
| | 即第N個可變參數(shù)的地址。
|——————————————— |
………………………….
|——————————————————————————|
|第一個可變參數(shù) | -va_start(arg_ptr,start)后arg_ptr所指的地方
| | 即第一個可變參數(shù)的地址
|——————————————— |
|———————————————————————— ——|
| |
|最后一個固定參數(shù) | - start的起始地址
|—————————————— —| .................
|—————————————————————————— |
| |
|——————————————— |- 低內(nèi)存地址處
三.printf研究
下面是一個簡單的printf函數(shù)的實現(xiàn),參考了中的156頁的例子,讀者可以結(jié)合書上的代碼與本文參照。
#include "stdio.h"
#include "stdlib.h"
void myprintf(char* fmt, ...) //一個簡單的類似于printf的實現(xiàn),//參數(shù)必須都是int 類型
{
char* pArg=NULL; //等價于原來的va_list
char c;
pArg = (char*) fmt; //注意不要寫成p = fmt !!因為這里要對//參數(shù)取址,而不是取值
pArg += sizeof(fmt); //等價于原來的va_start
do
{
c =*fmt;
if (c != '%')
{
putchar(c); //照原樣輸出字符
}
else
{
//按格式字符輸出數(shù)據(jù)
switch(*++fmt)
{
case 'd':
printf("%d",*((int*)pArg));
break;
case 'x':
printf("%#x",*((int*)pArg));
break;
default:
break;
}
pArg += sizeof(int); //等價于原來的va_arg
}
++fmt;
}while (*fmt != '\0');
pArg = NULL; //等價于va_end
return;
}
int main(int argc, char* argv[])
{
int i = 1234;
int j = 5678;
myprintf("the first test:i=%d",i,j);
myprintf("the secend test:i=%d; %x;j=%d;",i,0xabcd,j);
system("pause");
return 0;
}
在intel+win2k+vc6的機器執(zhí)行結(jié)果如下:
the first test:i=1234
the secend test:i=1234; 0xabcd;j=5678;
四.應(yīng)用
求最大值:
#include //不定數(shù)目參數(shù)需要的宏
int max(int n,int num,...)
{
va_list x;//說明變量x
va_start(x,num);//x被初始化為指向num后的第一個參數(shù)
int m=num;
for(int i=1;i {
//將變量x所指向的int類型的值賦給y,同時使x指向下一個參數(shù)
int y=va_arg(x,int);
if(ym)m=y;
}
va_end(x);//清除變量x
return m;
}
main()
{
printf("%d,%d",max(3,5,56),max(6,0,4,32,45,533));
}
//mvector.h?--?聲明
#ifndef?MVECTOR_H_
#define?MVECTOR_H_??
typedef?struct?_mvect
{
int?x;
int?y;
}vect;
void?getvect(vect*?v);//聲明
void?showvect(vect?v);//聲明
#endif
//mvector.c?--?實現(xiàn)
#include?"mvector.h"
#include?stdio.h?
void?getvect(vect*?v)
{
scanf("%d,%d",(v-x),(v-y));??
}??
void?showvect(vect?v)
{
printf("%d,%d",v.x,v.y);??
}
//main.c?--?使用
#include?"mvector.h"
#include?stdio.h
int?main()
{
vect?a;
getvect(a);
showvect(a);
}
注意mvector.h必須位于當(dāng)前文件夾下,且必須把mvector.c和main.c放在一個工程里編譯。
輸入:2,3
輸出:2,3
C一般不能直接調(diào)用C++函數(shù)庫,需要將C++庫封裝成C接口后,才可以使用C調(diào)用。
下面舉例,說明一個封裝策略:
//code in add.cxx
#include "add.h"
int sample::method()
{
cout"method is called!\n";
}
//code in add.h
#include
using namespace std;
class sample
{
public:
int method();
};
將上面的兩個文件生成動態(tài)庫libadd.so放到 /usr/lib目錄下,編譯命令如下:
sudo g++ -fpic -shared -g -o /usr/lib/libadd.so add.cxx -I ./
由于在C中不能識別類,所以要將上面類的成員函數(shù),要封裝成C接口函數(shù)才能被調(diào)用。下面進行封裝,將輸出接口轉(zhuǎn)換成C接口。
//code in mylib.cxx
#include "add.h"
#ifndef _cplusplus
#define _cplusplus
#include "mylib.h"
#endif
int myfunc()
{
sample ss;
ss.method();
return 0;
}
//code in mylib.h
#ifdef _cplusplus
extern "C"
{
#endif
int myfunc();
#ifdef _cplusplus
}
#endif
在linux下,gcc編譯器并沒用變量_cplusplus來區(qū)分是C代碼還是C++ 代碼(沒有宏定義),如果使用gcc編譯器,這里我們可以自己定義一個變量_cplusplus用于區(qū)分C和C++代碼,所以在mylib.cxx中定義 了一個變量_cplusplus用于識別是否需要“extern "C"”將函數(shù)接口封裝成C接口。但是如果使用g++編譯器則不需要專門定義_cplusplus,編譯命令如下:
g++ -fpic -shared -g -o mylib.so mylib.cxx -la -I ./
main.c
#include
#include
#include "mylib.h"
int
main()
{
int (*dlfunc)();
void *handle; //定義一個句柄
handle = dlopen("./mylib.so", RTLD_LAZY);//獲得庫句柄
dlfunc = dlsym(handle, "myfunc"); //獲得函數(shù)入口
(*dlfunc)();
dlclose(handle);
return 0;
}
編譯命令如下:
gcc -o main main.c ./mylib.so -ldl
下面就可以執(zhí)行了。
需要說明的是,由于main.c 和 mylib.cxx都需要包含mylib.h,并且要將函數(shù)myfunc封裝成C接口函數(shù)輸出需要“extern "C"”,而C又不識別“extern "C"”,所以需要定義_cplusplus來區(qū)別處理mylib.h中的函數(shù)myfunc。
在main.c的main函數(shù)中直接調(diào)用myfunc()函數(shù)也能執(zhí)行,這里介紹的是常規(guī)調(diào)用庫函數(shù)的方法。