c - 在C中,是否保证数组起始地址小于其他元素的地址?

标签 c arrays language-lawyer

换句话说当做

index = &array[x] - &array[0];

是否始终保证(根据 C 标准)&array[0] <= &array[x],还是取决于编译器? 与该主题相关的 C 标准章节有哪些?

最佳答案

地址顺序是有保证的。关系运算符的行为在 C11 6.5.8p5 中定义。 :

[...] pointers to array elements with larger subscript values compare greater than pointers to elements of the same array with lower subscript values. [...]

因此 &array[x] >= &array[0]如果 x 始终为真是元素的索引,或者比最大索引大一。 (如果 x 不是元素的索引,或者不是实际数组末尾后的索引,则行为未定义。)

但令人惊讶的是差异 &array[x] - &array[0]定义

  • x是元素的实际索引或大于数组中最大索引的一个 and
  • x不大于 PTRDIFF_MAX

因为有一个特殊的极端情况:C11 6.5.6p9

9 When two pointers are subtracted, both shall point to elements of the same array object, or one past the last element of the array object; the result is the difference of the subscripts of the two array elements. The size of the result is implementation-defined, and its type (a signed integer type) is ptrdiff_t defined in the <stddef.h> header. If the result is not representable in an object of that type, the behavior is undefined. In other words, if the expressions P and Q point to, respectively, the i-th and j-th elements of an array object, the expression (P)-(Q) has the value i-j provided the value fits in an object of type ptrdiff_t.[...]

如果签名ptrdiff_t与无符号 size_t 的宽度相同, 可以有一个存在索引 x 的数组大于 PTRDIFF_MAX ;然后&array[x] >= &array[0]仍然,但是&array[x] - &array[0]具有完全未定义的行为。


这是一个演示。我的计算机是运行 64 位 Ubuntu Linux 的 x86-64,但它也能够运行 32 位程序。在 32 位 X86 Linux + GCC 中,ptrdiff_t是一个 32 位有符号整数,size_t是 32 位无符号整数。在 64 位 Linux 中以 32 位模式运行的程序可以使用 malloc 轻松分配超过 2G 的内存,因为整个 4G 地址空间都保留给用户模式。

#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <stddef.h>

int main(void) {
    size_t size = (size_t)PTRDIFF_MAX + 2;
    size_t x = (size_t)PTRDIFF_MAX + 1;
    char *array = malloc(size);
    if (! array) {
        perror("malloc");
        exit(1);
    }
    array[0] = 42;
    array[x] = 84;
    printf("&array[0]: %p\n", (void *)&array[0]);
    printf("&array[x]: %p\n", (void *)&array[x]);
    printf("&array[x] >= &array[0]: %d\n", &array[x] >= &array[0]);
    printf("&array[x] - &array[1]: %td\n", &array[x] - &array[1]);
    printf("&array[x] - &array[0]: %td\n", &array[x] - &array[0]);
    printf("(&array[x] - &array[0]) < 0: %d\n", (&array[x] - &array[0]) < 0);
}

然后为 32 位模式编译并运行:

% gcc huge.c -m32 -Wall && ./a.out 
&array[0]: 0x77567008
&array[x]: 0xf7567008
&array[x] >= &array[0]: 1
&array[x] - &array[1]: 2147483647
&array[x] - &array[0]: -2147483648
(&array[x] - &array[0]) < 0: 1

内存分配成功,起始地址为0x77558008,&array[x]0xf7504008 , &array[x]大于 &array[0] .区别&array[x] - &array[1]产生了积极的结果,而 &array[x] - &array[0] ,由于其未定义的行为,现在产生了负面结果!

关于c - 在C中,是否保证数组起始地址小于其他元素的地址?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45186243/

相关文章:

c - Arduino - 如何将 char* 复制到 char**?

c - Linux 内核如何处理 TCP/IP 堆栈上的结构填充?

c - SDL2 加载嵌入的二进制文件

algorithm - 在 N×N 二进制矩阵中找到仅包含零的最大矩形

c - 指向 C 中指针的指针

c - 解析一个结构体以从结构体数组中运行

c++ - 模板化转换运算符的重载解析

c - "invalid conversion specification"到底是什么?

c++ - 模块接口(interface)中的内联含义

c++ - 阶乘 - 数组 - C++