Problem 36 gcc中格式化输出函数的研究
Ans:
1.前言
在gcc编程中,我们比较经常用到的字符格式化输出函数是printf的,实际上gcc继承了c语言处理字符具有强大功能的风格,它提供了一系列的格式化输出函数,主要存在两个库函数文件stdio.h/ stdarg.h中,具体函数如下:
#include <stdio.h>
printf, int printf(const char *format, ...);
fprintf, int fprintf(FILE *stream, const char *format, ...);
sprintf, int sprintf(char *str, const char *format, ...);
snprintf, int snprintf(char *str, size_t size, const char *format, ...);
#include <stdarg.h>
vprintf, vprintf(const char *format, va_list ap);
vfprintf, vfprintf(FILE *stream, const char *format, va_list ap);
vsprintf, int vsprintf(char *str, const char *format, va_list ap);
其中:
printf和vprintf函数主要是向一个标准输出设备或标准的字符流输出格式化后的字符。
fprintf和vfprintf 函数主要是向一个给定的字符流设备输出格式化后的字符。
而sprintf, snprintf, vsprintf 和vsnprintf函数则是将格式化后的结果赋值给一个字符串。
虽然函数的功能上有差异,返回的值的类型也不尽相同,但是在建立匹配格式的语法上还是相同的,这几个函数都有一些共同特征,就是你要设计好模板和格式化的字串。这些函数格式化字符串的命令主要是通过模板字符串中跟在“%”后面的字符来控制的。
如下一个例子中:
int pct = 37;
char filename[] = "foo.txt";
printf ("Processing of ‘%s' is %d%% finished.Please be patient.",filename, pct);
显然,这个例子的打印结果就是如下:
Processing of `foo.txt' is 37% finished.Please be patient.
如上例子我们可以看出一般格式化函数的语法特点就是包含格式化匹配的字符串,输出的字串和变量组合的结构。
2.参数详细介绍
由于大部分函数在如何格式化字串部分的语法非常相似,我们先研究他们的共同特点,然后再应用不同的例子来分析其不同特点:此类函数一般的调用格式为:printf("<格式化字符串>;", <参量表>;);
其中格式化字符串包括两部分内容: 一部分是正常字符, 这些字符将按原样输出; 另一部分是格式化规定字符, 以"%"开始, 后跟一个或几个规定字符,用来确定输出内容格式。参量表是需要输出的一系列参数, 其个数必须与格式化字符串所说明的输出参数个数一样多, 各参数之间用","分开, 且顺序一一对应, 否则将会出现意想不到的错误。
2.1 gcc提供的格式化规定符如下:
%d
十进制有符号整数
%i
十进制有符号整数
注:上面这两个函数在格式化输出时用途是相同的,但在输入时却是不一样的,%i可以接受任何形式的整数,而%d却不能。
%u
十进制无符号整数
%f
输出浮点数
%s
输出字符串
%c
输出单个字符
%p
输出指针的值
%e %E
指数形式的浮点数 ,其中:%e是以小写形式输出的 %E是以大写形式输出的
%x, %X
无符号以十六进制表示的整数,其中:%x是以小写形式输出的 %X是以大写形式输出的
’%g’, ‘%G’
根据输出数据的大小需要决定用普通形式还是指数形式的输出方式,其中: %g是以小写形式输出的 %G是以大写形式输出的
%o
无符号以八进制表示的整数
%c
输出单个字符
%n
得到输出字符的个数,但是本参数不产生任何的输出效果。
%m
输出错误时的相应的字符串提示。
%%
输出正文字符中的“%”字符
说明:
可以在"%"和字母之间插进数字表示最大场宽。例如: %3d 表示输出3位整型数, 不够3位右对齐。%9.2f 表示输出场宽为9的浮点数, 其中小数位为2, 整数位为6,小数点占一位, 不够9位右对齐。%8s 表示输出8个字符的字符串, 不够8个字符右对齐。如果字符串的长度、或整型数位数超过说明的宽度, 将按其实际长度输出。但对浮点数, 若整数部分位数超过了说明的整数位宽度, 将按实际整数位输出;若小数部分位数超过了说明的小数位宽度, 则按说明的宽度以四舍五入输出。另外, 若想在输出值前加一些0, 就应在场宽项前加个0。例如: %04d 表示在输出一个小于4位的数值时, 将在前面补0使其总宽度为4位。如果用浮点数表示字符或整型量的输出格式, 小数点后的数字代表最大宽度,小数点前的数字代表最小宽度。
例如: %6.9s 表示显示一个长度不小于6且不大于9的字符串。若大于9, 则第9个字符以后的内容将被删除。
可以在"%"和字母之间加小写字母l, 表示输出的是长型数。 例如: %ld 表示输出long整数%lf 表示输出double浮点数 。
可以控制输出左对齐或右对齐, 即在"%"和字母之间加入一个"-" 号可说明输出为左对齐, 否则为右对齐。例如: %-7d 表示输出7位整数左对齐%-10s表示输出10个字符左对齐。
2.2 一些特殊规定字符
换行
f 清屏并换页
回车
Tab符
xhh 表示一个ASCII码用16进表示,其中hh是1到2个16进制数
3.格式化转换的具体细则
3.1 整数转换部分
整数转换部分主要是'%d', '%i', '%o', '%u', '%x', 和 '%X'这几个参数命令的,由于参数的不同,可以输出不同格式的结果。如上表所列: '%d', '%i'是输出一个带符号的十进制的数,'%o', '%u', and '%x'是输出一个不带符号的数,而'%X是'%x''的大写形式。其中,针对这几种不同输出选择还有如下几个参数项:
'-’ 表示是左对齐,一般都是右对齐的。
'+’ 是对'%d', '%i'两个参数而言的,是指以'+’符号表示正数
' ' 是对'%d', '%i'两个参数而言的,如果输出不是以'+’'-’开头的,那么用空格做开头。
'#' 是对'%o'参数而言的,将在输出的结果强制加上'0’为开头。
''' 将输出的数字以LC_NUMERIC的分类法用’,’隔开。
'0' 将空格的地方用'0'填入。
如果没有特别指明,被格式化的参数被默认当作整数处理,或者可以用以下的类型指定参数来进行修改,如下:
'h' 指定传入参数是 short int 或unsigned short int类型的
'l' 指定传入参数是 long int或unsigned long int类型的
'q' 指定传入参数是 long long int类型的
'Z' 指定传入参数是size_t.。
为了方便理解给出一个例子:
对于如下的格式化匹配字串:
"|%5d|%-5d|%+5d|%+-5d|% 5d|%05d|%5.0d|%5.2d|%d|"
将产生类似如下的输出:
| 0|0 | +0|+0 | 0|00000| | 00|0|
| 1|1 | +1|+1 | 1|00001| 1| 01|1|
| -1|-1 | -1|-1 | -1|-0001| -1| -01|-1|
|100000|100000|+100000| 100000|100000|100000|100000|100000|
对于如下的格式化匹配字串:
"|%5u|%5o|%5x|%5X|%#5o|%#5x|%#5X|%#10.8x|"
将产生类似如下的输出:
| 0| 0| 0| 0| 0| 0x0| 0X0|0x00000000|
| 1| 1| 1| 1| 01| 0x1| 0X1|0x00000001|
|100000|303240|186a0|186A0|0303240|0x186a0|0X186A0|0x000186a0|
3.2 浮点数的转换部分
浮点数转换部分主要是'%f', '%e', '%E', '%g', 和 '%G' '这几个参数命令的,由于参数的不同,可以输出不同格式的结果。如上表所列: '%f'是输出一个比较固定形式的浮点数……其中,针对这几种不同输出选择还有如下几个参数项:
'-’ 表示是左对齐,一般都是右对齐的。
'+’ 是指以'+’符号表示正数
' ' 如果输出不是以'+’'-’开头的,那么用空格做开头
'#' 是对'%g'和'%G’参数而言的,将在输出的结果强制加上'0’为开头。
''' 将输出的数字以LC_NUMERIC的分类法用’,’隔开。
'0' 将空格的地方用“0'填入。
如果没有特别指定,传入的被格式化的参数默认是double类型的,可以用'L’表示是一个long double类型的。
如下例子可以看出浮点数格式化的字串:
如下的格式字串:
"|%12.4f|%12.4e|%12.4g|"
可能产生如下的输出:
| 0.0000| 0.0000e+00| 0|
| 1.0000| 1.0000e+00| 1|
| -1.0000| -1.0000e+00| -1|
| 100.0000| 1.0000e+02| 100|
| 1000.0000| 1.0000e+03| 1000|
| 10000.0000| 1.0000e+04| 1e+04|
| 12345.0000| 1.2345e+04| 1.234e+04|
| 100000.0000| 1.0000e+05| 1e+05|
| 123456.0000| 1.2346e+05| 1.234e+05|
3.3 其他格式的转换部分
这部分的函数比较简单一些,具体如下:
'%c’是指输出一个单个的字符串,默认的输出的被格式化的参数是unsigned char类型的,可以用'-’表示左对齐的。没有的别的参数,比如:
printf ("%c%c%c%c%c", 'h', 'e', 'l', 'l', 'o');
显示结果为: 'hello'
'%s’是输出一个字串,. 默认的输出的被格式化的参数是char * (or const char *). 类型的,可以用'-’表示左对齐的。没有的别的参数,比如:
printf ("%3s%-6s", "no", "where");
显示结果: ' nowhere '.
注: 如果你用这个参数来格式化输出一个指针类型的参数时,有可能会得到一个'(null)'的输出值。不过有时候用于指针为空的缘故程序运行时会产生“Segmentation fault”的错误,下面一个例子就会产生这样的错误:
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
main()
{
char a;
a = inet_addr("192.168.1.1");
if(a!=-1){
printf("ip:%s",a);/* 这里的%s可能会产生错误,应改用%p比较好一些*/
}
}
'%m’是输出error信息的。如下例子:
fprintf (stderr, "can't open '%s': %m", filename);
等于如下的输出命令:
fprintf (stderr, "can't open '%s': %s", filename, strerror (errno));
“%p”是输出指针类型参数的,显然被格式化的输入参数必须是指针,可以用“-”来表示左对齐的。
“%n”是比较特殊的参数,它不对格式化输出影响,而是得到输出结果的字符长度,可以用类型指定参数'h' 和 'l'来分别指定输出的参数分别是short int *和 long int *类型的。如下面的例子:
int nchar;
printf ("%d %s%n", 3, "bears", &nchar);
输出结果:
3 bears
同时将7的值赋给变量nchar。
'%%'是输出“%”的字符。
函数调用举例:
printf()函数是格式化输出函数系列中比较有具有普遍特点的, 一般用于向标准输出设备按规定格式输出信息。在编写程序时经常会用到此函数。printf()函数的调用格式为:
printf("<格式化字符串>;", <参量表>;);
#include <stdio.h>
#include <unistd.h>
int main()
{
char c, s[20], *p;
int a=1234, *i;
float f=3.141592653589;
double x=0.12345678987654321;
p="How do you do";
strcpy(s, "Hello, Comrade");
*i=12;
c='x41';
printf("a=%d", a); /*结果输出十进制整数a=1234*/
printf("a=%6d", a); /*结果输出6位十进制数a= 1234*/
printf("a=%06d", a); /*结果输出6位十进制数a=001234*/
printf("a=%2d", a); /*a超过2位, 按实际值输出a=1234*/
printf("*i=%4d", *i); /*输出4位十进制整数*i= 12*/
printf("*i=%-4d", *i); /*输出左对齐4位十进制整数*i=12*/
printf("i=%p", i); /*输出地址i=06E4*/
printf("f=%f", f); /*输出浮点数f=3.141593*/
printf("f=6.4f", f); /*输出6位其中小数点后4位的浮点数f=3.1416*/
printf("x=%lf", x); /*输出长浮点数x=0.123457*/
printf("x=%18.16lf", x);/*输出18位其中小数点后16位的长浮点数x=0.1234567898765432*/
printf("c=%c", c); /*输出字符c=A*/
printf("c=%x", c); /*输出字符的ASCII码值c=41*/
printf("s[]=%s", s); /*输出数组字符串s[]=Hello, Comrade*/
printf("s[]=%6.9s", s);/*输出最多9个字符的字符串s[]=Hello,Co*/
printf("s=%p", s); /*输出数组字符串首字符地址s=FFBE*/
printf("*p=%s", p); /* 输出指针字符串p=How do you do*/
printf("p=%p", p); /*输出指针的值p=0194*/
getch();
retunr 0;
}
Problem 37怎样获取Windows各个指针?
Ans:
1、获取应用程序指针
CMyApp* pApp=(CMyApp*)AfxGetApp();
2、获取主框架指针
CWinApp 中的公有成员变量 m_pMainWnd 就是主框架的指针
CMainFrame* pMainFrame = (CMainFrame*)(AfxGetApp()->m_pMainWnd);
或者
CMainFrame* pMainFrame = (CMainFrame*)AfxGetMainWnd();
3、获取菜单指针
CMenu* pMenu = AfxGetMainWnd()->GetMenu();
4、获取工具栏、状态栏指针
主框架中可以直接使用m_wndToolBar、m_wndStatusBar
其他:
CToolBar* pToolBar =
(CToolBar*)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_TOOLBAR);
CStatusBar* pStatusBar =
(CStatusBar*)AfxGetMainWnd()->GetDescendantWindow(AFX_IDW_STATUS_BAR);
5、获取控件指针
先用 GetDlgItem() 再转换,如:
CButton* pButton = (CButton*)GetDlgItem(IDC_MYBUTTON);
6、获取文档、视图指针
SDI:
CMainFrame* pMainFrame = (CMainFrame*)AfxGetMainWnd();
CYourDoc* pDoc = (CYourDoc*)pMainFrame->GetActiveDocument();
CYourView* pView = (CYourView*)pMainFrame->GetActiveView();
MDI:
CMainFrame* pMainFrame = (CMainFrame*)AfxGetMainWnd();
CChildFrame* pChildFrame = (CChildFrame*)pMainFrame->GetActiveFrame();
CYourDoc* pDoc = (CYourDoc*)pChildFrame->GetActiveDocument();
CYourView* pView = (CYourView*)pChildFrame->GetActiveView();
7、文档、视图
从视图获取文档指针:
CYourDoc* pDoc = GetDocument();
从文档获取视图指针:
利用成员函数 GetFirstViewPosition() 和 GetNextView() 遍历
virtual POSITION GetFirstViewPosition() const;
virtual CView* GetNextView(POSITION& rPosition) const;
SDI:
CYourView* pView;
POSITION pos = GetFirstViewPosition();
pView = GetNextView(pos);
MDI:
定义函数
CView* CYourDoc::GetView(CRuntimeClass* pClass)
{
CView* pView;
POSITION pos=GetFirstViewPosition();
while(pos!=NULL)
{
pView=GetNextView(pos);
if(!pView->IsKindOf(pClass))
break;
}
if(!pView->IsKindOf(pClass))
{
AfxMessageBox("Connt Locate the View.");
return NULL;
}
return pView;
}
使用如下:
CYourView* pView=(CYourView*)GetView(RUNTIME_CLASS(CYourView));
8、文档模版、文档
从文档获取文档模版指针:
CDocTemplate* GetDocTemplate() const;
从文档模版获取文档指针:
viaual POSITION GetFirstDocPosition( ) const = 0;
visual CDocument* GetNextDoc(POSITION & rPos) const = 0;
9、获取分割视图中各个视图的指针
主框架中定义:CSplitterWnd m_wndSplitter;
定义两个View类:CView1、CView2
框架类中重载:
BOOL CMainFrame::OnCreateClient(LPCREATESTRUCT, CCreateContext* pContext)
{
VERIFY(m_splitter.CreateStatic(this,2,1)); //分割成两行一列
VERIFY(m_splitter.CreateView(0,0,RUNTIME_CLASS(CView1),CSize(100,100),pContext));
VERIFY(m_splitter.CreateView(1,0,RUNTIME_CLASS(CView2),CSize(100,100),pContext));
return TRUE;
}
获取分割视图指针
CView1* pView1 = (CView1*)m_wndSplitter.GetPane(0,0);
CView2* pView2 = (CView2*)m_wndSplitter.GetPane(1,0);
10、通过鼠标获得子窗口指针
CWnd* ChildWindowFromPoint(POINT point) const;
CWnd* ChildWindowFromPoint(POINT point,UINT nFlags) const;
用于确定包含指定点的子窗口
如果指定点在客户区之外,函数返回NULL;
如果指定点在客户区内,但是不属于任何一个子窗口,函数返回该CWnd的指针;
如果有多个子窗口包含指定点,则返回第一个子窗口的指针。
还要注意的是,该函数返回的是一个伪窗口指针,不能将它保存起来供以后使用。
对于第二个参数nFlags有几个含义:
CWP_ALL //不忽略任何子窗口
CWP_SKIPNIVSIBLE //忽略不可见子窗口
CWP_SKIPDISABLED //忽略禁止的子窗口
CWP_SKIPRANSPARENT //忽略透明子窗口
Problem 38位操作or, and 和xor的用法?
Ans:
mov ax, 0c123h
or ax, 8 ;开启位3, ax = c12bh
and ax, 0ffdfh ;关闭位5, ax = c10bh
xor ax, 8000h ;求反位31, ax = 410bh
PS:求两个数中的较大者,不用分支语句
这个诀窍是产生一个可以用来选择出正确的最大值的掩码
xor ebx, ebx ; ebx = 0
cmp eax, [input1] ; 比较第一和第二个数值
setg bl ; ebx = (input2 > input1) ? 1 : 0
neg ebx ; ebx = (input2 > input1) ? 0xFFFFFFFF : 0
mov ecx, ebx ; ecx = (input2 > input1) ? 0xFFFFFFFF : 0
and ecx, eax ; ecx = (input2 > input1) ? input2 : 0
not ebx ; ebx = (input2 > input1) ? 0 : 0xFFFFFFFF
and ebx, [input1] ; ebx = (input2 > input1) ? 0 : input1
or ecx, ebx ; ecx = (input2 > input1) ? input2 : input1
PS:判断某个机器是大端还小端模式?
unsigned short word = 0x1234; /∗ 假定sizeof(short) == 2 ∗/
unsigned char ∗ p = (unsigned char ∗) &word;
if ( p[0] == 0x12 )
printf (”Big Endian Machine/n”);
else
printf (” Little Endian Machine/n”);
或
1 #include "unp.h"
2 int
3 main(int argc, char **argv)
4 {
5 union {
6 short s;
7 char c[sizeof(short)];
8 } un;
9 un.s = 0x0102;
10 printf("%s: ", CPU_VENDOR_OS);
11 if (sizeof(short) == 2) {
12 if (un.c[0] == 1 && un.c[1] == 2)
13 printf("big-endian/n");
14 else if (un.c[0] == 2 && un.c[1] == 1)
15 printf("little-endian/n");
16 else
17 printf("unknown/n");
18 } else
19 printf("sizeof(short) = %d/n", sizeof(short));
20 exit(0);
21 }
PS:计算位数
int count bits ( unsigned int data )
{
int cnt = 0;
while( data != 0 ) {
data = data & (data − 1);
cnt++;
}
return cnt ;
}
Problem 39计算结构体中某个成员的偏移值?
Ans:
#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
PS:已知某个结构成员的地址,求此结构的首地址。
/**
* container_of - cast a member of a structure out to the containing structure
* @ptr: the pointer to the member.
* @type: the type of the container struct this is embedded in.
* @member: the name of the member within the struct.
*
*/
#define container_of(ptr, type, member) ({ /
const typeof( ((type *)0)->member ) *__mptr = (ptr); /
(type *)( (char *)__mptr - offsetof(type,member) );})
//
// Calculate the address of the base of the structure given its type, and an
// address of a field within the structure.
//
#define CONTAINING_RECORD(address, type, field) ((type *)( / (PCHAR)(address) - / (ULONG_PTR)(&((type *)0)->field)))
Problem 40 Ubuntu解压缩的一些命令
Ans: |
.tar
解包:tar xvf FileName.tar
打包:tar cvf FileName.tar DirName
(注:tar是打包,不是压缩!)
---------------------------------------------
.gz
解压1:gunzip FileName.gz
解压2:gzip -d FileName.gz
压缩:gzip FileName
.tar.gz 和 .tgz
解压:tar zxvf FileName.tar.gz
压缩:tar zcvf FileName.tar.gz DirName
---------------------------------------------
.bz2
解压1:bzip2 -d FileName.bz2
解压2:bunzip2 FileName.bz2
压缩: bzip2 -z FileName
.tar.bz2
解压:tar jxvf FileName.tar.bz2
压缩:tar jcvf FileName.tar.bz2 DirName
---------------------------------------------
.bz
解压1:bzip2 -d FileName.bz
解压2:bunzip2 FileName.bz
压缩:未知
.tar.bz
解压:tar jxvf FileName.tar.bz
压缩:未知
---------------------------------------------
.Z
解压:uncompress FileName.Z
压缩:compress FileName
.tar.Z
解压:tar Zxvf FileName.tar.Z
压缩:tar Zcvf FileName.tar.Z DirName
---------------------------------------------
.zip
解压:unzip FileName.zip
压缩:zip FileName.zip DirName
---------------------------------------------
.rar
解压:rar x FileName.rar
压缩:rar a FileName.rar DirName
rar请到:http://www.rarsoft.com/download.htm 下载!
解压后请将rar_static拷贝到/usr/bin目录(其他由$PATH环境变量指定的目录也可以):
[root@www2 tmp]# cp rar_static /usr/bin/rar
---------------------------------------------
.lha
解压:lha -e FileName.lha
压缩:lha -a FileName.lha FileName
lha请到:http://www.infor.kanazawa-it.ac.jp/~ishii/lhaunix/下载!
>解压后请将lha拷贝到/usr/bin目录(其他由$PATH环境变量指定的目录也可以):
[root@www2 tmp]# cp lha /usr/bin/
---------------------------------------------
.rpm
解包:rpm2cpio FileName.rpm | cpio -div
---------------------------------------------
.deb
解包:ar p FileName.deb data.tar.gz | tar zxf -
---------------------------------------------
解压文件通用脚本 |
ex () { if [ -f $1 ] ; then case $1 in *.tar.bz2) tar xjf $1 ;; *.tar.gz) tar xzf $1 ;; *.bz2) bunzip2 $1 ;; *.rar) rar x $1 ;; *.gz) gunzip $1 ;; *.tar) tar xf $1 ;; *.tbz2) tar xjf $1 ;; *.tgz) tar xzf $1 ;; *.zip) unzip $1 ;; *.Z) uncompress $1 ;; *.7z) 7z x $1 ;; *) echo "'$1' cannot be extracted via extract()" ;; esac else echo "'$1' is not a valid file" fi } |