如何在C

时间:2016-09-03 22:11:03

标签: c string data-structures time struct

我正在尝试构建一个字符串,该字符串将使用以下格式进入日志文件:"可执行文件:时间:...错误:..."。但是,我在我的数据结构中分配我的时间变量时遇到了麻烦。我已经能够编码它,以便时间可以在字符串之后,但我无法弄清楚如何在可执行文件和错误消息之间有时间。如果有人能告诉我我做错了什么,我会很感激。

代码:

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;
}

2 个答案:

答案 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);
...