为什么memset尽管在内置类型上使用仍引起问题?

时间:2019-01-12 17:28:23

标签: c++ arrays memset built-in-types

我对C++很陌生,正在提交Codeforces上的this问题,突然发现使用memset()导致Wrong answer测试用例。

这是测试用例:

Input:
4 4
3 3 3 5
Participant's output
NO
Jury's answer
YES
1 2 3 4 
Checker comment
wrong answer Jury has the answer but the participant hasn't

代码如下:

#include<iostream>
using namespace std;


int check_if_painted[5010][5010];
int input_array[5010];
int main(){

    int n,k;
    cin>>n>>k;

    int occurence_count[n];//Keeps track of the total no. of occurences of an element in the input_array.
    memset(occurence_count,0,sizeof(occurence_count));
    /*
    The following loop checks if the occurrence of a particular 
    element is not more than k. If the occurence>k the "NO" is printed and program ends.
    */
    for (int i = 0; i < n; ++i)
    {
        cin>>input_array[i];
        ++occurence_count[input_array[i]];
        if(occurence_count[input_array[i]]>k){
            cout<<"NO";
            return 0;
        }
    }
    cout<<"YES\n";


    /*
    The following loop uses the array check_if_painted as a counter to check if the particular 
    occurrence of an element has been painted with a colour from 1 to k or not. 
    If some previous occurrence of this particular element has been painted with f%k+1, 
    then f is incremented until we encounter any value(of `f%k+1`) from 1 to k that hasn't been 
    used yet to colour and then we colour this element with that value by printing it.
    */
    int f=0;//
    /*
    f is a global value which increments to a very large value but f%k+1 is used 
    to restrict it within the 1 to k limit(both inclusive). So, we are able to check 
    if any previous occurrence of the current element has already been coloured with the value f%k+1 or not.  
    which essentially is 1 to k.
    */ 
    for(int i=0;i<n;++i){
        while(check_if_painted[input_array[i]][f%k+1]>0){
            ++f;
        }
        cout<<f%k+1<<" ";
        ++check_if_painted[input_array[i]][f%k+1];
        ++f;
    }
    return 0;
}

但是,当我尝试以下代码时,它成功接受

#include<iostream>
using namespace std;


int check_if_painted[5010][5010];
int input_array[5010];
int occurence_count[5010];
int main(){

    int n,k;
    cin>>n>>k;




    for (int i = 0; i < n; ++i)
    {
        cin>>input_array[i];
        ++occurence_count[input_array[i]];
        if(occurence_count[input_array[i]]>k){
            cout<<"NO";
            return 0;
        }
    }
    cout<<"YES\n";



    int f=0;

    for(int i=0;i<n;++i){
        while(check_if_painted[input_array[i]][f%k+1]>0){
            ++f;
        }
        cout<<f%k+1<<" ";
        ++check_if_painted[input_array[i]][f%k+1];
        ++f;
    }
    return 0;
}

this的SO帖子中,我发现memset在内置类型上效果很好。因此,为什么在int数组(默认类型)上使用它时会导致问题呢?

此外,我还读到std::fill()是更好的选择,并在this帖子中读到memset是一个危险函数,那么为什么它仍在使用?

1 个答案:

答案 0 :(得分:1)

这与memset没有任何关系。您的代码超出了数组的界限,简单明了。

在您的输入情况下,您有n = 4和k = 4,因此occurrence_count长为4个元素(其有效索引从0到3(含0和3))。然后,你做

    cin>>input_array[i];
    ++occurence_count[input_array[i]];

鉴于最后一个值为4,您最终将做++occurence_count[4],它超出了数组的边界。这是未定义的行为,在您的情况下,这表现为不属于该数组的递增内存,该内存很可能不会开始为0并弄乱了以后的检查。

在第二个代码段中未看到该问题,因为您将occurence_count 5010元素设置为大且默认为零初始化,因为它是全局变量。

现在,如果您要计算数组值的出现次数,那么将出现数组的大小确定为与元素数量一样大是错误的-这就是您要读取的数字的数量(这很好大小为input_array),不是是您可以读取的最大值。假设数组元素 values 的范围是1到5000,则出现数组的大小必须为5001(按原样保留值),或者大小为5000(将读取的值减1才能索引该数组) )。

(通常,要小心,因为问题文本中的所有索引都是基于1的,而C ++中的索引都是基于0的;如果对问题索引进行推理,然后将它们用作C索引,则可能会出现一次错误的错误。 ,除非您将数组的尺寸过大且忽略了第0个元素)。


最后,一些补充说明:

  • 如果启用了足够的警告或使用了足够新的编译器进行编译,它会正确地抱怨:memset未定义或被隐式定义(带有错误的原型BTW);您应该#include <string.h>使用memset
  • @Nicol Bolas 做了很长的解释,他在回答中说,当您声明一个仅在运行时才知道大小的本地数组时,您正在使用VLA(可变长度数组)。 {1}}。

    VLA不是标准的C ++,因此没有很好地指定它们,一些编译器不支持它们,并且通常在许多方面都存在问题(大多数情况下,您不应真正分配未知数量的VLA)堆栈上的数据,通常很小);

    您应该避免使用int occurence_count[n],或者考虑到问题为您提供了颜色和元素的上限(5000),只是静态数组。