1

这是我的 C++ 程序,它接受 2D 数组a[m][n]。如果一个元素a[i][j]为零,则将所有ith行和jth列元素设置为零。

这是代码示例:

#include <iostream>
#include <cstdlib>
#include <ctime>

using namespace std;

class SetZero{
public:
    static void setZero(int **, int , int);
};

void SetZero::setZero(int ** a, int m, int n){
    int i, j, k;
    int ** b = new int *[m]; //flags to identify whether set to zero or not.

    for(i = 0; i < m; i++){
        b[i] = new int[n];
        for(j = 0; j < n; j++)
            b[i][j] = 1;
    }

    for(i = 0; i < m; i++)
        for(j = 0; j < n; j++)
            if(a[i][j] == 0 && b[i][j]){//DUMP here. If I change it to (a+i)[j], then works.
                for (k = 0; k < n; k++){
                    a[i][k] = 0;//but there is NO dump here. Weird!
                    b[i][k] = 0;
                }
                for(k = 0; k < m; k++){
                    a[k][j] = 0;
                    b[k][j] = 0;
                }
                j = n;//break. next row loop.
            }

    for(int i = 0; i < m; i++)
        delete[] b[i];
    delete[] b;
}

int main(){
    int a[4][5];

    srand(time(NULL));
    for(int i = 0; i < 4; i++){//create an 2D array
        for(int j = 0; j < 5; j++){
            a[i][j] = rand() % 100;
            cout << a[i][j] << " ";
        }
        cout << endl;
    }

    SetZero::setZero((int **)a, 4, 5);//type cast.

    cout << endl;
    for(int i = 0; i < 4; i++){//print result
        for(int j = 0; j < 5; j++)
            cout << a[i][j] << " ";
        cout << endl;
    }

    return 0;
}

环境:WIN8 Visual Studio 2012。

编辑: 程序可以编译但不能正常执行。到达时会停止 if(a[i][j] == 0 && b[i][j]){

错误信息是:

CCLC.exe 中 0x012875DD 处的未处理异常:0xC0000005:访问冲突读取位置 0x0000004B。

4

2 回答 2

2
SetZero::setZero((int **)a, 4, 5)

a不是指针数组,它只是一个二维数组。

注意访问冲突是如何读取地址 0x0000004B 的?那是 75,一个介于 0 和 99 之间的数字 :) 因为您将二维数组(它只是一个具有简洁访问方式的一维数组)视为数组数组,它​​采用了您的值中的一个数组(75)作为子数组的地址,然后尝试读取地址 75(或 0x0000004B)处不存在的数组

我建议您将数组“展平”并将它们作为一维数组使用,我觉得这更简单:

void SetZero::setZero(int * a, int m, int n){
int i, j, k;
int * b = new int [m*n]; //flags to identify whether set to zero or not.

for(i = 0; i < m; i++){
    b[i] = new int[n];
    for(j = 0; j < n; j++)
        b[i*n+j] = 1;
}

for(i = 0; i < m; i++)
    for(j = 0; j < n; j++)
        if(a[i*n+j] == 0 && b[i*n+j]){//DUMP here. If I change it to (a+i)[j], then works.
            for (k = 0; k < n; k++){
                a[i*n+k] = 0;//but there is NO dump here. Weird!
                b[i*n+k] = 0;
            }
            for(k = 0; k < m; k++){
                a[k*n+j] = 0;
                b[k*n+j] = 0;
            }
            j = n;//break. next row loop.
        }


delete[] b;
}

int main(){
int a[4*5];

srand(time(NULL));
for(int i = 0; i < 4; i++){//create an 2D array
    for(int j = 0; j < 5; j++){
        a[i*5+j] = rand() % 100;
        cout << a[i*5+j] << " ";
    }
    cout << endl;
}

SetZero::setZero(a, 4, 5);//type cast.

cout << endl;
for(int i = 0; i < 4; i++){//print result
    for(int j = 0; j < 5; j++)
        cout << a[i*5+j] << " ";
    cout << endl;
}

return 0;
}
于 2013-06-13T04:42:29.023 回答
1

关于 SetZero() 的一项建议。有一个名为memset()的函数,它允许您将所有字节设置为给定起始指针和范围的特定值。这个函数可以让你的 SetZero() 函数更干净:


void * memset ( void * ptr, int value, size_t num );

填充内存块。将指向的内存块的第一个num字节设置ptr为指定的value(解释为无符号字符)。

参数

  • ptr:指向要填充的内存块的指针。
  • value:要设置的值。该值作为 int 传递,但该函数使用该值的无符号字符转换填充内存块。
  • num:要设置值的字节数,size_t 是无符号整数类型。

例如,您的程序中的以下代码块:

 for (k = 0; k < n; k++){
   a[i][k] = 0;//but there is NO dump here. Weird!
   b[i][k] = 0;
 }

可以通过memset更清洁的方式实现:

 memset(a[i], 0, n * sizeof(int));
 memset(b[i], 0, n * sizeof(int));
于 2013-06-13T04:23:25.710 回答