数组大小调整和realloc函数

数组大小调整和realloc函数,c,arrays,pointers,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;

现在我试图通过阅读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;
}
主要思想是将所有符号读入
缓冲区
,直到我们遇到
\n

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

因此,为了扩展
缓冲区
,我们使用:

char *newBuffer = realloc(buffer, maximumLength += sizeIncrement);
在这种情况下,
realloc()
返回指向扩展缓冲区的
newBuffer
指针

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

currentPosition = newBuffer + (currentPosition - buffer);
问题

以这种方式重新计算当前位置是否有效

正如我所知,在
realloc()
invocation
buffer
之后,指针无效。(例如,见)。任何对
缓冲区
指针的访问都会导致未定义的行为。所以我哪里错了?

是的。原因如下

currentPosition
buffer
,因为它们在表达式
currentPosition=newBuffer+(currentPosition-buffer)中使用,仅用于其算术值。在
realloc
解除对
buffer
的引用之后的任何时候


调用
realloc
时,指针不能再作为指向缓冲区内存区域的指针,这是正确的。但是,调用不会更改指针中的实际地址值。

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

currentPosition = newBuffer + (currentPosition - buffer);
将指针传递到
realloc
后,该指针变量(以及基于该指针的所有其他指针)变得不确定,这与未初始化变量的状态相同

参考:C116.2.4/2:

[…]当 它指向(或刚刚过去)的对象到达其生命周期的终点

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

当向指针添加或从指针中减去整数类型的表达式时,[…]如果指针操作数和结果都指向同一数组对象的元素,或指向数组对象最后一个元素之后的元素,则计算不应产生溢出;否则,行为是未定义的

指针操作数此时不指向对象。它用来指向的对象已被释放

事实上,计算指针可能会导致未定义的行为,因为不确定值可能是陷阱表示。(想象一下,在一个系统中,将一个值加载到地址寄存器中也会执行一个硬件检查,以确定地址是否属于该进程)。参考文献:C113.19.2,6.2.6.1/5:

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


编写代码的正确方法应该是:

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

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

    currentPosition = newBuffer + currentOffset;
    buffer = newBuffer;
}
(我个人会全程使用偏移量,而不是
currentPosition
,以完全避免此问题)

我的五美分。)

首先,我认为书中的节目很糟糕

它不检查
fgetc
是否返回
EOF

当遇到文件结尾且未读取任何数据时,它不会像往常一样返回
NULL

此缺点不允许以以下方式使用该功能

while ( ( buffer = getLine() ) != NULL )
{
    //...
    free( buffer );
}
#include <stdlib.h>
#include <stdio.h>

char * getLine( void ) 
{
    const size_t SIZE_INCREMENT = 10;

    char  *buffer = NULL;
    size_t length = 0;

    int c = fgetc( stdin );

    if ( c != EOF )
    {        
        while ( 1 )
        {
            if ( length % SIZE_INCREMENT == 0 )
            {
                char *tmp = realloc( buffer, length + SIZE_INCREMENT );
                if ( !tmp )
                {
                    free( buffer );
                    buffer = NULL;
                    break;
                }
                else
                {
                    buffer = tmp;
                }
            }

            if ( c == EOF || c == '\n' ) break;

            buffer[length++] = c;

            c = fgetc( stdin );
        }            

        if ( buffer ) buffer[length] = '\0';
    }

    return buffer;
}

int main( void )
{
    char *s;

    while ( ( s = getLine() ) != NULL )
    {        
        puts( s );
        free( s );
    }

    return 0;
}
还有太多的变量。同样的逻辑可以用更少的变量执行

因此,我将按照以下方式编写函数

while ( ( buffer = getLine() ) != NULL )
{
    //...
    free( buffer );
}
#include <stdlib.h>
#include <stdio.h>

char * getLine( void ) 
{
    const size_t SIZE_INCREMENT = 10;

    char  *buffer = NULL;
    size_t length = 0;

    int c = fgetc( stdin );

    if ( c != EOF )
    {        
        while ( 1 )
        {
            if ( length % SIZE_INCREMENT == 0 )
            {
                char *tmp = realloc( buffer, length + SIZE_INCREMENT );
                if ( !tmp )
                {
                    free( buffer );
                    buffer = NULL;
                    break;
                }
                else
                {
                    buffer = tmp;
                }
            }

            if ( c == EOF || c == '\n' ) break;

            buffer[length++] = c;

            c = fgetc( stdin );
        }            

        if ( buffer ) buffer[length] = '\0';
    }

    return buffer;
}

int main( void )
{
    char *s;

    while ( ( s = getLine() ) != NULL )
    {        
        puts( s );
        free( s );
    }

    return 0;
}
然后输出将回显输入

This is first line
This is second line
至于讨论这一说法是否正确

currentPosition = newBuffer + (currentPosition - buffer);
那么在我看来,它是定义明确的

根据C标准(6.5.6加法运算符)

6二进制运算符的结果是 从第一个操作数减去第二个操作数

尽管指针指向的对象尚未激活,但指针包含与引用同一数组元素的指针算法相关的有效值。 因此,如果应用子结构,结果将是非活动数组所具有的这两个指针之间元素的有效值。减法运算符只使用整数值进行算术运算,并考虑指针的类型,如

( second_pointer - first_pointer ) / sizeof( *first_pointer )

该代码使用了一种模式,在许多平台上,这种模式自然会得到任何实现的支持,而这些实现并不会以其他方式提供支持。不幸的是,一些实现偶尔会以“优化”的名义破坏这些代码,即使与要求程序员使用替代方法而导致的效率损失相比,由此获得的任何实际优化的价值都显得微不足道。

Rokyan这确实是书中所写的代码吗?如果是这样的话,那么这是一个错误的代码,我相信这本书也是错误的。如果你尊重它,它是无效的,但代码没有。它只是计算新指针。上一个指针仍保留其值。如果您确实想提高知识,请不要调用指针=>>Array@VladfromMoscow如果你愿意,你可以查一下。也许我漏掉了一些空行……我想你错了。如果代码取消了指针的引用,那么您是正确的,但它没有。由于
buffer
的值不能在调用中更改(因为它是按值调用的),因此保证变量具有buffer的原始值。这允许使用简单的指针算法来确定要添加到新
newBuffer
内存区域的正确偏移量。@DavidHoelzer C标准d