4

I'm making my own command prompt (school project) and I'm trying to keep track of the last 10 commands the user uses. So I have an array:

char* history[10];

From my understanding this means I have an array of pointers, which point to strings. My problem with this is that I have another variable, input that is the users input. But whenever the user inputs something new, then the value of input changes, meaning all of the strings in my array change to the user's new input.

I'm wondering how I can get around this?

I tried changing my array to the following:

char *history[10][MAX]    //Where MAX = 256

Where I could instead use strcpy instead but I was unable to figure out how to input an array of arrays into a method, and then use strcpy to copy the string into the array of arrays.

Here is my current method:

char* updateHistory(char *history[], char command[], int histIndex) {
    history[histIndex] = command;      
    return *history;
}

Any help on another solution or how to get my solution working?

Yu Hao
  • 119,891
  • 44
  • 235
  • 294
Logan
  • 1,047
  • 10
  • 33

4 Answers4

3

Your array of pointers needs to point to heap allocated memory, it sounds as if you point to some buffer that changes

So something like this should work

#define MAX_HISTORY 10

char* history[MAX_HISTORY];

if (fgets(input, sizeof(input), stdin) != NULL)
{
  input[strlen(input)-1] = '\0'; // remove \n
  history[n++] = strdup(input); // allocates and copies the input string
  if ( n == MAX_HISTORY )
  {
    // throw away the oldest and move pointers forward one step
  }
}

strdup is conceptually the same as

malloc( ) + strcpy() 

so when you move the pointers forward and when you want clear the history you need to free() what the pointers point to.

Alternatively if you do not want to use the heap you could have a big buffer where you put the history

char history[MAX_HISTORY][MAX_CMD_LEN] but then you would need to shift more data and that is not so elegant/effective or have some elaborate indexing system to keep track of the contents

AndersK
  • 35,813
  • 6
  • 60
  • 86
2

meaning all of the strings in my array change to the user's new input.

This happens probably because, you have a single variable to which command refers to inside updateHistory function. So anytime you make assignment on the first line of updateHistory function, all pointers in your array of pointers point to the same memory location - command.

To fix this, you need to allocate your array of pointers like this (for example you can do this outside your function):

char *history[10];
for ( i=0; i < 10; i++ )
{
    history[i] = malloc(MAXLEN);
}

Then to copy string (this could go inside your function):

strcpy(history[i], command);

Also don't forget to free each variable in the array in the end.

Giorgi Moniava
  • 27,046
  • 9
  • 53
  • 90
1

While you are free to allocate space on the heap with malloc or calloc, if you are limiting your history to a reasonable size, a simple 2D statically declared character array can work equally well. For example:

#include <stdio.h>
#include <string.h>

/* constants for max pointers, max chars */
enum {MAXP = 10, MAXC = 256};

int main (void) {

    char history[MAXP][MAXC] = {{0}};
    char buf[MAXC] = {0};
    size_t i, n = 0;

    while (printf ("prompt > ") && fgets (buf, MAXC, stdin)) {
        size_t buflen = strlen (buf);
        buf[--buflen] = 0;  /* strip newline */

        /* check buflen to prevent saving empty strings */
        if (!buflen) continue;

        strncpy (history[n++], buf, buflen);

        if (n == MAXP) /* handle full history */
            break;
    }

    for (i = 0; i < n; i++)
        printf (" history[%zu] : %s\n", i, history[i]);

    return 0;
}

Example Use/Output

$ ./bin/fgets_static2d_hist
prompt > ls -al
prompt > mv a b/foo.txt
prompt > rsync ~/tmp/xfer hostb:~/tmp
prompt > du -hcs
prompt > cat /proc/cpuinfo
prompt > grep buflen *
prompt > ls -rt debug
prompt > gcc -Wall -Wextra -Ofast -o bin/fgets_static2d_hist fgets_static2d_hist.c
prompt > objdump obj/fgets_static2d.obj
prompt > source-highlight -i fgets_static2d.c -o fgets_static2d.html
 history[0] : ls -al
 history[1] : mv a b/foo.txt
 history[2] : rsync ~/tmp/xfer hostb:~/tmp
 history[3] : du -hcs
 history[4] : cat /proc/cpuinfo
 history[5] : grep buflen *
 history[6] : ls -rt debug
 history[7] : gcc -Wall -Wextra -Ofast -o bin/fgets_static2d_hist fgets_static2d_hist.c
 history[8] : objdump obj/fgets_static2d.obj
 history[9] : source-highlight -i fgets_static2d.c -o fgets_static2d.html

The benefit you get from a statically declared array is automatic memory management of your array storage and a slight benefit in efficiency from the memory being allocated from the stack. Either will do, it is just a matter of how much information you are managing.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
0

When you want to pass array of pointer to a function, then you can use '&' sign to pass the address when you call a function.
For example:
This is what you have declared an array char* history[10];
This is the function you have used:

char* updateHistory(char *history[], char command[], int histIndex) {
    history[histIndex] = command;      
    return *history;
}

So, while calling the function in the body of main(), call it like this

main()
{
updateHistory(&history, command, histIndex);
}

I hope this will help you out.. ok.

Jaffer Wilson
  • 7,029
  • 10
  • 62
  • 139