程序接收信号 SIGABRT with free() [关闭]

问题描述 投票:0回答:0

我正在实现sscanf,我有这样一个功能:

my_string.h

#ifndef SRC_MY_STRING_H_
#define SRC_MY_STRING_H_

#define my_size_t unsigned long
#define my_NULL (void *)0

#include <stdarg.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h> //sscanf
#include <stdbool.h> //sscanf

my_size_t my_strlen(const char *string);          
char *my_strcpy(char *restrict dst, const char *restrict src);
char *my_strncpy(char *restrict dst, const char *restrict src, size_t n);

#endif  // SRC_MY_STRING_H_

my_string.c

#include "my_string.h"

my_size_t my_strlen(const char *string) {
  my_size_t len = 0;
  while (*(string + len)) {
    len++;
  }
  return len;
}

char *my_strcpy(char *restrict dst, const char *restrict src) {
  char *res = dst;
  while (*src) {
    *dst = *src;
    dst++;
    src++;
  }
  *dst = 0;
  return res;
}

char *my_strncpy(char *restrict dst, const char *restrict src, my_size_t n) {
  char *res = dst;
  while (*src && n--) {
    *dst = *src;
    dst++;
    src++;
  }
  *dst = 0;
  return res;
}

my_sscanf.c

#include "my_string.h"
/*sscanf implementation*/

/* for specifiers */
enum {
  spec_c = 1 << 0, 
  spec_d = 1 << 1,
  spec_i = 1 << 2,
  spec_e = 1 << 3,
  spec_E = 1 << 4,
  spec_f = 1 << 5,
  spec_g = 1 << 6,
  spec_G = 1 << 7,
  spec_o = 1 << 8,
  spec_s = 1 << 9,
  spec_u = 1 << 10,
  spec_x = 1 << 11,
  spec_X = 1 << 12,
  spec_p = 1 << 13,
  spec_n = 1 << 14,
  spec_percent = 1 << 15
};

/* for systems */
enum {
  OCT = 8,
  DEC = 10,
  HEX = 16
};

/* for shifts */
enum {
  SHIFT = 48, /* code of 0 in ASCII */
  SHIFT_HEX = 55, /* code of A - 10 in ASCII */
  SHIFT_hex = 87 /* code of a - 10 in ASCII */
};

/* check white-space characters */
static _Bool is_whitespace(char ch) {
  return (ch == ' ' || ch == '\t' || ch == '\n') ? true : false;
}

/* skip white-space chatacters */
static int skip_whitespaces(char **string) {
  int count = 0;
  while (is_whitespace(**string)) {
    (*string)++;
    count++;
  }
  return count;
}

/* skip the whole string */
static void skip_all(char **string) {
  while (**string) {
    (*string)++;
  }
}

/* sets the format_buf pointer to the character after the % */
static void get_specifier(char **str_buf, char **format_buf, _Bool *outsider_ch) {
  int white_count = skip_whitespaces(format_buf);
  if (white_count) {
    skip_whitespaces(str_buf);
  }
  while (**format_buf != '%') { /* skip all regular characters*/
    if (**format_buf != **str_buf) {
      *outsider_ch = true;
    }
    (*format_buf)++;
    (*str_buf)++;  
  }
  /* stoped at % */
  ++(*format_buf);
}

/* check character if it's a digit*/
static _Bool is_digit(char ch) {
  return ((ch >= '0') && (ch <= '9')) ? true : false;
}

/* converts from string to number */
static long str_to_dec(char **string, int width, int sign) {
  long res = **string - SHIFT; /* get the first digit */
  int count = 1;
  (*string)++;
  while (is_digit(**string) && ((count < width) || (!width))) {
    res = res * DEC + (**string - SHIFT);
    (*string)++;
    count++;
  }
  return res * sign;
}

/* check character if it's a length*/
static _Bool is_correct_length(char **format_buf) {
  _Bool res = false;
  char next_ch = *((*format_buf) + 1);
  if (((**format_buf == 'l') || (**format_buf == 'L')) && (next_ch = 'f')) {
    res = true;
  }
  return res;
}

