C++问题集合。
最近遇到的一些问题 想把它们彻底弄清楚。麻烦大大们了!
1. 关于数组越界问题
首先看下面的一段代码
int arr [5] = {0,1,2,3,4};
int* pt = arr;
pt = pt +2;
cout < < pt[0];
//运行以后显示的是2;
//运行以后 p[0] = 2 p[1] = 3 p[2] =4
为什么p[3] 不等于0呢
很显然p[3]以近越界了,它可能显示任何一个直,当然以后严格的编译系统会显示错误。
但是
pt = pt -2;
运行后p[0] = 3
pt [0]没越界吗?
难道是巧合?还是指针pt跳到了 显示 3的地址?
0 1 2 3 4
地址 100 104 108 112 116
我画了一个图
//默认 int 占4字节
要是pt = pt -2的话
因该是 地址为 092的内存 但是092什么都没有储存啊?
这个时候 pt[0]越界了吗?
2. 接上面一个问题的图纸
0 1 2 3 4
地址 100 104 108 112 116
图上面表示 100 104 108都分别储存了 0 1 2
我想问一下 那么地址 101 102 103 105等等会用来做什么?
被舍弃掉了吗? 还是用来储存其他非数组之类连续性的变量?
3. switch语句中的默认语句 可有可无吗?
4. 关于cin
见下面一段代码
int golf[5];
for (int i = 0; i < 5 ; i++)
{
cout < < "round # " < <(i+1) < < ": ";
while (!(cin> > golf[i]))
{
cin.clear (); // 重直input
while (cin.get ()!= '\n ')
continue; //清除错误的input
cout < < "Please enter a number "
}
}
上面一段代码表示的 输入一个数组的个项
当为数字的时候通过并储存在golf[i]里面。
当输入字母的时候,提示 "Please enter a number " 并重新输入一个数字
我想请问 为什么需要重置? 既然输入的是个字母,没有被储存在golf[i]里面,那就应该可以继续输入啊~我觉得 cin.clear ();有点多余。。
[解决办法]
1:越界也只是针对array说的,你定义pt,编译器又不知道你想干什么,当然不会提示越界。
你对地址的想法是正确的,=3应该是个巧合,你可以尝试给那个地址赋个值,再操作下看看。
2:int类型占用的就是4个字节,只不过高位地址的值在你这里是0x00
3:没有可以,但是不是好的风格
[解决办法]
我觉得 cin.clear ();有点多余。。
=================
因为输入字母的时候,
cin 输入流的输入标志被 置位,
表示输入流发生错误,
那么之后 cin 将保留这个错误标志,
导致后续输入不起任何效果。
cin.clear ();
就是为了清除这个错误标志,
从而可以继续输入 ...
[解决办法]
1. 关于数组越界问题
首先看下面的一段代码
int arr [5] = {0,1,2,3,4};
int* pt = arr;
pt = pt +2;
cout < < pt[0];
//运行以后显示的是2;
//运行以后 p[0] = 2 p[1] = 3 p[2] =4
为什么p[3] 不等于0呢
很显然p[3]以近越界了,它可能显示任何一个直,当然以后严格的编译系统会显示错误。
但是
pt = pt -2;
运行后p[0] = 3
pt [0]没越界吗?
难道是巧合?还是指针pt跳到了 显示 3的地址?
0 1 2 3 4
地址 100 104 108 112 116
我画了一个图
//默认 int 占4字节
要是pt = pt -2的话
因该是 地址为 092的内存 但是092什么都没有储存啊?
这个时候 pt[0]越界了吗?
pt=pt-2之后,pt 指向的位置一定是越界了,跑到了数组的前面,这时对它取 pt[0]所得的数应该是随机的。虽然楼主取到了一个还算正常的数,但不意味着这种做法是正确的
2. 接上面一个问题的图纸
0 1 2 3 4
地址 100 104 108 112 116
图上面表示 100 104 108都分别储存了 0 1 2
我想问一下 那么地址 101 102 103 105等等会用来做什么?
被舍弃掉了吗? 还是用来储存其他非数组之类连续性的变量?
当然没有舍弃,因为一个int占四个字节,每个字节八个二进制位,那么相当于32位,而这32位都用来存每个int的0,1表示,之所以会出现100 104这样的跳跃存储,正是因为102 103都已经存满了,101 104只不过是一个新的int的存储地址的首地址而已
3. switch语句中的默认语句 可有可无吗?
default语句通常是一个错误出口,例如你要判断键盘上输入的字母是A-Z中的哪一个,而这时用户输入了一个数字9,这就要用default来处理。因为你的判断里面是A-Z,没有数字判断的
4. 关于cin
见下面一段代码
int golf[5];
for (int i = 0; i < 5 ; i++)
{
cout < < "round # " < <(i+1) < < ": ";
while (!(cin> > golf[i]))
{
cin.clear (); // 重直input
while (cin.get ()!= '\n ')
continue; //清除错误的input
cout < < "Please enter a number "
}
}
重置input是为了防止输入流将表示确定的回车键也当成了一个输入
[解决办法]
1. 不管是向前越界还是向后越界,道理都是一样的,指针指向了非数组以外的位置,它的内容是不定的(至少不是通过数组内部保存的数据就可以预测的)。C++标准不要求做数组越界检查。如arr[5]的越界编译器没有义务指出,大部分的编译器简单地将它转换成*(array+5)。而如 int* pr = arr; pr[5]更是一点语法问题也没有,注意pr已经不是数组了,而是指针。C++一定得支持指针的移位访问,从而支持低层的内存管理。因此有的编译器会对arr[5]越界提出警告(这已经做出超出义务的事了,应该感谢它),但是一定不会拒绝pr[5].
至于int* pr = arr; pr[-2]恰好等于3,这也是很正常的。因为arr是在栈中,栈中的数据是与你的程序密切相关的。如果是地址向上生长的栈,pr[-2]是你在arr之前定义的局部变量(如果你定义过的话),或者是函数的返回地址,活动记录,C++函数调用头等等(具体是什么,依据你的编译器和代码上下文而定)。而如果是地址向下生长的栈,也可能你的程序曾经用到了pr[-2],并赋给了它确定的值,因为一个线程的任何地方,使用的都是同一个栈空间(至于向上生长还是向下生长,是由操作系统决定的)。所以只要你的程序不变,可能你运行一万次,你从pr[-2]中得到的都是同一个值。而修改这个值则是危险的。因为如果不幸修改了函数的返回地址,或者活动记录中的记录链表指针等等,就会造成程序崩溃;如果修改了其他局部变量,也会造成程序运行错误;只有在向下生长的栈中,修改的是目前已经不用的栈空间,则不会有问题(前提是你的修改发生在栈生长到这个位置之前)。因为,越界的数值不定、操作结果未知,是建立在无须知道底层运行机制的基础上。而这个机制往往是存在的。因此,当你发现数值总是同一个时,请不要感到奇怪。
2. 既然是4字节的整型变量,当然占用4字节的地址空间。所以101,102,103都是100的整型变量的一部分。如果你以一个char指向101,并将它改变,你会发现100所在的整型变量变了。
如:
int arr [5] = {0,1,2,3,4};
char* c = (char*)&(arr[0]);
*(c+1) = 1; // c+1为101
此时arr[0]已发生改变。至于是什么值,则于操作系统的字节序有关。在Windows中,是低字节在前,所以此时arr[0] == 256;在其他一些系统中,往往高字节在前,此时arr[0]=65536
3. switch中的default不是必要的。不少人往往增加仅带一个break;的default段。这是一种编程习惯,这里不讨论它的好坏。但是从语法上来说,它确实不是必要的。
4. while段是在cin返回0时才运行的,即此时cin已经出错了。正如jixingzhong所说,错误标志被设置并保留。这时因为一旦发生了一个错误,不管错误原因时什么,如果不做处理,有可能下面的都是错误的。所以cin不会自己清除错误标志,而是将权利交给程序员。而cin.clear();正是告诉cin,“我会处理这个错误的,请继续吧”。如此cin就可以放心地继续下去了。