将数组的地址转换为其他数据类型

时间:2013-07-16 13:53:18

标签: c arrays pointers

int main()
{
    char arr[5][7][6];
    char (*p)[5][7][6] = &arr;
    printf("%d\n", (&arr + 1) - &arr);
    printf("%d\n", (char *)(&arr + 1) - (char *)&arr);
    printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr);
    printf("%d\n", (unsigned)(p + 1) - (unsigned)p);

    return 0;
}

当我运行上面的代码时,我得到以下输出:

 1
 210 
 42
 210

为什么每种情况下输出都不是1

3 个答案:

答案 0 :(得分:6)

好吧,如果我想拆分头发:首先,代码在整个printf()语句中调用整个地方的未定义行为。两个指针的区别在于ptrdiff_t,为此,正确的转化说明符为%td,而不是%d

其余的只是猜测。假设您的系统是合理的,并且数字指针值&arr始终是相同的,无论它转换为何种类型。

现在,根据指针算法的规则,(&arr + 1) - &arr是1。 (两个指针之间的实际差异是210 * sizeof(int)字节,但这不是学校数学而是指针算术,这就是为什么结果以大小sizeof(T)为单位给出,其中T是指针的基本类型。)

然后(char *)(&arr + 1) - (char *)&arr将指针投射到char *,由于char的大小为1,这将打印差异以字节为单位; 你是在这里有效地欺骗/滥用指针算术。

此外:printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr)正在减去两个类型为int (*)[7][6]的指针。这就是arr衰变的原因。当然,7 * 6 = 42,因此arr + 1arr之间的大小差异为42个元素。

但是,

