数组大小调整和realloc函数
现在我试图通过阅读Richard Reese的“理解和使用C指针”来提高我的指针知识 这是本书中关于数组大小调整和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;
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()
invocationbuffer
之后,指针无效。(例如,见)。任何对缓冲区指针的访问都会导致未定义的行为。所以我哪里错了?是的。原因如下
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