【问题标题】:Use array annotation with pointers使用带指针的数组注释
【发布时间】:2018-09-29 06:24:36
【问题描述】:

我目前无法理解以下情况: 我有一个多维字符串数组,我想只使用指针来解决它,但是在指针上使用数组注释时总是会出现分段错误。这只是一个示例代码,我想在 pthread 中使用 3D 数组,所以我想通过结构将它作为指针传递,但它不起作用,我想知道为什么?我认为指针和数组在功能上是等效的?下面是示例代码:

#include <stdio.h>

void func(unsigned char ***ptr);

int main() {
  // Image of dimension 10 times 10
  unsigned char image[10][10][3];
  unsigned char ***ptr = image;

  memcpy(image[0][0], "\120\200\12", 3);

  // This works as expected
  printf("Test: %s", image[0][0]);

  func(image);

  return 0;
}

void func(unsigned char ***ptr) {
  // But here I get a Segmentation Fault but why??
  printf("Ptr: %s", ptr[0][0]);
}

提前感谢您的帮助:)

【问题讨论】:

  • 你不能用函数调用来初始化数组,只能用初始化器的文字列表。
  • 声明数组为数组分配空间,无需调用malloc()。
  • 我需要,因为 10x10 只是实际应用程序中的一个示例,这是动态的,取决于图像尺寸... char[3] 代表每个 0...255 的 RPG 值图像中的像素
  • C允许变长数组,所以可以写char test[height][width][3];

标签: c arrays pointers multidimensional-array


【解决方案1】:

我认为strdup 可能会混淆这个问题。指针和数组不总是等价的。让我尝试演示一下。我总是避免实际的多维数组,所以我在这里可能会犯错误,但是:

int main()
{
  char d3Array[10][10][4]; //creates a 400-byte contiguous memory area 
  char ***d3Pointer; //a pointer to a pointer to a pointer to a char.
  int i,j;
  d3Pointer = malloc(sizeof(char**) * 10);
  for (i = 0; i < 10; ++i)
  {
    d3Pointer[i] = malloc(sizeof(char*) * 10);
    for (j = 0; j < 4; ++j)
    {
      d3Pointer[i][j] = malloc(sizeof(char) * 4);
    }
  }
  //this
  d3Pointer[2][3][1] = 'a';
  //is equivalent to this
  char **d2Pointer = d3Pointer[2];
  char *d1Pointer = d2Pointer[3];
  d1Pointer[1] = 'a';


  d3Array[2][3][1] = 'a';
  //is equivalent to 
  ((char *)d3Array)[(2 * 10 * 4) + (3 * 4) + (1)] = 'a';
}

通常,我使用分层方法。如果我想要连续的记忆,我自己处理数学......就像这样:

  char *psuedo3dArray = malloc(sizeof(char) * 10 * 10 * 4);
  psuedo3dArray[(2 * 10 * 4) + (3 * 4) + (1)] = 'a';

更好的是,我使用像 uthash 这样的集合库。

请注意,正确封装数据会使实际代码非常容易阅读:

typedef unsigned char byte_t;
typedef struct
{
  byte_t r;
  byte_t g;
  byte_t b;
}pixel_t;

typedef struct
{
  int width;
  int height;
  pixel_t * pixelArray;
}screen_t;

pixel_t *getxyPixel(screen_t *pScreen, int x, int y)
{
  return pScreen->pixelArray + (y*pScreen->width) + x;
}

int main()
{
  screen_t myScreen;
  myScreen.width = 1024;
  myScreen.height = 768;
  myScreen.pixelArray = (pixel_t*)malloc(sizeof(pixel_t) * myScreen.height * myScreen.width);
  getxyPixel(&myScreen, 150, 120)->r = 255;
}

【讨论】:

  • 谢谢,这就是我要找的 :) 我只是觉得太复杂了,但实际上只需一个简单的指针就可以解决 :)
  • @tom1991te - 使用struct pixel {byte r;byte g; byte b;} 或任何用于声明结构的 C 垃圾语法,您也可以省去很多麻烦。现在你有一个二维数组......这就是它的真正含义。 (另外,如果编译器可以通过 4/8 字节对齐结构来加快速度,它会....)
【解决方案2】:

在 C 语言中,您应该一次为 2D 数组分配一行空间。您对 test 的定义声明了一个 10 x 10 的 char 指针数组,因此您不需要为它调用 malloc。但是要存储字符串,您需要为字符串分配空间。您对 strcpy 的调用会崩溃。请改用 strdup。编写代码的一种方法如下。

    char ***test = NULL;
    char *ptr = NULL;
    test = malloc(10 * sizeof(char **));
    for (int i = 0; i < 10; i++) {
            test[i] = malloc(10 * sizeof(char *));
    }
    test[0][0] = strdup("abc");
    ptr = test[0][0];

    printf("%s\n", ptr);

    test[4][5] = strdup("efg");
    ptr = test[4][5];

    printf("%s\n", ptr);

或者,如果您想保持 10 x 10 的定义,您可以这样编码:

    char *test[10][10];
    char *ptr = NULL;

    test[0][0] = strdup("abc");
    ptr = test[0][0];

    printf("%s\n", ptr);

    test[4][5] = strdup("efg");
    ptr = test[4][5];

    printf("%s\n", ptr);

