【问题标题】:Passing an array and its length with a struct in C weird behavior使用 C 奇怪行为中的结构传递数组及其长度
【发布时间】:2014-09-10 01:19:13
【问题描述】:

我正在尝试将一个数组及其长度从一个函数返回到另一个函数。我了解到,我不能简单地使用 sizeof() 确定父函数中返回数组的大小,因为数组在作为参数传递时会简化为指针。我发现我可以use a struct 将数组指针和大小返回给父函数,这就是我正在尝试的。

数组是 unsigned char* 类型,因为我正在处理内存地址。数组在发送到结构之前看起来很好。一旦我尝试通过父函数中的结构引用数组,所有内存地址都会被拆分。我有一种预感,结构填充可能是问题所在。

我已经写了一些打印来真正分析发生了什么。

struct arrayPasser{
    int length;
    unsigned char *arr;
};

unsigned char *arr[arrLength]; /*array of pointers*/

/*filling out the array with addresses*/

struct arrayPasser pass;

pass.length=arrLength;
pass.arr=&arr;

printf("pass.arr: %#x\n", pass.arr); /*print array location*/

printf("*(&arr): %#x\n", *(&arr));
printf("**(&arr): %#x\n", **(&arr));

/*original array*/
printf("arr[0] %#x\n", arr[0]);     
printf("arr[1] %#x\n", arr[1]);
printf("arr[2] %#x\n", arr[2];

/*referencing array through struct*/
printf("0 offset: %#x\n", *pass.arr);
printf("1 offset: %#x\n", *(pass.arr+1));
printf("2 offset: %#x\n", *(pass.arr+2));
printf("3 offset: %#x\n", *(pass.arr+3);
printf("4 offset: %#x\n", *(pass.arr+4));
printf("5 offset: %#x\n", *(pass.arr+5));
printf("6 offset: %#x\n", *(pass.arr+6));
printf("7 offset: %#x\n", *(pass.arr+7));

printf("pass.arr[0]: %#x\n", pass.arr[0]);
printf("pass.arr[1]: %#x\n", pass.arr[1]);
printf("pass.arr[2]: %#x\n", pass.arr[2]);
printf("pass.arr[3]: %#x\n", pass.arr[3]);
printf("pass.arr[4]: %#x\n", pass.arr[4]);
printf("pass.arr[5]: %#x\n", pass.arr[5]);
printf("pass.arr[6]: %#x\n", pass.arr[6]);
printf("pass.arr[7]: %#x\n", pass.arr[7]);

输出(注意 arr[0] 是一个完整的 32 位地址,但 pass.arr[0] 只是 32 位地址的一个字节):

arr[0] 0x4affb000
arr[1] 0x4affd000
arr[2] 0x4affc000
pass.arr: 0x58dcf10
*(&arr): 0x58dcf10
**(&arr): 0x4affb000
0 offset: 0
1 offset: 0xb0
2 offset: 0xff
3 offset: 0x4a
4 offset: 0
5 offset: 0xd0
6 offset: 0xff
7 offset: 0x4a
pass.arr[0]: 0
pass.arr[1]: 0xb0
pass.arr[2]: 0xff
pass.arr[3]: 0x4a
pass.arr[4]: 0
pass.arr[5]: 0xd0
pass.arr[6]: 0xff
pass.arr[7]: 0x4a

【问题讨论】:

  • &arr 不是unsigned char *
  • 我只是在将其发送到结构之前将其类型转换为 unsigned char *,并且它保留了它的正确值,所以我不确定这是问题的根源,但很高兴知道。跨度>
  • 演员阵容毫无意义。只需使用arr
  • 你不能用unsigned char *arr; 表示一个指针数组。这表示一个 char 数组(或一个简单的 char 指针)。
  • 我使用 unsigned char * 是因为我在嵌入式系统上使用原始内存,我想我会接受 this question 的建议。

标签: c arrays pointers memory struct


【解决方案1】:

unsigned char *arr[arrLength]是一个指针数组,但是由于数组本质上是指针,所以变量arr指向数组中的第一个指针,所以它等价于unsigned char **arr

但是unsigned char *arr 是指向char 的指针,而不是指向指针的指针。换句话说,它是char 的数组,而不是指针数组。

将你的结构声明为:

struct arrayPasser{
    int length;
    unsigned char **arr;
};

当您取消引用 arr 时,您将得到一个指针而不是 char。

让我试着解释一下为什么你的代码不起作用。考虑这个数组:

unsigned char *arr[3]
arr[0] = 0x4affb000;
arr[1] = 0x4affd000;
arr[2] = 0x4affc000;

当你这样做时,你输入的数据会像这样放在内存中:

00 b0 ff 4a   00 d0 ff 4a   00 c0 ff 4a
^             ^             ^
|             |             |
arr           arr + 1       arr + 2
points here   points here   points here

存储在内存中的整数字节序很小,所以它们的字节是相反的。

当你执行arr[0] 时,你会得到0x4affb000,因为 sizeof(arr[0]) 是 4,因此你得到 4 个字节。

然后,你这样做:

unsigned char *arr2 = arr;  /* which is equal to arr2 = &arr */

内存没有改变,但是指针的行为方式改变了。现在它是指向char 的指针,而不是指向指针的指针,因此指针引用的对象的大小现在是1 而不是4。换句话说,arrarr2 都指向同一个内存位置,但 arr 元素的大小为 4,arr2 元素的大小为 1。

这是arr2 获得的:

00 b0 ff 4a   00 d0 ff 4a   00 c0 ff 4a
^  ^  ^  ^
|  |  |  +--arr2 + 3
|  |  +--arr2 + 2
|  +--arr2 + 1
+--arr2

现在,当您执行 arr2[0] 时,您会得到 0x00,因为 sizeof(arr2[0]) 是 1 个字节。

【讨论】:

  • 这让我头疼,但我想这是有道理的,它解决了我的问题。谢谢!
  • 指针和数组完全不同。当在表达式中使用数组的名称时,它(有一些例外)被转换为指针,但这使它成为指针。
  • 确实,当我说数组几乎是指针时,我很兴奋,抱歉。我不得不说它们在某些操作子集(访问元素和取消引用指针)中是等价的。
  • 数组本质上不是指针。尝试将数组视为本质上的指针是令人困惑的。事实上,这是 OP 在原始问题中混淆的根源。
【解决方案2】:

(显然,输出来自与您发布的程序不同的程序,但没关系。)

pass.length=arrLength;
pass.arr=&arr;

您正在尝试将 指向数组的指针 转换为 指向无符号字符的指针。这两种类型不兼容,因此您需要将&arr 转换为正确的类型。但这不是你想要的。见下文。

printf("pass.arr: %#x\n", pass.arr); /*print array location*/

注意:%x 转换说明符需要 unsigned int 类型的参数。您应该将 pass.arr 转换为 unsigned int 或将其转换为 (void *) 并使用 %p 说明符打印。

printf("*(&arr): %#x\n", *(&arr));

arr 传递给printf()。这是指向arr 数组的第一个元素的指针。

printf("**(&arr): %#x\n", **(&arr));

*arr 传递给printf()。这是存储在arr 数组中的第一个指针,例如。 arr[0].

printf("arr[0] %#x\n", arr[0]);     
printf("arr[1] %#x\n", arr[1]);
printf("arr[2] %#x\n", arr[2]); /* Added missing ')'. */

将存储在arr 中的前三个指针传递给printf()

printf("0 offset: %#x\n", *pass.arr);
printf("1 offset: %#x\n", *(pass.arr+1));
printf("2 offset: %#x\n", *(pass.arr+2));
printf("3 offset: %#x\n", *(pass.arr+3);

pass.arr 指向的前四个元素传递给printf()pass.arr 指向的元素是 unsigned char 类型,而不是 unsigned char *。由于 unsigned char 被传递给可变参数函数,因此它将被提升为 int(或在不明显的情况下为 unsigned int),这很可能会被很好地打印出来。

地址似乎被拆分的原因是arr(指针)的内容现在被重新解释为通过pass.arr 的字符数组。

printf("pass.arr[0]: %#x\n", pass.arr[0]);
printf("pass.arr[1]: %#x\n", pass.arr[1]);
printf("pass.arr[2]: %#x\n", pass.arr[2]);
printf("pass.arr[3]: %#x\n", pass.arr[3]);

和上面的完全一样。 A[B] 表示法(大部分)等同于 *((A)+(B))

如果您想通过struct arrayPasser 返回指向arr 的指针,则需要将指针 传递给指针,而不仅仅是指针。将您的结构更改为:

struct arrayPasser{
    int length;
    unsigned char **arr;
};

并将指向arr的第一个元素的指针分配给它:

pass.arr = arr;

正如我在上面解释的,您不能将&arr 分配给pass.arr,即使指针 恰好完全相同。这两个指针具有完全不同的类型

显然您认为这个主题还不够令人困惑,因此您直接将两个不同的标识符命名为相同的名称。 :-)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多