简体   繁体   中英

Pass by reference String array to function and modify content in C

What am I doing wrong here with passing a char array to a function and in the function giving each index memory (using malloc()), then inserting something from the keyboard using gets().

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

void test(char *arr[]);
int main(){
  char *arr[2];//2 is the rows
 /* arr[0] = malloc(80);//This commented code works
  arr[1] = malloc(80);
  strcpy(arr[0], "hey");
  strcpy(arr[1], "whats up");
*/

  test(*arr);
  printf("in array[0]: %s", arr[0]);
  printf("in array[1]: %s", arr[1]);
  return 0;
}
void test(char *arr[]){
  int index;
  char *input = malloc(80);
  for(index = 0; index < 2; index++){
  arr[index] = malloc(80);
  gets(input);
  strcpy(arr[index], input);
  //arr[0] = input;
  }
}

Just a very basic program which for some reason I am having trouble with. Also one more question When I declare an array what is the difference between these forms

char *array

as oppose to

char *array[size]

or

char **array

Thanks, Kevin

You declare arr as char *arr[2] . You then pass in *arr , which has type char* to test. But test expects a char *[] . So that won't work. You should simply pass in arr as it is, ie test(arr) .

char * array is a pointer to character, typically used to point to the first character in an array of characters, ie a string. char **array is a pointer to pointer to character. Typically used to represent an array of strings. char *array[size] is mostly equivalent to the one above, but it unlike, that one, the top-level pointer is already pointing at a valid array, so the array does not need to be malloc ed.

By the way, your test function could be simplified a bit: the strcopy is unneccessary.

void test(char *arr[])
{
    int i;
    for(i=0;i<2;i++)
    {
        arr[i] = malloc(80);
        gets(arr[i]);
    }
}

OK, in C declarations, you only get the first thing allocated that you read from a declaration.

This is a bit obscure until you understand how to read a declaration.

Operators have precedence, and that precedence applies in a declaration. For simplicity, take:

() - Either raises precedence, or means "function taking parameters and returning..."
[] - Array of
* - Pointer to (right associative)

So, the declaration:

char *a[3];

is an "array of 3 pointers to char", whereas

char (*a)[3];

is a "pointer to arrays of 3 char" (a pointer to rows of a multi-dim char array with row width 3)

The compiler allocates the very first thing on the stack. So, in the first example, you get three pointers that are capable of pointing to (an as yet unallocated) chars.

| ptr | --> (unallocated char)
| ptr | --> (unalloc char)
| ptr | --> (unalloc char)

in the second:

| ptr | --> (unallocated block of 3 char)

the next rule is: If you have a pointer, you have an array. So in both cases, you end up with multi-dimensional arrays.

In the first, the number of rows is fixed (you already allocated it), but each row can have a different size, as the compiler knows the pointer can be offset by a char to get to the next char.

In the second, you have a fixed row size, but the compiler knows how to offset your base pointer (by 3 chars each), so you have any number of rows.

C is also a call-by-value language. So, if you've declared a variable:

char *a[3];

then 'a' by itself is "an array of 3 ptr to char", but a[0] is "a ptr to a char", and a[0][0] is a char.

When you write a function that expects:

void f(char *arg[]) {
...
}

you must pass it an array of pointers to char. It does not care how many rows there are (you have to keep track of that)...it only cares that the type is correct.

So, if you have 'a' declared as in our first example, you have to call f(a). A function call does an assignment-like thing to the function args:

f(a) means call f with (arg = a).

The precedence rules are especially important, and you should work on these until you can read declarations like this:

int (*f(int (*a)[4], void (*f)()))[6];

which is "f is a function that takes:
  - a pointer to arrays of 4 int
  - a pointer to a function that takes nothing and returns nothing)
and (then f) returns a pointer to arrays of 6 int".

char *array creates a pointer to bytes of memory. char *array[size] creates a pointer to arrays of length 'size'. char **array is just a pointer to a pointer to bytes of memory

So as you can see, the problem with your code is that you are trying to pass the test function something it doesn't expect. The test function wants a pointer to a pointer, you just give it a pointer. The correct passage would be: test(arr);

To put the second one into perspective: you see this when making command line programs (sort of).

consider the following code:

#include<stdio.h>

int main(int argc, char *argv[]) {
    printf("%s\n", argv[0]);
    return 0;
}

The argv pointer points to the command line arguments, each of which are a string (which is a just a pointer, or an array). So lets say you did the following for Linux:

./some_program -option opt-input

Each of these are treated as their own string. char *argv[] points to each string. The first printf will print out ./some_program, because this is the first string that argv points to.

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