I don't understand why my string is not copied.
The strings structures are similar to this one "KS 2H 5C JD TD"
Here is my code (the comments are all that I tried (memcpy and strcpy)):
typedef struct Hand Hand;
struct Hand {
char *cards;
double power;
};
Hand* initHand(char *set){
Hand *hand = malloc(sizeof(*hand));
if(hand == NULL)
exit(EXIT_FAILURE);
char card[5][3];
//strcpy(hand->cards,*cards);
int i=0;
char *p = strtok (set, " ");
while (p != NULL)
{
card[i] = p;
printf("%s\n",p);
p = strtok (NULL, " ");
i++;
}
hand->power=0;
}
I was willing to copy every 2 letters in an array with strtok (this is what I wanna do); then I tried to copy the whole string.
Thanks a lot. I understood my mistakes:
Mistakes:
I didn't know that strtok is a destructive function (I was trying to edit a readonly data since I was using a constant).
I didn't allocate memory for char *cards
Solution:
make a copy of the set.
allocate memory.
Here's the code that worked for me:
struct Hand {
char cards[5][3];
double power;
};
Hand* initHand(char *set){
Hand *hand = malloc(sizeof(*hand));
if(hand == NULL)
exit(EXIT_FAILURE);
char copy_set[15]="";
strcpy(copy_set,set);
char **str = (char**) malloc(5*sizeof(char*));
for(int i=0; i<3; i++){
str[i]= (char) malloc(3*sizeof(char));
}
strcpy(hand->cards,str);
int i=0;
char *p = strtok (copy_set, " ");
while (p != NULL)
{
strcpy(hand->cards[i], p);
p = strtok (NULL, " ");
i++;
}
hand->power=0;
return hand;
}
I think that this is reasonably correct code — a relatively simple modification of the code shown in revision 2 of the answer by the OP. There's always room for improvement, of course.
#include <stdlib.h>
#include <string.h>
struct Hand
{
char cards[5][3];
double power;
};
typedef struct Hand Hand;
extern Hand *initHand(const char *set);
Hand *initHand(const char *set)
{
Hand *hand = malloc(sizeof(*hand));
if (hand == NULL)
exit(EXIT_FAILURE);
char copy_set[15] = "";
strcpy(copy_set, set);
int i = 0;
char *p = strtok(copy_set, " ");
while (p != NULL)
{
strcpy(hand->cards[i++], p);
p = strtok(NULL, " ");
}
hand->power = 0;
return hand;
}
#include <stdio.h>
int main(void)
{
Hand *hand = initHand("KS 2H 5C JD TD");
for (int i = 0; i < 5; i++)
printf("Card %d: [%s]\n", i, hand->cards[i]);
printf("Power: %.2f\n", hand->power);
free(hand);
}
As I noted in a comment, the variable str isn't needed. With the revised structure, there is no need to allocate extra cards. I've marked the string input as const. Arguably, it would be better to do a dynamic memory allocation for copy_set (remembering to release the copy too), or at least check that the given data will fit in the allocated space. It would probably be better to print an error message before unilaterally exiting on a memory allocation failure. It is good to check for the failure. It is not good to exit with saying why. It would probably be better to return NULL on failure, and then let the calling code detect and handle the problem.
There's a simple test program attached to the code too.
Be cautious about using strtok(); it is dangerous to use it in library functions. You should generally use strtok_r() (POSIX) or strtok_s() (Microsoft) for preference.
Related
This is my first time posting question, and I did try to find solution, but, even if I did found it I didn't recognize it.
So, as the title says, the problem is in this triggered exception "Exception thrown at 0x0F26372D (ucrtbased.dll) in lab10.exe: 0xC0000005: Access violation reading location 0xCCCCCCC4.
If there is a handler for this exception, the program may be safely continued.", which happens when I step into line -> free(word).
This did happen to me a few times when I was learning malloc, but I overlooked it - thinking there was some other problem. But now I see that I'am doing something wrong.
The point of the program is - writing the struct "word". I need to input sentence and "cut" it into words, and then every word put in struct together with size of letters in the word and ordinal number of the word.
#include <stdio.h>
#include <string.h>
struct word {
char text_word[50];
unsigned sizee; //number of letters of the word
unsigned number; //ordinal number of the word
};
void cutting_sentence(struct word *p, char *sen) { //sen is sentence
int size_sen, i, j;
size_sen = strlen(sen) + 1; //size of sentence
p = (struct word*)malloc(size_sen * sizeof(struct word));
if (p == NULL) {
printf("\nNot enaugh memory!");
return 0;
}
strcpy(p[0].text_word, strtok(sen, " ,.!?"));
p[0].sizee = strlen(p[0].text_word);
p[0].number = 1;
printf("word:%s \t size:%u \t ordinal number of the word:%u\n",
p[0].text_word, p[0].sizee, p[0].number);
for (i = p[0].sizee - 1, j = 1;i < size_sen;++i) {
if (*(sen + i) == ' ' || *(sen + i) == '.' || *(sen + i) == ','
|| *(sen + i) == '?' || *(sen + i) == '!') {
strcpy(p[j].text_word, strtok(NULL, " ,.!?"));
p[j].sizee = strlen(p[j].text_word);
p[j].number = j + 1;
printf("word:%s \t size:%u \t ordinal number of the
word:%u\n", p[j].text_word, p[j].sizee, p[j].number);
j++;
}
}
}
int main() {
char sentence[1024];
struct word *word;
printf("Sentence: ");
gets(sentence);
cutting_sentence(&word, sentence);
free(word); //here is exception triggered
return 0;
}
You're changing the local value of the pointer argument passed, you need to change the memory at its target for the caller to discover the location of the allocated memory. Since you didn't do that, you're trying to free the local variable word which is stored on the stack of main().
First thing to fix is not to have a variable identical to the name of a type, that's just evil.
Then change the function prototype to pass a double pointer:
void cutting_sentence(struct word **p, char *sen);
And remember that where you were using p you now need to use *p or first assign a local (word *) with the address value contained there.
void cutting_sentence(struct word **p, char *sen) { //sen is sentence
int size_sen, i, j;
size_sen = strlen(sen) + 1; //size of sentence
*p = (struct word*)malloc(size_sen * sizeof(struct word));
if (*p == NULL) {
printf("\nNot enaugh memory!");
return; //void cannot return a value
}
and so on changing every usage of p to *p.
and then
int main() {
char sentence[1024];
struct word *words;
printf("Sentence: ");
gets(sentence);
cutting_sentence(&words, sentence);
if (words != NULL)
free(words); //now valid
return 0;
}
There are a few more issues than those previously discussed.
[As already pointed out] your first argument should be struct word **. But, a simpler way is to eliminate it and change the return type to struct word *. This allows the code within the function to be simpler (i.e. no double dereferencing of a pointer)
Although allocating as many word structs as there are characters in the input string will work, that is somewhat unusual.
A better way [more idiomatic, at least] to do this is to use realloc inside the loop.
In either case, the array size can be trimmed to only use what it needs via a final realloc.
I think that your loop that scans sen looking for delimiters is overly complex. Simply using strtok in the loop will give the same effect with less complexity.
Also, you're not conveying back a count of the number of word. One way is to add an extra element to the array that has a size of zero (e.g. an end-of-list marker)
Here's is a refactored version that should help:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct word {
char text_word[50];
unsigned sizee; // number of letters of the word
unsigned number; // ordinal number of the word
};
struct word *
cutting_sentence(char *sen)
{ // sen is sentence
int curcnt = 0;
int maxcnt = 0;
char *token;
struct word *word;
struct word *words;
while (1) {
token = strtok(sen," ,.!?\n");
if (token == NULL)
break;
sen = NULL;
if (curcnt >= maxcnt) {
maxcnt += 100;
words = realloc(words,sizeof(struct word) * (maxcnt + 1));
}
word = &words[curcnt];
strcpy(word->text_word,token);
word->number = curcnt;
word->sizee = strlen(token);
++curcnt;
}
words = realloc(words,sizeof(struct word) * (curcnt + 1));
// set end-of-list
word = &words[curcnt];
word->sizee = 0;
return words;
}
int
main()
{
char sentence[1024];
struct word *words;
struct word *word;
printf("Sentence: ");
fflush(stdout);
fgets(sentence,sizeof(sentence),stdin);
words = cutting_sentence(sentence);
for (word = words; word->sizee != 0; ++word)
printf("main: number=%u sizee=%u text_word='%s'\n",
word->number,word->sizee,word->text_word);
free(words);
return 0;
}
the following proposed code:
eliminates redundant code
properly checks for errors
properly outputs error message (and the text reason the system thinks an error occurred to stderr
performs the desired functionality
properly initializes the struct word pointer
properly updates the struct word pointer
changed int sizee to size_t sizee because the function: strlen() returns a size_t, not an int
changed int i to unsigned i because the declaration of the struct field number is declared as unsigned
documents why each header file is included
allocates an instance of the struct word for every character in the sentence This is 'overkill'. The most possible amount of words would be if every word was only a single character. So, immediately, the size of the allocated memory could be cut in half. A loop, counting the word separators would result in the correct amount of allocated memory. You could easily add that feature.
Note the way that the function: strtok() is used. I.E. initial call before loop, then following calls at end of loop
And now the proposed code:
#include <stdio.h> // printf(), fgets(), NULL
#include <stdlib.h> // exit(), EXIT_FAILURE, malloc(), free()
#include <string.h> // strlen(), strtok()
struct word
{
char text_word[50];
size_t sizee; //number of letters of the word
unsigned number; //ordinal number of the word
};
// notice the `**p` so can access the pointer in `main()` so it can be updated
void cutting_sentence(struct word **p, char *sen)
{ //sen is sentence
size_t size_sen = strlen(sen); //size of sentence
struct word *wordptr = *p;
wordptr = malloc(size_sen * sizeof(struct word));
if ( !wordptr )
{
perror("malloc failed");
exit( EXIT_FAILURE );
}
unsigned i = 0;
char * token = strtok(sen, " ,.!?");
while( token )
{
strcpy( wordptr[i].text_word, token );
wordptr[i].sizee = strlen( token );
wordptr[i].number = i;
printf("word:%s\t Length:%lu]tordinal number of the word:%u\n",
wordptr[i].text_word,
wordptr[i].sizee,
wordptr[i].number);
token = strtok( NULL, " ,.!?");
i++;
}
}
int main( void )
{
char sentence[1024];
struct word *wordArray = NULL;
printf("Sentence: ");
if( !fgets(sentence, sizeof( sentence ), stdin ) )
{
perror( "fgets failed" );
exit( EXIT_FAILURE );
}
// remove trailing new line
sentence[ strcspn( sentence, "\n") ] = '\0';
cutting_sentence(&wordArray, sentence);
free( wordArray ); //here is exception triggered
return 0;
}
A typical run of the code results in:
Sentence: hello my friend
word:hello Length:5 ordinal number of the word:0
word:my Length:2 ordinal number of the word:1
word:friend Length:6 ordinal number of the word:2
Notice that 'short' words result in an uneven output. You might want to correct that.
I want to create a function in C, so that I can pass the function a string, and a delimiter, and it will return to me an array with the parts of the string split up based on the delimiter. Commonly used to separate a sentence into words.
e.g.: "hello world foo" -> ["hello", "world", "foo"]
However, I'm new to C and a lot of the pointer things are confusing me. I got an answer mostly from this question, but it does it inline, so when I try to separate it into a function the logistics of the pointers are confusing me:
This is what I have so far:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void split_string(char string[], char *delimiter, char ***result) {
char *p = strtok(string, delimiter);
int i, num_spaces = 0;
while (p != NULL) {
num_spaces++;
&result = realloc(&result, sizeof(char *) * num_spaces);
if (&result == NULL) {
printf("Memory reallocation failed.");
exit(-1);
}
&result[num_spaces - 1] = p;
p = strtok(NULL, " ");
}
// Add the null pointer to the end of our array
&result = realloc(split_array, sizeof(char *) * num_spaces + 1);
&result[num_spaces] = 0;
for (i = 0; i < num_spaces; i++) {
printf("%s\n", &result[i]);
}
free(&result);
}
int main(int argc, char *argv[]) {
char str[] = "hello world 1 foo";
char **split_array = NULL;
split_string(str, " ", &split_array);
return 0;
}
The gist of it being that I have a function that accepts a string, accepts a delimiter and accepts a pointer to where to save the result. Then it constructs the result. The variable for the result starts out as NULL and without memory, but I gradually reallocate memory for it as needed.
But I'm really confused as to the pointers, like I said. I know my result is of type char ** as a string it of type char * and there are many of them so you need pointers to each, but then I'm supposed to pass the location of that char ** to the new function, right, so it becomes a char ***? When I try to access it with & though it doesn't seem to like it.
I feel like I'm missing something fundamental here, I'd really appreciate insight into what is going wrong with the code.
You confusing dereferencing with addressing (which is the complete opposite). Btw, I couldn't find split_array anywhere in the function, as it was down in main. Even if you had the dereferencing and addressing correct, this would still have other issues.
I'm fairly sure you're trying to do this:
#include <stdio.h>
#include <stdlib.h>
void split_string(char string[], const char *delimiter, char ***result)
{
char *p = strtok(string, delimiter);
void *tmp = NULL;
int count=0;
*result = NULL;
while (p != NULL)
{
tmp = realloc(*result, (count+1)*sizeof **result);
if (tmp)
{
*result = tmp;
(*result)[count++] = p;
}
else
{ // failed to expand
perror("Failed to expand result array");
exit(EXIT_FAILURE);
}
p = strtok(NULL, delimiter);
}
// add null pointer
tmp = realloc(*result, (count+1)*sizeof(**result));
if (tmp)
{
*result = tmp;
(*result)[count] = NULL;
}
else
{
perror("Failed to expand result array");
exit(EXIT_FAILURE);
}
}
int main()
{
char str[] = "hello world 1 foo", **toks = NULL;
char **it;
split_string(str, " ", &toks);
for (it = toks; *it; ++it)
printf("%s\n", *it);
free(toks);
}
Output
hello
world
1
foo
Honestly this would be cleaner if the function result were utilized rather than an in/out parameter, but you choice the latter, so there you go.
Best of luck.
I have an assignment to make a dictionary.
It will contain an x amount of words and their definitions (input by user).
Instructions say that the dictionary should be of type char*** (2D array of pointers=arrays=strings), but I've got absolutely no idea of how to dynamically allocate the size of the array. it should have 2 lines, 1 for words and another 1 for their definitions, and the number of columns is decided by how many words are in the dictionary. While looking for help online i came up with this:
char** AllocateArray(int line, int column)
{
char** pArray=(char**)malloc(line*sizeof(char*));
int i;
for(i=0;i<2;i++)
pArray[i]=(char*)malloc(column*sizeof(char));
return pArray;
}
What changes should i make in the code for it to work with my char*** ?
Using Visual studio 2012
Edit:
I have a problem with this right now:
void inputString(char* p1)
{
char buffer[80];
printf("\nEnter a word:");
scanf("%s",buffer);
p1=(char*)malloc(strlen(buffer)+1);
if(p1!=NULL)
{
strcpy(p1,buffer);
free(buffer);
}
}
it crashes right after i input a word. the char* that the function receives is dictionary[i][j]. –
Don't free() anything allocated on the stack (i.e. buffer).
Also, your function inputString() will not tell its client what memory it had allocated, since p1 is local to it.
Here is an example.
char*** dictionary;
int i = 0;
int j = 0;
int lines = 10;
dictionary = (char***)malloc(sizeof(char**)*lines);
for(i=0;i<lines;i++)
{
dictionary[i] = (char**)malloc(sizeof(char*)*4);
for(j=0;j<4;j++)
dictionary[i][j] = (char*)malloc(sizeof(char)*25);
}
You have to modify the malloc's parameters in order to adapt to your problem/ or modify them when you need more memory for your strings.
Also it might be a good idea to try and free memory when you do not need it
Don't forget to malloc like this...
dictionary[i][j] = (char*)malloc(sizeof(char)*strlen(word_to_insert)+1);
...because each word end with a supplementary byte filled with 0 "null terminate string".
a sample
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(){
char ***dictionary;
const char *words[] = { "ASEAN", "United Nations", "OPEC" };
size_t howManyWords = sizeof(words)/sizeof(*words);
int i;
dictionary = malloc(howManyWords*sizeof(char**));
printf("Please enter the definition of this word\n");
for(i = 0; i < howManyWords; ++i){
char buff[80];
char **keyValue;
printf("%s : ", words[i]);
fgets(buff, sizeof(buff), stdin);
keyValue = malloc(2*sizeof(char*));
keyValue[0] = (char*)words[i];
keyValue[1] = malloc(strlen(buff)+1);
strcpy(keyValue[1], buff);
dictionary[i] = keyValue;
}
//print
for(i=0;i<howManyWords;++i){
printf("%s : %s", dictionary[i][0], dictionary[i][1]);
}
//release
for(i=0;i<howManyWords;++i){
free(dictionary[i][1]);
free(dictionary[i]);
}
free(dictionary);
return 0;
}
In this program, there is a segmentation fault. The program can print out "loop end" successfully and segmentation fault appeared after "loop end", which means there is not error in read_name function. But I could not figure out any error in my free_memory function. Could anyone help me figure out? Thank you.
input file:
9
Clinton, Hillary R.
Bonds, Bobby S.
Bonds, Barry L.
Clinton, William I.
Clinton, Chelsea T.
Bush, Laura M.
Bush, George W.
Bush, Jenna F.
Bush, Barbara G.
program:
#include <stdio.h>
#include <malloc.h>
#include<string.h>
void alloc(char ***surname, char ***first, char **mid_init, int num);
void read_names(FILE *inp, char **surname, char **first, char *mid_init, int num );
void free_memory(char **surname, char **first, char *mid_init, int num);
int main(int argc, char *argv[])
{
int num = 0;
char **surname, **first, *mid_init;
FILE *inp = fopen(argv[1], "r");
FILE *outp = fopen(argv[2], "w");
char array[79];
fscanf(inp, "%d", &num);
printf("%d\n", num);
fgets(array, 79, inp);
alloc(&surname, &first, &mid_init, num);
read_names(inp, surname, first, mid_init, num);
free_memory(surname, first, mid_init, num);
fclose(inp);
fclose(outp);
return 0;
}
void alloc(char ***surname, char ***first, char **mid_init, int num)
{
int i;
*surname = (char**)malloc(num * sizeof(char*));
*first = (char**)malloc(num * sizeof(char*));
*mid_init = (char*)malloc(num * sizeof(char));
for(i=0; i<num; i++)
{
(*surname)[i] = (char*)malloc(15*sizeof(char));
(*first)[i] = (char*)malloc(10*sizeof(char));
}
}
void read_names(FILE *inp, char **surname, char **first, char *mid_init, int num )
{
char *token, array[79];
char delim[6] = ", .\n";
int i=0;
fgets(array, 79, inp);
printf("loop begins\n");
for(i=0; i<num; i++)
{
fgets(array, 79, inp);
printf("%s", array);
token = strtok(array, delim);
strcpy( (surname[i]), token);
printf("%s ", (surname[i]));
token = strtok(NULL, delim);
strcpy( (first[i]), token);
printf("%s ", (first[i]));
token = strtok(NULL, delim);
*mid_init = token[0];
printf("%s\n", mid_init);
printf("\n\n");
}
printf("\nloop ends\n");
}
void free_memory(char **surname, char **first, char *mid_init, int num)
{
int i;
for(i=0;i<num;i++)
{
free((surname)[i]);
free((first)[i]);
}
free(surname);
free(first);
free((mid_init));
}
First off, you're limiting yourself to 14-character first names and 9-character last names, so that would be the first thing I'd check, that your names aren't longer than this.
If they are, you'll probably corrupt the memory arena when copying them.
One way to check this is to simply print the length of token every time you set it, such as:
token = strtok(array, delim);
printf ("DEBUG: token length is %d\n", strlen (token));
Keep in mind that corruption does not necessarily have a visible immediately or even ever. In this case, what's most likely happened is that you've overwritten a vital piece of inline control information in the memory arena, such as a memory block size or a pointer to another memory block.
However, there's no code actively checking for that when you write to memory so it's probably only found when you next try to do a memory allocation or de-allocation call.
Your next call like this after the corruption is your free calls and that's almost certainly where it's being found, because the arena is corrupt.
Bottom line, writing beyond the end of allocated memory is undefined behaviour. That means you shouldn't do it.
If it turns out your names aren't too long (as you state in a comment), you need to then ask yourself why you have a superfluous fgets(array, 79, inp); in your code. I understand why it's needed in main so as to move to the next line after inputting the line count with a call to fscanf. And that one does its job well.
However, you have another one at the start of read_names which effectively throws away the first name in your list. That's going to cause problems because, while your code thinks there are X names in the file, you've thrown away the first one meaning that there are only X - 1 remaining. You can tell this because, when you begin to print out the names, the first one from the file appears to be missing.
If you remove the fgets at the start of read_names, you should find it's okay.
As an aside, there's a couple of other changes I'd make to the code. First you really should check all those malloc calls in case one of them fails. That's the general rule for all functions that can fail when you rely later on them not having failed.
Second, I'm not really a big fan of ever multiplying by sizeof(char) - this is guaranteed by the standard to always be 1, so multiplying by it clogs up the code and makes it less readable.
Try replacing
token = strtok(NULL, delim);
*mid_init = token[0];
printf("%s\n", mid_init);
with
token = strtok(NULL, delim);
mid_init[i] = token[0];
printf("%c\n", mid_init[i]);
When mid_init memory chunk is filled with garbages without any null in it, 'printf("%s\n", mid_init);' might read beyond the data segment causing segmentation fault.
But #paxdiablo's answer has a much better chance to be the case here.
#Bruce, segmentation fault doesn't always appear at the exact spot it happened.
I don't know why you are getting a segmentation fault, but if I was writing this, I would try and make it a bit simpler (I don't think you are doing yourself any favors) - it makes no sense to pass around something like char ***surname.
This is only my personal opinion, but I would do something like this:
#include <stdio.h>
#include <malloc.h>
typedef struct {
char **data;
unsigned int count;
unsigned int actualSize;
} StringArray;
void StringArray_init(StringArray *array)
{
array->count = 0;
array->actualSize = 0;
}
void StringArray_add(StringArray *array, char* value)
{
if (array->actualSize == 0)
{
array->data = (char**)malloc(sizeof(char*)* 4);
array->actualSize = 4;
}
else
{
if (array->count >= array->actualSize)
{
array->actualSize *= 2;
array->data = (char**)realloc(array->data,sizeof(char*) * array->actualSize);
}
}
array->data[array->count] = value;
array->count++;
}
char* StringArray_get(StringArray *array, unsigned int position)
{
if (position < array->count)
return array->data[position];
else
return 0;
}
void StringArray_clear(StringArray *array)
{
if (array->count >0) free(array->data);
array->count = 0;
array->actualSize = 0;
}
int main ()
{
StringArray surname;
StringArray_init(&surname);
StringArray_add(&surname, "Smith");
StringArray_add(&surname, "Jones");
for(int i=0;i<surname.count;i++)
{
printf("%s\n", StringArray_get(&surname,i));
}
StringArray_clear(&surname);
}
What the above code is doing is allocating memory when you add a value, but instead of allocating space for one item, it adds enough for four. If you get to the point where you add a fifth, it will then double the space to 8 items. This method should help with memory fragmentation. I'm also using a structure, which just makes it a bit easier to pass this array around.
I could also do something like this if I wanted to allocate memory for the strings (just include the string.h header):
int main ()
{
StringArray surname;
StringArray_init(&surname);
char *name = (char*)malloc(sizeof(char) * 6);
strcpy(name,"Smith");
StringArray_add(&surname, name);
name = (char*)malloc(sizeof(char) * 6);
strcpy(name,"Jones");
StringArray_add(&surname, name);
for(int i=0;i<surname.count;i++)
{
printf("%s\n", StringArray_get(&surname,i));
}
// Free memory
for(int i=0;i<surname.count;i++)
{
char *name = StringArray_get(&surname,i);
if (name != NULL) free(name);
}
StringArray_clear(&surname);
}
The size of each name is 6 because there are 5 characters and an extra one for 0, which is the string terminator.
Sorry if this doesn't directly answer your question, but hope it helps.
Bruce,
In your data file, you need a blank line between the number and list of names because of the first fgets() at the beginning of read_names() consumes the second line.
Because the program skipped the second line, it could read only 8 names and the last line read was a blank line, which caused strtok to return a null and the next strcpy tried to read from address 0, which is, of course, a segmentation fault.
And in my machine, the fault happened before printing "loop ends".
You need to check the return values of function calls (strtok in this case) for possible errors. Otherwise you will be wasting a lot of time debugging like this.
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.