Segmentation fault when returning a struct - c

I am trying to do a pretty simple thing - it is reading a file and then turning it into a char** splitting it into lines. However when I return a struct containing the char** and size i get Segmentation fault. I read here: C segmentation fault before/during return statement that it's probably "mangled stack". I still however don't know what I did to mangle it. This is my code:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#include "comp_words.h"
#define BLOCK 4096
struct sized_str {
char* str;
long size;
};
struct sized_arr {
char** content;
int size;
};
struct sized_str readfile(char* name) {
FILE *f;
long filesize;
char *buf;
struct sized_str res;
int r, p = 0;
f = fopen(name, "r");
fseek(f, 0, SEEK_END);
filesize = ftell(f);
rewind(f);
buf = calloc(filesize + 1, sizeof(char));
while ((r = fread(buf + p, sizeof(char), BLOCK, f))) {
p += r;
}
res.str = buf;
res.size = filesize + 1;
return res;
}
struct sized_arr read_dict() {
struct sized_str file_content;
struct sized_arr result;
char *buf, *buf_cpy, *buf_cpy_point, *line, **res;
int i = 0, j, line_count = 0;
file_content = readfile("/var/tmp/twl06.txt");
buf = file_content.str;
buf_cpy = (char*)malloc(file_content.size * sizeof(char));
strcpy(buf_cpy, buf);
buf_cpy_point = buf_cpy;
while (strtok(buf_cpy_point, "\n\r")) {
line_count++;
buf_cpy_point = NULL;
}
res = (char**)malloc(sizeof(char*) * line_count);
while ((line = strtok(buf, "\n\r"))) {
res[i] = (char*)malloc(sizeof(char) * strlen(line));
j = 0;
while ((res[i][j] = tolower(line[j]))) {
j++;
}
buf = NULL;
}
free(buf_cpy);
result.size = line_count;
result.content = res;
return result;
}
// ...
int main (int argc, char** argv) {
struct sized_str input;
struct sized_arr dict;
dict = read_dict();
// ...
return 0;
The code segfaults while returning from read_dict function.

At least at first glance, this seems to have a couple of problems. First:
while ((line = strtok(buf, "\n\r"))) {
To use strtok you normally pass the buffer on the first all, then make subsequent calls passing "NULL" for the first parameter until strtok returns a NULL (indicating that it's reached the end of the buffer). [Edit: upon further examination, it's apparent this isn't really a bug -- as pointed out by #Casablanca, he sets buf to NULL in the loop so the second and subsequent iterations actually do pass NULL for the first parameter -- so the current code is a bit hard to understand and (at least arguably) somewhat fragile, but not actually wrong.]
Second, when you allocate your space, it looks like you're not allocating space for the terminating NUL:
res[i] = (char*)malloc(sizeof(char) * strlen(line));
At least at first glance, it looks like this should be:
res[i] = malloc(strlen(line)+1);
[As an aside, sizeof(char)==1 and casting the return from malloc can mask the bug of failing to #include <stdlib.h> to get a proper prototype in scope.]
Some of your other code isn't exactly wrong, but strikes me as less readable than ideal. For example:
j = 0;
while ((res[i][j] = tolower(line[j]))) {
j++;
}
This appears to be a rather obfuscated way of writing:
for (j=0; line[j] != '\0'; j++)
res[i][j] = tolower((unsigned char)line[j]);
Also note that when you call tolower, you generally need/want to cast the parameter to unsigned char (passing a negative value gives undefined behavior, and quite a few characters with accents, umlauts, etc., will normally show up as negative in the typical case that char is signed).
You also seem to have a memory leak -- read_dict calls readfile, which allocates a buffer (with calloc -- why not malloc?) and returns a pointer to that memory in a structure. read_dict receives the structure, but unless I've missed something, the struct goes out of scope without your ever freeing the memory it pointed to.
Rather than try to find and fix the problem you've seen, my immediate reaction would be to start over. It seems to me that you've made the problem considerably more complex than it really is. If I were doing it, I'd probably start with a function to allocate space and read a line into the space, something on this order:
// Warning: Untested code.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *readline(FILE *file) {
char *buffer = NULL;
size_t current_size = 1;
char *temp;
const int block_size = 256;
do {
if (NULL == (temp = realloc(buffer, current_size+block_size)))
break;
buffer = temp;
buffer[current_size-1] = '\0';
if (fgets(buffer+current_size-1, block_size, file)==NULL)
return strlen(buffer) > 0 ? buffer : NULL;
current_size += block_size-1;
} while (strchr(buffer, '\n') == NULL);
strtok(buffer, "\n");
if (NULL != (temp = realloc(buffer, strlen(buffer)+1)))
buffer =temp;
return buffer;
}
Once that's working, reading all the lines in the file and converting them to upper-case comes out something like:
// Warning: more untested code.
while (res[i] = readline(file)) {
size_t j;
for (j=0; res[i][j]; j++)
res[i][j] = toupper((unsigned char)res[i][j]);
++i;
}

It looks like you forgot to increment i after storing each line into the result array, so you end up storing all lines into res[0]. But you still set result.size = line_count at the end, so all array elements beyond the first are undefined. An i++ at the end of this loop: while ((line = strtok(buf, "\n\r"))) should fix it.

Related

memcpy in a different function having a pointer to pointer argument

I have a following function process calling a routine dataFileBuffer which takes a pointer to a pointer and does a memcpy on the dereferenced pointer location.
int dataFileBuffer(uint8_t *index, char **tempBuf,int size)
{
if(index != stop_address)) /*stop_address is a fixed pointer to the end buffer*/
{
if(*tempBuf)
{
if(index + size < stop_address)
memcpy(*tempBuf,index,size);
else
{
size = stop_address-index-1;
memcpy(*tempBuf,index,size);
}
}
else
size = 0;
}
else
size = 0;
return size;
}
int process()
{
char *readBuf=NULL;
char *tBuf = (char *)malloc(MAX_LENGTH);
int readBytes = -1;
uint8_t *index = start_address;
uint8_t *complete = stop_address;
do
{
readBuf = tBuf+(sizeof(char)*40);
readBytes = 0;
readBytes = dataFileBuffer(index,&readBuf,MAX_LENGTH);
if(readBytes > 0)
{
index = index+readBytes;
}
}while(index <= complete);
return readBytes;
}
My process function is intermittently seeing stack corruptions which is making me think that something is wrong with my implementation of copy.
I just wanted to understand if we can pass a pointer to a pointer as an argument and safely memcpy to the dereferenced location in the called function ?
There are several things wrong with the question's code. Apart from some syntax errors, there is notably the function
dataFileBuffer(index, char **tempBuf,int size)
which does not compile for two reasons, there is no type declared for the argument index, and there is no return value declared - note that the function ends with
return size;
and is called like this:
readBytes = dataFileBuffer(index,&readBuf,MAX_LEN);
and my guess is that it should be
int dataFileBuffer(char *index, char **tempBuf, int size)
but I am puzzled why you have reversed the arguments given to dataFileBuffer() for the memcpy().
Next, you have used MAX_LEN, MAX_LENGTH and 40 to define buffers sizes or offsets, but there is no clear definition or checking as to the size of the available buffer index that you copy into - or is that from :-). It is more usual to offer a buffer size than a pointer limit.
You also have
...
readBytes = dataFileBuffer(index,&readBuf,MAX_LEN);
if(readBytes > 0)
{
index = index+readBytes;
}
} while(index <= complete);
which is likely to cause an infinite loop when readBytes == 0, and anyway will copy the same data on subsequent loops.
Sorry I can't offer a proper solution, as it's all a confused mess.
Added after OP comment
In reply to the specific question about deferencing a **pointer, this example succeeds in doing that, by finding the string length.
#include <stdio.h>
#include <string.h>
// return the length of the string
size_t slen(char **tempBuf)
{
return strlen (*tempBuf);
}
int main(void) {
char string[] = "abcde";
char *sptr = string;
printf ("Length of '%s' is %d\n", string, slen (&sptr));
return 0;
}
Program output:
Length of 'abcde' is 5

