??xml version="1.0" encoding="utf-8" standalone="yes"?>
DES是Data Encryption StandardQ数据加密标准)的羃写。它是由IBM公司研制的一U加密算法,国国家标准局?977q公布把它作为非部门使用的数据加密标准,二十q来Q它一直活跃在国际保密通信的舞CQ扮演了十分重要的角色[10]?br /> DES是一个分l加密算法,他以64位ؓ分组Ҏ据加密。同时DES也是一个对U算法:加密和解密用的是同一个算法。它的密匙长度是56位(因ؓ每个W?位都用作奇偶校验Q,密匙可以是Q意的56位的敎ͼ而且可以L时候改变。其中有极少量的数被认ؓ是弱密匙Q但是很Ҏ避开他们。所以保密性依赖于密钥?br /> 2 法框架[11]Q?br /> DES?4(bit)位的明文分组Mq行操作QMl过一个初始置换IP|换成m0Q将m0明文分成左半部分和右半部分m0=(L0,R0)Q各32位长。然后进?6轮完全相同的q算Q这些运被UCؓ函数fQ在q算q程中数据与密匙l合。经q?6轮后Q左Q右半部分合在一Lq一个末|换Q这样就完成了[12]?br /> 在每一轮中Q密匙位UMQ然后再从密匙的56位中选出48位。通过一个扩展置换将数据的右半部分扩展成48位,q过一个异或操作替代成新的32位数据,在将其置换换一ơ。这四步q算构成了函数f。然后,通过另一个异或运,函数f的输Z左半部分l合Q其l果成ؓ新的叛_部分Q原来的叛_部分成ؓ新的左半部分。将该操作重?6ơ,实C。具体图4所C?br />
? DES法框图 3 DES解密
在经q所有的代替、置换、异或盒循环之后Q你也许认ؓ解密法与加密算法完全不同。恰恰相反,l过_ֿ选择的各U操作,获得了一个非常有用的性质Q加密和解密使用相同的算法?br />DES加密和解密唯一的不同是密匙的次序相反。如果各轮加密密匙分别是K1,K2,K3?K16那么解密密匙是K16,K15,K14…K1?br /> 4 DES的几U工作方?/font>
W一U电子密本方式(ECBQ?/font>
明文分成n?4比特分组Q如果明文长度不?4比特的倍数Q则在明文末֡充适当数目的规定符受对明文l用l定的密钥分别进行加密,行密文C=(C0,C1,…?Cn-1)其中Ci=DES(K,xi),i=0,1,?.,n-1?br /> W二U密文分l链接方式(CBCQ?/font>
在CBC方式下,每个明文lxi在加密前与先一l密文按位模二加后,再送到DES加密QCBC方式克服了ECB方式报内l重的缺点,但由于明文组加密前与一l密文有养I因此前一l密文的错误会传播到下一l?br /> W三U密文反馈方式(CFBQ,可用于序列密?/font>
明文XQ?x0,x1,…?xn-1)Q其中xi由t个比特组?
与CFB唯一不同的是OFB是直接取DES输出的t个比特,而不是取密文的t个比特,其余都与CFB相同。但它取的是DES的输出,所以它克服了CFB的密文错误传播的~点
]]>
问题一Q?nbsp; E序~译通过Q但q行错误提示如下Q?BR> scanf : floating point formats not linked
Abnormal program termination
出错CZE序Q?/P>
#include <stdio.h>
int main(void)
{
int i,j ;
float s[3][3];
/*q里*/
for(i=0;i<3;i++)
for(j=0;j<3;j++)
scanf("%f",&s[i][j]);
for(i=0;i<3;i++)
for(j=0;j<3;j++)
printf("%f",s[i][j]);
}
q实际上是个与本文主题无关的问题Q也是与scanf()函数无关Q是~译器的问题?/P>
原因很明:没有链接点库。早期系l内存资源紧张,多维点数组占用内存量大Q一lQҎl就没有此问题)Q因此TC在编译时量不加入无关的部分Q在没发现需要Q点{换程序时Q就不在可执行程序中安装q个部分。而有时TC又不能正识别实际上实需要做点转换Q因此就会出C面错误?/P>
解决的方法:告诉TC需要做点数的输入转换。将以下语句加入上面E序中标?*q里*/处?/P>
Ҏ一: float c;
scanf("%f",&c);
Ҏ? float c,*t;//此处手误Q现已更?amp;t===?t;
t=&c;
.....
也就是说Q编译器只要有Q点{换的U烦QTC׃把Q点{换连?所以一般大一点的E序里的׃有Q点变量反而没有此问题?BR>
但问题到此ƈ没结束,我在上面有句“一lQҎl就没有此问题”,那么我们来看看这栯不行Q?/P>
#include <stdio.h>
int main(void)
{
int i,j ;
float s[3][3],*ptr;
ptr=&s[0][0];
for(i=0;i<3;i++)
for(j=0;j<3;j++)
scanf("%f",ptr+i*3+j);
for(i=0;i<3;i++)
for(j=0;j<3;j++)
printf("%7.2f\n",s[i][j]);
}
q样我们把多维点数组降ؓ一lQҎl来处理Q调试一下,E序q行正常。这说明TC~译器仅在处理多lQҎl(l构体)有此“未链接点库”的问题?/P>
问题二:scanf()函数不能正确接受有空格的字符Ԍ? I love you!
#include <stdio.h>
int main()
{
char str[80];
scanf("%s",str);
printf("%s",str);
return 0;
}
输入QI live you!
输出QI
scanf()函数接收输入数据Ӟ遇以下情늻束一个数据的输入Q(不是l束该scanf函数Qscanf函数仅在每一个数据域均有数据Qƈ按回车后l束Q?BR> ?遇空根{“回车”、“蟩格”键?BR> ?遇宽度结束?BR> ?遇非法输入?/P>
所以,上述E序q不能达到预期目的,scanf()扫描?I"后面的空格就认ؓ对str的赋值结束,q忽略后面的"love you!".q里要注意是"love you!"q在键盘~冲?/FONT>Q关于这个问题,|上我所见的说法都是如此Q但是,我经q调试发玎ͼ其实q时~冲区字W串首尾指针已经相等了,也就是说~冲区清IZQscanf()函数应该只是扫描stdin,q个D存信息是在stdin?。我们改动一下上面的E序来验证一下:
#include <stdio.h>
int main()
{
char str[80];
char str1[80];
char str2[80];
scanf("%s",str);/*此处输入:I love you! */
printf("%s",str);
sleep(5);/*q里{待5U?告诉你程序运行到什么地?/
scanf("%s",str1);/*q两句无需你再输入,是对键盘盘缓冲区再扫?nbsp; */
scanf("%s",str2);/*q两句无需你再输入,是对键盘盘缓冲区再扫?nbsp; */
printf("\n%s",str1);
printf("\n%s",str2);
return 0;
}
输入QI love you!
输出QI
love
you!
好了Q原因知道了Q那么scanf()函数能不能完成这个Q务?回答是:能!别忘了scanf()函数q有一?%[] 格式控制W(如果?[]不了解的h看本文的上篇Q?L下面的程序:
#include "stdio.h"
int main()
{
char string[50];
/*scanf("%s",string);不能接收I格W?/
scanf("%[^\n]",string);
printf("%s\n",string);
return 0;
}
问题三:键盘~冲区残余信息问?/FONT>
#include <stdio.h>
int main()
{
int a;
char c;
do
{
scanf("%d",&a);
scanf("%c",&c);
printf("a=%d c=%c\n",a,c);
/*printf("c=%d\n",c);*/
}while(c!='N');
}
scanf("%c",&c);q句不能正常接收字符,什么原因呢Q我们用printf("c=%d\n",c);C用int表示出来Q启用printf("c=%d\n",c);q一句,看看scanf()函数赋给C到底是什么,l果?c=10 ,ASCIIgؓ10是什么?换行即\n.对了Q我们每L一?Enter"键,向键盘缓冲区发去一个“回车?\r),一个“换?(\n),在这里\r被scanf()函数处理掉了Q姑且这么认为吧^_^Q,而\n被scanf()函数“错误”地赋给了c.
解决办法Q可以在两个scanf()函数之后加个fflush(stdin);Q还有加getch(); getchar();也可以,但是要视具体scanf()语句加那个,q里׃分析了,读者自己去摸烦吧。但是加fflush(stdin);不管什么情况都可行?/P>
函数? fflush
?nbsp; ? 清除一个流
?nbsp; ? int fflush(FILE *stream);
#include <stdio.h>
int main()
{
int a;
char c;
do
{
scanf("%d",&a);
fflush(stdin);
scanf("%c",&c);
fflush(stdin);
printf("a=%d c=%c\n",a,c);
}while(c!='N');
}
q里再给一个用“空格符”来处理~冲区残余信息的CZQ?/P>
q行出错的程序:
#include <stdio.h>
int main()
{
int i;
char j;
for(i = 0;i < 10;i++)
{
scanf("%c",&j);/*q里%前没有空?/
}
}
使用了空格控制符后:
#include <stdio.h>
int main()
{
int i;
char j;
for(i = 0;i < 10;i++)
{
scanf(" %c",&j);/*注意q里%前有个空?/
}
}
可以q行看看两个E序有什么不同?/P>
问题?nbsp; 如何处理scanf()函数误输入造成E序死锁或出错?
#include <stdio.h>
int main()
{
int a,b,c; /*计算a+b*/
scanf("%d,%d",&a,&b);
c=a+b;
printf("%d+%d=%d",a,b,c);
}
如上E序Q如果正输入a,b的|那么没什么问题,但是Q你不能保证使用者每一ơ都能正输入,一旦输入了错误的类型,你的E序不是死锁Q就是得C个错误的l果,呵呵Q这可能所有h都遇到过的问题吧Q?/P>
解决ҎQscanf()函数执行成功时的q回值是成功d的变量数,也就是说Q你q个scanf()函数有几个变量,如果scanf()函数全部正常dQ它p回几。但q里q要注意另一个问题,如果输入了非法数据,键盘~冲区就可能q个有残余信息问题?/P>
正确的例E:
#include <stdio.h>
int main()
{
int a,b,c; /*计算a+b*/
while(scanf("%d,%d",&a,&b)!=2)fflush(stdin);
c=a+b;
printf("%d+%d=%d",a,b,c);
}
此l束此文?最后还得照例谦虚几?本h水^有限(的的确有限^_^,q到是真?,谬误隑օq望达h指点一?在下在此谢过?
(全文?
knocker 2004.10.21
scanf()函数是所有C语言学习者在学习C语言q程中所遇到的第二个函数Q第一个函数是printf(),Brian W.Kerninghan & Dennis M.Ritchie的“hello,world”程序基本上是所有的C语言学习者第一个范例)Q所以scanf()函数应当是C学习者能熟练q用的一个函敎ͼ但有很多初学者对此函C能很好的q用Q在实际~程中错误用scanf()函数Q导至程序生某U错误不能正常运行,以至产生“scanf()函数有BUG”,“scanf()函数无用论”等{错误观炏V?BR> 本文l合W者在~程实践中及论坛上网友所遇到的问题作一释疑Q但W者水qx限(菜鸟U)Q难免有谬误之处Q还望达人指点一二。(Email:knocker.k@126.com)
本文分上Q下两篇讲述了C语言中的scanf()函数的用法,重点阐述使用scanf()函数q程中出现的常见错误及对{。当Ӟ文中某些解决ҎQ均可以采用其他函数和方法来更好地解冻I但本文仅限讨论scanf()函数本n?BR> 上篇Q详l介l了scanf()函数控制串的构成。下,用实际例E介lscanf()函数控制串运用出现的常见错误及对{技巧?/P>
二、scanf()函数的控制串
函数? scanf
?nbsp; ? 执行格式化输?
?nbsp; ? int scanf(char *format[,argument,...]);
scanf()函数是通用l端格式化输入函敎ͼ它从标准输入讑֤(键盘) d输入的信息。可以读入Q何固有类型的数据q自动把数值变换成适当的机内格式?/P>
其调用格式ؓ: scanf("<格式化字W串>"Q?lt;地址?gt;);
scanf()函数q回成功赋值的数据ҎQ出错时则返回EOF?/P>
其控制串׃cdW构成:
1。格式化说明W;
2。空白符Q?BR>3。非I白W;
QAQ?nbsp; 格式化说明符
格式字符 说明
%a d一个Q点?仅C99有效)
%A 同上
%c d一个字W?BR>%d d十进制整?BR>%i d十进Ӟ八进Ӟ十六q制整数
%o d八进制整?BR>%x d十六q制整数
%X 同上
%c d一个字W?BR>%s d一个字W串
%f d一个QҎ
%F 同上
%e 同上
%E 同上
%g 同上
%G 同上
%p d一个指?BR>%u d一个无W号十进制整?BR>%n x已读入值的{h字符?BR>%[] 扫描字符集合
%% ?W号
附加格式说明字符?/P>
修饰W?nbsp; 说明
L/l 长度修饰W?nbsp; 输入"?数据
h 长度修饰W?nbsp; 输入"?数据
W 整型常数 指定输入数据所占宽?BR>* 星号 I一个数?
hh,ll同上h,l但仅对C99有效?/P>
QBQ?nbsp; I白字符
I白字符会scanf()函数在读操作中略去输入中的一个或多个I白字符Q空白符可以是space,tab,newline{等Q直到第一个非I白W出Cؓ止?/P>
QCQ?nbsp; 非空白字W?
一个非I白字符会scanf()函数在读入时剔除掉与q个非空白字W相同的字符?
注:scanf()控制串知识就介绍到这里(应该比较齐全了^_^)Q如有遗漏下ơ补上。下面将l合实际例程Q一一阐述.
三、scanf()函数的控制串的?/FONT>
?.
#include "stdio.h"
int main(void)
{
int a,b,c;
scanf("%d%d%d",&a,&b,&c);
printf("%d,%d,%d\n",a,b,c);
return 0;
}
q行时按如下方式输入三个|
3?? ↙(输入a,b,c的|
3Q?Q? Qprintf输出的aQbQc的|
Q?Q?&a?amp;b?amp;c中的&是地址q算W,分别获得q三个变量的内存地址?BR> Q?Q?"%d%d%d"是按十进值格式输入三个数倹{输入时Q在两个数据之间可以用一个或多个I格、tab键、回车键分隔?BR> 以下是合法输入方式:
?3□□4□□□□5?BR> ?3?BR> 4??BR> ?3Qtab键)4?BR> 5?/P>
?.
#include "stdio.h"
int main(void)
{
int a,b,c;
scanf("%d,%d,%d",&a,&b,&c);
printf("%d,%d,%d\n",a,b,c);
return 0;
}
q行时按如下方式输入三个|
3,4,5 ↙(输入a,b,c的|
或?/P>
3,?,? ↙(输入a,b,c的|
3,□□?,? ↙(输入a,b,c的|
......
都是合法的,但是","一定要跟在数字后面Q如Q?BR>3□,4,? ↙就非法了,E序出错。(解决Ҏ与原因后面讲Q?/P>
再如Q?/P>
1、sacnf()中的变量必须使用地址?/P>
int a, b;
scanf("%d%d",a,b); //错误
scanf("%d%d",&a,&b);
2、scanf()的格式控制串可以使用其它非空白字W,但在输入时必输入这些字W?/P>
例:
scanf("%d,%d",&a,&b);
输入Q?3Q? ↙(逗号?%d,%d"中的逗号对应Q?
scanf("a=%d,b=%d",&a,&b);
输入Q?a=3Qb=4 ↙("a=","b=",逗号?%d,%d"中的"a=","b="及逗号对应Q?/P>
3、在?%c"输入ӞI格和“{义字W”均作ؓ有效字符?/P>
例:
scanf("%c%c%c",&c1,&c2,&c3);
输入Qa□b□c?
l果Qa→c1Q□→c2Qb→c3 (其余被丢?
scanf()函数接收输入数据Ӟ遇以下情늻束一个数据的输入Q(不是l束该scanf函数Qscanf函数仅在每一个数据域均有数据Qƈ按回车后l束Q?BR> ?遇空根{“回车”、“蟩格”键?BR> ?遇宽度结束?BR> ?遇非法输入?/P>
上篇写到这里吧Q第三小节的例程"?自网上的一个教E(原因有二Q一Q可以少打不字。二Q□↙我不知道怎么打。^_^)Qƈ删去其中的错误之?q里也顺便提醒本文读者一句:凡事要亲力而ؓQ即使是l典的书c也不免有疏漏之处,所以,用编译器说话是最可靠的,是对是错L译器告诉你?/P>
对于数组Q因为系l会按照你指定的大小为数l分配存储空_q也是ؓ什么数l必L定大的原因Q如Qchar array[5] ; //pȝ会自动ؓ光留sizeof(char)*5个字节的q箋内存Q注意是q箋的)。所以我们可以对array[0]...array[4]q五个变量随便访问(d写)都不会有问题?BR>对于指针Q系l只会ؓ所定义的指针变量分配空_指针所指向的地点ƈ未分配。D个例子: char *p ; q里会ؓ变量p分配I间Q大ؓ4字节Q?2位机Q,但是*pQ就是p指向的地方)却是随机的地方,q个地方pȝ也不为其分配I间。所以在q种情况下,你访问和lp赋|p=...)都是允许的,但是讉K*p或者给*p赋值都是错误的。我们要想?p必须先其指向有效区域,q可以通过动态申请内存或者赋|知道的有效地点赋给它)来实现?BR>提醒一下:对于指针Q在使用Ӟ不光所指向的区域能dQ指针变量本w也能读写,但是数组不同Q数l名的不能写的(允许读)。ؓ什么?因ؓ指针变量p有存储空_而数l名array是没有的?/P>
一U指针,char *p ; //p为指向charcd的指针变量?/P>
有了指针变量我们可以定义Q何类型的指针Q可以随心所Ʋ的用指针来讉KMcd的变量。但是有人发C问题Q用这U定义,我们无法用指针来操纵指针cdQ于是二U指针的定义被拿出来了?/P>
二指针是指向指针的指针,用来操纵一指针变量?BR>例:char **pp ;
定义的pp为指向一?指向一char变量的指?的指针?BR>我们可以?pp指向到前面定义的p : pp = &p ;
现在很容易看清他们之间个关系Q?BR>*pp是讉Kpp指向的位|,讉K的值当然就是p,
**pp是*(*pp)拉,当然{于*p.
注意 定义pp的时候,pȝ只ؓpp分配了空_q未?pp分配I间Q后来能讉K*pp是因为我们用pp=&ppp指向?amp;pq个已经分配好了的空间?/P>
同理Qؓ了控制二U指针于是有了三U指针,...
char array[5];//定义了array为含?个char元素的数l?BR>二维数组的定义我׃多说了,怿大家知道得已l很详细了。我q里p一说他们的名称吧。提醒一下:他提出的愿意是我们希望得到arraycd的数l?BR>char arrayMulti[3][5] ;
前面说过数组名不是变量,那么它是怎么使用的呢Q?BR>其实数组名只是在~译时刻~译器用来定位变量位|的一个标{。比?array[3],那么~译器就认ؓ是“标{?3”,你如果写 array[6]Q那么编译器p为是“标{?6”,q就是C不会l出数组界错误的原因。那么arrayMulti是什么呢Q细Ml数l的定义Q我们定义的是一个数l的数组。我们写了这个定义,意味着我们已经有了 char [5] q种cd的数l,而现在我们要定义一个现有数l类型的数组arrayMulti 。ƈ且在以后的程序中他会永远记得它的元素的类型是char [5]Q所以你在访问他的元素arrayMulti[1]Ӟ他知道这是一个char[5]的数l。同样二l数l也不检查越界,所以你写arrayMulti[2][5]不会有问题?BR>提示Q编译器不检查数l的界Q但会检查数l元素的cdQ所以:
对于array ,你给array[0]一个int型的值是不行的,因ؓ他的元素是charcdQ?BR>对于arrayMulti,你他的元素一个char[6]的g是不行的Q详l例子见“蜕化”)?/P>
数组在作为函数参数时Q数l名蜕化ؓ指针。C语言的书上是q么说的Q我q里要说得是Q这句话是不完全正确的!我们知道指针是占用内存的Q但是这个蜕化而成的家伙是不占有内存的Q仍然只是个标签。书上ؓ什么这么说呢?书上的意思是说这家伙已经蜕化得不知道自己有几个元素了。D个例子:
void fun(char array[5]);
在编译时~译器会当成?void fun(char *array);你在q个函数中用sizeof(a)得到的值是4Q而在定义char array[5]的函Csizeof(array) = 5,说明实已经蜕化为指针了。所以你写:
char *pa = array ; //正确Q指针到指针
数组名退化ؓ指针Q在q里一下:数组的元素类型仍然存在!q里要注意的是多l数l的情况?BR>?l数lؓ例:
void fun2(char arrayMulti[3][5]);
那么在函数fun2中,arrayMulti蜕化成的是char (*)[5],卻I指向char[5]cd的指针,因ؓ前面分析qarrayMulti的元素的cd是char[5]Q所以在E序中:
char **pm = arrayMulti ; //错误Q从char (*)[5]?char **的赋?BR>char (*pm5)[5] ;
pm5 = arrayMulti ; //正确?
6.对元素的讉K
数组名是一个标{,是一个记录地址的标{,在对元素的访问上所L作用和指针一P所以:
int a[5];
int *b;
b=a;
那么Q?BR>a[1],b[1],*(a+1),*(b+1)都是允许的?BR>更悬一点:因ؓ其在q儿所L作用仅仅是个标签而已Q编译的时候就是让两者相加)Q所以:
1[a],1[b],也是允许的?/P>
7.取地址
Ҏ针变量?amp;q算后得到的是指针变量的地址Q那么数l名呢?
他仅仅是个标{֕Q根本就没地方存放?BR>所以C语言中的规定是取数组地址的结果是Q仍然是其本w?BR>?&array 的值和 array一栗?BR>注意Q值是一Pcd是不一L?/P>
------------------------
虽然数组名的没有存储空_但把&a规定a的地址g样ƈ不是q个原因?BR>数组名是一个右|本来不符?amp;的语法的Q但是,数组却是一个对象,对一?BR>数组对象取地址是合理的QC标准委员会经q衡量,认ؓl护一个对象的完整?BR>更重要,因此允许&aQ只不过Q?amp;a的意义,q对一个数l名取地址Q而是?BR>一个数l对象取地址?/P>