输入移动位数n,然后接受用户输入的四个字符,每个字符向后移动n位实现凯撒密码加密。
例如,输入n为3,再输入abcd,输出defg。只考虑小写字母的情况下,部分代码如下:
int n;
char c[4];
cin>>n;
cin>>c;
for(int i=0;i<4;i++)
{
printf("%c",(c[i]+n-97)%26+97);
}
当代码运行后发现输入:
3
abcd
输出的内容依然是:
abcd
更好玩的事情是,当把源代码中声明变量的顺序调整一下,情况就发生了变化:
char c[4];
int n;
cin>>n;
cin>>c;
for(int i=0;i<4;i++)
{
printf("%c",(c[i]+n-97)%26+97);
}
运行程序,输入:
3
abcd
输出的内容就变为:
defg
这个代码在不同的机器上应该有不同的运行效果,但是在我的机器上确实发生了这个好玩的情况。存在即合理,到底发生了什么呢?
其实打印一下变量n和c的地址就知道事情的真相了:
int n;
char c[4];
cout<<&n<<endl;
cout<<&numbers<<endl;
cin>>n;
cin>>c;
n和c的地址分别是:
0x28fef8
0x28fef4
可以看见恰好数组c和变量n的内存地址居然是挨在一起的。
当执行到cin>>n的时候,输入3,在0x26fef8中存入数字3。
回车后执行cin>>c,输入abcd。因为c是数组的名称,所以cin会认为输入的abcd是一个字符串,于是编译器会自动在abcd后面补上一个NULL符,NULL符的ASCII码值为0,一般写作'\0'。所以接下来发生的事情就是:
C++把0当做写入输入在第五个元素即c[4],所以它的写入位置就是数组首地址(即a[0]的地址)偏移4位,这个地址恰好是变量n的地址,所以0被写入到了变量n中。
后续在做字符变化的时候,所有的字符都是与0在做加法运算,因此字符没有产生任何变化。
当交换了变量的声明顺序后,数组c和变量n的地址发生了变化:
char c[4];
int n;
cout<<&n<<endl;
cout<<&c<<endl;
cin>>n;
cin>>c;
此时n和c的地址如下:
0x28fef4
0x28fef8
于是再输入n和abcd的时候内存变为了如下形式:
此时从内存上看,程序中需要的变量n和c[0]c[1]c[2]c[3]都有需要的值,因此程序产生了效果。但是要注意数组依然影响了“无辜的”内存地址0x28fefc。
所以,如果以字符串的形式进行字符数组内容的初始化,必须要让数组的长度至少比字符串字面内容多1位,用来存储编译器添加的NULL结束字符,防止影响到“无辜的”内存空间,造成意想不到的错误。
int n;
char c[5];
//cout<<&n<<endl;
//cout<<&c<<endl;
cin>>n;
cin>>c;
for(int i=0;i<4;i++)
{
printf("%c",(c[i]+n-97)%26+97);
}