【讨论】:

  • 但它实际上是一个3D数组?我有一个大小为 3 的字符数组的二维数组...该数组应该代表一个图像,而这些字符应该代表图像中每个像素的 0 到 255 的 RPG 值
  • @tom1991te char test[10][10][3]; 怎么样?
  • 如果您知道每个字符串只有 3 个字符,那么您可以将声明更改为 chat test [10][10][4]; 添加一个额外的字符来保存空终止符。如果您不需要空终止符,则使用 3 而不是 4。如果您没有空终止符,则不能使用像 strcpy 这样的标准 C 库字符串函数,它希望在结束。
  • @bruceg 是的,我不需要空终止符,因为它们不是真正的字符串,而只是像素的值。但是为什么ptr[0][0] 不起作用?我需要将此数组作为结构参数传递给 pthread,我不能在结构中使用可变长度数组,但必须使用 char ***ptr :)
  • 我认为你崩溃了,因为 ptr 持有 test 的地址。但是,您将无法使用 ptr 遍历您的 3D 数组。不确定你打算用 ptr 做什么。
【解决方案3】:

您的问题是,char[10][10][3] 与char*** 的非常不同:第一个是数组数组的数组,后者是指向指针的指针.出现混淆是因为两者都可以使用相同的语法取消引用。所以,这里有一点解释:

  1. 语法a[b] 只不过是*(a + b) 的简写:首先执行指针运算,然后取消引用结果指针。

    但是,当a 是数组而不是指针时,你怎么能使用a[b]?嗯,因为...

  2. 数组衰减为指向其第一个元素的指针:如果您有一个声明为int array[10] 的数组,则说array + 3 会导致array 衰减为int* 类型的指针。

    但是,这对评估 a[b] 有何帮助?嗯,因为...

  3. 指针运算考虑了目标的大小:表达式array + 3 触发沿(size_t)array + 3*sizeof(*array) 行的计算。在我们的例子中,由 array-pointer-decay 产生的指针指向一个 int,它的大小是 4 个字节。因此,指针以3*4 字节递增。结果是一个指针,指向数组中的第四个int,前三个元素被指针运算跳过。

    请注意,这适用于任何元素类型的数组。数组可以包含字节、整数、浮点数、结构或其他数组。指针算法是一样的。

    但是,这对我们处理多维数组有何帮助?嗯,因为...

  4. 多维数组只是恰好包含数组作为元素的一维数组:当您使用char image[256][512]; 声明一个数组时,您声明的是一个包含 256 个元素的一维数组。这 256 个元素都是 512 个字符的数组。由于sizeof(char) == 1,外部数组的一个元素的大小是512*sizeof(char) = 512,并且由于我们有256这样的数组,image的总大小是256*512。现在,我可以使用char animation[24][256][512];...声明一个 3D 数组...


所以,回到你使用的例子

char image[10][10][3]

当你说image[1][2][1] 时会发生什么:表达式等同于这个:

*(*(*(image + 1) + 2) + 3)
  1. image 的类型为char[10][10][3] 衰减为指向其第一个元素的指针,该元素的类型为char(*)[10][3] 该元素的大小为10*3*1 = 30 字节。

  2. image + 1:执行指针运算以将 1 添加到结果指针,这会将其增加 30 字节。

  3. *(image + 1):指针被取消引用,我们现在直接讨论元素,它的类型是char[10][3]。

  4. 该数组再次衰减为指向其第一个元素的指针,该元素的类型为char(*)[3]。元素的大小为3*1 = 3。 此指针与第 2 步产生的指针指向内存中的相同字节。唯一的区别是,它具有不同的类型!

  5. *(image + 1) + 2:执行指针运算以将2 添加到结果指针,这会将其增加2*3 = 6 字节。连同第 2 步中的增量,我们现在总共有一个 36 字节的偏移量。

  6. *(*(image + 1) + 2):指针被取消引用,我们现在直接讨论元素,它的类型是char[3]。

  7. 该数组再次衰减为指向其第一个元素的指针,该元素的类型为char*。元素的大小现在只是一个字节。 同样,此指针与第 5 步产生的指针具有相同的值,但类型不同。

  8. *(*(image + 1) + 2) + 1:再次进行指针运算,将1*1 = 1字节添加到总偏移量,增加为37字节。

  9. *(*(*(image + 1) + 2) + 1):指针最后一次被取消引用,我们现在讨论的是char在37字节到image的偏移量。


那么,char*** 有什么区别? 当您取消引用 char*** 时,您不会得到任何数组指针衰减。当您尝试使用声明为的变量评估表达式 pointers[1][2][1] 时

char*** pointers;

表达式再次等价于:

*(*(*(pointers + 1) + 2) + 3)
  1. pointers 是一个指针,因此不会发生衰减。它的类型是char***,它指向一个char** 类型的值,它的大小可能是8 字节(假设是64 位系统)。

  2. pointers + 1:执行指针运算以将 1 添加到结果指针,这会将其增加 1*8 = 8 字节。

  3. *(pointers + 1):指针被取消引用,我们现在讨论的是指针值在内存中的8 字节的偏移处找到pointers 指向。

  4. 进一步的步骤取决于实际存储在 pointers[1] 的内容。这些步骤不涉及任何数组指针衰减,而是从内存中加载指针。


你看,char[10][10][3] 和 char*** 之间的区别是巨大的。在第一种情况下,array-pointer-decay 将过程转换为纯偏移计算到多维数组。在后一种情况下,我们在访问元素时反复从内存中加载指针,我们所拥有的只是一维指针数组。这完全取决于指针的类型!

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2012-03-03
    • 1970-01-01
    • 2023-03-21
    • 2013-12-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多