【C/C++踩坑记录】memset(...)初始化二维数组的注意事项

【C/C++踩坑记录】memset()初始化二维数组的注意事项

UVa232 Crossword Answers时遇到的坑,折腾了我一下午QAQ

为了后续的解释分析,这里我把踩坑处的核心问题简化如下:给定一个固定大小的 3 × 3 3\times 3 3×3整型二维数组 A A A,有很多轮测试用例,输入均为二维数组,且维度 n × m n\times m n×m始终不超过 3 × 3 3\times 3 3×3,因此我们有时候会用 A A A作为该问题的辅助数组,在每轮测试中重复利用。

假设某一轮的测试中, n = 3 , m = 1 n=3,m=1 n=3,m=1,我们需要将 A A A 3 × 1 3\times 1 3×1子块置零,这时我们经常会用到两种方法:
方法一:
for (int i=0; i<n; ++i) for (int j=0; j<m; ++j) A[i][j]=0;

方法二:
memset(A, 0, sizeof(int)*n*m);

如果看到这里你没有疑惑的话,那么恭喜你!可以继续往下看了(Doge)

实际上,这两个方法中有一个是,没发现的读者可以停下来自行思考一下。

首先简单介绍下memset(),它在string.h中,函数原型和解释如下:

void *memset( void *buffer, int ch, size_t count );

函数解释:拷贝chbuffer 从头开始的count 个字符里, 并返回buffer指针。

注意!该函数是对buffer首地址开始的一段连续的、字节数为count的内存进行整体赋值。

我们回想一下C/C++中的二维数组,它是以行序为主方式进行存储的。而内存单元构成的存储空间是一个一维的线性空间,这意味二维数组的数据是一行接一行地存储的。

因此,方法二的函数功能可以描述为:将二维数组A的前n*m个元素置零,对比问题需求:“将A的 n × m n\times m n×m子块置零”可知以上两者并不等价,因为A的 n × m n\times m n×m子块元素按行序往往不连续

下面举个简单的例子,直观地比较上述的不同之处:

A = ( 1 2 3 4 5 6 7 8 9 ) A=\begin{pmatrix}1 &2& 3\\ 4& 5& 6\\7& 8& 9\end{pmatrix} A=147258369,将 A A A的子块 n × m n\times m n×m置零,若 n = 2 , m = 2 n=2,m=2 n=2,m=2,则方法一操作后的结果为:

A 1 = ( 0 0 3 0 0 6 7 8 9 ) A_1=\begin{pmatrix}0 &0& 3\\ 0& 0& 6\\7& 8& 9\end{pmatrix} A1=007008369

方法二操作后的结果为:

A 2 = ( 0 0 0 0 5 6 7 8 9 ) A_2=\begin{pmatrix}0 &0& 0\\ 0& 5& 6\\7& 8& 9\end{pmatrix} A2=007058069

A 1 ≠ A 2 A_1 \neq A_2 A1=A2

解决方案:要么采用方法一对二维数组的子块置零,要么对整个数组置零:memset(A, 0, sizeof(A));(毕竟对于本轮测试用例,上一轮的计算结果都没有意义了,可以全部置零)。

说实话,犯这种错误还花了大量时间纠错真的很蠢,但有时也不可避免。这提醒我:重复使用某个数组时,一定要留个心眼,确保初始状态满足要求

最后吐槽一句,UVaOJ对于输入输出的格式要求也太严格而诡异了。。。
欢迎批评指正,希望对大家有所帮助~

猜你喜欢

转载自blog.csdn.net/weixin_42430021/article/details/121775402