/* set the specs in an integer number according to enum */
static int set_specs(char **format_buf, _Bool *ass_supress, int *width, int *length) {
  int specs = 0;
  while ((**format_buf) && !is_whitespace(**format_buf) && (!specs)) {
    switch (**format_buf) {
      case 'c': 
        specs |= spec_c;
        break;
      case 'd':
        specs |= spec_d;
        break;
      case 'i':
        specs |= spec_i;
        break;
      case 'e':
        specs |= spec_e;
        specs |= spec_E;
        break;
      case 'f':
        specs |= spec_f;
        break;
      case 'g':
        specs |= spec_g;
        break;
      case 'G':
        specs |= spec_G;
        break;
      case 'o':
        specs |= spec_o;
        break;
      case 's':
        specs |= spec_s;
        break;
      case 'u':
        specs |= spec_u;
        break;
      case 'x':
        specs |= spec_x;
        break;
      case 'X':
        specs |= spec_X;
        break;
      case 'p':
        specs |= spec_p;
        break;
      case 'n':
        specs |= spec_n;
        break;
      case '%':
        specs |= spec_percent;
        break;
      case '*':
        *ass_supress = true;
        break;

      default:
        if (is_digit(**format_buf) && (**format_buf > '0')) {
          *width = str_to_dec(format_buf, 0, 1); /* get width */
          (*format_buf)--;
        } else if (is_correct_length(format_buf)) {
          *length = **format_buf;
        } else {
          fprintf(stderr, "Incorrect specifier\n");
          // TODO:handle an error
        }
    }
    (*format_buf)++;
  }
  return specs;
}

/* puts string into another vararg*/
static void str_into_arg(va_list *argp, _Bool ass_supress, _Bool outsider_ch, /*int length,*/ int count, char *str_buf_start, int *ret) {
    if (!ass_supress && !outsider_ch) {
      char *dst_string = va_arg(*argp, char*); /* take argument address */
      my_strncpy(dst_string, str_buf_start, count); /* put string into argument */
      my_strncpy(dst_string + count, "\0", 1); /* cut extra garbage */
      (*ret)++;
    }
}

/* */
static int str_to_str(char **str_buf, int width/*, int length*/) {
  int count = 0; /* number of characters */
    while (**str_buf && !is_whitespace(**str_buf) && ((count < width) || !width)) {
      (*str_buf)++; /* go to end of string */
      count++;
    }
  return count;
}

/* put string from source string to another agrument of sscanf */
static void scan_string(char **str_buf, va_list *argp, _Bool ass_supress, _Bool outsider_ch, int width, int *ret) {
  skip_whitespaces(str_buf);
  char *str_buf_start = *str_buf; //save start of string 
  int count = str_to_str(str_buf, width); // number of characters 
  str_into_arg(argp, ass_supress, outsider_ch,  count, str_buf_start, ret);
}

/* check specs if it's a e/E/f/g/G */
static _Bool is_efg(int specs) {
  return ((specs & spec_e) || (specs & spec_E) || (specs & spec_f) || (specs & spec_g) || (specs & spec_G)) ? true : false;
}

/* puts floating point number into another vararg*/
static void fpnum_into_arg(va_list *argp, _Bool ass_supress, _Bool outsider_ch, int length, int specs, long double res, int *ret) {
  if (!ass_supress && !outsider_ch) {
    if (is_efg(specs) && (length == 'L')) {
      long double *dst_num = va_arg(*argp, long double*); /* take argument address */
      *dst_num = res;
    } else if (is_efg(specs) && (length == 'l')) {
      double *dst_num = va_arg(*argp, double*);
      *dst_num = res;
    } else {
      float *dst_num = va_arg(*argp, float*);
      *dst_num = res;
    }
    (*ret)++;
  }
}

/* checks if the character if a sign or not */
static _Bool is_sign(char ch) {
  return ((ch == '-') || (ch == '+')) ? true : false;
}

/* gets sign and checks that there is no double sign*/
static int sign_check(char **str_buf) {
  int sign = 1;
  char next_ch = *((*str_buf) + 1);
  if ((is_sign(**str_buf)) && (is_sign(next_ch))) {
    sign = 0;
    skip_all(str_buf);
  } else if (**str_buf == '-') {
    sign = -1;
    (*str_buf)++;
  } else if (**str_buf == '+') {
    (*str_buf)++;
  }
  return sign;
}

