简体   繁体   中英

Passing an array of pointers by reference

I'm trying to get commands from the keyboard in a similiar fashion as command line args int main( int argc, char *argv[] ) but in a separate function. When I parse and print them within the scope of the getCmd() function all looks and behaves as intended, but as soon as they return to the main function they become a bunch of garbage. My questions are below the code.

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

void getCmd(char *cmd, char *args[])
{
    char input[81] = { 0 };
    char *next_token = NULL;
    printf("$ ");
    fgets(input, 81, stdin);
    input[strcspn(input, "\n")] = 0;
    cmd = strtok_s(input, " ", &next_token);
    if (!strcmp(cmd, "mv"))
    {
        args[0] = strtok_s(NULL, " ", &next_token);
        args[1] = strtok_s(NULL, " ", &next_token);
        printf("\n\n%s\n%s\n%s\n\n", cmd, args[0], args[1]);
    }
}

int main(void)
{
    char *cmd = NULL, *args[5];

    cmd = (char *)calloc(20,sizeof(char));
    for (size_t i = 0; i < (size_t)5; i++)
    {
        args[i] = (char *)calloc(20,sizeof(char));
    }
    getCmd(cmd, args);
    printf("\n\n%s \n%s\n%s", cmd, args[0], args[1]);
    return 0;
}

I don't think its relevant but I'm using VS 2015 Community with the Visual C++ compiler on a 64 bit processor, Windows 7 OS.

My questions:

  • How should I pass the cmd and args[] by reference?
  • Are there any widely accepted idioms that deal with this sort of situations?

I've looked trough a few of the similiar questions and couldn't find a solution that works in this context, if the question is a duplicate, tell me and I'll close it.Since I'm new to stackoverflow any question formatting tips would be greatly appreciated. Cheers! (:

There are a number of different ways to approach the problem. While you are free to dynamically allocate memory for cmd and your args array, there is really no need, for this limited amount of memory you can use a static declaration for all. There is no need for a separate input array, you cause use cmd for that purpose and then tokenize cmd . This provides the benefit of leaving the first token nul-terminated in cmd after strtok is called.

note: in the example below, strtok is used, strtok_s was an optional compiler addition in C11, and unfortunately, I don't have a compiler that implements that option, so I test with strtok . You can easily make the change for VS.

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

enum { NARGS = 5, MAXC = 128 };

size_t getcmd (char *cmd, char (*args)[MAXC]);

int main (void) {

    char cmd[MAXC] = "", args[NARGS][MAXC] = { "" };
    size_t i, n;

    if (!(n = getcmd (cmd, args))) return 1;

    printf ("  %s", cmd);
    for (i = 0; i < n; i++)
        printf(" %s", args[i]);
    putchar ('\n');

    return 0;
}

size_t getcmd (char *cmd, char (*args)[MAXC])
{
    char *delim = " ,.\t\n";
    char *p = NULL;
    size_t idx = 0;

    printf ("$ ");
    if (!fgets (cmd, MAXC, stdin)) {
        fprintf (stderr, "error: invalid input.\n");
        return 0;
    }

    strtok (cmd, delim);            /* terminate after 1st token */

    for (p = strtok (NULL, delim); p; p = strtok (NULL, delim)) {
        strncpy (args[idx++], p, MAXC);  /* limit to avail chars */
        if (idx == NARGS) break;    /* limit to available bounds */
    }

    return idx;
}

Note above, the return type of getcmd is size_t . Always choose a meaningful type to return an indication of success/failure as well as returning some needed information (the number of arguments here). Also note the C-Style Guide disfavors camelCase variable/function names preferring all lower-case instead. Leave camelCase names for C++. See eg NASA - C Style Guide, 1994

Example Use/Output

$ ./bin/getcmd
$ mv /this/here/file /that/there/file
  mv /this/here/file /that/there/file

$ ./bin/getcmd
$ mv -i --strip-trailing-slashes /this/here/file /that/there/file
  mv -i --strip-trailing-slashes /this/here/file /that/there/file

Look it over and let me know if you have any additional questions.

strtok_s() return pointer into the buffer it's parsing ( input here).

input lives on getCmd() stack. It dies the moment getCmd() returns. From then on the addresses pointing into input and that had been stored in args 's elements does not point to valid memory any more.

The code needs allocate fresh memory and copy what strtok_s() returned a pointer to.

Have a look on ow this can be done:

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

void getCmd(char **pcmd, char *args[], size_t s)
{
    char input[81] = { 0 };
    char *next_token = NULL;
    printf("$ ");
    fgets(input, 81, stdin);
    input[strcspn(input, "\n")] = 0;
    (*pcmd) = _strdup(strtok_s(input, " ", &next_token));
    if (!strcmp(*pcmd, "mv"))
    {
        args[0] = _strdup(strtok_s(NULL, " ", &next_token));
        args[1] = _strdup(strtok_s(NULL, " ", &next_token));
        printf("\n\n%s\n%s\n%s\n\n", *pcmd, args[0], args[1]);
    }
}


#define ARGS_MAX (5)

int main(void)
{
    char *cmd, *args[ARGS_MAX] = {0};

    getCmd(&cmd, args, ARGS_MAX);
    printf("\n\n%s \n%s\n%s", cmd, args[0], args[1]);

    /* Clean up. */
    free(cmd);
    for (size_t i = 0; i < ARGS_MAX; ++i)
    {
      free(args[i]);
    }

    return 0;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM