3

First of all, this is a test program, i wanna test something specific that i wanted to see if it works. Lets say that i wanna assign x to arr[0][4] and wanna keep this change so that arr[0][4] is x in the main function too:

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

void changE(char *arr[10][5]);

int main(void){
    char *arr[10][5];
    arr[0][0] = "Johny";
    arr[0][1] = "Tony";
    arr[0][2] = "Tiki";
    arr[0][3] = "Kitty";
    arr[0][4] = "Douglas";
    arr[1][0] = "Piki";
    arr[1][1] = "Kati";
    arr[1][2] = "Sathi";
    arr[1][3] = "Dony";
    changE(arr);
    int i = 0;
    int j;
    for(i;i<=1;i++){
        for(j=0;j<=4;j++){
            printf("%s\n", arr[i][j]);
        }
        printf("\n\n\n");
    }
    return 0;
}
void changE(char *arr[10][5]){
    char x[50] = "Tinky";
    arr[0][4] = x;
}

The problem is that i cant assign x to arr[0][4], the program just shuts down. I also tried strcpy, this:

void changE(char *arr[10][5]){

    char x[50] = "Tinky";
    strcpy(arr[0][4], x);
}

Its the same thing with strcpy the program just shuts down. I can only do this:

void changE(char *arr[10][5]){
    arr[0][4] = "Tinky";
}

Which doesnt help me at all, considering that x is a string that i dont know(or a string from scanf). So if x comes from a scanf how can i assign x to arr[0][4]? Any help would be appreciated! Thanks :)

Cache
  • 471
  • 2
  • 5
  • 14
  • 1
    C does not nave a `string` type. Whether a sequence of `char` is a "string" is just convention of the functions used. Please read about pointers and arrays in C. – too honest for this site Jan 02 '16 at 22:05
  • this line: `for(i;i<=1;i++){` has a logic error. Your compiler should have told you about this problem. Suggest either: `for(i=0; i<=1; i++){` or `for(; i<=1; i++){` – user3629249 Jan 04 '16 at 06:32

6 Answers6

4

This method

void changE(char *arr[10][5]){
    char x[50] = "Tinky";
    arr[0][4] = x;
}

doesn't work because you are assigning a variable with automatic storage (aka "local") duration. Once the function returns, it doesn't exist anymore. This is undefined behaviour.

This method

void changE(char *arr[10][5]){

    char x[50] = "Tinky";
    strcpy(arr[0][4], x);
}

doesn't work because the arr[0][4] is pointing to a string literal. You can't modify a string literal. Again, this is undefined behaviour.

This method

void changE(char *arr[10][5]){
    arr[0][4] = "Tinky";
}

is the only correct way if you only have pointers and not allocated any memory for the pointees of those pointers.

So if x comes from a scanf how can i assign x to arr[0][4]?

For such scenario, you need to allocate memory and assign it to arr[0][4].

void changE(char *arr[10][5]){ 
   char x[40] = "Tinky";
   arr[0][4] = strdup(x); //POSIX, equivalent std C would be malloc()+strcpy()
}

This is quite messy though. The array has pointers to string literals. But just arr[0][4] is pointing to a malloc'ed memory. You need to track such pointers if you want to be able to modify it or later when you call free() on it.

I would suggest you use just arrays instead of arrays of pointers if you want to be able to modify them as it's hard to keep track various pointers that are malloc'ed and with rest of them pointing at string literals.

haccks
  • 104,019
  • 25
  • 176
  • 264
P.P
  • 117,907
  • 20
  • 175
  • 238
1

In the first function example that fails, you are trying to copy an automatic string variable to a string literal, which is read-only. That's why the function fails. arr[0][4] is a pointer to a string literal.

void changE(char *arr[10][5]){
    char x[50] = "Tinky";
    strcpy(arr[0][4], x);
}

But in the second example that succeeds, you assign a string literal pointer directly (as you did in the first initialisation).

void changE(char *arr[10][5]){
    arr[0][4] = "Tinky";
}

As an aside, note the difference between

char name[] = "Alex";

and

char *name = "Alex";

In the first case, name is initialised with a copy of the data given, and it is modifiable.

In the second case, all name knows is a pointer, and the text it points to (a "string literal") is not modifiable.

Weather Vane
  • 33,872
  • 7
  • 36
  • 56
  • Thanks for answering! I think i did understand what you said. So is there a way to achieve what i need(x to arr[0][4] without assign the string directly). Maybe make x global pointer or something? – Cache Jan 02 '16 at 22:28
  • 1
    *In the first function example that fails, you are trying to copy a dynamic string to a string literal, which is read-only.* No that's not the case. – haccks Jan 02 '16 at 22:32
  • You would need to allocate memory with `malloc` to the string pointer array elements first (allowing for string terminators!) Then copy the string literals, instead of assinging their pointers to the array. When you need a different string there, it might be a different size, so you would `realloc` the memory for that array element. – Weather Vane Jan 02 '16 at 22:32
  • @haccks slight tweak thanks of "dynamic string" to "automatic string variable". the other part is true, despite your comments under another answer. – Weather Vane Jan 02 '16 at 22:34
  • @haccks please post that as a question if you don't know it's OK. – Weather Vane Jan 02 '16 at 22:47
  • @haccks why don't you take a coffee break? You comments under these questions are ridiculous. – Weather Vane Jan 02 '16 at 22:52
  • 1
    I think haccks is confused because you both numbered the implementations differently. For haccks, the implementation using `strcpy` is the second one because the first one as actually in the first code block of the question (see his post). – Martin Zabel Jan 02 '16 at 22:57
  • @haccks there are two functions that fail. I originally answered about the one that copies a pointer that goes out of scope. I edited the answer to be about the one that copies to a string literal, because I thought I had misread it. However I made my answer clear as to which one, quite some time ago. Now go get a coffee. – Weather Vane Jan 02 '16 at 23:02
  • @WeatherVane; That's why I downvote answers rarely :). Time to some reward for you guys. – haccks Jan 02 '16 at 23:04