/* multiplies  the res by a power of 10 from the exponent */
static long double get_exp(long double res, char **str_buf) {
  (*str_buf)++; /* go to the next char, must be a '-' or '+' */
  int sign = sign_check(str_buf);
  int power10 = str_to_dec(str_buf, 0, 1);
  (*str_buf)--;
  res = res * pow(DEC, power10 * sign);
  return res;
}

/* */
static void get_first_fpnum(char **str_buf, int sign, long double *res, int* power10) {
  char next_ch = *((*str_buf) + 1);
  if (is_digit(**str_buf)) {
    *res = (**str_buf - SHIFT) * sign;
    (*str_buf)++;
  } else if ((**str_buf == '.') && (is_digit(next_ch))){
    (*power10)++;
    (*str_buf)++;
  } else {
    // TODO:hanlde error
  }
}

/* */
static void str_to_fpnum(char **str_buf, int width, int sign, int *power10, long double *res) {
  int count = 1; // number of characters (digits or .) 
  while (((count < width) || !width) && **str_buf && !is_whitespace(**str_buf) && (is_digit(**str_buf) || (**str_buf == '.') || (**str_buf == 'e') || (**str_buf == 'E'))) {
    if (is_digit(**str_buf) && (!(*power10))) {
      *res = (*res) * DEC + (**str_buf - SHIFT) * sign;
    } else if (is_digit(**str_buf) && (*power10)) {
      *res = (*res) + ((long double)(**str_buf - SHIFT) / pow(DEC, (*power10)++)) * sign;
    } else if ((**str_buf == '.') && (!(*power10))) {
      (*power10)++;
    } else if ((**str_buf == 'e') || (**str_buf == 'E')) { 
      *res = get_exp(*res, str_buf);
    } else {
      //TODO: handle error
      break;
    }
    (*str_buf)++; 
    count++;
  }
}

/* put floating-point number from source string to another agrument of sscanf */
static void scan_efg(char **str_buf, va_list *argp, _Bool ass_supress, _Bool outsider_ch, int width, int length, int specs, int *ret) {
  skip_whitespaces(str_buf);
  int sign = sign_check(str_buf); 
  long double res = 0;
  int power10 = 0; /* for power of 10 */
  get_first_fpnum(str_buf, sign, &res, &power10);
  str_to_fpnum(str_buf, width, sign, &power10, &res);
  fpnum_into_arg(argp, ass_supress, outsider_ch, length, specs, res, ret);
}

/* check if the character is a hexadecimal symbol */
static _Bool is_hex(char ch) {
  return ((is_digit(ch)) || ((ch >= 'A') && (ch <= 'F')) || ((ch >= 'a') && (ch <= 'f'))) ? true : false;
}

/* check if the character is a octal symbol */
static _Bool is_oct(char ch) {
  return ((ch >= '0') && (ch <= '7')) ? true : false;
}

/* check for a prefix and skipping it if it exists */
static int prefix_check(char **str_buf, int specs) {
  int prefix = DEC; // for decimal
  char next_ch = *((*str_buf) + 1);
  char next_next_ch = *((*str_buf) + 2);
  if ((specs & spec_x) || (specs & spec_X) || (specs & spec_p) || (specs & spec_i)) { /* for hexadecimal */
    if ((**str_buf == '0') && ((next_ch == 'x') || (next_ch == 'X')) && is_hex(next_next_ch)) {
      prefix = HEX;
      (*str_buf) += 2;
    }
  }
  if ((specs & spec_o) || (specs & spec_i)) { /* for octal */
    if ((**str_buf == '0') && is_oct(next_ch)) {
      prefix = OCT;
      (*str_buf)++;
    }
  }
  return prefix;
}

