2

I know there are multiple questions about creating dynamic arrays in C but they didn't really help me so let me ask in a different way.

My program needs to read in a variable number of command line arguments, each of variable length. This function takes the argv array passed into main and should return an array of char* containing only those arguments that are environment settings. (The program needs to replicate the UNIX env command.) Here is what I have now:

char** getEnvVariables(char* args[], int start) {
    int i = start;
    char** env_vars = (char **) malloc(1); 
    while (args[i]) {
        printf("size of env_vars: %d\n", sizeof(env_vars));
        if (isEnvironmentSetting(args[i])) {
            printf("arg: %s\n", args[i]);
            printf("size of arg: %d\n", sizeof(args[i]));
            printf("new size of env_vars: %d\n", (sizeof(env_vars) + sizeof(args[i])));

            env_vars = realloc(env_vars, (sizeof(env_vars) + sizeof(args[i])));
            memcpy(env_vars, args[i], sizeof(args[i]));
            i++;
        }
        else
            break;
    }

    return env_vars;
}

My idea was to create the array with malloc() and then use realloc() to allocate the space needed for each char* and memcpy() to add the new char* to the array. But the array isn't actually growing. At each iteration of the loop, it's size is 8 bytes. I'm still very new to C and the hands-on memory management so any help is appreciated.

2
  • 3
    sizeof(...) is a compile-time construct (except in the case of VLAs, which you don't need to worry about here), so sizeof(env_vars) is the size of the pointer env_vars, not the size of the block of allocated memory that env_vars points to. There is no portable way to determine the latter; you just have to keep track yourself of the value that you pass to malloc/realloc. Commented Sep 21, 2012 at 3:35
  • sizeof is also not strlen, so sizeof(args[i]) is also wrong. Commented Sep 21, 2012 at 4:12

3 Answers 3

1

You cannot copy the input C-strings to the output, unless you want to return a (char pointer) that points to all the strings concatenated together. To return an array of (char pointer) (or (char pointer pointer) ) you need to either malloc a new string and store the address of that in env_vars, or have env_vars store the address of args[i]. Here is an implementation of both approaches:

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

bool isEnvironmentSetting(const char * string)
{
   (void) string;
   return true;
}

char** getEnvVariables1(char* args[], int start) {
    int i = start;
    int num_args = 0;
    char** env_vars = NULL;
    char *string = NULL;
    printf("size of env_vars: %ld\n", num_args * sizeof(env_vars));
    while (args[i]) {
        if (isEnvironmentSetting(args[i])) {
            printf("arg: %s\n", args[i]);
            printf("size of arg: %ld\n", strlen(args[i]));
            num_args++;
            printf("new size of env_vars: %ld\n", num_args * sizeof(env_vars));
            env_vars = realloc( env_vars, sizeof(env_vars) * num_args );
            string = malloc(strlen(args[i]) + 1);
            strcpy(string,args[i]);
            env_vars[num_args - 1] = string;
            i++;
        }
        else
            break;
    }
    env_vars = realloc( env_vars, sizeof(env_vars) * (num_args + 1) );
    env_vars[num_args] = NULL;    
    return env_vars;
}

char** getEnvVariables2(char* args[], int start) {
    int i = start;
    int num_args = 0;
    char** env_vars = NULL;
    printf("size of env_vars: %ld\n", num_args * sizeof(env_vars));
    while (args[i]) {
        if (isEnvironmentSetting(args[i])) {
            printf("arg: %s\n", args[i]);
            printf("size of arg: %ld\n", strlen(args[i]));
            num_args++;
            printf("new size of env_vars: %ld\n", num_args * sizeof(env_vars));
            env_vars = realloc( env_vars, sizeof(env_vars) * num_args );
            env_vars[num_args - 1] = args[i];
            i++;
        }
        else
            break;
    }
    env_vars = realloc( env_vars, sizeof(env_vars) * (num_args + 1) );
    env_vars[num_args] = NULL;
    return env_vars;
}

int main(int argc, char *argv[])
{
   (void) argc;

   char **envVars = getEnvVariables1(argv,1);
   int i = 0;
   while (envVars[i] != NULL)
   {
      printf("env var: %s\n",envVars[i]);
      i++;
   }

   envVars = getEnvVariables2(argv,1);
   i = 0;
   while (envVars[i] != NULL)
   {
      printf("env var: %s\n",envVars[i]);
      i++;
   }

}
Sign up to request clarification or add additional context in comments.

1 Comment

Thanks, this worked great. I went with your second approach since it was more memory efficient to copy just the pointers instead of allocating memory for a copy of the string.
1

You can get environment variables specifing main function this way:

int main(int argc, char *argv[], char *envp[])

envp variable likes argv and contains environment variables.

1 Comment

That's not what the OP is asking for. Read the spec of the env command.
1

sizeof(env_vars) will always return the size of pointer, not the size of allocated memory the env_vars points to. You can use sizeof for such purpose only for statically-allocated arrays.

So in your case you need to maintain a separate variable that holds the array size.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.