p不是指向数组第一个元素的指针,而是指向数组本身的指针。其类型正确表示为int (*)[5][6][7]。现在,如果你使用那个类型来打印差异,但是你不让编译器通过欺骗它来进行划分,指针只是unsigned,那么你将获得{{1这是210。

答案 1 :(得分:5)

(&arr + 1) - &arr

&arr是包含6个char的7个数组的5个数组的数组的地址。如果我们有一个这些对象的数组而不是一个对象的数组,那么添加一个会产生5个7 char数组的5个数组的 next 数组的地址。减去原始地址&arr会产生两个地址之间的差异。根据C标准,此差异表示为两个地址之间的元素数,其中元素类型是指向的对象的类型。由于该类型是由7个6 char数组组成的5个数组的数组,因此两个地址之间的距离是一个元素。换句话说,从&arr(&arr + 1)的距离为一个数组,包含5个7个6 char数组的数组。

(char *)(&arr + 1) - (char *)&arr

&arr + 1再次指向7个6 char数组的5个数组的下一个数组的指针。当它转换为char *时,结果是指向下一个数组的第一个字节的指针。类似地,(char *)&arr是指向第一个数组的第一个字节的指针。然后减去两个指针会产生元素之间的差异。由于这些指针是指向char的指针,因此差异产生为char的数量。所以区别在于7个阵列的阵列中的字节数,其中7个阵列为6 char,即5•7•6 char或210 char

(unsigned)(arr + 1) - (unsigned)arr

由于arr未与&(或sizeof或其他例外情况)一起使用,因此会自动转换为包含7个6个{{1}数组的5个数组的数组指向第一个元素的指针。因此,它是一个指向7个6 char数组的数组的指针。然后char是指向7个6 arr + 1数组的下一个数组的指针。当此地址转换为char时,您正在使用的C实现中的结果实际上是对象的内存地址。 (这很常见,但C标准并不保证,当地址为64位但unsigned为32位时,肯定会中断。​​)同样,unsigned是第一个对象的地址。减去地址后,结果就是它们之间的距离(以字节为单位)。因此,差异是7个6 (unsigned)arr数组的数组中的字节数,即7•6字节或42字节。注意这种情况下的关键区别:char是一个指向由7个6 &arr数组组成的5个数组的数组的指针,但是char是一个指向7个数组6的数组的指针arr

char

(unsigned)(p + 1) - (unsigned)p是一个指向由7个6 p数组组成的5个数组的数组的指针。然后char是指向下一个数组的指针。转换为p+1的行为如上所述。减去产生差异的字节,所以它是由7个6 unsigned数组组成的5个数组的数组的大小,所以它又是210个字节。

暂且不说:

char的类型为(&arr + 1) - &arr,应使用ptrdiff_t打印。

%td的类型为(char *)(&arr + 1) - (char *)&arr,应使用ptrdiff_t打印。

%td的类型为(unsigned)(arr + 1) - (unsigned)arr,应使用unsigned int打印。

%u的类型为(unsigned)(p + 1) - (unsigned)p,应使用unsigned int打印。

答案 2 :(得分:5)

注意&arr是完整的三维char数组的地址,而arr指向第一个元素,即二维char数组。如下图所示:

 0xbf8ce2c6
+------------------+     ◄-- arr  =  0xbf8ce2c6  
|    0xbf8ce2f0    |  
|   +------------------+     ◄-- arr + 1 = 0xbf8ce2f0
|   |   0xbf8ce31a |   |
|   |   +------------------+      ◄-- arr + 2 = 0xbf8ce31a 
|   |   0xbf8ce344 |   |   |
|   |   |   +------------------+      ◄-- arr + 3 = 0xbf8ce344
|   |   0xbf8ce36e |   |   |   |
|   |   |   |  +------------------+      ◄-- arr + 4 = 0xbf8ce36e
|   |   |   |  |   |   |   |   |  |
+---|---|---|--|---+   |   |   |  |  Each are 7*6, 2-Dimensional 
    |   |   |  |       |   |   |  |  Consists Of 42 bytes 
    +---|---|--|-------+   |   |  |  
        |   |  |           |   |  |
        +---|--|-----------+   |  |
            |  |               |  |
            +--|---------------+  |
               |                  |
               +------------------+

 The diagram show: 
 1. How a 3-dimensional can be interpreted as series of 2-dimensional arrays
 2. Here (arr + i) points to a 2-D array 
 3. Notice difference between: (arr + i + 1) - (arr + i) = 0x2a = 42, where i = [0, 4]

&arr的类型是char(*)[5][7][6],它是维度[5][7][6]的字符3D数组的地址。 &arr&arr + 1之间的价值差异为5 * 7 * 6 * sizeof(char) = 210
因为char[5][7][6]的尺寸为5 * 7 * 6 * sizeof(char) 在您的代码&arr中指向3-D数组和&arry + 1下一个3-D数组(我们的代码中不存在)。

codepade处查看此工作代码:

int main()
{
    char arr[5][7][6];
    printf(" &arr  : %p", &arr);
    printf(" &arr+1: %p", &arr + 1);

    return 0;
}

输出:

 &arr  : 0xbf5dd7de
 &arr+1: 0xbf5dd8b0

(&arr + 1) - (&arr) = 0xbf5dd8b0 - 0xbf5dd7de = 0xd2 = 210之间的差异。

在你的第二张照片中:

printf("%d\n", (char *)(&arr + 1) - (char *)&arr);

您将char(*)[5][7][6]类型的地址强制转换为普通(char*),并且因为sizeof char[5][7][6]210,所以两个地址都是210远。 (记住sizeof(char) == 1)。这就是输出的原因:210

正如我在第一句话中所说,arr是第一个元素的地址,它是二维chars数组。 arr的类型为char(*)[7][6]。现在有一个元素(大小为二维的数组6 * 7 * sizeof(char) = 42) (注意:您可以将三维数组视为一维数组,其中每个元素都是一个二维数组)。

在你的第三张照片中:

printf("%d\n", (unsigned)(arr + 1) - (unsigned)arr);

您将类型转换为无符号值(但不转换为地址/指针类型)。 arr + 1arr之间的差异为42 * sizeof(char) = 42(等于char[7][6]的大小)。因此printf语句输出:42

注意:您应该阅读sizeof (int) == sizeof (void*)?,因为您要将地址转换为值。并且此转换尚未完全定义。 (我的解释是你的输出和我给出的输出)。

有关进一步说明,请查看codepade:

下方的工作代码
int main()
{
    char arr[5][7][6];
    printf(" arr  : %p\n", arr);
    printf(" arr+1: %p", arr + 1);

    return 0;
}

输出是:

 arr  : 0xbf48367e
 arr+1: 0xbf4836a8

区分(arr + 1) - (arr) = 0xbf4836a8 - 0xbf48367e = 0x2a = 42

上次打印:

 printf("%d\n", (unsigned)(p + 1) - (unsigned)p);

只需区分&arr+1&arr = 210(类似于第二个printf),因为p是指向3-D char数组的指针(= {{1} })。并且你将它类型化为值类型(不是指针类型)。

此外,(只是为了理解目的添加,我猜读者会发现它有用),

让我们使用sizeof运算符学习&arrarr之间的另一个区别,这将有助于您更深入地理解概念。首先阅读:sizeof Operator

  

&arr运算符应用于数组标识符时,结果是整个数组的大小而不是   由数组标识符表示的指针大小。

codepade处查看此工作代码:

sizeof

其输出:

int main()
{
    char arr[5][7][6];
    printf(" Sizeof(&arr)  : %lu and value &arr: %p\n", sizeof(&arr), &arr);
    printf(" Sizeof(arr)   : %lu and value arr : %p\n", sizeof(arr), arr);
    printf(" Sizeof(arr[0]): %lu and value a[0]: %p\n",sizeof(arr[0]), arr[0]);
    return 0;
}
  • 这里Sizeof(&arr) : 4 and value &arr: 0xbf4d9eda Sizeof(arr) : 210 and value arr : 0xbf4d9eda Sizeof(arr[0]): 42 and value a[0]: 0xbf4d9eda 只是一个地址,系统地址是四字节,这是完整的三维字符数组的地址。

  • &arr是三维数组的名称,arr运算符给出的数组总大小为sizeof

正如我在图中所示210 = 5 * 7 * 6 * sizeof(char)指向的第一个元素是二维数组。因为arr = arr。现在在(arr + 0)使用*取消引用运算符会在地址(arr + 0)处给出值。

  • 请注意*(arr + 0) = arr[0]给出sizeof(arr[0]) = 42。并且这在概念上证明了三维阵列注意到了二维阵列的阵列。

因为在我的回答中,我多次写道:7 * 6 * sizeof(char)的大小是char[5][7][6]。”所以我在@ {{3下面添加了一个有趣的代码}}:

5  * 7 * 6 * sizeof(char)

输出:

int main(){
 printf(" Char         : %lu \n", sizeof(char));
 printf(" Char[5]      : %lu \n", sizeof(char[6]));
 printf(" Char[5][7]   : %lu \n", sizeof(char[7][6]));
 printf(" Char[5][7][6]: %lu \n", sizeof(char[5][7][6]));

 return 1;
}