/* puts integer number into another vararg*/
static void inum_into_arg(va_list *argp, _Bool ass_supress, _Bool outsider_ch, int length, int specs, long res, int *ret) {
  if (!ass_supress && !outsider_ch) {
    if (is_efg(specs) && (length == 'l')) {
      long *dst_num = va_arg(*argp, long*); /* take argument address */
      *dst_num = res;
    } else if (is_efg(specs) && (length == 'h')) {
      short *dst_num = va_arg(*argp, short*);
      *dst_num = (short)res;
    } else {
      int *dst_num = va_arg(*argp, int*);
      *dst_num = (int)res;
    }
    (*ret)++;
  }
}

/* puts pointer into another vararg*/
static void pointer_into_arg(va_list *argp, _Bool ass_supress, _Bool outsider_ch, unsigned long res, int *ret) {
  if (!ass_supress && !outsider_ch) {
    void **dst_pointer;
    dst_pointer = va_arg(*argp, void**); 
    *dst_pointer = (void*)res;
    (*ret)++;
  }
}

/* converts hex characters to numbers */
static short hex_to_num(char hex) {
  short num = 0;
  if (is_digit(hex)) {
    num = hex - SHIFT;
  } else if ((hex >= 'A') && (hex <= 'F')) {
    num = hex - SHIFT_HEX;
  } else {
    num = hex - SHIFT_hex;
  }
  return num;
}

/* convert from string to hexadecimal integer */
static long str_to_hex(char **str_buf, int width, int sign) {
  int count = 0; // number of hexadecimal characters 
  char *hex_start = *str_buf;
  while (((count < width) || !width) && **str_buf && !is_whitespace(**str_buf) && is_hex(**str_buf)) { 
    (*str_buf)++; 
    count++;
  }
  long res = 0;
  char *hex_finish = --(*str_buf); // on last hex
  int power16 = 0;
  for (char *hex_cur = hex_finish; hex_cur >= hex_start; hex_cur--) {
    res += hex_to_num(*hex_cur) * pow(HEX, power16++); 
  }
  res *= sign;
  *str_buf = hex_finish + 1;
  return res;
}

/* put hexadecimal integer from source string to another agrument of sscanf */
static void scan_hex(char **str_buf, va_list *argp, _Bool ass_supress, _Bool outsider_ch, int width, int length, int specs, int *ret) {
  skip_whitespaces(str_buf);
  int sign = sign_check(str_buf); /* get sign or check for double sign */
  prefix_check(str_buf, specs); /* skip 0x/0X prefix */
  long res = str_to_hex(str_buf, width, sign); /* convert from string to hex int*/;
  inum_into_arg(argp, ass_supress, outsider_ch, length, specs, res, ret);

}

/* convert from string to octal integer */
static long str_to_oct(char **str_buf, int width, int sign) {
  int count = 0; /* number of octal characters */
  char *oct_start = *str_buf;
  while (((count < width) || !width) && **str_buf && !is_whitespace(**str_buf) && is_oct(**str_buf)) { 
    (*str_buf)++; 
    count++;
  }
  long res = 0;
  char *oct_finish = --(*str_buf); // on last oct
  int power8 = 0;
  for (char *oct_cur = oct_finish; oct_cur >= oct_start; oct_cur--) {
    res += hex_to_num(*oct_cur) * pow(OCT, power8++); 
  }
  res *= sign;
  *str_buf = oct_finish + 1;
  return res;
}

/* put octal integer from source string to another agrument of sscanf */
static void scan_oct(char **str_buf, va_list *argp, _Bool ass_supress, _Bool outsider_ch, int width, int length, int specs, int *ret) {
  skip_whitespaces(str_buf);
  int sign = sign_check(str_buf); /* get sign or check for double sign */
  prefix_check(str_buf, specs); /* skip 0 prefix */
  long res = str_to_oct(str_buf, width, sign); /* convert from string to octal int*/
  inum_into_arg(argp, ass_supress, outsider_ch, length, specs, res, ret); /* write down into another arg*/
}

