patterncMinor
A dynamic string interface in C
Viewed 0 times
interfacestringdynamic
Problem
So I've written up what the title says.
Obviously, I'm looking for speed, safety and ease of use. To achieve the perfect ratio I've made some functions straight away crash if the input is wrong (in debug mode only, otherwise UD). The design is inspired by the C++ counterpart.
Will be glad to hear other opinions. (It's a lot of code, so bear with me.)
dynstring.h
dynstring.c
```
#include "dynstring.h"
#include
#include
#include
#include
#include
bool string_create(string* str)
{
assert(str);
// allocate 1 for '\0'
str->data = malloc(sizeof(char));
if (!str->data) {
return false;
}
str->size = 0;
str->data[0] = '\0';
return true;
}
bool string_create_n(string* str, size_t size)
{
asse
Obviously, I'm looking for speed, safety and ease of use. To achieve the perfect ratio I've made some functions straight away crash if the input is wrong (in debug mode only, otherwise UD). The design is inspired by the C++ counterpart.
Will be glad to hear other opinions. (It's a lot of code, so bear with me.)
dynstring.h
#ifndef DYNSTRING_H
#define DYNSTRING_H
#include
#include
typedef struct {
char* data;
size_t size;
} string;
extern bool string_create(string*);
extern bool string_create_n(string*, size_t);
extern bool string_create_from_raw(string*, const char*);
extern void string_destroy(string*);
extern bool string_copy(string*, string);
extern bool string_copy_from_raw(string*, const char*);
extern void string_set(string*, size_t, char);
extern char string_get(string, size_t);
extern char* string_front(string);
extern char* string_back(string);
extern char* string_to_raw(string);
extern size_t string_size(string);
extern bool string_empty(string);
extern void string_erase(string*, size_t, size_t);
extern bool string_push_back(string*, char);
extern bool string_push_front(string*, char);
extern bool string_pop_back(string*);
extern bool string_pop_front(string*);
extern bool string_append(string*, string);
extern bool string_append_raw(string*, const char*);
extern bool string_substr(string, size_t, size_t, string*);
extern void string_swap(string*, string*);
extern int string_compare(string, string);
extern size_t string_find(string, char);
extern size_t string_find_substr(string, const char*);
#endifdynstring.c
```
#include "dynstring.h"
#include
#include
#include
#include
#include
bool string_create(string* str)
{
assert(str);
// allocate 1 for '\0'
str->data = malloc(sizeof(char));
if (!str->data) {
return false;
}
str->size = 0;
str->data[0] = '\0';
return true;
}
bool string_create_n(string* str, size_t size)
{
asse
Solution
Improvements
Initializing functions (string_create*)
I think it's a common practice that initializing functions return whole structure element. For example, your implementation
should look like this
It's also applicable to free/destroy function - this function should destroy whole object (not just data).
Try realloc before malloc
Let's say you calling
Be careful with Memory leaks
Your code contains a lot of problems with memory management. For example
Imagine that there is an allocated array in
Suggestions
numerous benefits to follow this advice.
TODO List
My version
dynstring.c
```
#include "dynstring.h"
#include
#include
#include
#include
dyn_string* dyn_string_alloc(const size_t n)
{
dyn_string *p;
assert(n);
p = (dyn_string *) malloc (sizeof (dyn_string));
assert(p);
p->data = (char ) malloc (n sizeof (char));
assert(p->data);
p->size = 0;
p->data[0] = '\0';
p->alloc_size = n;
return p;
}
void dyn_string_realloc(dyn_string * p, const size_t n)
{
char *c;
assert(p);
if (n > p->alloc_size) {
c = (char ) realloc(p->data, sizeof(char) n);
if(c == 0) {
c = (char ) malloc(sizeof(char) n);
assert(c);
memcpy(c, p->data, sizeof(char) * (p->size + 1));
free(p->data);
p->data = c;
}
p->alloc_size = n;
}
}
dyn_string dyn_string_alloc_raw(const char src)
{
size_t n = strlen(src) + 1;
dyn_string *p = dyn_string_alloc(n);
assert(p);
memcpy(p->data, src, sizeof(char) * n);
p->size = n - 1;
return p;
}
dyn_string dyn_string_alloc_cpy(const dyn_string src)
{
assert(src);
size_t n = src->size + 1;
dyn_string *p = dyn_string_alloc(n);
memcpy(p->data, src->data, sizeof(char) * n);
p->size = src->size;
p->alloc_size = n;
return p;
}
void dyn_string_free(dyn_string * p)
{
assert(p);
free(p->data);
free(p);
}
void dyn_string_cpy(dyn_string dst, const dyn_string src)
{
assert(dst && src);
size_t n = src->size + 1;
if (dst->alloc_size data, src->data, sizeof(char) * n);
dst->size = src->size;
}
void dyn_string_cpy_raw(dyn_string dst, const char src)
{
assert(dst && src);
size_t n = strlen(src) + 1;
if (dst->alloc_size data, src, sizeof(char) * n);
dst->size = n - 1;
}
void dyn_string_set(dyn_string* p, const size_t i, const char c)
{
assert(p && p->size > i);
p->data[i] = c;
}
char dyn_string_get(const dyn_string* p, const size_t i)
{
assert(p->size > i);
return p->data[i];
}
char dyn_string_front(const dyn_string p) {
return p->data;
}
char dyn_string_back(const dyn_string p) {
return p->data + p->size;
}
size_t dyn_string_size(const dyn_string * p) {
return p->size;
}
// erases characters iddn range [i, i+n] including
void dyn_string_erase(dyn_string* p, const size_t i, const size_t n)
{
assert(p && p->size > i);
if (i + n + 1 >= p->size) {
p->size = i;
} else {
p->size -= (n + 1);
memcpy(p->data + i,
p->data + i + n + 1,
sizeof(char) * (p->size - i));
}
p->data[p->size] = '\0';
}
void dyn_string_push_back(dyn_string* p, const char c)
{
assert(p);
if (p->size + 1 alloc_size) {
dyn_string_realloc(p, p->alloc_sizedata[p->size++] = c;
p->data[p->size] = '\0';
}
void dyn_string_push_front(dyn_string* p, const char c)
{
assert(p);
if (p->s
Initializing functions (string_create*)
I think it's a common practice that initializing functions return whole structure element. For example, your implementation
extern bool string_create_n(string* str, size_t size);should look like this
extern string * string_create_n(size_t size);It's also applicable to free/destroy function - this function should destroy whole object (not just data).
Try realloc before malloc
Let's say you calling
string_push_back function, so it basically means that you would like to add one more character to the data. Instead of using malloc in a first place I would suggest to try to extend current array by one sizeof(char) using realloc. I also introduced one more variable to the structure - size_t alloc_size keeps actual size of allocated memory. It allows to avoid frequent reallocations events (there is invariant that alloc_size >= size + 1).Be careful with Memory leaks
Your code contains a lot of problems with memory management. For example
bool string_create(string* str)
{
assert(str);
// allocate 1 for '\0'
str->data = malloc(sizeof(char));
if (!str->data) {
return false;
}
str->size = 0;
str->data[0] = '\0';
return true;
}Imagine that there is an allocated array in
str->data. By making malloc before freeing, you losing link to the first array. Suggestions
- I prefer to use const modifier as much as possible. There are
numerous benefits to follow this advice.
- Thanks to @toby-speight' comment I get to know that identifier should not start from reserved word like
str.
TODO List
- You should remove assert and write meaningful error messages. Instead of returning bool value, use int values which should be macro-defined.
- dyn_string_push_front and dyn_string_pop_front are implemented not-optimally.
- Structure dyn_string contains size of the data, so you can skip '\0' ending. It's good idea because, first, you will save memory and, second, you will avoid actions related to '\0' maintenance.
My version
dynstring.c
```
#include "dynstring.h"
#include
#include
#include
#include
dyn_string* dyn_string_alloc(const size_t n)
{
dyn_string *p;
assert(n);
p = (dyn_string *) malloc (sizeof (dyn_string));
assert(p);
p->data = (char ) malloc (n sizeof (char));
assert(p->data);
p->size = 0;
p->data[0] = '\0';
p->alloc_size = n;
return p;
}
void dyn_string_realloc(dyn_string * p, const size_t n)
{
char *c;
assert(p);
if (n > p->alloc_size) {
c = (char ) realloc(p->data, sizeof(char) n);
if(c == 0) {
c = (char ) malloc(sizeof(char) n);
assert(c);
memcpy(c, p->data, sizeof(char) * (p->size + 1));
free(p->data);
p->data = c;
}
p->alloc_size = n;
}
}
dyn_string dyn_string_alloc_raw(const char src)
{
size_t n = strlen(src) + 1;
dyn_string *p = dyn_string_alloc(n);
assert(p);
memcpy(p->data, src, sizeof(char) * n);
p->size = n - 1;
return p;
}
dyn_string dyn_string_alloc_cpy(const dyn_string src)
{
assert(src);
size_t n = src->size + 1;
dyn_string *p = dyn_string_alloc(n);
memcpy(p->data, src->data, sizeof(char) * n);
p->size = src->size;
p->alloc_size = n;
return p;
}
void dyn_string_free(dyn_string * p)
{
assert(p);
free(p->data);
free(p);
}
void dyn_string_cpy(dyn_string dst, const dyn_string src)
{
assert(dst && src);
size_t n = src->size + 1;
if (dst->alloc_size data, src->data, sizeof(char) * n);
dst->size = src->size;
}
void dyn_string_cpy_raw(dyn_string dst, const char src)
{
assert(dst && src);
size_t n = strlen(src) + 1;
if (dst->alloc_size data, src, sizeof(char) * n);
dst->size = n - 1;
}
void dyn_string_set(dyn_string* p, const size_t i, const char c)
{
assert(p && p->size > i);
p->data[i] = c;
}
char dyn_string_get(const dyn_string* p, const size_t i)
{
assert(p->size > i);
return p->data[i];
}
char dyn_string_front(const dyn_string p) {
return p->data;
}
char dyn_string_back(const dyn_string p) {
return p->data + p->size;
}
size_t dyn_string_size(const dyn_string * p) {
return p->size;
}
// erases characters iddn range [i, i+n] including
void dyn_string_erase(dyn_string* p, const size_t i, const size_t n)
{
assert(p && p->size > i);
if (i + n + 1 >= p->size) {
p->size = i;
} else {
p->size -= (n + 1);
memcpy(p->data + i,
p->data + i + n + 1,
sizeof(char) * (p->size - i));
}
p->data[p->size] = '\0';
}
void dyn_string_push_back(dyn_string* p, const char c)
{
assert(p);
if (p->size + 1 alloc_size) {
dyn_string_realloc(p, p->alloc_sizedata[p->size++] = c;
p->data[p->size] = '\0';
}
void dyn_string_push_front(dyn_string* p, const char c)
{
assert(p);
if (p->s
Code Snippets
extern bool string_create_n(string* str, size_t size);extern string * string_create_n(size_t size);bool string_create(string* str)
{
assert(str);
// allocate 1 for '\0'
str->data = malloc(sizeof(char));
if (!str->data) {
return false;
}
str->size = 0;
str->data[0] = '\0';
return true;
}#include "dynstring.h"
#include <stdbool.h>
#include <assert.h>
#include <stdlib.h>
#include <string.h>
dyn_string* dyn_string_alloc(const size_t n)
{
dyn_string *p;
assert(n);
p = (dyn_string *) malloc (sizeof (dyn_string));
assert(p);
p->data = (char *) malloc (n * sizeof (char));
assert(p->data);
p->size = 0;
p->data[0] = '\0';
p->alloc_size = n;
return p;
}
void dyn_string_realloc(dyn_string * p, const size_t n)
{
char *c;
assert(p);
if (n > p->alloc_size) {
c = (char *) realloc(p->data, sizeof(char) * n);
if(c == 0) {
c = (char *) malloc(sizeof(char) * n);
assert(c);
memcpy(c, p->data, sizeof(char) * (p->size + 1));
free(p->data);
p->data = c;
}
p->alloc_size = n;
}
}
dyn_string* dyn_string_alloc_raw(const char* src)
{
size_t n = strlen(src) + 1;
dyn_string *p = dyn_string_alloc(n);
assert(p);
memcpy(p->data, src, sizeof(char) * n);
p->size = n - 1;
return p;
}
dyn_string* dyn_string_alloc_cpy(const dyn_string * src)
{
assert(src);
size_t n = src->size + 1;
dyn_string *p = dyn_string_alloc(n);
memcpy(p->data, src->data, sizeof(char) * n);
p->size = src->size;
p->alloc_size = n;
return p;
}
void dyn_string_free(dyn_string * p)
{
assert(p);
free(p->data);
free(p);
}
void dyn_string_cpy(dyn_string * dst, const dyn_string * src)
{
assert(dst && src);
size_t n = src->size + 1;
if (dst->alloc_size < n) {
dyn_string_realloc(dst, n);
}
memcpy(dst->data, src->data, sizeof(char) * n);
dst->size = src->size;
}
void dyn_string_cpy_raw(dyn_string* dst, const char* src)
{
assert(dst && src);
size_t n = strlen(src) + 1;
if (dst->alloc_size < n) {
dyn_string_realloc(dst, n);
}
memcpy(dst->data, src, sizeof(char) * n);
dst->size = n - 1;
}
void dyn_string_set(dyn_string* p, const size_t i, const char c)
{
assert(p && p->size > i);
p->data[i] = c;
}
char dyn_string_get(const dyn_string* p, const size_t i)
{
assert(p->size > i);
return p->data[i];
}
char* dyn_string_front(const dyn_string * p) {
return p->data;
}
char* dyn_string_back(const dyn_string * p) {
return p->data + p->size;
}
size_t dyn_string_size(const dyn_string * p) {
return p->size;
}
// erases characters iddn range [i, i+n] including
void dyn_string_erase(dyn_string* p, const size_t i, const size_t n)
{
assert(p && p->size > i);
if (i + n + 1 >= p->size) {
p->size = i;
} else {
p->size -= (n + 1);
memcpy(p->data + i,
p->data + i + n + 1,
sizeof(char) * (p->size - i));
}
p->data[p->size] = '\0';
}
void dyn_string_push_back(dyn_string* p, const char c)
{
assert(p);
if (p->size + 1 <= p->alloc_size) {
dyn_string_realloc(p, p->alloc_size<<1);
}
p->data[p->size++] = c;
p->data[p->size]#ifndef DYNSTRING_H
#define DYNSTRING_H
#include <stddef.h>
typedef struct {
char* data;
size_t size;
size_t alloc_size;
} dyn_string;
extern dyn_string* dyn_string_alloc(const size_t);
extern void dyn_string_realloc(dyn_string*, const size_t);
extern dyn_string* dyn_string_alloc_raw(const char*);
extern dyn_string* dyn_string_alloc_cpy(const dyn_string*);
extern void dyn_string_free(dyn_string*);
extern void dyn_string_cpy(dyn_string*, const dyn_string*);
extern void dyn_string_cpy_raw(dyn_string*, const char*);
extern void dyn_string_set(dyn_string*, const size_t, const char);
extern char dyn_string_get(const dyn_string*, const size_t);
extern char* dyn_string_front(const dyn_string*);
extern char* dyn_string_back(const dyn_string*);
extern size_t dyn_string_size(const dyn_string*);
extern void dyn_string_erase(dyn_string*, const size_t, const size_t);
extern void dyn_string_push_back(dyn_string*, const char);
extern void dyn_string_push_front(dyn_string*, const char);
extern char dyn_string_pop_back(dyn_string*);
extern char dyn_string_pop_front(dyn_string*);
extern void dyn_string_append(dyn_string*, const dyn_string*);
extern void dyn_string_append_raw(dyn_string*, const char*);
extern void dyn_string_substr(dyn_string*, const dyn_string *, const size_t, const size_t);
extern void dyn_string_swap(dyn_string*, dyn_string*);
extern int dyn_string_compare(const dyn_string*, const dyn_string*);
extern size_t dyn_string_find(const dyn_string*, const char);
extern size_t dyn_string_find_substr(const dyn_string*, const char*);
#endifContext
StackExchange Code Review Q#159010, answer score: 2
Revisions (0)
No revisions yet.