Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 461
Kevin Avatar asked Sep 12 '26 09:09

Kevin


2 Answers

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 arras it is, i.e. test(arr).

char * array is a pointer to character, typically used to point to the first character in an array of characters, i.e. 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 malloced.

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]);
    }
}
like image 124
amaurea Avatar answered Sep 13 '26 23:09

amaurea


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".
like image 39
Tony K. Avatar answered Sep 13 '26 23:09

Tony K.