I am trying to do something useful for a wrapper I am working out. I am writing a wrapper for the standard string library for C because I don’t like it.
I don’t want to introduce C strings to my wrapper right now. Ihave a struct called `str which contains the string’s length and the buffer:
struct str
{
char *buf;
size_t len;
};
In my header file I have this:
typedef struct str str;
Now I have implemented encapsulation (I think).
So I declare strings like this:
str *a_string = NULL;
I have already done everything that I want to do with this, but I have one problem. What I would like to add something like this for one function:
str.h:
extern str *str_str(str *, char *);
and str.c
str *str_str(str *buf, char *ns)
{
buf->len = strlen(ns);
buf->buf = (char *) malloc(buf->len + 1);
strncpy(buf->buf, ns, buf->len);
return buf;
}
and testing this works well:
str *s = str_new();
printf("%s\n", str_cstr(str_str(s, "hello")));
output: hello
but will this work?
str_assign(s, str_str(s, "ok"));
That’s essentially assigning s to s, I think. When I print, it prints nothing!
I don’t get any errors!
All help is greatly appreciated. I am fairly new with the C language.
Source of str_assign():
void str_assign(str *s1, str *s2)
{
s1->len = s2->len;
s1->buf = (char *) malloc(s2->len + 1);
strncpy(s1->buf, s2->buf, s2->len);
}
I know this is C, but it is useful to compare this with C++. When you write an assignment operator in C++, you always have to consider the case of self-assignment. The same applies when you do the equivalent job in C.
Therefore, I think you need:
You can use
memcpy()because (a) the strings are guaranteed to be disjoint, and (b) you know how long the string is, so you don’t need to check for the end of string at each step of the way as you would withstrncpy()orstrcpy().Actually, there’s a case for saying you should never need to use
strcpy()orstrncpy()orstrcat()orstrncat(); you should always know how long the source and destination strings are (otherwise you can’t be sure that there won’t be a buffer overflow), so you can always usememmove()ormemcpy()instead.I also note that you’re going to have to worry about memory leaks at some point. I just amended the assignment above to release the old string before overwriting it with the new.
You could also optimize operations by only allocating new space when the new string is longer than the old. You could consider using
realloc()instead ofmalloc(), too. However, you must be careful of the memory leak trap. This is the wrong way to userealloc():If
realloc()fails, you’ve just overwritten your point with a null – losing the only reference you had to the old space. You should always store the result ofrealloc()in a different variable from the first argument:You might eventually decide to keep two lengths in your structure – the space allocated and the space used. Then you can reuse space more efficiently, only allocating more space when the new string is longer than the previously allocated space, but still allowing you to shorten a string at any time.