对 数组越界 和 地址分配 的 思考 和 测试 (提升理解)

简介: 对 数组越界 和 地址分配 的 思考 和 测试 (提升理解)

前言:


今天敲了个埃式筛的求素数的程序,求1000以内素数的个数,犯了低级的数组越界的错误,在这里做个小记录,非常有意思;


给出代码:


#include<bits/stdc++.h>
using namespace std;
const int N = 1e4;
int prime[N];
int cnt,cnts;
void Isprime()
{
  prime[0]=1;
  prime[1]=1;
  for(int i=2;i<=N;i++)
  {
  if(prime[i]==0)
  {
    for(int j=i*2;j<=N;j+=i)
    {
    prime[j]=1;
    }
  }
  }
  for(int i=1;i<=N;i++)
  {
  if(prime[i]==0)
  {
    prime[++cnt]=i;
  }
  }
}
int main()
{
  Isprime();
  for(int i=1;i<=1000;i++)
  {
  if(prime[i]<1000) cnts++;
  }
  cout<<cnts;
}


很经典的一个埃式筛求素数的程序(用了同一个数组做了内存优化),求前一千个数中素数的个数,答案应该是 168个,但是我的程序错误的求出了169个,于是我寻根溯源,不断地进行测试,发现cnt的初始值在函数中埃式筛的核心代码段被改变了,cnt是从 1开始的,而非0;


测试代码:


void Isprime()
{
  prime[0]=1;
  prime[1]=1;
  cout<<cnt<<endl;
  for(int i=2;i<=N;i++)
  {
  if(prime[i]==0)
  {
    for(int j=i*2;j<=N;j+=i)
    {
    prime[j]=1;
    }
  }
  }
  cout<<cnt<<endl;
  for(int i=1;i<=N;i++)
  {
  if(prime[i]==0)
  {
    prime[++cnt]=i;
  }
  }
}


测试输出:


0
1
169


当时我真是觉得离了天下之大谱了,我没有在埃式筛中改变计数器的值,但它自己好像有什么灵性一样自己改变了,我又看了好久并询问了学长学姐,终于找到了原因,罪魁祸首就是数组越界;


素数数组的大小是 N 下标就是 0 ~ N -1 但是我错误的让埃式筛的代码跑到了 N 的 位置,恰好定义的时候先定义的素数数组后定义的cnt计数器,导致 通过prime[N]错误的访问到了cnt的地址 这时恰好 N=1e4不是一个素数,被赋值成 1 ,计数器下标从 1 开始,我使用的又是++cnt计数器从二开始计数,所以多出的一个就找到了;


验证1:


为了证明我们的思想,我们可以输出prime[N-1]的地址和cnt的地址进行验证看他们是否相邻,int类型只要相差四个字节就是相邻


printf("%d\n",&prime[N-1]);
  printf("%d\n",&cnt);


果然他们是相邻的


验证2:


我们前面说 通过prime[N]错误的访问到了cnt的地址 ,我们也可以验证一下这个,给所有不是素数的值赋值一个1212,看cnt的初始值


for(int i=2;i<=N;i++)
  {
  if(prime[i]==0)
  {
    for(int j=i*2;j<=N;j+=i)
    {
    prime[j]=1212;
    }
  }
  }
  cout<<cnt<<endl;


结果:


3673c2fc205a9240fad1118de0255abe_f945dc386b564f088a3a84391e1f905c.png


结语:


当然我们也可以通过很多其他的方法进行验证,这个探索过程让我知道了数组千万不能越界,不然会带来很多乌龙,很多不可思议的错误,闹和我一样的笑话,数组千万不能越界!!!


最后给出正确的程序:(把埃式筛核心程序里的等于号都去掉即可)


**#include<bits/stdc++.h>
using namespace std;
const int N = 1e4;
int prime[N];
int cnt,cnts;
void Isprime()
{
  prime[0]=1;
  prime[1]=1;
  for(int i=2;i<N;i++)
  {
  if(prime[i]==0)
  {
    for(int j=i*2;j<N;j+=i)
    {
    prime[j]=1;
    }
  }
  }
  for(int i=1;i<N;i++)
  {
  if(prime[i]==0)
  {
    prime[++cnt]=i;
  }
  }
}
int main()
{
  Isprime();
  for(int i=1;i<=1000;i++)
  {
  if(prime[i]<1000) cnts++;
  }
  cout<<cnts;
}

以后敲代码要更加细心,加油!


目录
相关文章
|
7月前
|
存储 人工智能 C#
【Unity 3D】C#中数组、集合、栈、队列、哈希表、字典的讲解(附测试代码)
【Unity 3D】C#中数组、集合、栈、队列、哈希表、字典的讲解(附测试代码)
85 0
|
算法 测试技术 C#
C++算法前缀和的应用:分割数组的最大值的原理、源码及测试用例
C++算法前缀和的应用:分割数组的最大值的原理、源码及测试用例
|
3月前
|
Python
numpy | 插入不定长字符数组测试OK
本文介绍了如何在numpy中创建和操作不定长字符数组,包括插入和截断操作的测试。
|
算法 测试技术 C#
C++前缀和算法:生成数组原理、源码及测试用例
C++前缀和算法:生成数组原理、源码及测试用例
|
7月前
蓝桥杯vip测试题系统-数组求和(解题思路以及解题代码,手画思路图虽然丑丑的)
蓝桥杯vip测试题系统-数组求和(解题思路以及解题代码,手画思路图虽然丑丑的)
54 0
|
算法 测试技术 C#
C++前缀和算法的应用:分割数组的最多方案数 原理源码测试用例
C++前缀和算法的应用:分割数组的最多方案数 原理源码测试用例
|
算法 测试技术 C#
C++前缀和算法应用:和至少为 K 的最短子数组的原理、源码及测试用例
C++前缀和算法应用:和至少为 K 的最短子数组的原理、源码及测试用例
|
存储 C语言 C++
【C语言】指针数组测试题(1万字长文)(下)
【C语言】指针数组测试题(1万字长文)(下)
75 0
|
C语言
【C语言】指针数组测试题(1万字长文)(中)
【C语言】指针数组测试题(1万字长文)(中)
94 0
|
存储 C语言
【C语言】指针数组测试题(1万字长文)(上)
【C语言】指针数组测试题(1万字长文)
56 0