/* put pointer from source string to another agrument of sscanf */
static void scan_pointer(char **str_buf, va_list *argp, _Bool ass_supress, _Bool outsider_ch, int width, int specs, int *ret) {
  skip_whitespaces(str_buf);
  prefix_check(str_buf, specs);
  int count = 0; /* number of hexadecimal characters */
  char *hex_start = *str_buf;
  while (((count < width) || !width) && **str_buf && !is_whitespace(**str_buf) && (is_digit(**str_buf) || is_hex(**str_buf))) { 
    (*str_buf)++; 
    count++;
  }
  unsigned long res = 0;
  char *hex_finish = --(*str_buf); // on last hex
  int power16 = 0;
  for (char *hex_cur = hex_finish; hex_cur >= hex_start; hex_cur--) {
    res += hex_to_num(*hex_cur) * pow(HEX, power16++); 
  }
  *str_buf = hex_finish + 1;
  // TODO: str_to_pointer into separate function
  pointer_into_arg(argp, ass_supress, outsider_ch, res, ret);
}

/* */
static void count_chars(char **str_buf, const char* const *str_start, va_list *argp, _Bool ass_supress, _Bool outsider_ch, int length, int specs, int *ret) {
  int amount = *str_buf - *str_start;
  inum_into_arg(argp, ass_supress, outsider_ch, length, specs, amount, ret);
}

/* put signed decimal/octal/hexadecimal integer from source string to another agrument of sscanf */
static void scan_doh(char **str_buf, va_list *argp, _Bool ass_supress, _Bool outsider_ch, int width, int length, int specs, int *ret) {
  skip_whitespaces(str_buf);
  if (**str_buf) {
  int sign = sign_check(str_buf); /* get sign or check for double sign */
  int prefix = prefix_check(str_buf, specs);
  long res = 0;
  switch (prefix) {
    case DEC: 
      res = str_to_dec(str_buf, width, sign);
      break;
    case OCT:
      res = str_to_oct(str_buf, width, sign);
      break;
    case HEX:
      res = str_to_hex(str_buf, width, sign);
      break;
  }
  inum_into_arg(argp, ass_supress, outsider_ch, length, specs, res, ret);
  } else {
    *ret = EOF;
  }
}

/* scan processing*/
static void scan_proc(char **str_buf, const char* const *str_start, int specs, va_list *argp, _Bool ass_supress, _Bool outsider_ch, int width, int length, int *ret) {
  if (specs & spec_s) { /* scan strings */
    scan_string(str_buf, argp, ass_supress, outsider_ch, width, ret);
  }
  if (is_efg(specs)) { /* scan decimal numbers with floating point or scientific notation */
    scan_efg(str_buf, argp, ass_supress, outsider_ch, width, length, specs, ret);
  }
  if ((specs & spec_x) || (specs & spec_X)) { /* scan hexadecimal integers */
    scan_hex(str_buf, argp, ass_supress, outsider_ch, width, length, specs, ret); 
  }
  if (specs & spec_o) { /* scan octal integers */
    scan_oct(str_buf, argp, ass_supress, outsider_ch, width, length, specs, ret); 
  }
  if (specs & spec_p) { /* scan pointer */
    scan_pointer(str_buf, argp, ass_supress, outsider_ch, width, specs, ret); 
  }
  if (specs & spec_n) { /* count characters read before n */
    count_chars(str_buf, str_start, argp, ass_supress, outsider_ch, width, specs, ret); 
  }
  if ((specs & spec_i) || (specs & spec_d)) { /* scan signed integer: dec/oct/hex */
    scan_doh(str_buf, argp, ass_supress, outsider_ch, width, length, specs, ret); 
  }
}



int my_sscanf(const char *str, const char *format, ...) {
  char *str_buf = (char*)malloc(my_strlen(str) * sizeof(char));
  char *format_buf = (char*)malloc(my_strlen(format) * sizeof(char));
  const char * const str_start = str_buf; /* save start of string for %n and free() */
  const char * const format_start = format_buf; /* save start of string for free() */
  my_strcpy(str_buf, str);
  my_strcpy(format_buf, format);
  va_list argp;
  va_start(argp, format);
  _Bool outsider_ch = false; /* for outsider characters in the format string*/
  int ret = 0;
  if (!(*str_buf) && *format_buf) {
    ret = EOF;
  }
  while (*str_buf && *format_buf) {
    get_specifier(&str_buf, &format_buf, &outsider_ch); /* set format_buf to the start of specifier*/
    _Bool ass_supress = false; /* supress assignment (*) */
    int width = 0, length = 0;
    int specs = set_specs(&format_buf, &ass_supress, &width, &length); /* fill the specs number */
    scan_proc(&str_buf, &str_start, specs, &argp, ass_supress, outsider_ch, width, length, &ret);
  }
  va_end(argp);
  free((void*)str_start);
  free((void*)format_start); // free(): invalid pointer Aborted (core dumped)
  return ret;
}

