C doesn't work like your typical high-level language.
You are required to alloc every bit of memory you want to use (either be it on the stack or on the heap). That's why on the internet people usually do stuff like char str[100]
: they are saying: I'm not expecting a string longer than 99 characters (+ '\0' char at the end of the string).
If you want your typical Java string
, you need to create it yourself from scratch. Here's a demo of an implementation with no-so-good performances. variadic string api is left to the reader :)
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
struct string_t {
///number of bytes of the string (\0 included)
size_t capacity;
///strlen of the string
size_t len;
///pointer to the actual string
char* str;
};
struct string_t* newString(const char* str);
void initString(struct string_t* this);
void destroyString(const struct string_t* this);
bool isEmpty(const struct string_t* this);
void tryExpand(struct string_t* this, size_t additionalSpace);
char* getNullTerminatorAddress(const struct string_t* this);
void setString(struct string_t* this, const char* other);
struct string_t* newString(const char* str) {
struct string_t* result = malloc(sizeof(struct string_t));
if (result == NULL) {
fprintf(stderr, "error!\n");
exit(1);
}
initString(result);
setString(result, str);
return result;
}
void initString(struct string_t* this) {
this->len = 0;
this->capacity;
this->str = 0;
}
void destroyString(const struct string_t* this) {
if (this->str != NULL) {
free((char*)this->str);
}
free((char*)this);
}
bool isEmpty(const struct string_t* this) {
return this->len == 0;
}
void tryExpand(struct string_t* this, size_t additionalSpace) {
if ((this->len + additionalSpace) > this->capacity) {
if (this->str != NULL) {
int newcapacity = this->len + additionalSpace;
this->str = realloc(this->str, newcapacity);
this->capacity = newcapacity;
} else {
this->str = malloc(sizeof(char) * additionalSpace);
if (this->str == NULL) {
exit(1);
}
}
}
}
void trySetting(struct string_t* this, size_t spaceRequired) {
if ((spaceRequired) > this->capacity) {
if (this->str != NULL) {
int newcapacity = spaceRequired;
this->str = realloc(this->str, newcapacity);
this->capacity = newcapacity;
} else {
this->str = malloc(sizeof(char) * spaceRequired);
if (this->str == NULL) {
exit(1);
}
this->capacity = spaceRequired;
}
}
}
char* getNullTerminatorAddress(const struct string_t* this) {
if (this->str != NULL) {
return &this->str[this->len];
} else {
exit(2);
}
}
void setString(struct string_t* this, const char* other) {
int alen = strlen(other);
trySetting(this, alen + 1);
//beware of buffer overrun
strcpy(this->str, other);
this->len = alen;
}
void appendString(struct string_t* this, const char* other) {
int alen = strlen(other);
tryExpand(this, alen + 1);
//beware of buffer overrun
strcpy(getNullTerminatorAddress(this), other);
this->len += alen;
}
void appendInt(struct string_t* this, int other) {
int bytes = snprintf( NULL, 0, "%d", other); //0 not considered
tryExpand(this, bytes + 1);
snprintf(getNullTerminatorAddress(this), bytes+1, "%d", other);
this->len += bytes;
}
const char* getString(const struct string_t* this) {
return (const char*)this->str;
}
int getLength(const struct string_t* this) {
return this->len;
}
int getCapacity(const struct string_t* this) {
return this->capacity;
}
int main() {
int points = 5;
int total = 10;
struct string_t* str = newString("You have ");
appendInt(str, points);
appendString(str, " points (total: ");
appendInt(str, total);
appendString(str, ")");
printf("%s\n", getString(str));
destroyString(str);
return 0;
}
Note you can retrieve the number of bytes to write in snprintf
thanks to the trick of user2622016