??xml version="1.0" encoding="utf-8" standalone="yes"?>久久婷婷五月综合国产尤物app,香港aa三级久久三级老师2021国产三级精品三级在
,四虎国产永久免费久久 http://www.shnenglu.com/jht/category/1651.htmlzh-cn Fri, 23 May 2008 19:07:47 GMT Fri, 23 May 2008 19:07:47 GMT 60 转蝲-sprintf你知道多? http://www.shnenglu.com/jht/archive/2007/11/12/36444.html季浩 季浩 Mon, 12 Nov 2007 07:28:00 GMT http://www.shnenglu.com/jht/archive/2007/11/12/36444.html http://www.shnenglu.com/jht/comments/36444.html http://www.shnenglu.com/jht/archive/2007/11/12/36444.html#Feedback 0 http://www.shnenglu.com/jht/comments/commentRss/36444.html http://www.shnenglu.com/jht/services/trackbacks/36444.html 选自《CSDN C电子杂志——C/C++杂志?/font>
在将各种cd的数据构造成字符串时Qsprintf 的强大功能很会让你失望。由于sprintf 跟printf 在用法上几乎一P只是打印的目的地不同而已Q前者打印到字符串中Q后者则直接在命令行上输出。这也导致sprintf 比printf 有用得多?
sprintf 是个变参函数Q定义如下:int sprintf( char *buffer, const char *format [, argument] ... ); 除了前两个参数类型固定外Q后面可以接L多个参数。而它的精华,昄在W二个参敎ͼ 格式化字W串上?
printf 和sprintf
都用格式化字符串来指定串的格式Q在格式串内部用一些以“%”开头的格式说明W(format
specificationsQ来占据一个位|,在后边的变参列表中提供相应的变量Q最l函数就会用相应位置的变量来替代那个说明W,产生一个调用者想?
的字W串?br>
格式化数字字W串 sprintf 最常见的应用之一莫过于把整数打印到字W串中,所以,spritnf 在大多数场合可以替代 itoa?
如://把整?23 打印成一个字W串保存在s 中?br>sprintf(s, "%d", 123); //产生"123" 可以指定宽度Q不的左边补空| sprintf(s, "%8d%8d", 123, 4567); //产生Q? 123 4567" 当然也可以左寚wQ?br>sprintf(s, "%-8d%8d", 123, 4567); //产生Q?123 4567" 也可以按?6 q制打印Q?br>sprintf(s, "%8x", 4567); //写16 q制Q宽度占8 个位|,叛_?br>sprintf(s, "%-8X", 4568); //大写16 q制Q宽度占8 个位|,左对?/font>
q样Q一个整数的16 q制字符串就很容易得刎ͼ但我们在打印16 q制内容Ӟ通常惌一U左边补0 的等宽格式,那该怎么做呢Q很单,在表C宽度的数字前面加个0 可以了?br>sprintf(s, "%08X", 4567); //产生Q?000011D7" 上面?#8221;%d”q行?0 q制打印同样也可以用这U左边补0 的方式?/font>
q里要注意一个符h展的问题Q比如,假如我们x印短整数QshortQ?1 的内?6 q制表示形式Q在Win32 q_上,一个short 型占2 个字节,所以我们自然希望用4 ?6 q制数字来打印它Q?br>short si = -1; sprintf(s, "%04X", si); ?
?#8220;FFFFFFFF”Q怎么回事Q因为spritnf
是个变参函数Q除了前面两个参C外,后面的参数都不是cd安全的,函数更没有办法仅仅通过一?#8220;%X”p得知当初函数调用前参数压栈时被压q来的到?
是个4 字节的整数还是个2 字节的短整数Q所以采取了l一4 字节的处理方式,D参数压栈时做了符h展,扩展成了32 位的整数-1Q打印时4
个位|不够了Q就?2 位整?1 ? ?6 q制都打印出来了?
如果你想看si 的本来面目,那么应该让~译器做0 扩展而不是符h展(扩展时二q制左边? 而不是补W号位)Q?br>sprintf(s, "%04X", (unsigned short)si); 可以了。或者: unsigned short si = -1; sprintf(s, "%04X", si);
sprintf 和printf q可以按8 q制打印整数字符Ԍ使用”%o”。注? q制?6 q制都不会打 印出负数Q都是无W号的,实际上也是变量的内部编码的直接?6 q制? q制表示?br>
控制点数打印格?br> 点数的打印和格式控制是sprintf 的又一大常用功能,点C用格式符”%f”控制Q默认保 留小数点? 位数字,比如Q?br>sprintf(s, "%f", 3.1415926); //产生"3.141593" 但有时我们希望自己控制打印的宽度和小C敎ͼq时应该用:”%m.nf”格式Q其中m ?br>C打印的宽度Qn 表示数点后的位数。比如: sprintf(s, "%10.3f", 3.1415626); //产生Q? 3.142" sprintf(s, "%-10.3f", 3.1415626); //产生Q?3.142 " sprintf(s, "%.3f", 3.1415626); //不指定d度,产生Q?3.142"
注意一个问题,你猜 int i = 100; sprintf(s, "%.2f", i); 会打Z么东东来Q?#8220;100.00”Q对吗?自己试试q道了Q同时也试试下面q个Q?br>sprintf(s, "%.2f", (double)i); W?
一个打出来的肯定不是正结果,原因跟前面提到的一P参数压栈时调用者ƈ不知道跟i相对应的格式控制W是?#8221;%f”。而函数执行时函数本n则ƈ不知道当
q被压入栈里的是个整敎ͼ于是可怜的保存整数i 的那4
个字节就被不由分说地作ؓ点数格式来解释了,整个乱套了。不q,如果有h有兴用手工编码一个QҎQ那么倒可以用这U方法来验一下你手工~?
排的l果是否正确?br>
字符/Ascii 码对?br> 我们知道Q在C/C++语言中,char 也是一U普通的scalable cdQ除了字长之外,它与shortQ?br>intQlong q些cd没有本质区别Q只不过被大家习惯用来表C字W和字符串而已。(或许当年该把 q?
个类型叫?#8220;byte”Q然后现在就可以Ҏ实际情况Q用byte 或short 来把char 通过typedef
定义出来Q这h合适些Q于是,使用”%d”或?#8221;%x”打印一个字W,便能得出它的10 q制?6 q制的ASCII
码;反过来,使用”%c”打印一个整敎ͼ便可以看到它所对应的ASCII 字符。以下程序段把所有可见字W的ASCII
码对照表打印到屏q上Q这里采用printfQ注?#8221;#”?#8221;%X”合用时自动ؓ16 q制数增?#8221;0X”前缀Q: for(int i = 32; i < 127; i++) { printf("[ %c ]: %3d 0x%#04X ", i, i, i); }
q接字符?/strong> sprintf 的格式控制串中既然可以插入各U东西,q最l把它们“q成一?#8221;Q自然也p够连 接字W串Q从而在许多场合可以替代strcatQ但sprintf 能够一ơ连接多个字W串Q自然也可以同时 在它们中间插入别的内容,M非常灉|Q。比如: char* who = "I"; char* whom = "CSDN"; sprintf(s, "%s love %s.", who, whom); //产生Q?I love CSDN. " strcat
只能q接字符Ԍ一D以’’l尾的字W数l或叫做字符~冲Qnull-terminated-stringQ,但有时我们有两段字符~冲区,他们q不是以
’’l尾。比如许多从W三方库函数中返回的字符数组Q从g或者网l传输中读进来的字符,它们未必每一D字W序列后面都有个相应?#8217;’来结。如果直?
q接Q不是sprintf q是strcat 肯定会导致非法内存操作,而strncat
也至要求第一个参数是个null-terminated-stringQ那该怎么办呢Q我们自然会惌v前面介绍打印整数和QҎ时可以指定宽度,字符?
也一L。比如: char a1[] = {'A', 'B', 'C', 'D', 'E', 'F', 'G'}; char a2[] = {'H', 'I', 'J', 'K', 'L', 'M', 'N'}; 如果Q?br>sprintf(s, "%s%s", a1, a2); //Don't do that! 十有八九要出问题了。是否可以改成: sprintf(s, "%7s%7s", a1, a2); 也没好到哪儿去,正确的应该是Q?br>sprintf(s, "%.7s%.7s", a1, a2);//产生Q?ABCDEFGHIJKLMN" q?
可以cL打印点数的”%m.nf”Q在”%m.ns”中,m 表示占用宽度Q字W串长度不时补I格Q超Z则按照实际宽度打华ͼQn
才表CZ相应的字W串中最多取用的字符数。通常在打印字W串时m 没什么大用,q是点号后面的n 用的多。自Ӟ也可以前后都只取部分字符Q?br>sprintf(s, "%.6s%.5s", a1, a2);//产生Q?ABCDEFHIJKL"?
许多时候,我们或许q希望这些格式控制符中用以指定长度信息的数字是动态的Q而不是静态指定的Q因多时候,E序要到q行时才会清楚到底需要取字符数组
中的几个字符Q这U动态的宽度/_ֺ讄功能在sprintf 的实C也被考虑CQsprintf
采用”*”来占用一个本来需要一个指定宽度或_ֺ的常数数字的位置Q同P而实际的宽度或精度就可以和其它被打印的变量一栯提供出来Q于是,上面的例?
可以变成Q?br>sprintf(s, "%.*s%.*s", 7, a1, 7, a2); 或者: sprintf(s, "%.*s%.*s", sizeof(a1), a1, sizeof(a2), a2); 实际上,前面介绍的打印字W、整数、QҎ{都可以动态指定那些常量|比如Q?br>sprintf(s, "%-*d", 4, 'A'); //产生"65 " sprintf(s, "%#0*X", 8, 128); //产生"0X000080"Q?#"产生0X sprintf(s, "%*.*f", 10, 2, 3.1415926); //产生" 3.14"
打印地址信息 有时调试E序Ӟ我们可能x看某些变量或者成员的地址Q由于地址或者指针也不过是个32 位的敎ͼ你完全可以用打印无W号整数?#8221;%u”把他们打印出来: sprintf(s, "%u", &i); 不过通常Zq是喜欢使用16 q制而不?0 q制来显CZ个地址Q?br>sprintf(s, "%08X", &i); 然而,q些都是间接的方法,对于地址打印Qsprintf 提供了专门的”%p”Q?br>sprintf(s, "%p", &i); 我觉得它实际上就相当于: sprintf(s, "%0*x", 2 * sizeof(void *), &i); 利用sprintf 的返回?br>较少有h注意printf/sprintf 函数的返回|但有时它却是有用的,spritnf q回了本ơ函数调?br>最l打印到字符~冲Z的字W数目。也是说每当一ơsprinf 调用l束以后Q你无须再调用一?br>strlen 便已l知道了l果字符串的长度。如Q?br>int len = sprintf(s, "%d", i); 对于正整数来_len 便等于整数i ?0 q制位数?br>下面的是个完整的例子Q?0 个[0, 100)之间的随机数Qƈ他们打印到一个字W数ls 中, 以逗号分隔开?br>#include #include #include int main() { srand(time(0)); char s[64]; int offset = 0; for(int i = 0; i < 10; i++) { offset += sprintf(s + offset, "%d,", rand() % 100); } s[offset - 1] = ' ';//最后一个逗号换成换行W?br>printf(s); return 0; } 设想当你从数据库中取Z条记录,然后希望把他们的各个字段按照某种规则q接成一个字 W串Ӟ可以用这U方法,从理ZԌ他应该比不断的strcat 效率高,因ؓstrcat 每次调用 都需要先扑ֈ最后的那个’’的位|,而在上面l出的例子中Q我们每ơ都利用sprintf q回值把q?br>个位|直接记下来了?
使用sprintf 的常见问?/strong> sprintf 是个变参函数Q用时l常出问题,而且只要出问题通常是能导致程序崩溃的内存?br>问错误,但好在由sprintf 误用D的问题虽然严重,却很Ҏ扑ևQ无非就是那么几U情况,?br>常用眼睛再把出错的代码多看几眼就看出来了?
?? ~冲区溢?br>W一个参数的长度太短了,没的_l个大点的地方吧。当然也可能是后面的参数的问 题,变参对应一定要l心Q而打印字W串Ӟ量使用”%.ns”的Ş式指定最大字W数?
?? 忘记了第一个参?br>低得不能再低问题Q用printf 用得太惯了?/偶就常犯。:。(
?? 变参对应出问?br>通常是忘C提供对应某个格式W的变参Q导致以后的参数l统错位Q检查检查吧。尤 其是对应”*”的那些参敎ͼ都提供了吗?不要把一个整数对应一?#8221;%s”Q编译器会觉得你 ƺ她太甚了(~译器是obj 和exe 的妈妈,应该是个女的Q?PQ?br>
strftime sprnitf q有个不错的表妹QstrftimeQ专门用于格式化旉字符串的Q用法跟她表哥很像,?br>是一大堆格式控制W,只是毕竟姑娘家心细Q她q要调用者指定缓冲区的最大长度,可能是ؓ 了在出现问题时可以推卸责d。这里D个例子: time_t t = time(0); //产生"YYYY-MM-DD hh:mm:ss"格式的字W串?br>char s[32]; strftime(s, sizeof(s), "%Y-%m-%d %H:%M:%S", localtime(&t)); sprintf 在MFC 中也能找C的知韻ICString::FormatQstrftime 在MFC 中自然也有她的同道: CTime::FormatQ这一对由于从面向对象哪里得到了赞助,用以写出的代码更觉优雅?
C语言标准库函?:strncmp()
函数原型
int strncmp(char *str1, char *str2, int maxlen);
函数功能
比较字符串s1和s2的前n个字W?br> 当s1<s2Ӟq回?lt;0 当s1=s2Ӟq回?0 当s1>s2Ӟq回?gt;0
声明文g
<string.h>
用法CZ
#include <stdio.h> #include <string.h> int main(void) { char *buf1 = "aaabbb", *buf2 = "bbbccc", *buf3 = "ccc"; int ptr; ptr = strncmp(buf2,buf1,3); if (ptr > 0) printf("buffer 2 is greater than buffer 1\n"); else printf("buffer 2 is less than buffer 1\n"); ptr = strncmp(buf2,buf3,3); if (ptr > 0) printf("buffer 2 is greater than buffer 3\n"); else printf("buffer 2 is less than buffer 3\n"); return(0); }
]]> C语言 文g操作 -转蝲自编E爱好者论?/title> http://www.shnenglu.com/jht/archive/2006/05/31/7933.html季浩 季浩 Wed, 31 May 2006 05:33:00 GMT http://www.shnenglu.com/jht/archive/2006/05/31/7933.html http://www.shnenglu.com/jht/comments/7933.html http://www.shnenglu.com/jht/archive/2006/05/31/7933.html#Feedback 1 http://www.shnenglu.com/jht/comments/commentRss/7933.html http://www.shnenglu.com/jht/services/trackbacks/7933.html 普通文件是指驻留在盘或其它外部介质上的一个有序数据集Q可以是源文件、目标文件、可执行E序Q?也可以是一l待输入处理的原始数据,或者是一l输出的l果。对于源文g、目标文件?可执行程序可以称作程序文Ӟ对输入输出数据可UC数据文g?br /> 讑֤文g是指与主机相联的各种外部讑֤Q如昄器、打印机、键盘等。在操作pȝ中,把外部设备也看作是一个文件来q行理Q把它们的输入、输出等同于对磁盘文件的d写?通常把显C器定义为标准输出文Ӟ 一般情况下在屏q上昄有关信息是向标准输出文件输出。如前面l常使用的printf,putchar 函数是q类输出。键盘通常被指定标准的输入文gQ?从键盘上输入意味着从标准输入文件上输入数据。scanf,getchar函数属于这c输入? 从文件编码的方式来看Q文件可分ؓASCII码文件和二进制码文g两种?br /> ASCII文g也称为文本文Ӟq种文g在磁盘中存放时每个字W对应一个字节,用于存放对应的ASCII码。例如,?678的存储Ş式ؓQ?br />ASC码: 00110101 00110110 00110111 00111000 ? ↓ ? ?br />十进制码Q?5 6 7 8 共占?个字节。ASCII码文件可在屏q上按字W显C, 例如源程序文件就是ASCII文gQ用DOS命oTYPE可显C文件的内容?׃是按字符昄Q因此能L文g内容?br /> 二进制文件是按二q制的编码方式来存放文g的?例如Q??678的存储Ş式ؓQ?00010110 00101110只占二个字节。二q制文g虽然也可在屏q上昄Q?但其内容无法L。Cpȝ在处理这些文件时Qƈ不区分类型,都看成是字符,按字节进行处理?输入输出字符的开始和l束只由E序控制而不受物理符?如回车符)的控制?因此也把q种文gUC“流式文件”?br /> 本章讨论式文g的打开、关闭、读、写?定位{各U操作。文件指针在C语言中用一个指针变量指向一个文Ӟ q个指针UCؓ文g指针。通过文g指针可对它所指的文gq行各种操作?定义说明文g指针的一般Ş式ؓQ?FILE* 指针变量标识W; 其中FILE应ؓ大写Q它实际上是ql定义的一个结构, 该结构中含有文g名、文件状态和文g当前位置{信息?在编写源E序时不必关心FILEl构的细节。例如:FILE *fpQ?表示fp是指向FILEl构的指针变量,通过fp 卛_扑֭放某个文件信息的l构变量Q然后按l构变量提供的信息找到该文gQ?实施Ҏ件的操作。习惯上也笼l地把fpUCؓ指向一个文件的指针。文件的打开与关闭文件在q行d操作之前要先打开Q用完毕要关闭?所谓打开文gQ实际上是徏立文件的各种有关信息Q?q文g指针指向该文Ӟ以便q行其它操作。关闭文件则断开指针与文件之间的联系Q也q止再对该文gq行操作?br /> 在C语言中,文g操作都是由库函数来完成的?在本章内介l主要的文g操作函数?br /> 文g打开函数fopen fopen函数用来打开一个文Ӟ其调用的一般Ş式ؓQ?文g指针?fopen(文g名,使用文g方式) 其中Q“文件指针名”必L被说明ؓFILE cd的指针变量,“文件名”是被打开文g的文件名?“用文件方式”是指文件的cd和操作要求。“文件名”是字符串常量或字符串数l。例如: FILE *fpQ?br />fp=("file a","r"); 其意义是在当前目录下打开文gfile aQ?只允许进行“读”操作,qfp指向该文件?br /> 又如Q?br /> FILE *fphzk fphzk=("c:\\hzk16',"rb") 其意义是打开C驱动器磁盘的根目录下的文件hzk16Q?q是一个二q制文gQ只允许按二q制方式q行L作。两个反斜线“\\ ”中的第一个表C{义字W,W二个表C根目录。用文件的方式共有12U,下面l出了它们的W号和意义? 文g使用方式 ??br />“rt” 只读打开一个文本文Ӟ只允许读数据 “wt” 只写打开或徏立一个文本文Ӟ只允许写数据 “at” q加打开一个文本文Ӟq在文g末尾写数?br />“rb” 只读打开一个二q制文gQ只允许L?br />“wb” 只写打开或徏立一个二q制文gQ只允许写数?br />“ab? q加打开一个二q制文gQƈ在文件末ֆ数据 “rt+” d打开一个文本文Ӟ允许d?br />“wt+” d打开或徏立一个文本文Ӟ允许d “at+” d打开一个文本文Ӟ允许读,或在文g末追加数 ?br />“rb+” d打开一个二q制文gQ允许读和写 “wb+” d打开或徏立一个二q制文gQ允许读和写 “ab+? d打开一个二q制文gQ允许读Q或在文件末q加数据 对于文g使用方式有以下几点说明: 1. 文g使用方式由r,w,a,t,bQ?六个字符拼成Q各字符的含义是Q?br /> r(read): ?br /> w(write): ?br /> a(append): q加 t(text): 文本文gQ可省略不写 b(banary): 二进制文?br /> +: d?br /> 2. 凡用“r”打开一个文件时Q该文g必须已经存在Q?且只能从该文件读出?br /> 3. 用“w”打开的文件只能向该文件写入?若打开的文件不存在Q则以指定的文g名徏立该文gQ若打开的文件已l存在,则将该文件删去,重徏一个新文g?br /> 4. 若要向一个已存在的文件追加新的信息,只能用“a ”方式打开文g。但此时该文件必L存在的,否则会出错?br /> 5. 在打开一个文件时Q如果出错,fopen返回一个空指针值NULL。在E序中可以用q一信息来判别是否完成打开文g的工作,q作相应的处理。因此常用以下程序段打开文gQ?br /> if((fp=fopen("c:\\hzk16","rb")==NULL) { printf("\nerror on open c:\\hzk16 file!"); getch(); exit(1); } q段E序的意义是Q如果返回的指针为空Q表CZ能打开C盘根目录下的hzk16文gQ则l出提示信息“error on open c:\ hzk16file!”,下一行getch()的功能是从键盘输入一个字W,但不在屏q上昄。在q里Q该行的作用是等待, 只有当用户从键盘敲Q一键时Q程序才l箋执行Q?因此用户可利用这个等待时间阅d错提C。敲键后执行exit(1)退出程序?br /> 6. 把一个文本文件读入内存时Q要ASCII码{换成二进制码Q?而把文g以文本方式写入磁盘时Q也要把二进制码转换成ASCII码,因此文本文g的读写要p较多的{换时间。对二进制文件的d不存在这U{换?br /> 7. 标准输入文g(键盘)Q标准输出文?昄?)Q标准出错输?出错信息)是由pȝ打开的,可直接用。文件关闭函敎ͽClose文g一旦用完毕,应用关闭文g函数把文件关闭, 以避免文件的数据丢失{错误?br /> fclose函数 调用的一般Ş式是Q?fclose(文g指针)Q?例如Q?br /> fclose(fp); 正常完成关闭文g操作Ӟfclose函数q回gؓ0。如q回非零值则表示有错误发生。文件的dҎ件的d写是最常用的文件操作? 在C语言中提供了多种文gd的函敎ͼ ·字符d函数 Qfgetc和fputc ·字符串读写函敎ͼfgets和fputs ·数据块读写函敎ͼfreed和fwrite ·格式化读写函敎ͼfscanf和fprinf 下面分别予以介绍。用以上函数都要求包含头文件stdio.h。字W读写函敎ͽgetC和fputC字符d函数是以字符(字节)为单位的d函数?每次可从文gd或向文g写入一个字W?br /> 一、读字符函数fgetc fgetc函数的功能是从指定的文g中读一个字W,函数调用的Ş式ؓQ?字符变量=fgetc(文g指针)Q?例如Qch=fgetc(fp);其意义是从打开的文件fp中读取一个字Wƈ送入ch中?br /> 对于fgetc函数的用有以下几点说明Q?br /> 1. 在fgetc函数调用中,d的文件必L以读或读写方式打开的?br /> 2. d字符的结果也可以不向字符变量赋|例如Qfgetc(fp);但是d的字W不能保存?br /> 3. 在文件内部有一个位|指针。用来指向文件的当前d字节。在文g打开Ӟ该指针L指向文g的第一个字节。用fgetc 函数后, 该位|指针将向后Ud一个字节?因此可连l多ơ用fgetc函数Q读取多个字W?应注意文件指针和文g内部的位|指针不是一回事。文件指针是指向整个文g的,dE序中定义说明,只要不重新赋|文g指针的值是不变的。文件内部的位置指针用以指示文g内部的当前读写位|,每读写一ơ,该指针均向后UdQ它不需在程序中定义说明Q而是ql自动设|的?br /> [?0.1]d文ge10-1.cQ在屏幕上输出?br /> #include<stdio.h> main() { FILE *fp; char ch; if((fp=fopen("e10_1.c","rt"))==NULL) { printf("Cannot open file strike any key exit!"); getch(); exit(1); } ch=fgetc(fp); while (ch!=EOF) { putchar(ch); ch=fgetc(fp); } fclose(fp); } 本例E序的功能是从文件中逐个d字符Q在屏幕上显C?E序定义了文件指针fp,以读文本文g方式打开文g“e10_1.c”, qfp指向该文件。如打开文g出错Q?l出提示q出程序。程序第12行先d一个字W,然后q入循环Q?只要d的字W不是文件结束标?每个文g末有一l束标志EOF)把该字W显C在屏幕上,再读入下一字符。每Mơ,文g内部的位|指针向后移动一个字W,文gl束Ӟ该指针指向EOF。执行本E序显C整个文件?br /> 二、写字符函数fputc fputc函数的功能是把一个字W写入指定的文g中,函数调用?形式为: fputc(字符量,文g指针)Q?其中Q待写入的字W量可以是字W常量或变量Q例如:fputc('a',fp);其意义是把字Wa写入fp所指向的文件中?br /> 对于fputc函数的用也要说明几点: 1. 被写入的文g可以用、写、读写,q加方式打开Q用写或d方式打开一个已存在的文件时清除原有的文g内容Q写入字W从文g首开始。如需保留原有文g内容Q希望写入的字符以文件末开始存放,必须以追加方式打开文g。被写入的文件若不存在,则创文g?br /> 2. 每写入一个字W,文g内部位置指针向后Ud一个字节?br /> 3. fputc函数有一个返回|如写入成功则q回写入的字W, 否则q回一个EOF。可用此来判断写入是否成功?br /> [?0.2]从键盘输入一行字W,写入一个文Ӟ 再把该文件内容读出显C在屏幕上?br /> #include<stdio.h> main() { FILE *fp; char ch; if((fp=fopen("string","wt+"))==NULL) { printf("Cannot open file strike any key exit!"); getch(); exit(1); } printf("input a string:\n"); ch=getchar(); while (ch!='\n') { fputc(ch,fp); ch=getchar(); } rewind(fp); ch=fgetc(fp); while(ch!=EOF) { putchar(ch); ch=fgetc(fp); } printf("\n"); fclose(fp); } E序中第6行以d文本文g方式打开文gstring。程序第13行从键盘d一个字W后q入循环Q当d字符不ؓ回RW时Q?则把该字W写入文件之中,然后l箋从键盘读入下一字符?每输入一个字W,文g内部位置指针向后Ud一个字节。写入完毕, 该指针已指向文g末。如要把文g从头dQ须把指针移向文件头Q?E序W?9行rewind函数用于把fp所指文件的内部位置指针Ud文g头?W?0?5行用于读出文件中的一行内宏V?br /> [?0.3]把命令行参数中的前一个文件名标识的文Ӟ 复制到后一个文件名标识的文件中Q?如命令行中只有一个文件名则把该文件写到标准输出文?昄?中?br /> #include<stdio.h> main(int argc,char *argv[]) { FILE *fp1,*fp2; char ch; if(argc==1) { printf("have not enter file name strike any key exit"); getch(); exit(0); } if((fp1=fopen(argv[1],"rt"))==NULL) { printf("Cannot open %s\n",argv[1]); getch(); exit(1); } if(argc==2) fp2=stdout; else if((fp2=fopen(argv[2],"wt+"))==NULL) { printf("Cannot open %s\n",argv[1]); getch(); exit(1); } while((ch=fgetc(fp1))!=EOF) fputc(ch,fp2); fclose(fp1); fclose(fp2); } 本程序ؓ带参的main函数。程序中定义了两个文件指?fp1 和fp2Q分别指向命令行参数中给出的文g。如命o行参C没有l出文g名,则给出提CZ息。程序第18行表C如果只l出一个文件名Q则使fp2指向标准输出文g(xC器)。程序第25行至28行用循环语句逐个d文g1中的字符再送到文g2中。再ơ运行时Q给Z一个文件名(׃10.2所建立的文?Q?故输出给标准输出文gstdoutQ即在显C器上显C文件内宏V第三次q行Q给Z二个文g名,因此把string中的内容dQ写入到OK之中。可用DOS命otype昄OK的内容: ******************************************************************************************** 字符串读写函数fgets和fputs 一、读字符串函数fgets函数的功能是从指定的文g中读一个字W串到字W数l中Q函数调用的形式为: fgets(字符数组名,nQ文件指?Q?其中的n是一个正整数。表CZ文g中读出的字符串不过 n-1个字W。在d的最后一个字W后加上串结束标?\0'。例如:fgets(str,n,fp);的意义是从fp所指的文g中读出n-1个字W送入字符数组str中?br /> [?0.4]从e10_1.c文g中读入一个含10个字W的字符丌Ӏ?br /> #include<stdio.h> main() { FILE *fp; char str[11]; if((fp=fopen("e10_1.c","rt"))==NULL) { printf("Cannot open file strike any key exit!"); getch(); exit(1); } fgets(str,11,fp); printf("%s",str); fclose(fp); } 本例定义了一个字W数lstr?1个字节,在以L本文件方式打开文ge101.c后,从中d10个字W送入str数组Q在数组最后一个单元内加?\0'Q然后在屏幕上显C出str数组。输出的十个字符正是?0.1E序的前十个字符?br /> 对fgets函数有两点说明: 1. 在读出n-1个字W之前,如遇C换行W或EOFQ则dl束?br /> 2. fgets函数也有q回|其返回值是字符数组的首地址?br /> 二、写字符串函数fputs fputs函数的功能是向指定的文g写入一个字W串Q其调用形式为: fputs(字符Ԍ文g指针) 其中字符串可以是字符串常量,也可以是字符数组名, 或指?变量Q例如: fputs(“abcd“,fp)Q?br /> 其意义是把字W串“abcd”写入fp所指的文g之中。[?0.5]在例10.2中徏立的文gstring中追加一个字W串?br /> #include<stdio.h> main() { FILE *fp; char ch,st[20]; if((fp=fopen("string","at+"))==NULL) { printf("Cannot open file strike any key exit!"); getch(); exit(1); } printf("input a string:\n"); scanf("%s",st); fputs(st,fp); rewind(fp); ch=fgetc(fp); while(ch!=EOF) { putchar(ch); ch=fgetc(fp); } printf("\n"); fclose(fp); } 本例要求在string文g末加写字W串Q因此,在程序第6行以q加d文本文g的方式打开文gstring ?然后输入字符Ԍ q用fputs函数把该串写入文件string。在E序15行用rewind函数把文件内部位|指针移到文仉?再进入@环逐个昄当前文g中的全部内容?br /> 数据块读写函数fread和fwrite C语言q提供了用于整块数据的读写函数?可用来读写一l数据,如一个数l元素,一个结构变量的值等。读数据块函数调用的一般Ş式ؓQ?fread(buffer,size,count,fp); 写数据块函数调用的一般Ş式ؓQ?fwrite(buffer,size,count,fp); 其中buffer是一个指针,在fread函数中,它表C存放输入数据的首地址。在fwrite函数中,它表C存放输出数据的首地址?size 表示数据块的字节数。count 表示要读写的数据块块数。fp 表示文g指针?br /> 例如Q?br /> fread(fa,4,5,fp); 其意义是从fp所指的文g中,每次?个字?一个实?送入实数lfa中,q箋?ơ,卌5个实数到fa中?br /> [?0.6]从键盘输入两个学生数据,写入一个文件中Q?再读两个学生的数据显C在屏幕上?br /> #include<stdio.h> struct stu { char name[10]; int num; int age; char addr[15]; }boya[2],boyb[2],*pp,*qq; main() { FILE *fp; char ch; int i; pp=boya; qq=boyb; if((fp=fopen("stu_list","wb+"))==NULL) { printf("Cannot open file strike any key exit!"); getch(); exit(1); } printf("\ninput data\n"); for(i=0;i<2;i++,pp++) scanf("%s%d%d%s",pp->name,&pp->num,&pp->age,pp->addr); pp=boya; fwrite(pp,sizeof(struct stu),2,fp); rewind(fp); fread(qq,sizeof(struct stu),2,fp); printf("\n\nname\tnumber age addr\n"); for(i=0;i<2;i++,qq++) printf("%s\t%5d%7d%s\n",qq->name,qq->num,qq->age,qq->addr); fclose(fp); } 本例E序定义了一个结构stu,说明了两个结构数lboya?boyb以及两个l构指针变量pp和qq。pp指向boya,qq指向boyb。程序第16行以d方式打开二进制文件“stu_list”,输入二个学生数据之后Q写入该文g中, 然后把文件内部位|指针移到文仉Q读Z块学生数据后Q在屏幕上显C?br /> 格式化读写函数fscanf和fprintf fscanf函数Qfprintf函数与前面用的scanf和printf 函数的功能相|都是格式化读写函数?两者的区别在于 fscanf 函数和fprintf函数的读写对象不是键盘和昄器,而是盘文g。这两个函数的调用格式ؓQ?fscanf(文g指针Q格式字W串Q输入表?Q?fprintf(文g指针Q格式字W串Q输?Q?例如Q?br /> fscanf(fp,"%d%s",&i,s); fprintf(fp,"%d%c",j,ch); 用fscanf和fprintf函数也可以完成例10.6的问题。修改后的程序如?0.7所C?br /> [?0.7] #include<stdio.h> struct stu { char name[10]; int num; int age; char addr[15]; }boya[2],boyb[2],*pp,*qq; main() { FILE *fp; char ch; int i; pp=boya; qq=boyb; if((fp=fopen("stu_list","wb+"))==NULL) { printf("Cannot open file strike any key exit!"); getch(); exit(1); } printf("\ninput data\n"); for(i=0;i<2;i++,pp++) scanf("%s%d%d%s",pp->name,&pp->num,&pp->age,pp->addr); pp=boya; for(i=0;i<2;i++,pp++) fprintf(fp,"%s %d %d %s\n",pp->name,pp->num,pp->age,pp-> addr); rewind(fp); for(i=0;i<2;i++,qq++) fscanf(fp,"%s %d %d %s\n",qq->name,&qq->num,&qq->age,qq->addr); printf("\n\nname\tnumber age addr\n"); qq=boyb; for(i=0;i<2;i++,qq++) printf("%s\t%5d %7d %s\n",qq->name,qq->num, qq->age, qq->addr); fclose(fp); } 与例10.6相比Q本E序中fscanf和fprintf函数每次只能d一个结构数l元素,因此采用了@环语句来d全部数组元素?q要注意指针变量pp,qq׃循环改变了它们的|因此在程序的25?2行分别对它们重新赋予了数l的首地址?br /> 文g的随?br /> 前面介绍的对文g的读写方式都是顺序读写, 卌写文件只能从头开始,序d各个数据?但在实际问题中常要求只读写文件中某一指定的部分?Z解决q个问题可移动文件内部的位置指针到需要读写的位置Q再q行dQ这U读写称为随写?实现随机d的关键是要按要求Ud位置指针Q这UCؓ文g的定位。文件定位移动文件内部位|指针的函数主要有两个, ?rewind 函数和fseek函数?br /> rewind函数前面已多ơ用过Q其调用形式为: rewind(文g指针)Q?它的功能是把文g内部的位|指针移到文仉?下面主要介绍 fseek函数?br /> fseek函数用来Ud文g内部位置指针Q其调用形式为: fseek(文g指针Q位U量Qv始点)Q?其中Q“文件指针”指向被Ud的文件?“位U量”表C移动的字节敎ͼ要求位移量是long型数据,以便在文仉度大?4KB 时不会出错。当用常量表CZU量Ӟ要求加后~“L”。“v始点”表CZ何处开始计位U量Q规定的起始Ҏ三种Q文仉Q当前位|和文g?br /> 其表C方法如?0.2? 起始? 表示W号 数字表示 ────────────────────────── 文g? SEEK—SET 0 当前位置 SEEK—CUR 1 文g末尾 SEEK—END 2 例如Q?br /> fseek(fp,100L,0);其意义是把位|指针移到离文g?00个字节处。还要说明的是fseek函数一般用于二q制文g。在文本文g中由于要q行转换Q故往往计算的位|会出现错误。文件的随机d在移动位|指针之后, 卛_用前面介l的MU读写函数进行读写。由于一般是d一个数据据块,因此常用fread和fwrite函数。下面用例题来说明文件的随机d?br /> [?0.8]在学生文件stu list中读出第二个学生的数据?br /> #include<stdio.h> struct stu { char name[10]; int num; int age; char addr[15]; }boy,*qq; main() { FILE *fp; char ch; int i=1; qq=&boy; if((fp=fopen("stu_list","rb"))==NULL) { printf("Cannot open file strike any key exit!"); getch(); exit(1); } rewind(fp); fseek(fp,i*sizeof(struct stu),0); fread(qq,sizeof(struct stu),1,fp); printf("\n\nname\tnumber age addr\n"); printf("%s\t%5d %7d %s\n",qq->name,qq->num,qq->age, qq->addr); } 文gstu_list已由?0.6的程序徏立,本程序用随机d的方法读出第二个学生的数据。程序中定义boy为stucd变量Qqq为指向boy的指针。以Mq制文g方式打开文gQ程序第22行移动文件位|指针。其中的igؓ1Q表CZ文g头开始,Ud一个stucd的长度, 然后再读出的数据即ؓW二个学生的数据?br /> 文g函?br /> C语言中常用的文g函数有以下几个?br /> 一、文件结束检函数feof函数调用格式Q?feof(文g指针)Q? 功能Q判断文件是否处于文件结束位|,如文件结束,则返回gؓ1Q否则ؓ0?br /> 二、读写文件出错检函数ferror函数调用格式Q?ferror(文g指针)Q? 功能Q检查文件在用各U输入输出函数进行读写时是否出错?如ferrorq回gؓ0表示未出错,否则表示有错?br /> 三、文件出错标志和文gl束标志|?函数clearerr函数调用格式Q?clearerr(文g指针); 功能Q本函数用于清除出错标志和文件结束标志,使它们ؓ0倹{?br /> C库文?br /> Cpȝ提供了丰富的pȝ文gQ称为库文gQC的库文g分ؓ两类Q一cL扩展名ؓ".h"的文ӞUCؓ头文Ӟ 在前面的包含命o中我们已多次使用q。在".h"文g中包含了帔R定义?cd定义、宏定义、函数原型以及各U编译选择讄{信息。另一cL函数库,包括了各U函数的目标代码Q供用户在程序中调用?通常在程序中调用一个库函数Ӟ要在调用之前包含该函数原型所在的".h" 文g?br /> 在附录中l出了全部库函数?br /> ALLOC.H 说明内存理函数(分配、释攄)?br />ASSERT.H 定义 assert调试宏?br />BIOS.H 说明调用IBM—PC ROM BIOS子程序的各个函数?br />CONIO.H 说明调用DOS控制台I/O子程序的各个函数?br />CTYPE.H 包含有关字符分类及{换的名类信息(?isalpha和toascii{??br />DIR.H 包含有关目录和\径的l构、宏定义和函数?br />DOS.H 定义和说明MSDOS?086调用的一些常量和函数?br />ERRON.H 定义错误代码的助记符?br />FCNTL.H 定义在与open库子E序q接时的W号帔R?br />FLOAT.H 包含有关点q算的一些参数和函数?br />GRAPHICS.H 说明有关囑Ş功能的各个函敎ͼ囑Ş错误代码的常量定义,正对不同驱动E序的各U颜色|及函数用到的一些特D结构?br />IO.H 包含低I/O子程序的l构和说明?br />LIMIT.H 包含各环境参数、编译时间限制、数的范围等信息?br />MATH.H 说明数学q算函数Q还定了 HUGE VAL 宏, 说明了matherr和matherr子程序用到的Ҏl构?br />MEM.H 说明一些内存操作函?其中大多C在STRING.H 中说??br />PROCESS.H 说明q程理的各个函敎ͼspawn…和EXEC …函数的l构说明?br />SETJMP.H 定义longjmp和setjmp函数用到的jmp bufcdQ?说明q两个函数?br />SHARE.H 定义文g׃n函数的参数?br />SIGNAL.H 定义SIG[ZZ(Z] [ZZ)]IGN和SIG[ZZ(Z] [ZZ)]DFL帔RQ说明rajse和signal两个函数?br />STDARG.H 定义d数参数表的宏?如vprintf,vscarf函数)?br />STDDEF.H 定义一些公共数据类型和宏?br />STDIO.H 定义Kernighan和Ritchie在Unix System V 中定义的标准和扩展的cd和宏。还定义标准I/O 预定义流Qstdin,stdout和stderrQ说?I/O子E序?br />STDLIB.H 说明一些常用的子程序:转换子程序、搜? 排序子程序等?br />STRING.H 说明一些串操作和内存操作函数?br />SYS\STAT.H 定义在打开和创建文件时用到的一些符号常量?br />SYS\TYPES.H 说明ftime函数和timebl构?br />SYS\TIME.H 定义旉的类型time[ZZ(Z] [ZZ)]t?br />TIME.H 定义旉转换子程序asctime、localtime和gmtime的结构,ctime?difftime?gmtime?localtime和stime用到的类型,q提供这些函数的原型?br />VALUE.H 定义一些重要常量, 包括依赖于机器硬件的和ؓ与Unix System V相兼容而说明的一些常量,包括点和双_ֺ值的范围?br /> ]]> UNIXpȝ~程常用库函数说明[转蝲] http://www.shnenglu.com/jht/archive/2006/05/31/7928.html季浩 季浩 Wed, 31 May 2006 05:17:00 GMT http://www.shnenglu.com/jht/archive/2006/05/31/7928.html http://www.shnenglu.com/jht/comments/7928.html http://www.shnenglu.com/jht/archive/2006/05/31/7928.html#Feedback 0 http://www.shnenglu.com/jht/comments/commentRss/7928.html http://www.shnenglu.com/jht/services/trackbacks/7928.html
UNIXpȝ为程序员提供了许多子E序,q些子程序可存取各种安全属?? 些是信息子程?q回文g属?实际的和有效的UID,GID{信?有些子程序可 改变文g属?UID,GID{有些处理口令文件和组文g,q有些完成加密和解密. 本文主要讨论有关pȝ子程?标准C库子E序的安?如何写安全的CE序 q从root的角度介l程序设?仅能被root调用的子E序). 1.pȝ子程? (1)I/O子程? *creat():建立一个新文g或重写一个暂存文? 需要两个参?文g名和存取许可?8q制方式).? creat(?usr/pat/read_write?0666) /* 建立存取许可方式?666的文?*/ 调用此子E序的进E必要有徏立的文g的所在目录的写和执行许可,|? lcreat()的许可方式变量将被umask()讄的文件徏立屏蔽值所修改,? 文g的所有者和组由有效的UID和GID军_. q回gؓ新徏文g的文件描q符. *fstat():见后面的stat(). *open():在CE序内部打开文g. 需要两个参?文g路径名和打开方式(I,O,I&O). 如果调用此子E序的进E没有对于要打开的文件的正确存取许可(包括? 件\径上所有目录分量的搜烦许可),会引v执行p|. 如果此子E序被调用去打开不存在的文g,除非讄了O_CREAT标志,调用 不成功.此时,新文件的存取许可作ؓW三个参?可被用户的umask? ?. 当文件被q程打开后再改变该文件或该文件所在目录的存取许可,不媄? 对该文g的I/O操作. *read():从已由open()打开q用作输入的文g中读信息. 它ƈ不关心该文g的存取许?一旦文件作入打开,卛_从该文g中读 取信? *write():输出信息到已由open()打开q用作输出的文g?同read()一? 它也不关心该文g的存取许? (2)q程控制 *exec()?包括execl(),execv(),execle(),execve(),execlp()和execvp() 可将一可执行模快拷贝到调用q程占有的存贮空?正被调用q? E执行的E序不复存?新程序取代其位置. q是UNIXpȝ中一个程序被执行的唯一方式:用将执行的程序复盖原有的 E序. 安全注意事项: . 实际的和有效的UID和GID传递给由exec()调入的不hSUID和SGID? 可的E序. . 如果由exec()调入的程序有SUID和SGID许可,则有效的UID和GID设 |给该程序的所有者或组. . 文g建立屏蔽值将传递给新程? . 除设了对exec()关闭标志的文件外,所有打开的文仉传递给新程? 用fcntl()子程序可讄对exec()的关闭标? *fork():用来建立新进E?其徏立的子进E是与调用fork()的进E?父进E? 完全相同的拷?除了q程号外) 安全注意事项: . 子进E将l承父进E的实际和有效的UID和GID. . 子进E承文件方式徏立屏蔽? . 所有打开的文件传l子q程. *signal():允许q程处理可能发生的意外事件和中断. 需要两个参?信号~号和信号发生时要调用的子程? 信号~号定义在signal.h? 信号发生时要调用的子E序可由用户~写,也可用系l给的??SIG_IGN 则信号将被忽?SIG_DFL则信号将按系l的~省方式处理. 如许多与安全有关的程序禁止终端发中断信息(BREAK和DELETE),以免自己 被用L端终止运? 有些信号使UNIXpȝ的生进E的核心转储(q程接收Ch所占内? 的内?有时含有重要信息),此系l子E序可用于禁止核心{? (3)文g属? *access():指定文件的存取能力是否W合指定的存取类? 需要两个参?文g名和要检的存取cd(整数). 存取cd定义如下: 0: 查文件是否存? 1: 查是否可执行(搜烦) 2: 查是否可? 3: 查是否可写和执行 4: 查是否可? 5: 查是否可d执行 6: 查是否可d写可执行 q些数字的意义和chmod命o中规定许可方式的数字意义相同. 此子E序使用实际的UID和GID文件的存取能力(一般有效的UID和GID 用于查文件存取能?. q回? 0:许可 -1:不许? *chmod():指定文件或目录的存取许可方式改成新的许可方? 需要两个参?文g名和新的存取许可方式. *chown():同时改变指定文g的所有者和组的UID和GID.(与chown命o? ?. ׃此子E序同时改变文g的所有者和组,故必d消所操作文g的SUID 和SGID许可,以防止用户徏立SUID和SGIDE序,然后q行chown()去获得别 人的权限. *stat():q回文g的状?属?. 需要两个参?文g路径名和一个结构指?指向状态信息的存放 的位|? l构定义如下: st_mode: 文gcd和存取许可方? st_ino: I节点? st_dev: 文g所在设备的ID st_rdev: 特别文g的ID st_nlink: 文g链接? st_uid: 文g所有者的UID st_gid: 文g组的GID st_size: 按字节计数的文g大小 st_atime: 最后存取时?? st_mtime: 最后修Ҏ??和最后状态的改变 st_ctime: 最后的状态修Ҏ? q回? 0:成功 1:p| *umask():调用进E及其子q程的文件徏立屏蔽D|ؓ指定的存取许? 需要一个参? 新的文g建立屏? (4)UID和GID的处? *getuid():q回q程的实际UID. *getgid():q回q程的实际GID. 以上两个子程序可用于定是谁在运行进E? *geteuid():q回q程的有效UID. *getegid():q回q程的有效GID. 以上两个子程序可在一个程序不得不定它是否在q行某用戯不是运? 它的用户的SUIDE序时很有用,可调用它们来查确认本E序的确是以? 用户的SUID许可在运? *setuid():用于改变有效的UID. 对于一般用?此子E序仅对要在有效和实际的UID之间变换的SUIDE序? 有用(从原有效UID变换为实际UID),以保护进E不受到安全危害.实际上该 q程不再是SUID方式q行. *setgid():用于改变有效的GID. 2.标准C? (1)标准I/O *fopen():打开一个文件供L?安全斚w的考虑同open()一? *fread(),getc(),fgetc(),gets(),scanf()和fscanf():从已由fopen()? 开供读的文件中d信息.它们q不兛_文g的存取许?q一? 同read(). *fwrite(),put(),fputc(),puts,fputs(),printf(),fprintf():写信息到 已由fopen()打开供写的文件中.它们也不兛_文g的存取许? 同write(). *getpass():从终端上读至?个字W长的口?不回昄戯入的字符. 需要一个参? 提示信息. 该子E序提CZ息显C在l端?止字符回显功能,?dev/ttyd? ?然后再恢复字W回昑֊?q回刚敲入的口o的指? *popen():在(5)q行shell中介l? (2)/etc/passwd处理 有一l子E序可对/etc/passwd文gq行方便的存?可对文gd到入? Ҏ写新的入口项或更新等{? *getpwuid():?etc/passwd文g中获取指定的UID的入口项. *getpwnam():对于指定的登录名,?etc/passwd文g索入口项. 以上两个子程序返回一指向passwdl构的指?该结构定义在 /usr/include/pwd.h?定义如下: struct passwd { char * pw_name; /* d?*/ char * pw_passwd; /* 加密后的口o */ uid_t pw_uid; /* UID */ gid_t pw_gid; /* GID */ char * pw_age; /* 代理信息 */ char * pw_comment; /* 注释 */ char * pw_gecos; char * pw_dir; /* ȝ?*/ char * pw_shell; /* 使用的shell */ }; *getpwent(),setpwent(),endpwent():对口令文件作后箋处理. 首次调用getpwent(),打开/etc/passwdq返回指向文件中W一个入口项? 指针,保持调用之间文g的打开状? 再调用getpwent()可顺序地q回口o文g中的各入口项. 调用setpwent()把口令文件的指针重新|ؓ文g的开始处. 使用完口令文件后调用endpwent()关闭口o文g. *putpwent():修改或增?etc/passwd文g中的入口? 此子E序入口项写到一个指定的文g?一般是一个时文?直接写口 令文件是很危险的.最好在执行前做文g锁,使两个程序不能同时写一? 文g.法如下: . 建立一个独立的临时文g,?etc/passnnn,nnn是PID? . 建立C生的临时文g和标准时文?etc/ptmp的链,若徏铑֤? 则ؓ有h正在使用/etc/ptmp,{待直到/etc/ptmp可用为止或退? . ?etc/passwd拯?etc/ptmp,可对此文件做M修改. . ?etc/passwdUd备䆾文g/etc/opasswd. . 建立/etc/ptmp?etc/passwd的链. . 断开/etc/passnnn?etc/ptmp的链. 注意:临时文g应徏立在/etc目录,才能保证文g处于同一文gpȝ?? 链才能成?且时文件不会不安全.此外,若新文g已存?即便? 铄是root用户,也将p|,从而保证了一旦时文件成功地建链? 没有再插q来q扰.当然,使用临时文g的程序应保清除所? 临时文g,正确地捕捉信? (3)/etc/group的处? 有一l类g前面的子E序处理/etc/group的信?使用时必ȝinclude 语句?usr/include/grp.h文g加入到自qE序?该文件定义了group l构,由getgrnam(),getgrgid(),getgrent()q回groupl构指针. *getgrnam():?etc/group文g中搜索指定的组?然后q回指向组? 口项的指? *getgrgid():cM于前一子程?不同的是搜烦指定的GID. *getgrent():q回group文g中的下一个入口项. *setgrent():group文g的文件指针恢复到文g的v? *endgrent():用于完成工作?关闭group文g. *getuid():q回调用q程的实际UID. *getpruid():以getuid()q回的实际UID为参?定与实际UID相应的登? ?或指定一UID为参? *getlogin():q回在终端上d的用L指针. pȝ依次查STDIN,STDOUT,STDERR是否与终端相?与终端相联的标准? 入用于确定终端名,l端名用于查扑ֈ?etc/utmp文g中的用户,该文? 由loginl护,由whoE序用来认用户. *cuserid():首先调用getlogin(),若getlogin()q回NULL指针,再调? getpwuid(getuid()). *以下为命? *logname:列出dq终端的用户? *who am i:昄行这条命令的用户的登录名. *id:昄实际的UID和GID(若有效的UID和GID和实际的不同时也昄有效? UID和GID)和相应的d? (4)加密子程? 1977q??NBS宣布一个用于美国联邦政府ADPpȝ的网l的标准加密?? 据加密标准即DES用于非机密应用方?DES一ơ处?4BITS的块,56位的? 密键. *setkey(),encrypt():提供用户对DES的存? 此两子程序都?4BITS长的字符数组,数组中的每个元素代表一个位,? ?.setkey()讄按DES处理的加密键,忽略每第8位构成一?6位的? 密键.encrypt()然后加密或解密给定的64BITS长的一?加密或解密取? 于该子程序的W二个变?0:加密 1:解密. *crypt():是UNIXpȝ中的口o加密E序,也被/usr/lib/makekey命o调用. crypt()子程序与crypt命o无关,它与/usr/lib/makekey一样取8个字W长 的关键词,2个salt字符.关键词送给setkey(),salt字符用于混合encrypt() 中的DES法,最l调用encrypt()重复25ơ加密一个相同的字符? q回加密后的字符串指? (5)q行shell *system():q行/bin/sh执行其参数指定的命o,当指定命令完成时q回. *popen():cM于system(),不同的是命oq行?其标准输入或输出联到? popen()q回的文件指? 二者都调用fork(),exec(),popen()q调用pipe(),完成各自的工?因? fork()和exec()的安全方面的考虑开始v作用. 3.写安全的CE序 一般有两方面的安全问题,在写E序时必考虑: (1)保自己建立的Q何时文件不含有机密数据,如果有机密数?讄 临时文g仅对自己可读/?保建立临时文g的目录仅对自己可? (2)保自己要运行的M命o(通过system(),popen(),execlp(), execvp()q行的命?的确是自pq行的命?而不是其它什么命 ?其是自qE序为SUID或SGID许可时要心. W一斚w比较?在程序开始前调用umask(077).若要使文件对其他人可 ?可再调chmod(),也可用下q语名徏立一个”不可见”的临时文g. creat(?tmp/xxx?0); file=open(?tmp/xxx?O_RDWR); unlink(?tmp/xxx?; 文g/tmp/xxx建立?打开,然后断开?但是分配l该文g的存储器q未? ?直到最l指向该文g的文仉道被关闭时才被删除.打开该文件的q程 和它的Q何子q程都可存取q个临时文g,而其它进E不能存取该文g,? 为它?tmp中的目录已被unlink()删除. W二斚w比较复杂而微?׃system(),popen(),execlp(),execvp()执行 ?若不l出执行命o的全路径,p”骗”用LE序L行不同的命o.? 为系l子E序是根据PATH变量定哪种序搜烦哪些目录,以寻找指定的? ?q称为SUID陷井.最安全的办法是在调用system()前将有效UID改变成实 际UID,另一U比较好的方法是以全路径名命令作为参?execl(),execv(), execle(),execve()都要求全路径名作为参?有关SUID陷井的另一方式? 在程序中讄PATH,׃system()和popen()都启动shell,故可使用shell? ?? system(”PATH=/bin:/usr/bin cd?; q样允许用户q行pȝ命o而不必知道要执行的命令在哪个目录?但这U? Ҏ不能用于execlp(),execvp()?因ؓ它们不能启动shell执行调用序列 传递的命o字符? 关于shell解释传递给system()和popen()的命令行的方?有两个其它的? ? *shell使用IFS shell变量中的字符,命令行分解成单?通常q个 shell变量中是I格,tab,换行),如IFS中是/,字符?bin/ed被解释成单词 bin,接下来是单词ed,从而引起命令行的曲? 再强调一?在通过自己的程序运行另一个程序前,应将有效UID改ؓ实际? UID,{另一个程序退出后,再将有效UID改回原来的有效UID. SUID/SGIDE序指导准则 (1)不要写SUID/SGIDE序,大多数时候无此必? (2)讄SGID许可,不要讄SUID许可.应独自徏立一个新的小l? (3)不要用exec()执行ME序.Cexec()也被system()和popen()调用. . 若要调用exec()(或system(),popen()),应事先用setgid(getgid()) 有效GID|加实际GID. . 若不能用setgid(),则调用system()或popen()?应设|IFS: popen(”IFS=\t\n;export IFS;/bin/ls?”r?; . 使用要执行的命o的全路径? . 若不能用全路径?则应在命令前先设|PATH: popen(”IFS=\t\n;export IFS;PATH=/bin:/usr/bin;/bin/ls?”r?; . 不要用戯定的参数传给system()或popen();若无法避免则应检? 变元字符串中是否有特D的shell字符. . 若用h个大E序,调用exec()执行许多其它E序,q种情况下不要将 大程序设|ؓSGID许可.可以写一?或多?更小,更简单的SGIDE序 执行必须hSGID许可的Q?然后由大E序执行q些SGIDE序. (4)若用户必M用SUID而不是SGID,以相同的序C(2),(3)内?q? 相应调整.不要讄root的SUID许可.选一个其它户? (5)若用hl予其他人执行自qshellE序的许?但又不想让他们能 读该E序,可将E序讄Z执行许可,q只能通过自己的shellE序? q行. ~译,安装SUID/SGIDE序时应按下面的Ҏ (1)保所有的SUID(SGID)E序是对于小l和其他用户都是不可写的,存取 权限的限制低?755(2755)带来麻?只能更严?4111(2111) 其他人无法寻扄序中的安全漏z? (2)警惕外来的编码和make/installҎ . 某些make/installҎ不加选择地徏立SUID/SGIDE序. . 查违背上q指导原则的SUID/SGID许可的编? . 查makefile文g中可能徏立SUID/SGID文g的命? 4.rootE序的设? 有若q个子程序可以从有效UID?的进E中调用.许多前面提到的子E序, 当从rootq程中调用时,完成和原来不同的处?主要是忽略了许可权限的检 ? 由root用户q行的程序当然是rootq程(SUID除外),因有效UID用于定? 件的存取权限,所以从hroot的程序中,调用fork()产生的进E?也是rootq程. (1)setuid():从rootq程调用setuid()?其处理有所不同,setuid()把? 效的和实际的UID都置为指定的?q个值可以是M整型?而对非root q程则仅能以实际UID或本q程原来有效的UID为变量D用setuid(). (2)setgid():在系l进E中调用setgid()?与setuid()cM,实际和有效 的GID都改变成其参数指定的? * 调用以上两个子程序时,应当注意下面几点: . 调用一ơsetuid()(setgid())同时设|有效和实际UID(GID),独立? 别设|有效或实际UID(GID)固然很好,但无法做到这? . setuid()(setgid())可将有效和实际UID(GID)讄成Q何整型数,其数 g必一定与/etc/passwd(/etc/group)中用?组)相关? . 一旦程序以一个用LUID了setuid(),该程序就不再做ؓrootq行,? 不可能再获rootҎ. (3)chown():当rootq程q行chown()?chown()不删除文g的SUID?? SGID许可,但当非rootq程q行chown()?chown()取消文件的SUID? 或SGID许可. (4)chroot():改变q程Ҏ目录的概?调用chroot()?q程׃能把当前 工作目录改变到新的根目录以上的Q一目录,所有以/开始的路径搜烦,? 从新的根目录开? (5)mknod():用于建立一个文?cM于creat(),差别是mknod()不返回所打开 文g的文件描q符,q且能徏立Q何类型的文g(普通文?Ҏ文g,目录 文g).若从非rootq程调用mknod()执行失?只有建立FIFO特别文g (有名道文g)时例?其它M情况?必须从rootq程调用mknod().? 于creat()仅能建立普通文?mknod()是徏立目录文件的唯一途径,因而仅
有root能徏立目?q就是ؓ什么mkdir命ohSUID许可q属root所? 一般不从程序中调用mknod().通常?etc/mknod命o建立特别讑֤文g? q些文g一般不能在使用着时徏立和删除,mkdir命o用于建立目录.当用 mknod()建立特别文g?应当注意从所建的特别文g不允许存取内? 盘,l端和其它设? (6)unlink():用于删除文g.参数是要删除文g的\径名指针.当指定了目录 ?必须从rootq程调用unlink(),q是必须从rootq程调用unlink()的唯 一情况,q就是ؓ什么rmdir命ohroot的SGID许可的原? (7)mount(),umount():由rootq程调用,分别用于安装和拆卸文件系l?q两 个子E序也被mount和umount命o调用,其参数基本和命o的参数相?? 用mount(),需要给Z个特别文件和一个目录的指针,特别文g上的文g pȝ将安装在该目录?调用时还要给Z个标识选项,指定被安装的? 件系l要被读/?0)q是仅读(1).umount()的参数是要一个要拆卸的特? 文g的指? 本文由isbase成员~译或原创,如要转蝲请保持文章的完整?
]]>[转]C++中类的多态与虚函数的使用 http://www.shnenglu.com/jht/archive/2006/05/05/6647.html季浩 季浩 Fri, 05 May 2006 09:37:00 GMT http://www.shnenglu.com/jht/archive/2006/05/05/6647.html http://www.shnenglu.com/jht/comments/6647.html http://www.shnenglu.com/jht/archive/2006/05/05/6647.html#Feedback 0 http://www.shnenglu.com/jht/comments/commentRss/6647.html http://www.shnenglu.com/jht/services/trackbacks/6647.html 阅读全文 ]]> [转]C/C+语言struct深层探烦 http://www.shnenglu.com/jht/archive/2006/05/05/6646.html季浩 季浩 Fri, 05 May 2006 09:26:00 GMT http://www.shnenglu.com/jht/archive/2006/05/05/6646.html http://www.shnenglu.com/jht/comments/6646.html http://www.shnenglu.com/jht/archive/2006/05/05/6646.html#Feedback 0 http://www.shnenglu.com/jht/comments/commentRss/6646.html http://www.shnenglu.com/jht/services/trackbacks/6646.html
C/C+语言struct深层探烦
出处QPConline
[ 2005-08-11 10:30:50 ]
作者:宋宝?
责Q~辑Qxietaoming
1. struct的巨大作?/h4>
面对一个h的大型C/C++E序Ӟ只看其对struct的用情冉|们就可以对其~写者的~程l验q行评估。因Z个大型的C/C++E序Q势必要涉及一?甚至大量)q行数据l合的结构体Q这些结构体可以原本意义属于一个整体的数据l合在一赗从某种E度上来_会不会用structQ怎样用struct是区别一个开发h员是否具备丰富开发经历的标志 在网l协议、通信控制、嵌入式pȝ的C/C++~程中,我们l常要传送的不是单的字节(char型数l)Q而是多种数据l合h的一个整体,其表现Ş式是一个结构体?/p>
l验不的开发h员往往所有需要传送的内容依顺序保存在char型数l中Q通过指针偏移的方法传送网l报文等信息。这样做~程复杂Q易出错Q而且一旦控制方式及通信协议有所变化Q程序就要进行非常细致的修改?/p>
一个有l验的开发者则灉|q用l构体,举一个例子,假设|络或控制协议中需要传送三U报文,其格式分别ؓpacketA、packetB、packetCQ?/p>
struct
structA
{
int
a;
char
b; }
;
struct
structB
{
char
a;
short
b; }
;
struct
structC
{
int
a;
char
b;
float
c; }
优秀的程序设计者这栯计传送的报文Q?/p>
struct
CommuPacket
{
int
iPacketType;
//
报文cd标志
union
//
每次传送的是三U报文中的一U,使用union
{
struct
structA packetA;
struct
structB packetB;
struct
structC packetC; }
}
;
在进行报文传送时Q直接传送struct CommuPacket一个整体?/p>
假设发送函数的原Ş如下Q?/p>
//
pSendDataQ发送字节流的首地址QiLenQ要发送的长度
Send(
char
*
pSendData, unsigned
int
iLen); 发送方可以直接q行如下调用发送struct CommuPacket的一个实例sendCommuPacketQ?br /> Send( (
char
*
)
&
sendCommuPacket ,
sizeof
(CommuPacket) ); 假设接收函数的原形如下:
//
pRecvDataQ发送字节流的首地址QiLenQ要接收的长?br />
//
q回|实际接收到的字节?/span>
unsigned
int
Recv(
char
*
pRecvData, unsigned
int
iLen)Q?/span>
接收方可以直接进行如下调用将接收到的数据保存在struct CommuPacket的一个实例recvCommuPacket中:
Recv( (
char
*
)
&
recvCommuPacket ,
sizeof
(CommuPacket) ); 接着判断报文cdq行相应处理Q?br />
switch
(recvCommuPacket. iPacketType)
{
case
PACKET_A: … ?/span>
//
AcL文处?/span>
break
;
case
PACKET_B: …
//
BcL文处?/span>
break
;
case
PACKET_C: … ?/span>
//
CcL文处?/span>
break
; }
以上E序中最值得注意的是
Send( (char *)&sendCommuPacket , sizeof(CommuPacket) ); Recv( (char *)&recvCommuPacket , sizeof(CommuPacket) );
中的强制cd转换Q?char *)&sendCommuPacket?char *)&recvCommuPacketQ先取地址Q再转化为char型指针,q样可以直接利用处理字节流的函数?/p>
利用q种强制cd转化Q我们还可以方便E序的编写,例如要对sendCommuPacket所处内存初始化?Q可以这栯用标准库函数memset()Q?/p>
memset((char *)&sendCommuPacket,0, sizeof(CommuPacket));
2. struct的成员对?/h4>
Intel、微软等公司曄一道类似的面试题:
1
. #include
<
iostream.h
>
2
. #pragma pack(
8
)
3
.
struct
example1
4
.
{
5
.
short
a;
6
.
long
b;
7
. }
;
8
.
struct
example2
9
.
{
10
.
char
c;
11
. example1 struct1;
12
.
short
e;
13
. }
;
14
. #pragma pack()
15
.
int
main(
int
argc,
char
*
argv[])
16
.
{
17
. example2 struct2;
18
. cout
<<
sizeof
(example1)
<<
endl;
19
. cout
<<
sizeof
(example2)
<<
endl;
20
. cout
<<
(unsigned
int
)(
&
struct2.struct1)
-
(unsigned
int
)(
&
struct2)
<<
endl;
21
.
return
0
;
22
. }
问程序的输入l果是什么?
{案是:
8 16 4
不明白?q是不明白?下面一一道来Q?/p>
2.1 自然对界
struct是一U复合数据类型,其构成元素既可以是基本数据类型(如int、long、float{)的变量,也可以是一些复合数据类型(如array、struct、union{)的数据单元。对于结构体Q编译器会自动进行成员变量的寚wQ以提高q算效率。缺省情况下Q编译器为结构体的每个成员按其自然对界(natural alignmentQ条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,W一个成员的地址和整个结构的地址相同?/p>
自然对界(natural alignment)即默认对齐方式,是指按结构体的成员中size最大的成员寚w?/p>
例如Q?/p>
struct naturalalign { char a; short b; char c; };
在上q结构体中,size最大的是shortQ其长度?字节Q因而结构体中的char成员a、c都以2为单位对齐,sizeof(naturalalign)的结果等?Q?/p>
如果改ؓQ?/p>
struct naturalalign { char a; int b; char c; };
其结果显然ؓ12?/p>
2.2指定对界
一般地Q可以通过下面的方法来改变~省的对界条Ӟ
· 使用伪指?pragma pack (n)Q编译器按照n个字节对齐; · 使用伪指?pragma pack ()Q取消自定义字节寚w方式?/p>
注意Q?strong>如果#pragma pack (n)中指定的n大于l构体中最大成员的sizeQ则其不起作用,l构体仍然按照size最大的成员q行对界?/strong>
例如Q?/p>
#pragma pack (n) struct naturalalign { char a; int b; char c; }; #pragma pack ()
当n???6Ӟ其对齐方式均一Psizeof(naturalalign)的结果都{于12。而当n?Ӟ其发挥了作用Q得sizeof(naturalalign)的结果ؓ8?/p>
在VC++ 6.0~译器中Q我们可以指定其对界方式Q见?Q,其操作方式ؓ依次选择projetct > setting > C/C++菜单Q在struct member alignment中指定你要的对界方式?/p>
?Q?/strong>
在VC++ 6.0中指定对界方?/em>
另外Q通过__attribute((aligned (n)))也可以让所作用的结构体成员寚w在n字节边界上,但是它较被使用Q因而不作详l讲解?/p>
2.3 面试题的解答
xQ我们可以对Intel、微软的面试题进行全面的解答?/p>
E序中第2?pragma pack (8)虽然指定了对界ؓ8Q但是由于struct example1中的成员最大size?Qlong变量size?Q,故struct example1仍然?字节对界Qstruct example1的size?Q即W?8行的输出l果Q?/p>
struct example2中包含了struct example1Q其本n包含的简单数据成员的最大size?Qshort变量eQ,但是因ؓ其包含了struct example1Q而struct example1中的最大成员size?Qstruct example2也应?对界Q?pragma pack (8)中指定的对界对struct example2也不起作用,?9行的输出l果?6Q?/p>
׃struct example2中的成员?为单位对界,故其char变量c后应补充3个空Q其后才是成员struct1的内存空_20行的输出l果??/p>
3. C和C++间struct的深层区?/h4>
在C++语言中structh了“类” 的功能,其与关键字class的区别在于struct中成员变量和函数的默认访问权限ؓpublicQ而class的ؓprivate?/p>
例如Q定义structcdclassc:
struct structA { char a; ?br />} class classB { char a; ?br />}
则:
struct A a; a.a = 'a'; //讉Kpublic成员Q合?br />classB b; b.a = 'a'; //讉Kprivate成员Q不合法
许多文献写到q里p为已l给ZC++中struct和class的全部区别,实则不然Q另外一炚w要注意的是:
C++中的struct保持了对C中struct的全面兼容(q符合C++的初衷——“a better c”)Q因而,下面的操作是合法的:
//定义struct struct structA { char a; char b; int c; }; structA a = {'a' , 'a' ,1}; // 定义时直接赋初?/p>
即struct可以在定义的时候直接以{ }对其成员变量赋初|而class则不能,在经怹目《thinking C++ 2nd edition》中作者对此点q行了强调?/p>
4. struct~程注意事项
看看下面的程序:
1. #include <iostream.h> 2. struct structA 3. { 4. int iMember; 5. char *cMember; 6. }; 7. int main(int argc, char* argv[]) 8. { 9. structA instant1,instant2; 10.char c = 'a'; 11. instant1.iMember = 1; 12. instant1.cMember = &c; 13.instant2 = instant1; 14.cout << *(instant1.cMember) << endl; 15.*(instant2.cMember) = 'b'; 16. cout << *(instant1.cMember) << endl; 17. return 0; }
14行的输出l果是:a 16行的输出l果是:b
Why?我们?5行对instant2的修Ҏ变了instant1中成员的|
原因在于13行的instant2 = instant1赋D句采用的是变量逐个拯Q这使得instant1和instant2中的cMember指向了同一片内存,因而对instant2的修改也是对instant1的修攏V?/p>
在C语言中,当结构体中存在指针型成员Ӟ一定要注意在采用赋D句时是否?个实例中的指针型成员指向了同一片内存?/p>
在C++语言中,当结构体中存在指针型成员Ӟ我们需要重写struct的拷贝构造函数ƈq行?”操作符重蝲?/p>
]]>
þþžоƷ23ٻӰԺ |
þAV |
ƷŮþþþ |
þþƷһ |
Ʒľþþ |
99þþƷѿһ
|
AVþþƷ |
ŷպľþ |
2022Ʒþþþ |
Ʒݾþþþø99 |
Ʒ99þþþĻ
|
ƷþþþAV |
99þùۺϾƷ鶹 |
þ97þ97Ʒӿϼ |
ɫվwwwþþ |
99þþƷëƬ |
ľþþƷ |
Ʒ99þþþ
|
þrоƷƵ |
ɫ99þþþø߳ۺӰԺ |
˾þþƷһ |
һþ㽶 |
9191ƷѾþ |
һɫþۺϺݺ |
պݺݾþ͵͵ɫۺ |
ƷһþþƷ |
Ļɫ͵͵þ |
պӰþþñ |
Ʒ99þþþþլ |
ɫۺϾþۺ |
Ʒþþþþ |
һɫþۺƷ |
þþƷһ99 |
ŷսþþþþþ |
ȾþùŷһƷ |
ƷŮþþ |
ƷȾþav |
þseƷһӰԺ |
þþþþݴۺϾƷ |
˾þþƷӰԺ |
ƷþþþþþþþĻ |