RAH*_*sen 0 c c++ malloc valgrind
我有一个包含 3 条染色体字符串的文件,我想将其连接成一个基因组。然后我必须跨多个线程访问这个串联字符串(我使用 pthread_t)。为此,我必须在提取数据时使用 pthread_mutex_lock,然后使用 strcat 连接使用 const char* 函数 fai_fetch 提取的数据,然后将数据保存为 char* (见下文)。
// genome_size the size of all the chromosomes together
// chr_total the number of chromosomes I wish to concatenate
char* genome = (char*) malloc(sizeof(char) * (genome_size+chr_total));
for (int i = 0; i < chr_total; i++){
pthread_mutex_lock(&data_mutex);
const char *data = fai_fetch(seq_ref,chr_names[i],&chr_sizes[i]);
pthread_mutex_unlock(&data_mutex);
//sprintf(&genome[strlen(genome)],data);
strcat(genome,data);
//sprintf(genome+strlen(genome),data); //All three gives conditional jump or move error
//sprintf(genome,data); // THIS SOLVES VALGRIND ISSUE ONE BUT DOES NOT GIVE A CONCATENATED CHAR*
}
Run Code Online (Sandbox Code Playgroud)
所有这些都有效,但是运行 valgrind 我得到
条件跳转或移动取决于引用“strcat(genome,data);”的未初始化值
未初始化的值是通过堆分配创建的,引用“char*genome=(char*)malloc(sizeof(char)*(genome_size+chr_total));”
根据其他 StackOverflow 答案,我尝试了 sprintf(&genome[strlen(genome)],data); 和 sprintf(基因组+strlen(基因组),数据); 而不是 strcat。然而,他们也给出了相同的 valgrind 消息。
唯一可以减轻此错误的方法是使用 sprintf(genome,data); 然而,这样我就不会得到完整的基因组,而只会得到一条染色体。
尝试基因组+= sprintf(基因组,数据); 给我 ./a.out': munmap_chunk(): 无效指针:和 ./a.out': free()
关于“未初始化的值是由堆分配创建的”错误 -> 那么我的问题是我只能在所有线程运行完毕后释放该内存。所以我不确定当我使用 malloc 时如何初始化堆分配中的值。
是否可以解决其中一些特定的 valgrind 错误?
“条件跳转或移动取决于未初始化的值”消息意味着 Valgrind 已确定程序的某些结果取决于未初始化的内存。使用该--track-origins=yes标志来跟踪未初始化值的来源。它可能会帮助您找到该价值。从man 1 valgrind:
当设置为 yes 时,Memcheck 会跟踪所有未初始化值的来源。然后,当报告未初始化值错误时,Memcheck 将尝试显示该值的来源。来源可以是以下四个位置之一:堆块、堆栈分配、客户端请求或其他各种来源(例如,对 brk 的调用)。
更具体地说,在您的程序中:
genome线路
char* genome = (char*) malloc(sizeof(char) * (genome_size+chr_total));
Run Code Online (Sandbox Code Playgroud)
genome使用分配缓冲区malloc(2),然后在以下位置消耗它:
strcat(genome,data);
Run Code Online (Sandbox Code Playgroud)
请注意,诸如strlen(3)和 之类的函数strcat(3)适用于 C 字符串,它们是以空字符 ('\0') 结尾的缓冲区。
malloc(2)只是分配内存并且不会初始化它,因此您分配的缓冲区可能包含任何值(并被视为未初始化)。您应该避免将字符串相关的函数与未初始化的缓冲区一起使用,因为这会导致未定义的行为。
幸运的calloc(2)是,这个技巧做到了——它分配缓冲区并将其所有位初始化为零,从而产生一个可以操作的有效的 0 长度 C 字符串。我建议进行以下修复以确保已genome初始化:
char* genome = (char*) malloc(sizeof(char) * (genome_size+chr_total));
Run Code Online (Sandbox Code Playgroud)
另请注意,我已添加了+1分配的缓冲区的长度。这样做是为了保证结果genome将以空终止符结束(假设这genome_size+chr_total是从返回的所有缓冲区的总大小fai_fetch)。
另请注意,就性能而言,calloc它比(因为它初始化数据)慢一点malloc,但在我看来,它更安全,因为它初始化整个缓冲区。出于您的特定程序的目的,您可以通过malloc仅使用和初始化第一个字节来节省性能负担:
char* genome = malloc(sizeof(char) * (genome_size + chr_total + 1));
if (NULL == genome) {
perror("malloc of genome failed");
exit(1);
}
// So it will be a valid 0 length c-string
genome[0] = 0;
Run Code Online (Sandbox Code Playgroud)
我们不必将最后一个字节初始化为 0,因为strcat它为我们写入了终止空字符。
data符strcat正如您在问题中所描述的,fai_fetch提取一些数据:
strcat(genome,data);
Run Code Online (Sandbox Code Playgroud)
然后在行中消耗它strcat:
char* genome = calloc(genome_size+chr_total+1, sizeof(char));
Run Code Online (Sandbox Code Playgroud)
正如我上面所写,因为您使用strcat,data也应该以空终止。
我不确定fai_fetch是如何实现的,但如果它返回一个有效的 C 字符串,那么一切都很好。
如果没有,那么您应该使用strncat它适用于非空终止的缓冲区。
从man 3 strcat:
strncat() 函数类似,不同之处在于
- 它将使用 src 中的最多 n 个字节;和
- 如果 src 包含 n 个或更多字节,则不需要以 null 终止。
我建议进行以下修复:
char* genome = malloc(sizeof(char) * (genome_size + chr_total + 1));
if (NULL == genome) {
perror("malloc of genome failed");
exit(1);
}
// So it will be a valid 0 length c-string
genome[0] = 0;
Run Code Online (Sandbox Code Playgroud)