我有一个分配代码,我想创建并打开一个文件使用相同的名称"在自己"通过命令行参数给出的输入文件,但具有不同的扩展名(例如:我传递文件"filename.in"在终端通过argv,我想创建并打开文件"filename.out")。然而,我一直得到错误"无效大小写入大小为1";当我在valgrind中运行我的程序时,我真的看不出它们是从哪里来的。
代码:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "roap.h"
int main(int argc, char** argv) {
FILE* filePtr;
LabList* head = NULL;
if (argc != 3) {
printf("Numero de argumentos errado! n");
exit(EXIT_FAILURE);
}
char flag[] = "-s";
if ( strcmp(argv[1] , flag) != 0 )
{
fprintf(stdout, "Flag '-s' necessária para esta fase do projeto!"); //verifica se a flag -s está presente
exit(EXIT_FAILURE);
}
char *file_arg /*argumento indicado no terminal para referir ao ficheiro*/, *filename /*nome "próprio" do ficheiro*/, *file_arg_aux;
char dot = '.';
char ponto[] = ".";
char *extencao;
int read_ok;
file_arg = (char*) calloc(1, strlen(argv[2]) +1 ); //verifica se de facto a extensão é .in1
file_arg_aux = (char*) calloc(1, strlen(argv[2]) +1 );
strcpy(file_arg, argv[2]);
strcpy(file_arg_aux, argv[2]);
filename = strtok(file_arg, ponto);
extencao = strrchr(file_arg_aux, dot);
if ((read_ok = strcmp(extencao, ".in1")) != 0 )
{
fprintf(stdout, "Extensão inválida!");
exit(EXIT_FAILURE);
}
filePtr = fopen(argv[2], "r");
if (filePtr == NULL) {
printf("Erro ao abrir o ficheiro %s !n", argv[2]);
exit(EXIT_FAILURE);
} else {
head = readLab(filePtr, head);
fclose(filePtr);
}
char extencao_out[] = ".sol1";
FILE* file_out = fopen( strcat(filename, extencao_out) , "w");
Valgrind输出:
==123== Invalid write of size 1
==123== at 0x483EC5E: strcat (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==123== by 0x1095B2: main (main.c:60)
==123== Address 0x4a47051 is 0 bytes after a block of size 17 alloc'd
==123== at 0x483DD99: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==123== by 0x10944E: main (main.c:36)
==123==
==123== Syscall param openat(filename) points to unaddressable byte(s)
==123== at 0x4962D1B: open (open64.c:48)
==123== by 0x48E5195: _IO_file_open (fileops.c:189)
==123== by 0x48E5459: _IO_file_fopen@@GLIBC_2.2.5 (fileops.c:281)
==123== by 0x48D7B0D: __fopen_internal (iofopen.c:75)
==123== by 0x48D7B0D: fopen@@GLIBC_2.2.5 (iofopen.c:86)
==123== by 0x1095C1: main (main.c:60)
==123== Address 0x4a47051 is 0 bytes after a block of size 17 alloc'd
==123== at 0x483DD99: calloc (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==123== by 0x10944E: main (main.c:36)
第36行:file_arg = (char*) calloc(1, strlen(argv[2]) +1 );
60行:FILE* file_out = fopen( strcat(filename, extencao_out) , "w");
这个…
file_arg = (char*) calloc(1, strlen(argv[2]) +1 )
…为argv[2]
的副本分配足够的空间,并将其分配给file_arg
。这很好,它是ok之后strcpy(file_arg, argv[2])
。
给定ponto
的值,这个…
filename = strtok(file_arg, ponto);
…通过用字符串终止符覆盖第一个'.'
(如果有的话)来截断file_arg
所指向的字符串,并返回一个(指针)file_arg
的副本。这本身没有问题。
但是这里:
FILE* file_out = fopen( strcat(filename, extencao_out) , "w");
,strcat(filename, extencao_out)
尝试将字符串extencao_out
(".sol1"
)的内容附加到原始扩展名的位置,您已经验证了原始扩展名是".in1"
,有点尴尬。因为已经为原始文件名分配了足够的空间,所以没有足够的空间来容纳程序现在试图构造的较长的文件名。正如Valgrind告诉您的那样,分配的空间将被覆盖一个字节。
我建议将extencao_out
的声明移动到足够早的位置,以便您可以像这样分配:
file_arg = (char*) calloc(1, strlen(argv[2]) + strlen(extencao_out) + 1);
使用当前的扩展组合将会超额分配4个字节,但是
- 四个字节可以忽略;
- 它可能在75%的时间内不会给系统带来额外的内存负担;和
- 对于您以后可能想要支持的其他行为变化,例如没有扩展名的输入文件名,它将具有灵活性。
在单独的行中声明变量,并带注释;它们都是指针,并且声明/不保留任何内存。
char* file_arg; /*argumento indicado no terminal para referir ao ficheiro*/
char* filename; /*nome "próprio" do ficheiro*/
char* file_arg_aux;
char* extencao;
在这里,您将内存分配给file_arg,但仅足以容纳原始argv字符串内容(加上null终止符)。由于您以后会重用这个缓冲区,因此您可以为将来的需要分配足够的额外空间。
file_arg = (char*) calloc(1, strlen(argv[2]) +1 ); //verifica se de facto a extensão é .in1
file_arg_aux = (char*) calloc(1, strlen(argv[2]) +1 );
只使用第6行的)而不是calloc()和strcpy ()
strcpy(file_arg, argv[2]);
strcpy(file_arg_aux, argv[2]);
这两个函数都能找到'.'/";在稍后用于附加新文件扩展名(extencao?)的filename/file_arg中,
filename = strtok(file_arg, ponto);
extencao = strrchr(file_arg_aux, dot);
这是您期望的扩展名,长度=4,
strcmp(extencao, ".in1")
这是你的新文件扩展名,长度=5,
char extencao_out[] = ".sol1";
用足够的空间来容纳文件名和新的扩展名
char* outfilename = calloc( strlen(filename)+strlen(extencao_out)+1 );
strcpy(outfilename, filename);
strcat(outfilename, extencao_out);
FILE* file_out = fopen( outfilename, "w");