My returnList[0] gets rewritten to #5'

I am trying to return an array of strings and while I copy the strings something weird happens when it passes the 4th index. For example, when it loops through the first 3 times it is stored as "the" but then it sudden becomes rewritten but it writes the next index just fine[index 5]. Can you guys find anything wrong with it because I'm stumped.
#include <stdlib.h>
#include <stdio.h>
#include "hash.h"
#include <string.h>
#define MAX 200
#define TERMINATE "asdfghjkl"
int createTable(int numFiles, char** files, char** stopList)
{
printf("stepped into create table\n");
FILE* fp1;
char oneWord[100];
HashTable hash = InitializeTable(900000);
int index = 2;
while(numFiles >0) {
fp1 = fopen(files[index++], "r");
while(fscanf(fp1, "%s", oneWord)!=EOF){
Insert(oneWord, hash, stopList);
}
numFiles--;
}
return 0;
}
char** createStopList(char* stopL)
{
FILE* fp1;
fp1 = fopen(stopL, "r");
char oneWord[100];
int i = 0;
char* stopList[MAX];
while(fscanf(fp1, "%s", oneWord)!=EOF){
stopList[i] = (char*)malloc(sizeof(oneWord));
strcpy(stopList[i++], oneWord);
}
stopList[i] = (char*)malloc(sizeof(char*));
strcpy(stopList[i], TERMINATE);
char** strings = stopList;
char** returnList = malloc(sizeof(strings));
i=0;
while(strcmp(strings[i], TERMINATE)!=0){
returnList[i] = malloc(sizeof(char*));
strcpy(returnList[i], strings[i]);
i++;
}
returnList[i] = (char*)malloc(sizeof(char*));
strcpy(returnList[i], TERMINATE);
return returnList;
}
int main(int argc, char** argv)
{
printf("start of prg\n");
char** stopList= createStopList(argv[1]);
createTable(argc-2, argv, stopList);
return 0;
}
This code causes a buffer overflow:
#define TERMINATE "asdfghjkl"
// ...
returnList[i] = (char*)malloc(sizeof(char*));
strcpy(returnList[i], TERMINATE);
The length of TERMINATE is 10, but sizeof(char*) is probably less than 10.
To fix it:
returnList[i] = malloc( sizeof TERMINATE );
strcpy(returnList[i], TERMINATE);
Your comments suggest you used strdup instead (that function is not in Standard C, but it is commonly provided).
This is also completely fubar'd:
char** strings = stopList;
char** returnList = malloc(sizeof(strings));
// ...
returnList[i] = malloc(sizeof(char*));
sizeof(strings) is the same as sizeof(char **), which is probably 4 or 8, but you go on to write past the end of this array, as soon as i gets to 1! This is probably the cause of your symptoms.
I think perhaps you have a misconception about what sizeof does. It tells you how many bytes are used to store a variable (NOT how many bytes are at the location the variable is pointing to, if that variable is a pointer).
Presumably you meant:
returnList = malloc( (i+1) * sizeof *returnList );
which gives you enough pointers for indices returnList[0] through returnList[i].
The code after that is badly designed, you have unnecessary code duplication. Change the while loop to do...while, then the last iteration will copy TERMINATE for you without you having to write extra code for it.
Earlier on in that same function, this line is poor:
while(fscanf(fp1, "%s", oneWord)!=EOF){
You should prevent the input overflowing. Also you never check whether i exceeds MAX. And could make another improvement. Instead of copying TERMINATE into stopList, just save i, and write TERMINATE on the end of returnList.
Finally you seem to be pointlessly storing and copying your array instead of just dynamically allocating it in the first place. Oh, and your mallocs have warts.
Putting all of those changes together:
char **createStopList(char const *stopL)
{
FILE* fp1;
fp1 = fopen(stopL, "r");
char oneWord[100];
size_t i;
char **stopList;
if ( !fp1 )
return NULL;
stopList = malloc(MAX * sizeof *stopList);
if ( !stopList )
exit(EXIT_FAILURE);
for (i = 0; i < MAX - 1 && fscanf(fp1, "%99s", oneWord) == 1; ++i)
{
stopList[i] = malloc( strlen(oneWord) + 1);
if ( !stopList[i] )
exit(EXIT_FAILURE);
strcpy(stopList[i], oneWord);
}
stopList[i] = malloc(sizeof TERMINATE);
strcpy(returnList[i], TERMINATE);
// (optional) free entries you didn't use in the list
stopList = realloc(stopList, (i+1) * sizeof *returnList);
if ( !stopList )
exit(EXIT_FAILURE);
return stopList;
}

Realloc array of Strings in C? segmentation error

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void sortString(const char* input, char* output);
int cmpstr(void const *a,void const *b);
int readAllWords(FILE* f, char*** res, int * num_read);
int main (int argc, char ** argv)
{
char **wordList;
FILE* fid;
int numWords;
fid = fopen(argv[1],"r");
readAllWords(fid, &wordList,&numWords);
}
int readAllWords(FILE* f, char*** res, int * num_read)
{
char buffer[128];
*num_read = 0;
int size;
while(fgets(buffer,128,f))
{
*num_read = *num_read +1;
size = strlen(buffer);
res = (char***)malloc(sizeof(char**));
*res = (char **)realloc(*res,sizeof(char*)*(*num_read));
(*res)[(*num_read)-1] = (char *)realloc((*res)[(*num_read)-1],sizeof(char)*size);
strcpy((*res)[(*num_read)-1],buffer);
printf("%s\n",(*res)[(*num_read)-1]);
}
printf("%s\n",(*res)[0]);
}
The values are storing and it prints out inside the while loop. But after the while loop, it cannot print out the strings.
The File is given in the main function. Do not understand why realloc is causing the loss of data?
One problem is that the code doesn't initialize res in main(), so you attempt to realloc() an indeterminate value. Either NULL or a value previously returned by malloc() or realloc() (or calloc()) would be OK, but since you pass an indeterminate value, you are invoking undefined behaviour, and a crash is a valid response to doing that.
However, there's a lot of other code in the function that should be reviewed as well.
This code works, and gets a clean bill of health from valgrind.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void readAllLines(FILE *f, char ***res, int *num_read);
int main(int argc, char **argv)
{
char **wordList = 0;
FILE *fid;
int numLines = 0;
if (argc > 1 && (fid = fopen(argv[1], "r")) != 0)
{
readAllLines(fid, &wordList, &numLines);
fclose(fid);
for (int i = 0; i < numLines; i++)
printf("%d: %s", i, wordList[i]);
for (int i = 0; i < numLines; i++)
free(wordList[i]);
free(wordList);
}
return 0;
}
void readAllLines(FILE *f, char ***res, int *num_read)
{
char buffer[128];
int size;
while (fgets(buffer, sizeof(buffer), f))
{
*num_read = *num_read + 1;
size = strlen(buffer) + 1;
char **space = (char **)realloc(*res, sizeof(char *) * (*num_read));
if (space == 0)
return;
*res = space;
(*res)[*num_read - 1] = (char *)malloc(sizeof(char) * size);
if ((*res)[*num_read - 1] == 0)
return;
strcpy((*res)[*num_read - 1], buffer);
printf("%s\n", (*res)[*num_read - 1]);
}
printf("%s\n", (*res)[0]);
}
Possible reason for a segmentation fault:
res = (char***)malloc(sizeof(char**));
*res = (char **)realloc(*res,sizeof(char*)*(*num_read));
In the second line you try to reallocate whatever *res is pointing to. However since you did not initialize *res this could be anything. This will work only if *res == NULL. I guess it should be malloc, not realloc.
Other problems:
You allocate everything new in each loop iteration. This is a huge memory leak.
You already pass a valid memory address pointing to an char** by res, you shouldn't allocate for it again. It is an out parameter. (Remove the malloc call)
You need an initial malloc for *res before the loop (Or set *res = NULL).
The second realloc for *res[...] should be a malloc, because you never actually reallocate here. Also instead of allocating size bytes, you should allocate size+1 bytes for the terminating \0.
Your function has no return statement although it is non-void.

Copying a file line by line into a char array with strncpy

So i am trying to read a text file line by line and save each line into a char array.
From my printout in the loop I can tell it is counting the lines and the number of characters per line properly but I am having problems with strncpy. When I try to print the data array it only displays 2 strange characters. I have never worked with strncpy so I feel my issue may have something to do with null-termination.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[])
{
FILE *f = fopen("/home/tgarvin/yes", "rb");
fseek(f, 0, SEEK_END);
long pos = ftell(f);
fseek(f, 0, SEEK_SET);
char *bytes = malloc(pos); fread(bytes, pos, 1, f);
int i = 0;
int counter = 0;
char* data[counter];
int length;
int len=strlen(data);
int start = 0;
int end = 0;
for(; i<pos; i++)
{
if(*(bytes+i)=='\n'){
end = i;
length=end-start;
data[counter]=(char*)malloc(sizeof(char)*(length)+1);
strncpy(data[counter], bytes+start, length);
printf("%d\n", counter);
printf("%d\n", length);
start=end+1;
counter=counter+1;
}
}
printf("%s\n", data);
return 0;
}
Your "data[]" array is declared as an array of pointers to characters of size 0. When you assign pointers to it there is no space for them. This could cause no end of trouble.
The simplest fix would be to make a pass over the array to determine the number of lines and then do something like "char **data = malloc(number_of_lines * sizeof(char *))". Then doing assignments of "data[counter]" will work.
You're right that strncpy() is a problem -- it won't '\0' terminate the string if it copies the maximum number of bytes. After the strncpy() add "data[counter][length ] = '\0';"
The printf() at the end is wrong. To print all the lines use "for (i = 0; i < counter; i++) printf("%s\n", data[counter]);"
Several instances of bad juju, the most pertinent one being:
int counter = 0;
char* data[counter];
You've just declared data as a variable-length array with zero elements. Despite their name, VLAs are not truly variable; you cannot change the length of the array after allocating it. So when you execute the lines
data[counter]=(char*)malloc(sizeof(char)*(length)+1);
strncpy(data[counter], bytes+start, length);
data[counter] is referring to memory you don't own, so you're invoking undefined behavior.
Since you don't know how many lines you're reading from the file beforehand, you need to create a structure that can be extended dynamically. Here's an example:
/**
* Initial allocation of data array (array of pointer to char)
*/
char **dataAlloc(size_t initialSize)
{
char **data= malloc(sizeof *data * initialSize);
return data;
}
/**
* Extend data array; each extension doubles the length
* of the array. If the extension succeeds, the function
* will return 1; if not, the function returns 0, and the
* values of data and length are unchanged.
*/
int dataExtend(char ***data, size_t *length)
{
int r = 0;
char **tmp = realloc(*data, sizeof *tmp * 2 * *length);
if (tmp)
{
*length= 2 * *length;
*data = tmp;
r = 1;
}
return r;
}
Then in your main program, you would declare data as
char **data;
with a separate variable to track the size:
size_t dataLength = SOME_INITIAL_SIZE_GREATER_THAN_0;
You would allocate the array as
data = dataAlloc(dataLength);
initially. Then in your loop, you would compare your counter against the current array size and extend the array when they compare equal, like so:
if (counter == dataLength)
{
if (!dataExtend(&data, &dataLength))
{
/* Could not extend data array; treat as a fatal error */
fprintf(stderr, "Could not extend data array; exiting\n");
exit(EXIT_FAILURE);
}
}
data[counter] = malloc(sizeof *data[counter] * length + 1);
if (data[counter])
{
strncpy(data[counter], bytes+start, length);
data[counter][length] = 0; // add the 0 terminator
}
else
{
/* malloc failed; treat as a fatal error */
fprintf(stderr, "Could not allocate memory for string; exiting\n");
exit(EXIT_FAILURE);
}
counter++;
You are trying to print data with a format specifier %s, while your data is a array of pointer s to char.
Now talking about copying a string with giving size:
As far as I like it, I would suggest you to use
strlcpy() instead of strncpy()
size_t strlcpy( char *dst, const char *src, size_t siz);
as strncpy wont terminate the string with NULL,
strlcpy() solves this issue.
strings copied by strlcpy are always NULL terminated.
Allocate proper memory to the variable data[counter]. In your case counter is set to 0. Hence it will give segmentation fault if you try to access data[1] etc.
Declaring a variable like data[counter] is a bad practice. Even if counter changes in the subsequent flow of the program it wont be useful to allocate memory to the array data.
Hence use a double char pointer as stated above.
You can use your existing loop to find the number of lines first.
The last printf is wrong. You will be printing just the first line with it.
Iterate over the loop once you fix the above issue.
Change
int counter = 0;
char* data[counter];
...
int len=strlen(data);
...
for(; i<pos; i++)
...
strncpy(data[counter], bytes+start, length);
...
to
int counter = 0;
#define MAX_DATA_LINES 1024
char* data[MAX_DATA_LINES]; //1
...
for(; i<pos && counter < MAX_DATA_LINES ; i++) //2
...
strncpy(data[counter], bytes+start, length);
...
//1: to prepare valid memory storage for pointers to lines (e.g. data[0] to data[MAX_DATA_LINES]). Without doing this, you may hit into 'segmentation fault' error, if you do not, you are lucky.
//2: Just to ensure that if the total number of lines in the file are < MAX_DATA_LINES. You do not run into 'segmentation fault' error, because the memory storage for pointer to line data[>MAX_DATA_LINES] is no more valid.
I think that this might be a quicker implementation as you won't have to copy the contents of all the strings from the bytes array to a secondary array. You will of course lose your '\n' characters though.
It also takes into account files that don't end with a new line character and as pos is defined as long the array index used for bytes[] and also the length should be long.
#include <stdio.h>
#include <stdlib.h>
#define DEFAULT_LINE_ARRAY_DIM 100
int main(int argc, char* argv[])
{
FILE *f = fopen("test.c", "rb");
fseek(f, 0, SEEK_END);
long pos = ftell(f);
fseek(f, 0, SEEK_SET);
char *bytes = malloc(pos+1); /* include an extra byte incase file isn't '\n' terminated */
fread(bytes, pos, 1, f);
if (bytes[pos-1]!='\n')
{
bytes[pos++] = '\n';
}
long i;
long length = 0;
int counter = 0;
size_t size=DEFAULT_LINE_ARRAY_DIM;
char** data=malloc(size*sizeof(char*));
data[0]=bytes;
for(i=0; i<pos; i++)
{
if (bytes[i]=='\n') {
bytes[i]='\0';
counter++;
if (counter>=size) {
size+=DEFAULT_LINE_ARRAY_DIM;
data=realloc(data,size*sizeof(char*));
if (data==NULL) {
fprintf(stderr,"Couldn't allocate enough memory!\n");
exit(1);
}
}
data[counter]=&bytes[i+1];
length = data[counter] - data[counter - 1] - 1;
printf("%d\n", counter);
printf("%ld\n", length);
}
}
for (i=0;i<counter;i++)
printf("%s\n", data[i]);
return 0;
}

