My main question is, is my scheme just plain bad practice? Can it be done? Should it be done?
I'm writing a little dinky key-value pair "dictionary" structure just to familiarize my self with C. One of the functions I've written is intended to return an array of strings of all the values associated with a provided key. The function definition:
char** get_values(const struct dict* d, const char* key)
{
// if key does not exist
if(contains_key(d, key) == 0)
{
return NULL;
}
// count the number of times the key occurs in the dict
int count = count_keys(d, key);
// make a return value
char** key_values = alloc_list(count);// definition included below
int curr_count = 0;
// fill return array
for(int pos = 0; pos < DSIZE; pos++)
{// if current key in dict matches requested key...
if(strcmp(d->keys[pos], key) == 0)
{
// add current value to return array
strcpy(key_values[curr_count], d->values[pos]);
curr_count++;
}
}
return key_values;
}
This function allocates the memory for the string array:
static char** alloc_list(int count)
{
// Allocate list of strings
char** slist = (char**)malloc(sizeof(char*) * count);
// if allocation was great success...
if(slist)
{
// ... allocate memory for each string
for(int pos = 0; pos < DSIZE; pos++)
{
slist[pos] = (char*)malloc(DSIZE * sizeof *slist[pos]);
}
}
return slist;
}
Then in main():
add(&dictionary, "a", "a");
add(&dictionary, "a", "aa");
add(&dictionary, "a", "aaa");
char** a_val = get_values(&dictionary, "a"); // TODO: how do I free this!
assert(strcmp(a_val[0], "a") == 0);
assert(strcmp(a_val[1], "aa") == 0);
assert(strcmp(a_val[2], "aaa") == 0); // all these assertions pass
free(*a_val); // with * omitted, execution aborts, with * included, no warnings
// from gcc, yes I am stabbing in the dark here.
a_val = NULL;
I don't believe the last two lines are doing what I hope they are doing, when I print the values of a_val[0-2] in gdb, they are still there.
I realize I could fix this problem by allocating a string array in main(), and then change get_values() to accept the array and then let get_values() do its business, and then free() the allocated array of strings when I am done with it. But before I go ahead and do that, I was just wanted to see if and how one goes about deallocating memory from a calling function. I've read a little bit about it, and all that was said was "it is the programmers responsibility to deallocate memory in the calling function", but the book did not say how for a situation such as this.
Thanks in advance for any help!
In order to properly deallocate a_val you will need first a for-loop to free/deallocate the char arrays allocated previously and then free the double pointer (i.e., a_val). Otherwise you will create a memory leak since the memory pointed by elements/pointers of a_val will be unreferenced/orphaned:
char** a_val = get_values(&dictionary, "a");
...
for(int pos = 0; pos < DSIZE; pos++) {
free(a_val[pos]);
}
free(a_val);
Stating free(*a_val); is equivalent as stating free(a_val[0]). Thus only the first string of a_val is going to be deallocated.
I am trying to read a directory using the Dirent Header file in C.
I am having trouble storing the file names inside the given directory inside a char array.
the code is below.
char * FileNames;
while ( ( DirentPointer = readdir(DirectoryPointer) ) != NULL) {
strcpy(&FileNames[Counter], DirentPointer -> d_name);
Counter++;
}
When I run the application I am getting a segmentation fault. I think that the strcpy is faulting the application because of memory allocations.
Can anyone tell me how malloc and realloc can be used to dynamically add memory allocation to the FileNames Char *?
Your code would produce undefined behaviour or most probably crash. That's because FileNames is a pointer to a character, not to a memory buffer to copy your strings into and the function strcpy does not do bounds checking for the buffer being copied into. So strcpy will try to write into the memory you have not allocated for the purpose. You need to allocate memory to copy the file names first.
#define MAX_FILE_NUM 1000
char *FileNames[MAX_FILE_NUM] = {0}; // initialize to NULL
int Counter = 0;
while((DirentPointer = readdir(DirectoryPointer)) != NULL) {
FileNames[counter] = malloc(strlen(DirentPointer->d_name) + 1); // + 1 for the null byte
// check for NULL
if(FileNames[i] != NULL) {
strcpy(FileNames[i], DirentPointer->d_name);
Counter++;
}
else {
// handle the NULL case
// maybe try malloc again
}
}
// after you are done with the FileNames
for(int i = 0; i < Counter; i++) {
free(FileNames[i]);
FileNames[i] = NULL;
}
it is realloc problem again. It seems that I could not find any similar problems in a lot of previous realloc statements. I would appreciate your interest.
I am trying to read text input of format:
g:<expression>;0,1,0,1,0.
I have a set of lines of this format in the source text file and a snippet of code (that follows) reading this line in a "for" loop. expression between : and ; is read into propstr 2D char array. Everything after ; determines a vector (of size DIM) of numbers separated by comma ,. This vector is read into upstr (just as a string) and then converted into array of integers upvec by function process_update_vector. At every iteration of the loop realloc is used to adjust the sizes of the mentioned arrays (propstr,upstr and upvec). Number of lines read so far in the loop is NREAC. Here is the code:
/*Before this point, current line in the source is read into `temp'*/
NREAC++;
for(i=0;i<strlen(temp);i++){
if(temp[i]==':') colon=i;//Here we find colon
if(temp[i]==';') semicolon=i;//...and semicolon positions
}
memset(temp1,'\0',STRLEN);
if(NREAC==1)
ptrchar=(char **)malloc(sizeof(char *));
else
ptrchar=realloc(propstr,NREAC*sizeof(char *));
if(ptrchar==NULL){
fprintf(stderr,"Error: could not allocate memory for propstr\n");
if(propstr!=NULL) free(propstr);
return 1345;
}else{propstr=ptrchar;ptrchar=NULL;}
propstr[NREAC-1]=(char *)malloc((semicolon-colon)*sizeof(char));
if(propstr[NREAC-1]==NULL){
fprintf(stderr,"Error: couldn't get memory for propstr[NREAC-1]\n");
return 1344;
}
for(i=colon+1;i<semicolon;i++)/*Copy the propensity part of the line*/
temp1[i-colon-1]=temp[i];
temp1[i-colon-1]='\n';/*Include newline symbol for correct parsing*/
strcpy(propstr[NREAC-1],temp1);
memset(temp1,'\0',STRLEN);
if(NREAC==1)
ptrchar=(char **)malloc(sizeof(char *));
else
ptrchar=realloc(upstr,NREAC*sizeof(char *));
if(ptrchar==NULL){
fprintf(stderr,"Error could not allocate memory for upstr\n");
if(upstr!=NULL) free(upstr);
return 1343;
}else{upstr=ptrchar;ptrchar=NULL;}
upstr[NREAC-1]=(char *)malloc((strlen(temp)-semicolon-1)*sizeof(char));
if(upstr[NREAC-1]==NULL){
fprintf(stderr,"Error: couldn't get memory for upstr[NREAC-1]\n");
return 1342;
}
if(strlen(temp)-semicolon==2){/*No vector is specified*/
fprintf(stderr,"Error: no update vector found:\n");
fprintf(stderr,"`%s'",temp);
return 1;
}
if(NREAC==1)
ptrint=(int **)malloc(sizeof(int *));
else
ptrint=(int **)realloc(upvec,NREAC*(sizeof(int *)));/*!!!!!!!!!!!!!!!!!!!!*/
if(ptrint==NULL){
fprintf(stderr,"Error: could not allocate memory for upvec\n");
if(upvec!=NULL) free(upvec);
return 1341;
}else{upvec=ptrint;ptrint=NULL;}
upvec[NREAC-1]=(int *)malloc(DIM*sizeof(int));
if(upvec[NREAC-1]==NULL){
fprintf(stderr,"Error: couldn't get memory for upvec[NREAC-1]\n");
return 1340;
}
for(i=semicolon+1;i<strlen(temp)-1;i++)
temp1[i-semicolon-1]=temp[i];
temp1[i-semicolon-1]='\n';/*Include newline for more convenient way of parsing*/
strcpy(upstr[NREAC-1],temp1);
/*Get update vector*/
upvec[NREAC-1]=process_update_vector(upstr[NREAC-1],upvec[NREAC-1]);
memset(temp1,'\0',STRLEN);
memset(temp,'\0',STRLEN);
continue;
This snippet appears in the for loop. The "invalid pointer" error appears in the place marked with /*!!!!!!!!!!!!!!!!!!!!*/.
Conditions of the error. For small enough DIM everything works fine and always worked. At some point, I had to increase DIM up to 11 and then the error occured in the middle of the parsing procedure (It is usual error, I guess, *** glibc detected *** dinamica: realloc(): invalid pointer: 0x000000000165d190 ***). The value of NREAC seems to be not affecting the realloc behavior. It is always the same place in the code where the error occurs. Do I wrongly allocate memory for int type variable, since the allocation for char type was never a problem?
The process_update_vector function:
int * process_update_vector(const char *upstr,int *upvec)
{
int i,j,k;
char symbuf[5];/*5 symbols, max 99999 is possible*/
i = 0;
j = 0;
k = 0;
while(upstr[i] != '\n'){
if(upstr[i] == ','){/*',' is the delimiter*/
symbuf[j] = '\0';
j = 0;
upvec[k] = atoi(symbuf);
k++;
i++;
continue;
}
symbuf[j] = upstr[i];
j++;
i++;
}
/*For the last entry*/
upvec[k] = atoi(symbuf);
k++;
return upvec;
}
Ok, I tried to look at your code. My eyes were sore but I managed to get through the code. here my changes for the first part, where you read the expression between the colon and the semicolon. I changed some types but let more or less the same error handling, even if I think it is overkill or better said, it is at the wrong place (I tend to separate the allocation/error work from the business code, makes it easier to debug).
/*Before this point, current line in the source is read into `temp'*/
char **propstr=NULL; /* I hope this variable was initialized to NULL or else you get problems */
NREAC++; /* This is bad naming, all uppercase is by convention reserved for macros */
char *colon = strchr(temp, ':'); /* There a lib function to do the searching, use them */
char *semicolon = strchr(temp, ';');
if(!colon || !semicolon) {
fprintf(stderr,"Error: syntax error\n");
return 2112; /* whatever */
}
ptrchar=realloc(propstr,NREAC*sizeof(char *)); /* realloc called with a NULL pointer is the same as a malloc, typecasts of mallocs/reallocs are not good. */
if(!ptrchar) {
fprintf(stderr,"Error: could not allocate memory for propstr\n");
free(propstr); /* The check against NULL is also done by free, it's therefoe redundant */
return 1345;
}
else
propstr=ptrchar; /* There's no point in NULLing a variable that will be overwritten anyway */
size_t lenexpr = semicolon-colon; /* The length of the expression can be found by subtracting both pointers */
propstr[NREAC-1]=malloc(lenexpr+1); /* +1 for the \n */
if(!propstr[NREAC-1]) {
fprintf(stderr,"Error: couldn't get memory for propstr[NREAC-1]\n");
return 1344;
}
memcpy(propstr[NREAC-1], colon+1, lenexpr); /* We copy directly without a temporary that way */
propstr[NREAC-1][lenexpr] = '\n'; /* add the linefeed */
propstr[NREAC-1][lenexpr+1] = 0; /* terminate the string */
Here I stopped, because there is a fundamental error in your second part that I do not understand. Do you want to store the vector as a string or as an integer array. If the former, then you have to allocate chars and not sizeof (int), if the latter there must be some atoi or strtol somewhere.
There are several other things that would be nice when you submit a question to SO, you should include the declarations of the variables you use, you should show the defines of the macros you use.
EDIT: second part
// Edit3 ptrchar=realloc(upstr, NREAC*sizeof(char *));
// Edit3 if(!ptrchar) {
// Edit3 fprintf(stderr,"Error could not allocate memory for upstr\n");
// Edit3 free(upstr);
// Edit3 return 1343;
// Edit3 }
// Edit3 else
// Edit3 upstr=ptrchar;
// Edit3 upstr[NREAC-1] = malloc(strlen(semicolon)+1); /* +1 for the \n */
// Edit3 if(!upstr[NREAC-1]) {
// Edit3 fprintf(stderr,"Error: couldn't get memory for upstr[NREAC-1]\n");
// Edit3 return 1342;
// Edit3 }
if(strlen(semicolon)<2) {/*No vector is specified*/
fprintf(stderr,"Error: no update vector found:\n'%s'", temp);
return 1;
}
ptrint = realloc(upvec, NREAC*sizeof(int *));/*!!!!!!!!!!!!!!!!!!!!*/
if(!ptrint) {
fprintf(stderr,"Error: could not allocate memory for upvec\n");
free(upvec);
return 1341;
}
else
upvec=ptrint;
upvec[NREAC-1] = malloc(DIM*sizeof(int));
if(!upvec[NREAC-1]) {
fprintf(stderr,"Error: couldn't get memory for upvec[NREAC-1]\n");
return 1340;
}
// Edit3 memcpy(upstr[NREAC-1], semicolon+1, strlen(semicolon+1)+1); /* +1 will include the \0 */
// Edit3 strcat(upstr[NREAC-1], "\n"); /*Include newline for more convenient way of parsing*/
/*Get update vector*/
// Edit3 upvec[NREAC-1] = process_update_vector(upstr[NREAC-1], upvec[NREAC-1]);
// Edit3, let's reuse our initial pointer, it's still valid.
process_update_vector(semicolon+1, upvec[NREAC-1]);
continue;
The signature of that function process_update_vector seems odd, does it realloc upvec[NREAC-1]? If not, there's no point in returning it and reassigning it. So it would be a good idea to show that function too.
Conclusion: The only errors I detected in your code are, the length of the allocation were 1 too short because of the appended \n.
Other point: By replacing the if(first) malloc else realloc by my realloc you have to make sure that the pointer is NULL initially or else you have a problem.
EDIT2: Here an updated version of process_update_vector, that wasn't incorrect per se, but a was bit complicated for what it does. It also had a high buffer overflow risk, with the temporary buffer of only 5 characters!
This version doesn't need a temp buffer.
void process_update_vector(const char *upstr, int *upvec)
{
const char *p = strchr(upstr, ','); /* There are fine library functions for string handling */
int k = 0;
while(p) {
upvec[k++] = atoi(upstr);
upstr = p+1; /* Position upstr to after , */
p = strchr(upstr, ',');
}
upvec[k++] = atoi(upstr);
/* We don't need to return upvec, it doesn't change in the function */
}
Two comments:
- There's no check of the DIM, so on broken input we can have a buffer oveflow.
- There isn't any white space handling, often strings are entered with spaces after commas (it's more readable), this case wouldn't work in that case, but several while(*p==' ') p++; placed at the right places can take care of that.
EDIT3:
The change in the called function also changes the caller, you don't need to copy to the upstr, so that allocation can be removed completely. I added // Edit3 comments in the 2nd listing. Unless of course if you intend to reuse the copied string elsewhere.
PS: On SO, thanking is done by upvoting the answers.
Using Microsoft Visual Studio 2005 I pasted the code into a temp file and used the reformat command to reformat it to the text below.
See this Wikipedia article on Programming style which can help you from getting flamed for stackoverflow submissions.
Also see this sample of a C coding styles document.
EDIT begin
The runtime is complaining about an invalid pointer which indicates that the pointer you are passing to realloc() is not a pointer that was created with a call to malloc() or to calloc(). It knows because whenever you do a malloc() there is a memory management header that is part of the data area and the pointer you are given is a pointer to allocated memory after the header. See this question How does realloc know how much to copy?
Finally, I rewrote this using a state machine approach (see also state machine explanation) and put that source code at the bottom of the reformatted example.
EDIT end
Looking at the source this appears to be part of a loop. It appears that the variable upvec may not be initialized and it is some kind of an array so is it actually malloced or not?
See section with //-------------------- indicated.
Reformatted source follows:
/*Before this point, current line in the source is read into `temp'*/
NREAC++;
for(i=0;i<strlen(temp);i++){
if(temp[i]==':') colon=i;//Here we find colon
if(temp[i]==';') semicolon=i;//...and semicolon positions
}
memset(temp1,'\0',STRLEN);
if(NREAC==1)
ptrchar=(char **)malloc(sizeof(char *));
else
ptrchar=realloc(propstr,NREAC*sizeof(char *));
if(ptrchar==NULL){
fprintf(stderr,"Error: could not allocate memory for propstr\n");
if(propstr!=NULL) free(propstr);
return 1345;
} else {
propstr=ptrchar;
ptrchar=NULL;
}
propstr[NREAC-1]=(char *)malloc((semicolon-colon)*sizeof(char));
if(propstr[NREAC-1] == NULL){
fprintf(stderr,"Error: couldn't get memory for propstr[NREAC-1]\n");
return 1344;
}
for(i=colon+1;i<semicolon;i++)/*Copy the propensity part of the line*/
temp1[i-colon-1]=temp[i];
temp1[i-colon-1]='\n';/*Include newline symbol for correct parsing*/
strcpy(propstr[NREAC-1],temp1);
memset(temp1,'\0',STRLEN);
if(NREAC==1)
ptrchar=(char **)malloc(sizeof(char *));
else
ptrchar=realloc(upstr,NREAC*sizeof(char *));
if(ptrchar==NULL){
fprintf(stderr,"Error could not allocate memory for upstr\n");
if(upstr!=NULL) free(upstr);
return 1343;
} else {
upstr=ptrchar;
ptrchar=NULL;
}
upstr[NREAC-1]=(char *)malloc((strlen(temp)-semicolon-1)*sizeof(char));
if(upstr[NREAC-1]==NULL){
fprintf(stderr,"Error: couldn't get memory for upstr[NREAC-1]\n");
return 1342;
}
if(strlen(temp)-semicolon==2){/*No vector is specified*/
fprintf(stderr,"Error: no update vector found:\n");
fprintf(stderr,"`%s'",temp);
return 1;
}
// -----------------------------------------------------
if(NREAC==1)
ptrint=(int **)malloc(sizeof(int *));
else
ptrint=(int **)realloc(upvec,NREAC*(sizeof(int *)));/*!!!!!!!!!!!!!!!!!!!!*/
if(ptrint==NULL){
fprintf(stderr,"Error: could not allocate memory for upvec\n");
if(upvec!=NULL) free(upvec);
return 1341;
} else {
upvec=ptrint;
ptrint=NULL;
}
upvec[NREAC-1]=(int *)malloc(DIM*sizeof(int));
if(upvec[NREAC-1]==NULL){
fprintf(stderr,"Error: couldn't get memory for upvec[NREAC-1]\n");
return 1340;
}
// ---------------
for(i=semicolon+1;i<strlen(temp)-1;i++)
temp1[i-semicolon-1]=temp[i];
temp1[i-semicolon-1]='\n';/*Include newline for more convenient way of parsing*/
strcpy(upstr[NREAC-1],temp1);
/*Get update vector*/
upvec[NREAC-1]=process_update_vector(upstr[NREAC-1],upvec[NREAC-1]);
memset(temp1,'\0',STRLEN);
memset(temp,'\0',STRLEN);
continue;
EDIT
A suggested approach for this problem using a state machine with a function that will parse each line of text.
#include <malloc.h>
#include <stdlib.h>
// pLine is a line of text containing a zero terminated string of the format of
// g:<expression>;0,1,0,1,0
// This function will process the line and return the expression as a string
// and a list of the integers.
void processExpression (char *pLine, char *pExpression, int **pIntList)
{
int stateMachineIndex = 1;
int integerCount = 0;
char *pLineSave = 0;
int iIntListIndex = 0;
*pIntList = 0;
while (*pLine) {
switch (stateMachineIndex) {
case 1:
// initial state
if (*pLine == ':') {
// colon found so now start getting the expression
stateMachineIndex = 2;
}
pLine++;
break;
case 2:
if (*pLine != ';') {
*pExpression++ = *pLine++;
} else if (*pLine) {
// if we have not reached end of string yet then go to the
// next state of parsing the list of integers.
stateMachineIndex = 3;
pLine++;
pLineSave = pLine;
}
break;
case 3:
// at this point we begin to process the list of integers.
// however we are not sure how many there are so we will count them first
if (*pLine == ',') {
integerCount++;
}
pLine++;
break;
case 4:
// we now have a count of the integers we expect however it
// may be off by one so we will allocate a smidge more space
*pIntList = (int *)calloc ((integerCount + 2), sizeof(int));
stateMachineIndex = 5;
*pExpression = 0; // and while we are at it lets terminate our expression string
break;
case 5:
// now we get an integer value from the list of integers
(*pIntList)[iIntListIndex++] = atoi (pLine);
// eat up characters to the next integer in the list
while (*pLine && *pLine != ',') pLine++;
if (*pLine == ',') pLine++; // if we found a comma, skip it to the next field
break;
default:
break;
}
if (*pLine == 0 && stateMachineIndex < 4) {
// end of the string so now lets do our integer thing
// if we are still in the first phase of processing
if (pLineSave && *pLineSave && integerCount > 0) {
stateMachineIndex = 4;
pLine = pLineSave; // restart our parser back to the integer area
} else {
break;
}
}
}
}
// simple test harness to test the concept.
int main(int argc, char* argv[])
{
char *pLine = "g:expression and stuff;1,2,3,4,5";
char expressionBuffer[128];
int *pIntList = 0;
processExpression (pLine, expressionBuffer, &pIntList);
return 0;
}
I'm trying to find a memory leak in the folowing code. valgrind gives me this:
==14160== 1,850 (592 direct, 1,258 indirect) bytes in 9 blocks are definitely lost in loss record 2 of 5
==14160== at 0x4904A06: malloc (vg_replace_malloc.c:149)
==14160== by 0x405B1F: tsCreate (ticket_set.c:55)
==14160== by 0x401ECA: test1TS (main.c:62)
==14160== by 0x40557C: main (main.c:424)
and here's the function:
TicketSetStatus tsCreate(TicketSet* t, int n, int c) {
if(t==NULL){
return TS_CANNOT_CREATE;
}
if (n <= 0){
return TS_ILLEGAL_PARAMETER;
}
t->usedTravels = 0;
t->originalTravels = n;
t->cost = c;
t->moneyLeft = n * c;
//Date time is array of travels:
t->dates = malloc(sizeof(DateTime *)* (n)); //todo maybe c99 allows dynamic arrays?
for (int i = 0; i < n; i++) {
t->dates[i] = malloc(sizeof(char)*GOOD_LENGTH+1);
if (t->dates[i] == NULL) {
free( t->dates);
return TS_CANNOT_CREATE;
}
}
return TS_SUCCESS;
}
TicketSetStatus tsDestroy(TicketSet* t, int* moneyLeft) {
if (t == NULL) {
return TS_FAIL;
}
*moneyLeft = (t->cost) * (t->originalTravels-t->usedTravels);
for (int i = 0; i < t->originalTravels; i++){
free(t->dates[i]);
}
free(t->dates);
t=NULL;
return TS_SUCCESS;
}
when the struct is:
struct TS_element {
int usedTravels;
int originalTravels;
int cost;
DateTime* dates;
int moneyLeft;
};
and
typedef char* DateType
actually playing with free crashes the program more often than not so i'm inclined to live with the memory leak as long as the program functions correctly.
How are you using this array of DateTime? If you are stomping on the values later you will get leaks. Perhaps a confusion about string assignment? ie
char someDateValue[] = "2012-08-15";
t->dates[0] = someDateValue; // Leak -- your allocated string is lost
Instead:
strcpy( t->dates[0], someDateValue );
There is a definite leak in your error condition in tsCreate:
for (int i = 0; i < n; i++) {
t->dates[i] = malloc(sizeof(char)*GOOD_LENGTH+1);
if (t->dates[i] == NULL) {
free(t->dates); // Leak -- every element up to i-1 is lost
return TS_CANNOT_CREATE;
}
}
Are you calling tsDestroy after you've finished with data initialised by tsCreate? Perhaps you're returning from main without cleaning up.
If none of this helps, you should post additional code to show how you are using your data structure.
For at least one error you can focus solely on
...
t->dates = malloc(sizeof(DateTime*) * (n)); /* first malloc */
for (int i = 0; i < n; i++) { /* call this loop 1 */
t->dates[i] = malloc(sizeof(char)*GOOD_LENGTH+1); /* second malloc */
if (t->dates[i] == NULL) { /* test for malloc error */
free( t->dates); /* free the base array/list */
return TS_CANNOT_CREATE; /* exit function */
}
}
...
The problem is if the second malloc fails, the free only frees the base (first) malloc. It does not free any other memory allocations created by the second malloc
in loop 1, on a previous loop 1 iteration. I.e. if t->dates[i] = malloc(... fails when i is equal to 5 then the memory blocks allocated in the iterations 0 to 4 are not freed before exiting the function.
Hopefully that makes sense.
Update #paddy is correct in noting the error of t->dates[0] = someDateValue
which in this case what that is saying is:
char someDateValue[] = "2012-08-15";
could also be written in this case as
char *someDateValue = "2012-08-15";
so that
t->dates[0] = someDateValue;
simply assigns the pointer of the string, replacing the pointer to the freshly allocated block in the preceding malloc.
Ref: If you are still confused you can read the C FAQ question 6.3 So what is meant by the ``equivalence of pointers and arrays'' in C? as well as the rest of the C FAQ.
And is correct to suggest str[n]cpy (or similar replacements) to copy the array contents (rather than its pointer) to the freshly allocated memory block.
I cant figure out where I am messing up. I am passing an array of character pointers. Inside the function I am trying to use strtok to break up a string into smaller pieces to be assigned to the char * array. I can try printing it off in the function and it all shows up correctly. As soon as I try to print it back in main I just get garbage.
#include <stdio.h>
#include <string.h>
#define CMDLEN 100
#define MAXARG 5
void prompt();
int getCommand (char* cmdAndParameters[]);
int main() {
int numArgs = 0;
char* cmdAndParameters [MAXARG];
while (true){
prompt ();
numArgs = getCommand (cmdAndParameters);
}
}
void prompt() {
printf("shell> ");
}
int getCommand(char* cmdAndParameters[]){
int argNum = 0;
bool foundArg = true;
char* delimiters = " \t\n";
char userRequest[CMDLEN];
fgets(userRequest,sizeof(userRequest), stdin);
if ((cmdAndParameters[argNum] = strtok(userRequest, delimiters)) != NULL)
{
argNum++;
for (; argNum < MAXARG && foundArg; argNum++) {
if ((cmdAndParameters[argNum] = strtok(NULL,delimiters))
== NULL)
{
foundArg = false;
}
// merely to test output remove later
else {printf("%s\n", cmdAndParameters[argNum]);}
}
}
return argNum;
}
In this case, your inner array of chars is allocated "automatic", which is to say, on the stack. When you do the strtok, you're assigning a pointer to memory allocated on the stack, and then returning -- which means the memory is no longer allocated.
Move the userRequest array into file scope (ie, outside a block) or make the allocation 'static' and you'll have a better shot.
Update
Well, it's a little more than that, now that I look again.
First of all, you can clean it up considerably if you use a while loop, something like
argNum = 0;
while((cmdAndParameters[argNum++] = strtok(userRequest, delimiters)) != NULL)
; /* all the work is happening in the conditional part of the while */
or even a for loop as
for(argNum = 0;
(cmdAndParameters[argNum] = strtok(userRequest, delimiters)) != NULL);
argNum++)
; /* still all the work is in the for */
and now if argNum > 0 you know you found something.
Second, you need to think about how and when you're allocating memory. Your cmdAndParameters array is allocated when main starts (on the stack, it's "automatic") so it's around as long as your program, you're okay there. But your userRequest array is allocated auto in getCommand; when getCommand returns, the memory is deallocated; the stack pointer moves back over it and you have no guarantees any longer. So when you do the strtok, you're saving pointers into stack, which can lead to no good.
Do you want
for (; argNum < MAXARG && foundArg; argNum++)
or something like
for(argCntr = argNum; argCntr < MAXARG && foundArg; argCntr++)
Hope that helps.