未定义的行为在C中串联字符串

问题描述

我正在编写一个C语言程序,该程序将连接所有行(包括“ \n”),同时将指针保存到最终字符串的最后一个字符。但是,我没有得到预期的结果。可能是什么问题?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct Node {
  struct Node *next;
  struct Node *prev;
};

struct Node *CreateNewNode() {
  struct Node *newNode = malloc(sizeof(struct Node));
  return newNode;
}

struct PieceTable {
  char *buffer;
  char *ptr_to_last_character;
} PT;

void strconcatenate(char *source) {
  size_t source_len = strlen(source);
  size_t buffer_len = strlen(PT.buffer);
  PT.buffer = realloc(PT.buffer,buffer_len + source_len + 1);
  while (*source)
    *PT.ptr_to_last_character++ = *source++;
  *PT.ptr_to_last_character = '\0';
}

int main(int argc,char *argv[]) {

  char input_line[1024];

  PT.buffer = malloc(sizeof(char) * 2);
  *PT.buffer = '\0';
  PT.ptr_to_last_character = PT.buffer;

  struct Node *new_node = CreateNewNode();
  new_node->next = NULL;
  new_node->prev = NULL;

  strconcatenate("Lorem ipsum\n");
  strconcatenate("dolor sit amet\n");
  strconcatenate("consectetur adipiscing elit\n");

  printf("%s",PT.buffer);

  return 0;
}

预期输出:

Lorem ipsum
dolor sit amet
consectetur adipiscing elit

输出:

etur adipiscing elit

解决方法

您重新分配了PT.buffer,但没有更新PT.ptr_to_last_character。当realloc不能只是扩展当前分配,而是在另一个地址返回一个新的较大区域,而使ptr_to_last_character指向旧内存时,则会导致未定义的行为。

可能更好的方法是存储缓冲区的长度,而不是存储指向最后一个字符的指针,这样您就不必担心使其无效。

,

注意事项: Paul已经找到了问题,但是由于您说您需要一个指针...

您可以通过保存/恢复偏移量来调整“最后一个”指针:

raise exception_class(message,screen,stacktrace)
selenium.common.exceptions.WebDriverException: Message: 

我在下面的完整代码中注释了其他一些清理问题。 ptrdiff_t offset = PT.ptr_to_last_character - PT.buffer; PT.buffer = realloc(PT.buffer,buffer_len + source_len + 1); PT.ptr_to_last_character = PT.buffer + offset; 包装旧的/原始的代码。 #if 0包装新的/重构的代码:

#if 1
,

对于初学者来说,不清楚为什么在此语句中分配两个字节而不是一个字节。

PT.buffer = malloc(sizeof(char) * 2);
*PT.buffer = '\0';

在函数中此语句

size_t buffer_len = strlen(PT.buffer);
考虑到您已经有一个指向终止零的指针,

没有任何意义。因此效率很低。

在致电realloc

之后
PT.buffer = realloc(PT.buffer,buffer_len + source_len + 1);

指针PT.ptr_to_last_character可能无效。

还应该检查内存分配是否成功。否则,如果发生故障,指针PT.buffer可以设置为NULL。

该功能可以通过以下演示程序中所示的以下方式实现。

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

struct PieceTable {
  char *buffer;
  char *ptr_to_last_character;
} PT;

int strconcatenate( const char *source ) 
{
    int success = 1;
    
    size_t n = PT.ptr_to_last_character - PT.buffer;
    
    size_t source_len = strlen( source );
    
    if ( source_len != 0 )
    {
        char *tmp = realloc( PT.buffer,n + source_len + 1 );
        
        if ( ( success = tmp != NULL ) )
        {
            PT.buffer = tmp;
            PT.ptr_to_last_character = PT.buffer + n;

            while ( ( *PT.ptr_to_last_character = *source++ ) ) ++PT.ptr_to_last_character;
        }
    }
    
    return success;
}   

int main(void) 
{
    PT.buffer = malloc( sizeof(char) );
    *PT.buffer = '\0';
    PT.ptr_to_last_character = PT.buffer;
    
    strconcatenate( "Lorem ipsum\n" );
    strconcatenate( "dolor sit amet\n" );
    strconcatenate( "consectetur adipiscing elit\n" );

    puts( PT.buffer );
    
    return 0;
}

程序输出为

Lorem ipsum
dolor sit amet
consectetur adipiscing elit

还要考虑到这一点,而不是手动编写的循环

while ( ( *PT.ptr_to_last_character = *source++ ) ) ++PT.ptr_to_last_character;

您可以使用效率更高的标准C字符串函数strcatstrcpy

在这种情况下,您可以通过以下方式查看功能

int strconcatenate( const char *source ) 
{
    int success = 1;
    
    size_t n = PT.ptr_to_last_character - PT.buffer;
    
    size_t source_len = strlen( source );
    
    if ( source_len != 0 )
    {
        char *tmp = realloc( PT.buffer,n + source_len + 1 );
        
        if ( ( success = tmp != NULL ) )
        {
            PT.buffer = tmp;
            PT.ptr_to_last_character = PT.buffer + n;

            strcpy( PT.ptr_to_last_character,source );
            
            PT.ptr_to_last_character += source_len;
        }
    }
    
    return success;
}   

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...