C: creating array of strings from delimited source string

What would be an efficient way of converting a delimited string into an array of strings in C (not C++)? For example, I might have:
char *input = "valgrind --leak-check=yes --track-origins=yes ./a.out"
The source string will always have only a single space as the delimiter. And I would like a malloc'ed array of malloc'ed strings char *myarray[] such that:
myarray[0]=="valgrind"
myarray[1]=="--leak-check=yes"
...
Edit I have to assume that there are an arbitrary number of tokens in the inputString so I can't just limit it to 10 or something.
I've attempted a messy solution with strtok and a linked list I've implemented, but valgrind complained so much that I gave up.
(If you're wondering, this is for a basic Unix shell I'm trying to write.)
What's about something like:
char* string = "valgrind --leak-check=yes --track-origins=yes ./a.out";
char** args = (char**)malloc(MAX_ARGS*sizeof(char*));
memset(args, 0, sizeof(char*)*MAX_ARGS);
char* curToken = strtok(string, " \t");
for (int i = 0; curToken != NULL; ++i)
{
args[i] = strdup(curToken);
curToken = strtok(NULL, " \t");
}
if you have all of the input in input to begin with then you can never have more tokens than strlen(input). If you don't allow "" as a token, then you can never have more than strlen(input)/2 tokens. So unless input is huge you can safely write.
char ** myarray = malloc( (strlen(input)/2) * sizeof(char*) );
int NumActualTokens = 0;
while (char * pToken = get_token_copy(input))
{
myarray[++NumActualTokens] = pToken;
input = skip_token(input);
}
char ** myarray = (char**) realloc(myarray, NumActualTokens * sizeof(char*));
As a further optimization, you can keep input around and just replace spaces with \0 and put pointers into the input buffer into myarray[]. No need for a separate malloc for each token unless for some reason you need to free them individually.
Were you remembering to malloc an extra byte for the terminating null that marks the end of string?
From the strsep(3) manpage on OSX:
char **ap, *argv[10], *inputstring;
for (ap = argv; (*ap = strsep(&inputstring, " \t")) != NULL;)
if (**ap != '\0')
if (++ap >= &argv[10])
break;
Edited for arbitrary # of tokens:
char **ap, **argv, *inputstring;
int arglen = 10;
argv = calloc(arglen, sizeof(char*));
for (ap = argv; (*ap = strsep(&inputstring, " \t")) != NULL;)
if (**ap != '\0')
if (++ap >= &argv[arglen])
{
arglen += 10;
argv = realloc(argv, arglen);
ap = &argv[arglen-10];
}
Or something close to that. The above may not work, but if not it's not far off. Building a linked list would be more efficient than continually calling realloc, but that's really besides the point - the point is how best to make use of strsep.
Looking at the other answers, for a beginner in C, it would look complex due to the tight size of code, I thought I would put this in for a beginner, it might be easier to actually parse the string instead of using strtok...something like this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
char **parseInput(const char *str, int *nLen);
void resizeptr(char ***, int nLen);
int main(int argc, char **argv){
int maxLen = 0;
int i = 0;
char **ptr = NULL;
char *str = "valgrind --leak-check=yes --track-origins=yes ./a.out";
ptr = parseInput(str, &maxLen);
if (!ptr) printf("Error!\n");
else{
for (i = 0; i < maxLen; i++) printf("%s\n", ptr[i]);
}
for (i = 0; i < maxLen; i++) free(ptr[i]);
free(ptr);
return 0;
}
char **parseInput(const char *str, int *Index){
char **pStr = NULL;
char *ptr = (char *)str;
int charPos = 0, indx = 0;
while (ptr++ && *ptr){
if (!isspace(*ptr) && *ptr) charPos++;
else{
resizeptr(&ptr, ++indx);
pStr[indx-1] = (char *)malloc(((charPos+1) * sizeof(char))+1);
if (!pStr[indx-1]) return NULL;
strncpy(pStr[indx-1], ptr - (charPos+1), charPos+1);
pStr[indx-1][charPos+1]='\0';
charPos = 0;
}
}
if (charPos > 0){
resizeptr(&pStr, ++indx);
pStr[indx-1] = (char *)malloc(((charPos+1) * sizeof(char))+1);
if (!pStr[indx-1]) return NULL;
strncpy(pStr[indx-1], ptr - (charPos+1), charPos+1);
pStr[indx-1][charPos+1]='\0';
}
*Index = indx;
return (char **)pStr;
}
void resizeptr(char ***ptr, int nLen){
if (*(ptr) == (char **)NULL){
*(ptr) = (char **)malloc(nLen * sizeof(char*));
if (!*(ptr)) perror("error!");
}else{
char **tmp = (char **)realloc(*(ptr),nLen);
if (!tmp) perror("error!");
*(ptr) = tmp;
}
}
I slightly modified the code to make it easier. The only string function that I used was strncpy..sure it is a bit long-winded but it does reallocate the array of strings dynamically instead of using a hard-coded MAX_ARGS, which means that the double pointer is already hogging up memory when only 3 or 4 would do, also which would make the memory usage efficient and tiny, by using realloc, the simple parsing is covered by employing isspace, as it iterates using the pointer. When a space is encountered, it reallocates the double pointer, and malloc the offset to hold the string.
Notice how the triple pointers are used in the resizeptr function.. in fact, I thought this would serve an excellent example of a simple C program, pointers, realloc, malloc, passing-by-reference, basic element of parsing a string...
Hope this helps,
Best regards,
Tom.

Resources