How one can create a char array with an unspecified size and then pass it to snprint for formatting it?
For example:
char str[];
int total = 100;
int points = 20;
snprintf(str, sizeof(str), "You have %d points (total: %d)", points, total);
printf("%s\n",str);
This code is going certainly send me an error because first line is wrong.
All the examples I saw on internet had something like str[100].
I am new to C programming and I would appreciate it if you don't down vote this post.
Thank you
At some point you must know how long your string will be. Simple examples like this you can just choose a sufficiently big number. You're doing the right thing using snprintf if you happen to be wrong at some point. In cases where this isn't possible snprintf returns the number of characters it will use if the first two parameters are NULL and 0. From there you malloc and free buffers determined at runtime.
size_t size = snprintf(NULL, 0, ...);
char* str = malloc(size);
snprintf(str, size, ...);
//After doing what you want with the string
free(str);
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
Related
First I will explain what I want to program. I want to open a directory and get all the files names and their number (in the directory). I want to allocate certain amount of memory for the number of files I have found, and a certain amount of memory for their names. I will give an example.
Lets say we have got a directory named dir and that directory has 4 files in it. The 4 files are named as follows: lalo, camo, mara, sarw.
I want my program to work as follows
Go into dir and while finding files keep allocating memory in order to store them (like a table of strings where I don't know the exact size because I don't know how many files I will find). I am storing the filenames character by character and I keep allocating memory as needed, every time I read one char I am realocating memory and incrementing by 1. Same logic I follow to the files number where I keep incrementing the number of files and reallocating memory as needed. A table like this (pointers) is what I imagine filenames[numberOfFiles][stringCharacter].
I get a segmentation fault in function GetFiles(...) on the second loop here : *(*(entries+*number)+size) = dp->d_name[size];
int main()
{
int number,i;
char *mainDirectory = NULL,**names = NULL;
printf("Give the movies directory: ");
mainDirectory = ReadMainDirectory();
if(GetFiles(mainDirectory,&names,&number) != 0)
{
system("PAUSE");
return 1;
}
system("PAUSE");
return 0;
}
char* ReadMainDirectory()
{
char *dir,c;
int size = 1;
dir = (char*)malloc(size+1);
dir[size-1] = '\0';
while((c = getchar()) != '\n')
{
dir[size-1] = c;
size++;
dir = (char*)realloc(dir,size+1);
dir[size-1] = '\0';
}
return dir;
}
int GetFiles(char *dir,char ***names,int *number)
{
struct dirent *dp;
DIR *fd;
int size;
char **entries = NULL;
if ((fd = opendir(dir)) == NULL)
{
printf("Can't open directory %s!\n",dir);
return 0;
}
*number = 0;
size = 0;
entries = (char**)malloc((*number+1) * sizeof(char*));
*entries = (char*)malloc((size+1) * sizeof(char));
while ((dp = readdir(fd)) != NULL)
{
if (!strcmp(dp->d_name, ".") || !strcmp(dp->d_name, ".."))
{
continue;
}
size = 0;
while(dp->d_name[size] != '\0')
{
*(*(entries+*number)+size) = dp->d_name[size];
size++;
*(entries+*number) = (char*)realloc(entries[*number],(size+1) * sizeof(char));
}
entries[*number][size] = '\0';
printf("%s\n",entries[*number]);
(*number)++;
entries = (char**)realloc(entries,(*number+1) * sizeof(char*));
}
*names = entries;
closedir(fd);
return 1;
}
int GetStringSize(char *string)
{
int size = 0;
while(string[size] != '\0')
{
size++;
}
return size;
}
int StringEndsWith(char* string,char* extension)
{
return !strcmp(string + strlen(string) - strlen(extension), extension);
}
A proper implementation of getfiles (gets all the files in a directory in a dynamic array) and provides a deallocator:
#include <stdio.h>
#include <dirent.h>
#include <string.h>
#include <stdlib.h>
/* returns a NULL terminated array of files in directory */
char **getfilelist(const char *dirname) {
DIR *dp;
char **entries = NULL;
struct dirent *entry;
off_t index = 0;
dp = opendir(dirname);
if (!dp) {
perror(dirname);
return NULL;
}
while((entry = readdir(dp)) != NULL) {
/* increase entries array size by one pointer-size */
entries = realloc(entries, (1+index)*sizeof(char *));
/* strdup returns a newly allocated duplicate of a string that
* you must free yourself
*/
entries[index] = strdup(entry->d_name);
index++;
}
/* need one more entry for NULL termination */
entries = realloc(entries, (1+index)*sizeof(char *));
entries[index] = NULL;
return entries;
}
void putfilelist(char **entries) {
char **p;
if(!entries)
return;
for(p = entries; *p !=NULL ; p++) {
free(*p); /* free all the strdups */
}
free(entries); /* free the array of pointers */
}
An example of how you could use it:
int main(int argc, char **argv) {
char **p, **entries;
char *dir = (argc == 1 ? "." : argv[1]);
entries = getfilelist(dir);
for (p = entries; p && *p; p++) {
printf("%s\n", *p);
}
putfilelist(entries);
}
Updated solution,
In order to implement your solution without using any of the library code, and keep it to system calls, here is an example that implements everything that the above example does, without relying on higher-level library calls.
Note
The bottom functions are the same as the above example.
/*
* A partially low-level implementation using a direct system calls
* and internal memory management
*
* This is an opinionated (linux only) implementation of OP
*
* linux headers are only included for some of the constants and
* where it would be trivial to re-implement system calls (e.g. open, write etc.)
* which I am too lazy to do in this example.
*
*/
#define NDEBUG
#include <stdarg.h>
#include <unistd.h>
#include <sys/syscall.h>
#include <linux/fcntl.h>
#include <linux/unistd.h>
/* replace this macro with your own error handling messages */
#define handle_error(msg) \
do { my_fdputs(2, msg); my_exit(-127); } while (0)
#if !defined(NDEBUG)
#define assert(x) do { int __rv = (x); if(!__rv) { my_fdprintf(2, "assertion failed %s\n", #x); my_exit(__rv); } } while(0)
#else
#define assert(x) do {} while(0)
#endif
/* returns a NULL terminated array of files in directory */
/* low-level list builder using getdents */
void my_exit(int x)
{
syscall(SYS_exit_group, x);
}
/* a trivial malloc / realloc / free */
/* simple linked list memory accounting */
struct memblock {
struct memblock *next;
size_t size;
int free; /* flag */
int magic; /* canary value for debugging */
};
#define METASIZE sizeof(struct memblock)
void *global_base = NULL;
struct memblock *find_free_block(struct memblock **last, size_t size)
{
struct memblock *current = global_base;
while(current && !(current->free && current->size >= size)) {
*last = current;
current = current->next;
}
return current;
}
/*
* instead of using sbrk, we should really use mmap on newer
* linux kernels and do better accounting, however this is a
* simple example to get you started
*/
struct memblock *request_space(struct memblock *last, size_t size)
{
struct memblock *block;
void *request;
block = sbrk(0); /* get current program break */
request = sbrk(size + METASIZE);
assert((void *)block == request);
if(request == (void *)-1) {
return NULL;
}
if(last) {
last->next = block;
}
block->size = size;
block->next = NULL;
block->free = 0;
block->magic = 0x12345678;
return block;
}
struct memblock *get_memblock_ptr(void *ptr)
{
return (struct memblock *)ptr - 1;
}
/* a simple memcpy, can be optimized by taking alignment into account */
void *my_memcpy(void *dst, void *src, size_t len)
{
size_t i;
char *d = dst;
const char *s = src;
struct memblock *bd, *bs;
bd = get_memblock_ptr(dst);
for(i = 0; i < len; i++) {
d[i] = s[i];
}
return dst;
}
/* now to implement malloc */
void *my_malloc(size_t size)
{
struct memblock *block;
if(size == 0)
return NULL;
if(!global_base) {
block = request_space(NULL, size);
if(!block)
return NULL;
global_base = block;
}
else {
struct memblock *last = global_base;
block = find_free_block(&last, size);
if(!block) {
block = request_space(last, size);
if(!block) {
return NULL;
}
}
else {
block->free = 0;
block->magic = 0x77777777;
}
}
return (block+1);
}
void my_free(void *ptr)
{
struct memblock *block;
if (!ptr)
return;
block = get_memblock_ptr(ptr);
assert(block->free == 0);
assert(block->magic == 0x77777777 || block->magic == 0x12345678);
block->free = 1;
block->magic = 0x55555555;
}
void *my_realloc(void *ptr, size_t size)
{
struct memblock *block;
void *newptr;
if(!ptr)
return my_malloc(size);
block = get_memblock_ptr(ptr);
if(block->size >= size)
return ptr;
newptr = my_malloc(size);
if(!newptr) {
return NULL;
}
my_memcpy(newptr, ptr, block->size);
my_free(ptr);
return newptr;
}
/* trivial string functions */
size_t my_strlen(const char *src) {
size_t len = 0;
while(src[len])
len++;
return len;
}
char *my_strdup(const char *src)
{
char *dst;
char *p;
size_t len = 0, i;
len = my_strlen(src);
dst = my_malloc(1+len);
if(!dst)
return NULL;
for(i = 0; i < len; i++) {
dst[i] = src[i];
}
dst[i] = 0;
return dst;
}
/* trivial output functions */
my_fdputs(int fd, const char *str)
{
return write(fd, str, my_strlen(str));
}
int my_fdputc(int fd, char c)
{
return write(fd, &c, sizeof(char));
}
/* a very limited implementation of printf */
int my_fdvprintf(int fd, const char *fmt, va_list ap)
{
const char *p;
int count = 0;
for(p = fmt; p && *p; p++ ) {
if(*p == '%') {
p++;
switch(*p) {
case 's':
count += my_fdputs(fd, va_arg(ap, char *));
break;
case '%':
count += my_fdputc(fd, '%');
break;
default:
#ifndef NDEBUG
my_fdputs(2, "warning: unimplemented printf format specifier %");
my_fdputc(2, *p);
my_fdputc(2, '\n');
#endif
break;
}
}
else {
my_fdputc(fd, *p);
}
}
return count;
}
int my_fdprintf(int fd, const char *fmt, ...)
{
int rv;
va_list ap;
va_start(ap, fmt);
rv = my_fdvprintf(fd, fmt, ap);
va_end(ap);
return rv;
}
/* wrapper to linux getdents directory entry call */
/* linux dirent structure */
struct linux_dirent {
long d_ino;
off_t d_off;
unsigned short d_reclen;
char d_name[];
};
/* system call wrapper */
int getdents(int fd, void *buf, size_t bufsize)
{
return syscall(SYS_getdents, fd, buf, bufsize);
}
/* reimplement getfilelist using our getdents */
#define BUF_SIZE 1024
char **getfilelist(const char *dirname) {
int fd, nread;
char **entries = NULL;
off_t index = 0;
char buf[BUF_SIZE];
struct linux_dirent *d;
int bpos;
/* O_DIRECTORY since kernel 2.1 */
fd = open(dirname, O_DIRECTORY|O_RDONLY);
if (fd < 0) {
handle_error(dirname);
}
for(;;) {
nread = getdents(fd, buf, BUF_SIZE);
if (nread == -1)
handle_error("getdents");
if (nread == 0)
break;
for (bpos = 0; bpos < nread;) {
d = (struct linux_dirent *) (buf + bpos);
entries = my_realloc(entries, (1+index) * sizeof(char *));
entries[index++] = my_strdup(d->d_name);
bpos += d->d_reclen;
}
}
/* need one more entry for NULL termination */
entries = my_realloc(entries, (1+index)*sizeof(char *));
entries[index] = NULL;
close(fd);
return entries;
}
void putfilelist(char **entries) {
char **p;
if(!entries)
return;
for(p = entries; *p !=NULL ; p++) {
my_free(*p); /* free all the strdups */
}
my_free(entries); /* free the array of pointers */
}
int main(int argc, char **argv) {
char **p, **entries;
char *dir = (argc == 1 ? "." : argv[1]);
entries = getfilelist(dir);
for (p = entries; p && *p; p++) {
my_fdprintf(1, "%s\n", *p);
}
putfilelist(entries);
}
Hope you enjoy
I have a problem with reversing string using stack structure.
I made some code to reverse 'apple' to 'elppa' and seems like it worked well...
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MAX_STACK_SIZE 5
typedef int element;
element stack[MAX_STACK_SIZE];
int top = -1;
void initialize() {
top = -1;
}
int isEmpty() {
return (top == -1);
}
int isFull() {
return (top == (MAX_STACK_SIZE - 1));
}
void push(element item) {
if (isFull()) {
printf("stack is full, cannot add element.\n");
}
else{
stack[++top] = item;
}
}
element pop() {
if (isEmpty()) {
printf("stack is empty\n");
}
else {
return stack[top--];
}
}
char* reverse(char* s) {
const int len = sizeof(s) + 1;
char* rstring = new char[len];
initialize();
for (int i = 0; i < len; i++) {
push(s[i]);
}
int tmp = 0;
while(isEmpty()==false){
rstring[tmp++]=pop();
}
for (int i = 0; i < len; i++) {
printf("%c\n", rstring[i]);
}
return rstring;
}
void main() {
char* str1 = "apple";
char* str2 = reverse(str1);
printf("before : %s \n", str1);
printf("after : %s \n", str2);
getchar();
}
the result is here I got the answer(elppa) but it also printed out some other characters what I wasn`t intended. Why I got this thing? It may something to do with memory array or something but do not know exactly what was happened memory. How to fix the problem?
You haven't taken into account the string termination character '\0'. You are printing the sting using %s with printf.
Once rstring has been calculated after unwinding the stack, you should append '\0' to rstring. Hopefully this will solve your problem.
By using sizeof you will get the data_type size (in this case, size of char*) in bytes. You should use strlen instead.
This is a simple program that should create a substring from a string, then it should return the substring as something that can be printed out.
It's actually an exercise and only the substring function can be changed. The problem is that I can't find a return type that doesn't spark all kinds of warnings and errors.
How should I change the return type?
static void panic(const char *serror)
{
printf("%s", serror);
exit(1);
}
static void *xmalloc(size_t size)
{
void *ptr;
if (size == 0)
panic("Size is 0!\n");
ptr = malloc(size);
if (!ptr)
panic("No mem left!\n");
return ptr;
}
static char *substring(const char *str, off_t pos, size_t len)
{
char out [len];
int index;
for(index = 0; index < (pos + len); index++)
{
if(index >= pos && index < (pos + len))
{
out[index - pos] = str[index];
}
}
return out;
}
int main(int argc, char **argv)
{
char *foo = "Nicht\n";
char *bar = substring(foo, 2, 3);
printf("%s", bar);
free(bar);
return 0;
}
You invoked two undefine behavior by
dereferencing a pointer bar that points at already vanished local variable.
passing a non-NULL pointer which doesn't point at buffer allocated via malloc(), calloc() or realloc().
Also note that
You have to terminate the string by adding null character.
Your loop is not efficient.
corrected code:
static char *substring(const char *str, off_t pos, size_t len)
{
char *out = xmalloc(len + 1);
int index;
for(index = pos; index < (pos + len); index++)
{
out[index - pos] = str[index];
}
out[len] = '\0';
return out;
}
I am a beginner in C. Below is my scenario - I have created a pointer variable in main function and it has been passed on to several functions(in this example 2 levels). And one of the functions frees it up. Now I need to have check in Main to see whether the pointer is freed or not, that means i need to setup the value of &str in main() to point to NULL. Not sure my approach is right here. Any help would be much appreciated
void func2(char *str)
{
free(str);
}
void func1(char *str)
{
func2(str);
}
int main()
{
char *str;
str=(char *) malloc(10);
func1(str);
if(str){ do something; } // if condition to check whether str is freed
}
#include <stdio.h>
#include <stdlib.h>
func2(char **str)
{
free(*str); //free
*str = NULL; //Set to NULL
}
func1(char **str) //func1 receives as **
{
func2(str); //Pass pointer to func2()
}
int main()
{
char *str = NULL;
str=(char *) malloc(10);
func1(&str); //Pass Address of pointer to func1()
if(str) //Check for NULL
{
printf("\n Not - Freed...\n");
}
else
{
printf("\n Freed...\n");
}
return 0;
}
In C all are pass by value. I suggest to study http://www.cs.fsu.edu/~myers/cgs4406/notes/pointers.html for understanding of this.
You could try something like this - first redefine malloc and free (track.h)
#ifndef track_h
#define track_h
extern void* trackmalloc(size_t size);
extern void trackfree(void* array);
extern void trackismalloc(void* array);
#define malloc trackmalloc
#define free trackfree
#endif
Then for every piece of code that uses malloc and free, replace #include with #include "track.h"
#include <stdlib.h>
#include <stdio.h>
#include "track.h" /* was <malloc.h> */
// A function which has a 20% chance of freeing the pointer
void twentypercent(char* array)
{
if (rand() < (RAND_MAX / 5))
free(array);
}
int main(int argc, char* argv[])
{
char* list = malloc(256);
int ii;
for (ii = 0; ii < 10; ++ii)
twentypercent(list);
if (trackismalloc(list)
printf("Not freed yet");
return 0;
}
Now define track.c. This will only free memory that has been allocated by by trackmalloc. If it was not allocated by trackmalloc, then it will report that the memory has already been freed.
#include <stdio.h>
#include <malloc.h>
#define TRACKER_MAX 2048
static void* tracker[TRACKER_MAX] = { 0 };
static int track_last = -1;
void* trackmalloc(size_t size)
{
// For simplicity, tracker will not be reused
tracker[++track_last] = malloc(size);
return tracker[track_last];
}
void trackfree(void* array)
{
// This will slow down as the list gets filled up.
// You will need a more efficient way of searching lists (possibly bsearch)
int tt;
for (tt = 0; tt < track_last; ++tt)
{
if (array == tracker[tt])
{
free(tracker[tt]);
tracker[tt] = 0;
break;
}
}
if (tt == track_last)
printf("%p already freed\n", array);
}
int trackismalloc(void* array)
{
// This will slow down as the list gets filled up.
// You will need a more efficient way of searching lists (possibly bsearch)
int tt, result = 0;
for (tt = 0; tt < track_last; ++tt)
{
if (array == tracker[tt])
{
result = 1;
break;
}
}
return result;
}
void func1(char** str) {
free(*str);
*str = NULL;
}
void func2(char** str) {
free(*str);
*str = NULL;
}
int main() {
char *str;
str = (char*) malloc(10);
func1(&str);
if (str) {
do something;
}
}
void func2(char **str)
{
free(*str);
*str = 0;
}
void func1(char **str)
{
func2(str);
}
int main()
{
char *str;
// I'd recommend using sizeof(type_you_want) * amount_of_elements instead of
// a constant number: -> malloc(sizeof(char) * 10);
str=(char *) malloc(10);
func1(&str); // You must pass the address of the pointer, because you want
// to change "WHAT IT POINTS TO", not "WHAT IS POINTED BY IT"
if(str){ do something; } // if condition to check whether str is freed
}
When you call a function in C, you pass a copy of those arguments, so you are passing a copy of that pointer (that copy still points to the same place, so you can change that place that it points to) but you want to change the pointer value, so you need to pass its address.
I have explained a little bit how pointers inside functions can be used in here
#include <stdio.h>
#include <stdlib.h>
void func2(char **str)
{
printf("%d %s\n",__LINE__,__func__);
free(*str);
*str = NULL;
}
void func1(char **str)
{
printf("%d %s\n",__LINE__,__func__);
func2(str);
}
char * allocaMem(char **ptr)
{
*ptr=(char *) malloc(sizeof(char)* 10);
if(!*ptr)
{
perror("");
}
else
{
return *ptr;
}
}
int main()
{
char *str = allocaMem(&str);
if (!str) {
printf("Error in malloc()\n");
return -1;
}
func1(&str);
if (str) {
printf("Memory Not freed\n");
} else {
printf("Memory freed\n");
}
}
i got a problem with my C code.
int split(char* source, char*** target, char* splitChar) {
int i;
int currentLength;
int splitCharPosition;
char* currentSubstring = source;
int splitCount = charcount(source, splitChar) + 1;
*target = (char**) malloc(splitCount * sizeof(char**));
for(i=0;i<splitCount;i++) {
splitCharPosition = indexOf(currentSubstring, splitChar);
substring(currentSubstring, target[i], 0, splitCharPosition);
currentLength = strlen(currentSubstring);
substring(currentSubstring, ¤tSubstring, splitCharPosition + 1, curr entLength-splitCharPosition);
}
return splitCount;
}
The problem is that if I use the Debugger, the pointer to splitChar is set to 0x0 after the first run of the for loop.
Does anybody know why it is set to 0x0?
EDIT:
int indexOf(char* source, char* template) {
int i;
int j;
int index;
for (i = 0; source[i]; i++) {
index = i;
for (j = 0; template[j]; j++) {
if (source[i + j] != template[j]) {
index = -1;
break;
}
}
if (index != -1) {
return index;
}
}
return -1;
}
EDIT2:
int charcount(char* source, const char* countChar) {
int i;
int count = 0;
for(i=0;source[i];i++) {
if(source[i] == countChar[0]) {
count++;
}
}
return count;
}
EDIT3:
char* substring(char* source, char** target, int start, int length) {
*target = (char*) malloc(length + 1);
strncpy(*target, source + start, length);
target[length] = '\0';
return *target;
}
EDIT4:
I just noticed that if I add
char* sndfpgjps = splitChar;
to my split() code it does not delete the reference. Anyone know why?
This line:-
substring(currentSubstring, ¤tSubstring, splitCharPosition + 1, curr entLength-splitCharPosition);
... will cause a memory leak, as well as being incredibly inefficient. The old substring is left dangling. and never freed.
It would be much better to write
currentSubString += splitCharPosition + 1;
I don't think that's the problem, but it's a problem.
Also, as you're using C library functions like strlen(), why aren't you using strtok or better yet, strtok_r?
I have some reservations about the code, but this works cleanly under valgrind (no leaks, no abuse). I've left the sub-functions largely unchanged except that constant strings are marked constant. The code in split() has been simplified. As I noted in a comment, I suggest writing the main split() function so that you have a local char **string_list; which you allocate and fill. Then, when you're about to return, you assign *target = string_list;. This will make it easier for you to understand what's going on. Triple indirection is nasty. You can justify it here (just), but minimize the time you spend working with triple pointers. The revision adopts that strategy.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern int split(const char *source, char ***target, const char *splitStr);
static int
indexOf(const char *source, const char *template)
{
int i;
int j;
int index;
for (i = 0; source[i]; i++)
{
index = i;
for (j = 0; template[j]; j++)
{
if (source[i + j] != template[j])
{
index = -1;
break;
}
}
if (index != -1)
return index;
}
return -1;
}
static int
charcount(const char *source, const char *countChar)
{
int count = 0;
for (int i = 0; source[i]; i++)
{
if (source[i] == countChar[0])
count++;
}
return count;
}
static char *
substring(const char *source, int start, int length)
{
char *target = (char *)malloc(length + 1);
if (target != 0)
{
memmove(target, source + start, length);
target[length] = '\0';
}
return target;
}
int
split(const char *source, char ***target, const char *splitStr)
{
int splitCount = charcount(source, splitStr) + 1;
char **result = (char **)malloc(splitCount * sizeof(*result));
if (result == 0)
return -1;
int splitLength = strlen(splitStr);
char **next = result;
const char *currentSubstring = source;
for (int i = 0; i < splitCount; i++)
{
int splitCharPosition = indexOf(currentSubstring, splitStr);
if (splitCharPosition < 0)
break;
*next++ = substring(currentSubstring, 0, splitCharPosition);
currentSubstring += splitCharPosition + splitLength;
}
*next++ = substring(currentSubstring, 0, strlen(currentSubstring));
*target = result;
return (next - result); /* Actual number of strings */
}
static void print_list(int nstrings, char **strings)
{
for (int i = 0; i < nstrings; i++)
{
if (strings[i] != 0)
printf("%d: <<%s>>\n", i, strings[i]);
}
}
static void free_list(int nstrings, char **strings)
{
for (int i = 0; i < nstrings; i++)
free(strings[i]);
free(strings);
}
int main(void)
{
const char source[] = "This is a string; it is really!";
char **strings;
int nstrings;
nstrings = split(source, &strings, " ");
printf("Splitting: <<%s>> on <<%s>>\n", source, " ");
print_list(nstrings, strings);
free_list(nstrings, strings);
nstrings = split(source, &strings, "is");
printf("Splitting: <<%s>> on <<%s>>\n", source, "is");
print_list(nstrings, strings);
free_list(nstrings, strings);
return 0;
}
Note that in the second example, charcount() returns 6 but there are only 4 strings. This caused a late adjustment to the source code. (You could realloc() the result so it is exactly the right size, but it probably isn't worth worrying about unless the discrepancy is really marked — say 'more than 10 entries'.) The error handling is not perfect; it doesn't access invalid memory after failure to allocate, but it doesn't stop trying to allocate, either. Nor does it report failures to allocate individual strings — it does for failure to allocate the array of pointers.
I'd probably avoid the triple pointer by creating a structure:
typedef struct StringList
{
size_t nstrings;
char **strings;
} StringList;
You can then pass a pointer to one of these into split(), and into the utility functions such as free_list() and print_list(). The free_list() function would then modify the structure so that both elements are zeroed after the data pointed at by the structure is freed.
I'd also be tempted to use a different implementation of indexOf():
int indexOf(const char *haystack, const char *needle)
{
const char *pos = strstr(haystack, needle);
if (pos != 0)
return (pos - haystack);
return -1;
}
I do not know what substring does, nor what signature it has, but in the line
substring(currentSubstring, target[i], 0, splitCharPosition);
target[i] is only defined for i==0. I believe you wanted to write
substring(currentSubstring, (*target)[i], 0, splitCharPosition);
See if your debugger also supports data breakpoints, i.e. break if some place in memory is modified. Then place one at the actual address of splitChar, and another at the address it points to. (Since you didn't specify whether the pointer is null or points to nil.) See where it breaks. It may be that it is a completely unrelated place; that would indicate a buffer overflow.
Also, you could make at least splitChar a pointer to const. You don't actually want to modify it, right? Better idea, make it a char, not a pointer, since its name suggests that there is only one character on which you split, not a string.
The first call to substring does not look correct:
substring(currentSubstring, target[i], 0, splitCharPosition);
I suspect it should be something like the following where it indexes the actual memory that was allocated:
substring(currentSubstring, &((*target)[i]), 0, splitCharPosition);
You first need to get the value that target points at (*target) and then index off of that and pass the address of that array location.