【问题标题】:Why can I convert from int ** to int * in the case of the two-dimensional array, but not in the case of the array of pointers为什么我可以在二维数组的情况下从int **转换为int *,而在指针数组的情况下却不行
【发布时间】:2018-04-21 09:03:33
【问题描述】:
#include <stdio.h>
#include <string.h>
int main(void)
{
    int arr2d[3][3] = { {1,2,3},{4,5,6},{7,8,9} };
    int* arr = (int*)arr2d;
    int i = 0;
    for (i = 0; i < 9; i++)
    {
        printf("%d ", arr[i]);
    }

    printf("\n\n");
    int arr1[] = { 11,12 };
    int arr2[] = {13, 14, 15, 16};
    int arr3[] = { 17, 18, 19 };
    int * parr[3] = { arr1,arr2,arr3 };
    int *parr1 = (int*)parr;
    for (i = 0; i < 9; i++)
    {
        printf("%d ", parr1[i]);
    }
    return 0;

一开始我是从二维数组转换并打印出来的,但之后我用指针数组做了它,但它不起作用,有人可以解释一下吗?

【问题讨论】:

标签: c arrays pointers casting int


【解决方案1】:

您可能想了解这些对象在内存中的布局方式:

int array[][3] = { {1,2,3}, {4,5,6}, {7,8,9} };
int *p_array = (int *) array;

这会在内存(在本例中为堆栈)中创建一个类型为 array 的对象 array,该对象以假设的(16 位字长)架构物理布局为:

  array 0x8000: 0x0001
        0x8002: 0x0002
        0x8004: 0x0003
        0x8006: 0x0004
        0x8008: 0x0005
        0x800A: 0x0006
        0x800C: 0x0007
        0x800E: 0x0008
        0x8010: 0x0009
p_array 0x8012: 0x8000

其中0x8000 表示array 的虚构堆栈地址。

但是,在您的第二个示例中:

int array1[] = { 11, 12 };
int array2[] = { 13, 14, 15, 16 };
int array3[] = { 17, 18, 19 };
int *array_pointers[] = { array1, array2, array3 };

int *array_pointers2 = (int*) array_pointers;

这里的array_pointersint *[] 类型,这意味着它是一个指向ints 数组的指针。内存布局如下:

array1 0x8000: 0x000B
       0x8002: 0x000C
array2 0x8004: 0x000D
       0x8006: 0x000E
       0x8008: 0x000F
       0x800A: 0x0010
array3 0x800C: 0x0011
       0x800E: 0x0012
       0x8010: 0x0013

array_pointers:  0x8012: 0x8000 address of array1
                 0x8014: 0x8004 address of array2
                 0x8016: 0x800C address of array3
array_pointers2: 0x8018: 0x8012 address of array_pointers

因此,当您尝试打印 array_pointers2 的内容(现在包含指向 ints 数组的指针)时,您将打印地址值而不是它们指向的数字。

这是一个输出这些对象的内存地址的示例:

https://ideone.com/T52Ow0

哪些打印:

array      0x7ffeda682090: 0x00000000000001
           0x7ffeda682094: 0x00000000000002
           0x7ffeda682098: 0x00000000000003
           0x7ffeda68209c: 0x00000000000004
           0x7ffeda6820a0: 0x00000000000005
           0x7ffeda6820a4: 0x00000000000006
           0x7ffeda6820a8: 0x00000000000007
           0x7ffeda6820ac: 0x00000000000008
           0x7ffeda6820b0: 0x00000000000009
p_array    0x7ffeda682038: 0x007ffeda682090

array1     0x7ffeda68204c: 0x0000000000000b
           0x7ffeda682050: 0x0000000000000c
array2     0x7ffeda682060: 0x0000000000000d
           0x7ffeda682064: 0x0000000000000e
           0x7ffeda682068: 0x0000000000000f
           0x7ffeda68206c: 0x00000000000010
array3     0x7ffeda682054: 0x00000000000011
           0x7ffeda682058: 0x00000000000012
           0x7ffeda68205c: 0x00000000000013
array_poin 0x7ffeda682070: 0x007ffeda68204c
           0x7ffeda682078: 0x007ffeda682060
           0x7ffeda682080: 0x007ffeda682054
array_poi2 0x7ffeda682040: 0x007ffeda682070

在我的电脑上。

外部链接过期时的源代码:

#include <stdio.h>
#include <string.h>

int main(void)
{
    int array[][3] = { {1,2,3}, {4,5,6}, {7,8,9} };
    int *p_array = (int*) array;

    for (int i = 0; i < 9; i++) {
        printf("%-10s %08p: %016p\n", i == 0 ? "array" : "", &p_array[i], p_array[i]);
    }

    printf("%-10s %08p: %016p\n", "p_array", &p_array, p_array);

    int array1[] = { 11, 12 };
    int array2[] = { 13, 14, 15, 16 };
    int array3[] = { 17, 18, 19 };

    int *array_pointers[] = { array1, array2, array3 };
    int *array_pointers2 = (int*) array_pointers;

    putchar('\n');

    for (int i = 0; i < sizeof(array1) / sizeof(array1[0]); i++) {
        printf("%-10s %08p: %016p\n", i == 0 ? "array1" : "", &array1[i], array1[i]);
    }

    for (int i = 0; i < sizeof(array2) / sizeof(array2[0]); i++) {
        printf("%-10s %08p: %016p\n", i == 0 ? "array2" : "", &array2[i], array2[i]);
    }

    for (int i = 0; i < sizeof(array3) / sizeof(array3[0]); i++) {
        printf("%-10s %08p: %016p\n", i == 0 ? "array3" : "", &array3[i], array3[i]);
    }

    for (int i = 0; i < sizeof(array_pointers) / sizeof(array_pointers[0]); i++) {
        printf("%-10s %08p: %016p\n", i == 0 ? "array_poin" : "", &array_pointers[i], array_pointers[i]);
    }

    printf("%-10s %08p: %016p\n", "array_poi2", &array_pointers2, array_pointers2);

    return 0;
}

【讨论】:

    【解决方案2】:

    您不能像使用arr2d 这样的连续整数数组那样简单地将int * 用于从0 到N 的单个循环,因为:

    1. 指针在内存中可能相距很远
    2. 您需要取消对每个指针的引用,而 int * 无法做到这一点,因为 int * 会将 A[i] 解释为 int,而不是指针。

    要了解如何修复它,您可能需要了解事物在内存中的布局方式。让我们从一个简单的开始:您的arr2d 声明。假设是一个 2 字节的 int,它的外观如下:

    arr2d @ 0x8000
     =======================
    |  arr2d[0] @ 0x8000    |
    |   -----------------   |
    |  | 1      2      3 |  |
    |   -----------------   |
    |  @8000  @8002  @8004  |
    |                       |
    |  arr2d[1] @ 0x8006    |
    |   -----------------   |
    |  | 4      5      6 |  |
    |   -----------------   |
    |  @8006  @8008  @800A  |
    |                       |
    |  arr2d[0] @ 0x800C    |
    |   -----------------   |
    |  | 7      8      9 |  |
    |   -----------------   |
    |  @800C  @800E  @8010  |
     =======================
    

    请注意从一项到下一项的内存地址是如何全部相隔 2 个字节的?这就是您可以执行int* arr = (int*)arr2d 并打印arr[i] 以获得所需值的原因之一。另一个原因是您存储的是整数。

    现在让我们看看parrarr1arr2arr3

    arr1 @ 0xA000
     ----------
    | 11    12 |
     ----------
    @A000  @A002
    
    arr2 @ 0xB000
     -------------------------
    | 13     14     15     16 |
     -------------------------
    @B000  @B002  @B004  @B006
    
    arr3 @ 0xC000
     ------------------
    | 17     18     19 |
     ------------------
    @C000  @C002  @C004
    
    parr @ 0xE000
     ------------------------
    | @A000   @B000   @C000  |
    | (arr1)  (arr2)  (arr3) |
     ------------------------
      @E000   @E004   @E008
    

    还记得我说过arr2d 存储整数吗?好吧,parr 存储指针,所以事情的工作方式有点不同。一方面,您需要使用* 运算符取消引用指针,但您使用的是int *parr1,因此parr1[i] 导致指针被错误地解释为整数。另请注意,每个数组中整数的地址增加 2,而指针 (@E000..@E008) 的地址增加 4。换句话说,您将 4 字节指针解释为 2 字节整数,如果您的系统对intint * 使用不同的大小,这会使您的问题变得更糟。如果您改为声明int **parr1 = parr 并使用*parr1[i] 进行打印,情况会好很多。

    但是,这只会打印每个数组中的第一个值。这是因为您要存储指向每个数组中第一项的指针(因为 数组衰减arr1 变成 &amp;arr1[0]arr2 变成 &amp;arr2[0]arr3 变成 &amp;arr3[0] )。指针没有元素计数,因为它们不是数组,并且由于您存储的是指向单个 int 的指针而不是指向数组的指针,因此无论如何您实际上都在删除元素计数。您需要自己跟踪每个数组的元素计数,也许在另一个数组中。以下是一些修改后的代码,可以解决您的问题:

    // Only works for real arrays, not pointers returned by malloc/calloc.
    #define ARRAY_COUNT(a) (sizeof (a) / sizeof (a)[0])
    int arr1[] = { 11,12 };
    int arr2[] = {13, 14, 15, 16};
    int arr3[] = { 17, 18, 19 };
    int * parr[3] = { arr1,arr2,arr3 };
    int **parr1 = (int*)parr;
    int counts[] = { ARRAY_COUNT(arr1), ARRAY_COUNT(arr2), ARRAY_COUNT(arr3) };
    for (i = 0; i < ARRAY_COUNT(counts); i++)
    {
        for (j = 0; j < counts[i]; j++)
        {
            printf("%d ", parr1[i][j]);
        }
    }
    

    请注意,我使用了两个循环:1 个用于计数,1 个用于每个数组的内容。如果你真的想要,你可以合并这两个循环,但遵循逻辑有点困难:

    i = 0;
    j = 0;
    while (i < ARRAY_COUNT(counts))
    {
        if (j < counts[i]) {
            printf("%d ", parr1[i][j]);
            j++;
        } else {
            i++;
            j = 0;
        }
    }
    

    【讨论】:

      猜你喜欢
      • 2021-07-25
      • 1970-01-01
      • 2021-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-06
      相关资源
      最近更新 更多