数组大小调整和重新分配功能

Edg*_*jān 2 c arrays pointers

现在我尝试提高我对Richard Reese的"理解和使用C指针"指针的认识.

这是本书中有关realloc()函数的一个代码示例.

char* getLine(void) {
    const size_t sizeIncrement = 10;
    char* buffer = malloc(sizeIncrement);
    char* currentPosition = buffer;
    size_t maximumLength = sizeIncrement;
    size_t length = 0;
    int character;

    if(currentPosition == NULL) { return NULL; }

    while(1) {
        character = fgetc(stdin);

        if(character == '\n') { break; }

        if(++length >= maximumLength) {
            char *newBuffer = realloc(buffer, maximumLength += sizeIncrement);

            if(newBuffer == NULL) {
                free(buffer);
                return NULL;
            }

            currentPosition = newBuffer + (currentPosition - buffer);
            buffer = newBuffer;
        }

        *currentPosition++ = character;
    }

    *currentPosition = '\0';
    return buffer;
}
Run Code Online (Sandbox Code Playgroud)

主要思想是将所有符号都读到buffer我们见面之前\n.

我们不知道要读取的符号总数,因此使用realloc()函数buffer定期扩展是合理的.

所以,为了扩展buffer我们使用:

char *newBuffer = realloc(buffer, maximumLength += sizeIncrement);
Run Code Online (Sandbox Code Playgroud)

在这种情况下,realloc()返回newBuffer指向扩展缓冲区的指针.

之后,如果realloc()成功调用,currentPosition则重新计算为:

currentPosition = newBuffer + (currentPosition - buffer);
Run Code Online (Sandbox Code Playgroud)

问题:

currentPosition以这种方式重新计算是否有效?

据我所知,realloc()调用buffer指针失效后.(参见,例如,).对buffer指针的任何访问都会导致未定义的行为.那么......我哪里错了?

M.M*_*M.M 6

此代码导致未定义的行为:

currentPosition = newBuffer + (currentPosition - buffer);
Run Code Online (Sandbox Code Playgroud)

传递指针后realloc,该指针变量(以及基于该指针的所有其他指针)变得不确定,这与未初始化变量具有相同的状态.

参考:C11 6.2.4/2:

[...]当指针指向(或刚刚过去)的对象到达其生命周期的末尾时,指针的值变得不确定.

然后,对无效指针执行指针运算会导致未定义的行为,C11 6.5.6/8:

当一个具有整数类型的表达式被添加到指针或从指针中减去时,[...]如果指针操作数和结果都指向同一个数组对象的元素,或者指向数组对象的最后一个元素,则评估不得产生溢出; 否则,行为未定义

指针操作数当时不指向对象.它曾经指向的对象已经被释放.

实际上,完全评估指针可能会导致未定义的行为,因为不确定的值可能是陷阱表示.(想象一下,将值加载到地址寄存器中的系统也会执行硬件检查,该地址属于此过程).参考:C11 3.19.2,6.2.6.1/5:

如果对象的存储值具有这样的表示,并且由不具有字符类型的左值表达式读取,则行为未定义


编写代码的正确方法是:

if(++length >= maximumLength)
{
    size_t currentOffset = currentPosition - buffer;

    char *newBuffer = realloc(......
    // ...

    currentPosition = newBuffer + currentOffset;
    buffer = newBuffer;
}
Run Code Online (Sandbox Code Playgroud)

(就个人而言,我会全程使用偏移,而不是currentPosition完全避免这个问题)

  • @DavidHoelzer C标准不同意你的看法 (4认同)
  • [here](http://www.google.com/patents/US7966480)是针对包含无效指针值的寄存器进行硬件捕获的专利.[here](http://www.lynx.com/using-the-microprocessor-mmu-for-software-protection-in-real-time-systems/)描述*MMU*如何允许同一指针一次有效,稍后无效.C标准允许的另一个有用特性是编译器将释放的指针设置为已知的无效表示(例如"0xCDCDCDCD"); 因此,如果程序继续使用释放的指针,那么很明显发生了什么. (3认同)
  • 我觉得你错了.如果代码*取消引用*指针,那么你会是正确的,但事实并非如此.由于`buffer`*的值不能*在调用中改变(因为它是按值调用),因此保证变量具有缓冲区的原始值.这允许简单的指针算法来确定要添加到新的`newBuffer`内存区域的正确偏移量. (2认同)
  • @CareyGregory C标准根本不保证(见我的第一个引用).例如,编译器可以将`free(p);`转换为对`free`库函数的调用,然后将`p`设置为null(或其他一些对调试有用的值). (2认同)