【问题标题】:c style string and dynamic allocation in c++c ++中的c样式字符串和动态分配
【发布时间】:2013-05-04 12:24:31
【问题描述】:

我必须实现一组分配、修改和释放 c 样式字符串的二维数组的方法。我不能使用字符串、向量或任何 stl 容器。

getNewMat:

char*** getNewMat(int w, int h){
    char*** newMat = new char**[h];
    for(int i = 0 ; i < h ; i++){
        newMat[i] = new char*[w];
        for(int j = 0 ; j < w ; j++)
            newMat[i][j] = NULL;
    }
    return newMat;
}

填充垫

void fillMat(char***mat, int x, int y, char* newEl){
    mat[y][x] = newEl; //this will produce a segfault (even with good index)
}

表演垫:

void showMat(char*** mat, int w, int h){
    for(int i = 0 ; i < h ; i++){
        for(int j = 0 ; j  < w ; j++)
            cout << mat[i][j];  
    }
    cout << endl;
}

那么,谁能告诉我这是怎么回事?

【问题讨论】:

  • showMat 中,您使用h 作为两个循环的限制。 j 循环应该使用 w
  • 更好的解决方案,我可以在这里看到:您应该实现自己的类string 并使用它而不是char*
  • 这是一种可能性,但无论如何我想知道为什么这不起作用,因为理解本身(另外我担心这表明缺乏对基本机制的理解)

标签: c++ c multidimensional-array


【解决方案1】:

在您的 fillMat 方法中,您可以这样做:

mat[y][x] = newEl;

其中xy 是数组的两个等级的维度。该行将导致分段错误,因为您超出了数组的范围。 mat 的索引从 0 到 length - 1 并且由 xy 设置将 1 超出数组边界。

也许您打算循环并设置它们:

for (int i = 0; i < y; ++i)
{
     for (int k = 0; k < x; ++k)
         mat[i][k] = newEl;
}

此外,在你的 showMat 函数中,你有这个:

cout << showMat[i][j];  

我认为你的意思是mat

cout << mat[i][j];  

【讨论】:

  • x, y 不是 mat 的宽度和高度,而是进行分配的索引,即使 x, y = 0, 0,此行也会产生段错误
  • 我并不是要循环或其他任何事情,只是为了分配一个值,但你是正确的 cout,我将编辑我的代码
【解决方案2】:
  1. newMat[i][j] = NULL - 这是个坏主意。在showMat 中,您将尝试取消引用 NULL 指针 - 这是 UB,可能会导致段错误。

  2. char* - 它不是字符串 - 它只是一个指向 char 的指针,可能指向内存位置,可以是字符串的开头。如果你想像处理字符串一样使用它,你也应该为它分配内存。

  3. mat[y][x] = newEl - 这也是个坏主意。正如我已经说过的,char* 不是字符串,因此,您不能只使用赋值运算符将数据从一个 C-string 复制到另一个。您应该使用std::copystd::strncpy

  4. 使用后不要忘记释放分配的内存。

您应该实现自己的 string 类 - 这是更好的解决方案,我可以在那里看到。至少,因为它更简单,更容易理解。

【讨论】:

  • char *x = something; x 可以包含字符串,y:unsigned int y = something; 也可以。我想你的意思是说“它不是一个字符串。它可以指向一个字符串,但它没有(因为你将它设置为NULL)。”
  • @undefinedbehaviour,我的意思是,x 可能指向内存位置,其中可以是字符串的开头。但它仍然是一个指针。不是字符串。是您要问的问题,还是我错过了什么?
  • 听起来更好。 char *x; strncpy((char *) &amp;x, "", sizeof x); x 可能包含一个字符串,但它极不可能指向一个。
【解决方案3】:

我必须实现一堆方法来分配、修改和释放一个 c 风格字符串的二维数组。 ...snip...所以,谁能告诉我 这有什么问题?

“c 样式字符串”不是类型。它是类型中数据的表示。 '\0' 终止的字符串通常存储在 char 数组中,但您也可以轻松地将其存储在 unsigned int 数组中。例如:

unsigned int message[32] = { 0 };
strcpy((char *) message, "Hello, world!");
printf("%s\n", (char *) message);

我不鼓励这样的编程,但好处可能看起来微乐观。也可以将字符串存储在不是数组的东西中。考虑char 可能适合存储空字符串:

char x = '\0';
printf("%s\n", &x);

当您说“c 样式字符串的二维数组”时,可以合理地假设您的意思是“char 数组的数组数组”。让我们朝着这个方向前进。


我对 C++ 知之甚少,但有一个数组属性列表,您在尝试模仿 实际数组 时可能没有考虑过这些属性。我将使用assertions 来总结这些:

#define x 7
#define y 13
#define z 1
char foo[x][y][z] = { 0 };
assert((char *) foo == foo[0]);
assert(sizeof foo == (char *) &foo[1] - (char *) &foo[0]);
assert(sizeof foo == x * y * z);

我不确定您是否能够通过在 C++ 中传递的这些 assertions 中的任何一个来解决您的问题,但我愿意接受其他人的任何输入,以提示您可能如何...... .

数组是连续的。这意味着newMat[x] + wnewMat[x+1],对于0 .. h-1 中的x 值。在您的代码中,这不是现实,因为您分别分配了newMat[x]newMat[x+1]。同样,预计newMay[0][y] == newMat[0][y+1] + n,其中n 是您的字符串 的最大长度。这在使用通用数组排序算法时可能会出现问题,因为它们可能依赖于您的 数组 是连续的。

最接近解决此问题的方法似乎是每个维度只分配一次,而不是 h 为第一个维度分配时间,w 为第二个维度分配时间。这看起来像这样:

char ***getNewMat(size_t w, size_t h, size_t n){
    char ***newMat = new char **[h];
    newMat[0] = new char *[h*w];
    newMat[0][0] = new char[h*w*n];

    for(size_t i = 0; i < h; i++){
        newMat[i] = newMat[0] + i * w;
        for (size_t j = 0; j < w; j++) {
            newMat[i][j] = newMat[0][0] + i * w * n + j * n;
        }
    }

    return newMat;
}

数组连续的一个副作用是您不能分配 C 风格的字符串,只需将数组中的指针更改为指向不同的位置。指针是从 数组表达式 转换为不是左值的 指针表达式 的结果。正如我之前所说,我对 C++ 了解不多,但是在 C 中这意味着以下代码无法编译:

char foo[x][y][z] = { 0 };
foo[a][b] = "hello";

但是,以下代码可以编译:

char *foo[x][y] = { 0 };
foo[a][b] = "hello";

前者可能构成一个 C 风格的字符串数组,但后者不能,因为我们已经介绍了邻接规则,而且它从大多数指向 NULL 的元素开始,a指针不能指向任何东西,更不用说字符串了。您可以执行一些运算符重载魔术来允许前者编译。我也愿意提供任何正确方向的提示,以便在此处为 OP 提供示例。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-10-09
    • 1970-01-01
    • 1970-01-01
    • 2013-03-09
    • 2018-01-29
    • 1970-01-01
    • 2011-10-21
    • 1970-01-01
    相关资源
    最近更新 更多