我正在尝试构建一个字符串,该字符串将使用以下格式进入日志文件:"可执行文件:时间:...错误:..."。但是,我在我的数据结构中分配我的时间变量时遇到了麻烦。我已经能够编码它,以便时间可以在字符串之后,但我无法弄清楚如何在可执行文件和错误消息之间有时间。如果有人能告诉我我做错了什么,我会很感激。
代码:
log.h
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <getopt.h>
#include <time.h>
typedef struct data_struct
{
time_t time;
char *string;
} data_t;
int addmsg(data_t data, char *arg0);
void clearlog(void);
char *getlog(void);
int savelog(char *filename);
loglib.c
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <getopt.h>
#include "log.h"
//Basic template from Excercise 2.13
typedef struct list_struct
{
data_t item;
struct list_struct *next;
} log_t;
static log_t *headPtr = NULL;
static log_t *tailPtr = NULL;
//Like book example with add on for executable name
int addmsg(data_t data, char *arg0)
{
log_t *newnode;
int nodesize;
char timeString[] = ": Time: ";
char errorString[] = " Error: ";
//Allocate size for "Executable: time: Error: "
nodesize = sizeof(log_t) + strlen(data.string) + strlen(arg0) + sizeof(time_t) + strlen(timeString) + strlen(errorString) + 1;
if((newnode = (log_t *)(malloc(nodesize))) == NULL)
{
perror("Malloc failed: ");
return -1;
}
//Makes string "Executable: time: Error: "
newnode->item.time = data.time;
char *timeCode = ctime(&newnode->item.time);
newnode->item.string = (char *)newnode + sizeof(log_t);
strcpy(newnode->item.string, arg0);
newnode->item.string = strcat(newnode->item.string, timeString);
newnode->item.string = strcat(newnode->item.string, timeCode);
newnode->item.string = strcat(newnode->item.string, errorString);
newnode->item.string = strcat(newnode->item.string, data.string);
newnode->next = NULL;
//Puts string as EOF
if(headPtr == NULL)
{
headPtr = newnode;
}
else
{
tailPtr->next = newnode;
}
tailPtr = newnode;
return 0;
}
//Clears log
void clearlog(void)
{
log_t *nextPtr = headPtr;
//Loop through and clear the log
while(nextPtr != NULL)
{
nextPtr = headPtr->next;
free(headPtr);
headPtr = nextPtr;
}
}
char *getlog(void)
{
int size = 1;//Set to 1 because of null terminator
int entryNum = 0; //Number of error logs
log_t *node = headPtr; //Start at beggining
char *wholeLog = NULL; //Used to return the entire log
char *entryString = NULL;
//Get size
while(node != NULL)
{
entryNum++;
size += strlen(node->item.string);
node = node->next;
}
//Memory allocation
wholeLog = malloc(sizeof(char)*(size + 1 + (entryNum * 2)));
if(wholeLog == NULL)
{
perror("Malloc failed: ");
return NULL;
}
//Reset node to beggining
node = headPtr;
//Builds the entire log to return
while(node != NULL)
{
entryString = strcat(entryString, node->item.string);
wholeLog = strcat(wholeLog, entryString);
wholeLog = strcat(wholeLog, "\n"); //New line
node = node->next;
}
//Make space
wholeLog += (strlen(wholeLog) - size - (entryNum-1));
return wholeLog;
}
int savelog(char *filename)
{
FILE *f;
char *logPrinter;
f = fopen(filename, "a");
if(!f)
{
perror("Error opening file: ");
return -1;
}
//Get the log to print
logPrinter = getlog();
if(logPrinter == NULL)
{
printf("Empty Log\n");
return 0;
}
fprintf(f, "%s\n", logPrinter);
fclose(f);
return 0;
}
答案 0 :(得分:2)
您的代码似乎倾向于计算内存缓冲区的大小,该内存缓冲区将 log_t
节点结构和连接的消息部分,具有字符串指针在单个内存块内的链接列表节点的data_t
成员中,只传递存储消息的链表节点内容。简而言之,一个分配同时包含节点和消息。
也就是说,利用标准库API的事实,特别是snprintf
可以为你计算格式化的消息缓冲区长度要求,然后你可以跳过大部分的字符串管理不赞成这个的真正目的,通过对malloc
的单一调用来管理链接列表结构和动态消息内容(并且通过环境,单个调用free()
当这个惨败需要撤消时):
char
传递结构布局。结果是动态长度的单个分配,具体取决于正在格式化的消息的内容。
使用snprintf
代替
int addmsg(data_t data, const char *arg0)
{
static const char fmt[] = "%s: Time: %s Error: %s";
char stime[32] = ""; // per ctime docs, must be at least 26 chars
int res = -1;
// get time string, trim newline
ctime_r(&data.time, stime);
if (*stime)
stime[strlen(stime)-1] = 0;
// find needed message size
int req = snprintf(NULL, 0, fmt, data.string, stime, arg0);
if (req > 0)
{
// include size of log record, formatted message, and terminator
log_t *node = malloc(sizeof (log_t) + req + 1);
if (node != NULL)
{
node->item.string = (char*)(node+1); // NOTE: see below
node->item.time = data.time;
node->next = NULL;
snprintf(node->item.string, req+1, fmt, data.string, stime, arg0);
// link into list
if (!headPtr)
headPtr = node;
else
tailPtr->next = node;
tailPtr = node;
// good return value
res = 0;
}
else
{
perror("Failed to allocate memory for log mesage: ");
}
}
else
{
perror("Failed to perform message formatting: ");
}
return res;
}
除了可能的NOTE
之外,上述所有内容都相当直接,我现在将解释。它使用指针算法。给定某种类型node
的指针log_t*
表达式:
(node + 1)
计算后续log_t
对象可以驻留的内存中的 next 位置,或者序列结束时的“一个过去”内存位置。当我们分配内存时,我们这样做了一个看起来像这样的布局:
node ---> |=== log_t ===|===== message text =====|
表达式(node+1)
,使用类型指针算法,给我们:
node ---> |=== log_t ===|===== message text =====|
(node+1)-----------------^
然后转换为char*
,保存在node->data.string
中,并用作使用snprintf
进行最终邮件格式化的目标。简短版本:node->item.string
指向我们在log_t
指向的node
结构后分配的额外内存。
就是这样。结果是对包含节点管理数据的链接列表节点进行单一分配,并且还包含指向存储在超过log_t
核心数据的单个分配的后缀内存中的动态消息字符串的指针。
答案 1 :(得分:1)
如果你用log_t
替换了addmsg
的{{1}}构造片段,你会得到更好的效果。您计算所需的内存大小有点偏差。可能还想避免使用malloc来假设关于内存的事情(即分配额外的内存以存储结构和该结构的指针成员的内容可能很容易让你陷入麻烦)
...
log_t *newnode = NULL;
void *vp = NULL;
if (NULL == (vp = malloc(sizeof(log_t)))) {
perror("malloc failed (sizeof(log_t))");
return -1;
}
newnode = (log_t *)vp;
newnode->item.time = data.time;
char *timeCode = ctime(&newnode->item.time);
int msgsize = strlen(": Time: Error: ")
+ strlen(arg0)
+ strlen(timeCode)
+ strlen(data.string)
+ 1;
if (NULL == (vp = malloc(msgsize))) {
perror("malloc failed (msgsize)");
free(newnode);
return -1;
}
newnode->item.string = (char *)vp;
sprintf(newnode->item.string, "%s: Time: %s Error: %s", arg0, timeCode, data.string);
...