1

All the elements of arr are pointers to char. You initialized them in main, no memory is allocated. They just point to string literals.

The first version of changeE

void changE(char *arr[10][5]){
    char x[50] = "Tinky";
    arr[0][4] = x;
} 

doesn't work because x is an automatic local variable and returning pointer to it leads to undefined behavior.

In function

void changE(char *arr[10][5]){

    char x[50] = "Tinky";
    strcpy(arr[0][4], x);
}

strcpy wouldn't work because arr[0][4] is pointing to a string literal and it can't be modified. You can modify arr[0][4] by allocating space for it

void changE(char *arr[10][5]){

    char x[50] = "Tinky";
    arr[0][4] = malloc(strlen(x) + 1);
    strcpy(arr[0][4], x);
}   
haccks
  • 104,019
  • 25
  • 176
  • 264
1

At first, you should initialize your array arr, so that, it contains no invalid pointers:

char *arr[10][5] = {0};

Your first implementation of changE does not work because char x[50] is a local variable (allocated on the stack) initialized to "Tinky". You cannot excess this variable (by derefencing the pointer) once the function returns.

The implementation using strcpy does not work, because the destination memory is the string literal "Douglas" (as assigned in main) which cannot be overriden.

The pointer assignment from the first implementation is just fine, but you have to allocate memory from the heap as in this scanf example:

void changE(char *arr[10][5]){
    char *x = (char *)malloc(100 * sizeof *x);
    if(!x) exit(1); // error handling
    scanf("%99s", x);
    arr[0][4] = x;
}
Martin Zabel
  • 3,589
  • 3
  • 19
  • 34
1

String literals have static storage duration. So these assignments

arr[0][0] = "Johny";
arr[0][1] = "Tony";
arr[0][2] = "Tiki";
arr[0][3] = "Kitty";
arr[0][4] = "Douglas";
arr[1][0] = "Piki";
arr[1][1] = "Kati";
arr[1][2] = "Sathi";
arr[1][3] = "Dony";

or this assignment in function changE

arr[0][4] = "Tinky";

are coorect.

However you want to assign an element of the array with address of some character array that you are going to enter. In this case the storage duration of the array should be at least the same as the storage duration of the two-dimensional array.

The only reasonable approach is to dynamically allocate memory for readable characters and store their addresses in elements of the two-dimensional array.

You could use POSIX function strdup to do this. For example

arr[0][0] = strdup( "Johny" );
arr[0][1] = strdup( "Tony" );
arr[0][2] = strdup( "Tiki" );
arr[0][3] = strdup( "Kitty" );
arr[0][4] = strdup( "Douglas" );
arr[1][0] = strdup( "Piki" );
arr[1][1] = strdup( "Kati" );
arr[1][2] = strdup( "Sathi" );
arr[1][3] = strdup( "Dony" );

Or you could write such a function yourself.

In this case you have to use also standard C function free to free all allocated memory by strdup or similar function.

The same approach should be used in the function changE

For example

void changE(char *arr[10][5]){
    char x[50] = "Tinky";
    arr[0][4] = malloc( strlen( x ) + 1 );
    strcpy(arr[0][4], x);
}
Vlad from Moscow
  • 301,070
  • 26
  • 186
  • 335
  • 1
    I was going to suggest the `strdup` approach myself. But, now, `changE` should do `free(arr[0][4])` before reassigning it. Actually, `realloc` may work as well. If you clean a bit, I'll be glad to upvote. – Craig Estey Jan 02 '16 at 22:51
  • And, you should maybe use a `scanf` example within `changE`. – Martin Zabel Jan 03 '16 at 00:12
0

A possible solution is to have character arrays instead of pointers to characters like below. The strings' maximum length is 50 in this example.

void changE(char arr[10][5][50]);

int main(void){

    char arr[10][5][50];

    //...

    strcpy(arr[0][0], "Johny");
    strcpy(arr[0][1], "Tony");

    //...
    changE(arr);
    //...
}
void changE(char arr[10][5][50]){
    //this works now
    char x[50] = "Tinky";
    strcpy(arr[0][4], x);

    //scanf works as usual
    scanf("%s", &arr[0][4]);
}
Andrea Dusza
  • 2,080
  • 3
  • 18
  • 28
  • You should explicitly mention that the maximum string length is now 50. And you should respect this the `scanf` call. – Martin Zabel Jan 03 '16 at 00:35
  • Ups, at first, I have to correct me, the maximum string length is 49 because of the terminating NUL character. So if someone enters 50 character at the `scanf` prompt, then the terminating NUL character will be written at `arr[0][4][50]` which is beyond the limits of `arr[0][4]`, so that, the NUL character is actually placed at `arr[1][0][0]`. Thus, you must define the maximum string length in the call with `scanf("%49s")`, for example. – Martin Zabel Jan 03 '16 at 17:41