my_sscanf.c 的驱动

#include <stdio.h>

int my_sscanf(const char *str, const char *format, ...);

int main(void) {
  int a1 = 0, a2 = 0, a3 = 0, a4 = 0;
  int b1 = 0, b2 = 0, b3 = 0, b4 = 0;
  const char *src = "5555621 0666 0x777 0X888";
  const char *format = "%8i %10i %i %i";
  int res = 0, test_res = 0;
  puts("===sscanf===");
  res = sscanf(src, format, &a1, &a2, &a3, &a4); 
  printf("a1 - a2: %d %d %d %d\n", a1, a2, a3, a4);
  printf("res: %d\n", res);
  puts("===s21_sscanf===");
  test_res = s21_sscanf(src, format, &b1, &b2, &b3, &b4); 
  printf("b1 - b2: %d %d %d %d\n", b1, b2, b3, b4);
  printf("test_res: %d\n", test_res);
  return 0;
}

当我使用格式字符串

"%8i %10i %i %i"
和源字符串
"5555621 0666 0x777 0X888"
进行测试时,我得到一个错误
free(): invalid pointer Aborted (core dumped)
。当我释放格式字符串时会发生此错误。但是我使用
malloc
为它分配了内存,所以它存储在堆中,我应该能够用
free
清除它。

特别为此,我将地址保存在

format_start
中。同样,我使用
str_start
并且没有错误。这是回溯显示的内容:

Program received signal SIGABRT, Aborted.
__pthread_kill_implementation (no_tid=0, signo=6, threadid=140737350555456) at ./nptl/pthread_kill.c:44
44  ./nptl/pthread_kill.c: No such file or directory.
(gdb) backtrace
#0  __pthread_kill_implementation (no_tid=0, signo=6, threadid=140737350555456)
    at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (signo=6, threadid=140737350555456) at ./nptl/pthread_kill.c:78
#2  __GI___pthread_kill (threadid=140737350555456, signo=signo@entry=6) at ./nptl/pthread_kill.c:89
#3  0x00007ffff7cda476 in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff7cc07f3 in __GI_abort () at ./stdlib/abort.c:79
#5  0x00007ffff7d216f6 in __libc_message (action=action@entry=do_abort, 
    fmt=fmt@entry=0x7ffff7e73b8c "%s\n") at ../sysdeps/posix/libc_fatal.c:155
#6  0x00007ffff7d38d7c in malloc_printerr (str=str@entry=0x7ffff7e71764 "free(): invalid pointer")
    at ./malloc/malloc.c:5664
#7  0x00007ffff7d3aac4 in _int_free (av=<optimized out>, p=<optimized out>, have_lock=0)
    at ./malloc/malloc.c:4439
#8  0x00007ffff7d3d4d3 in __GI___libc_free (mem=<optimized out>) at ./malloc/malloc.c:3391
#9  0x0000555555557128 in my_sscanf (str=0x555555558238 "5555621 0666 0x777 0X888", 
    format=0x555555558251 "%8i %10i %i %i") at my_sscanf.c:546
#10 0x0000555555557273 in main () at sscanf_driver.c:27
(gdb) frame 9
#9  0x0000555555557128 in my_sscanf (str=0x555555558238 "5555621 0666 0x777 0X888", 
    format=0x555555558251 "%8i %10i %i %i") at my_sscanf.c:546
warning: Source file is more recent than executable.
546   free((void*)format_start);

此外,使用格式字符串的其他值,一切正常,没有错误!你能告诉我是什么原因造成的吗?我该如何解决?谢谢

c scanf free abort
© www.soinside.com 2019 - 2024. All rights reserved.