-1

I know there are other ways to solve this like with a calloc but the question here is why does it work while it shouldn't?

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

void function (char **, char **);

int main () {
  char *list[2], *list1[] = {"word0", "word1", "word2"};

  function(list, list1);
  return 0;
}

void function (char **list, char **list1) {
  for(int i=0; i<2; i++) {
    printf("Insert string\n");
    scanf("%s", (list+i));          //adds 2 strings in list
  }

  for(int i=0; i<2; i++) {
    printf("%s\n", (list+i));       //reads the strings that I just added but if I place * before (list+i) the code crashes
  }

  printf("%s\n", *(list1+1));    //reads the second string of list1 but if I don't place * before (list+1) it reads some weird chars
}

I was trying to better understand pointers and arrays of strings, I expected that it would not work the way it's written in the comments in the code. Like why is the "*" needed before (list1+1) and not before (list+i)

Niconsky
  • 1
  • 3
  • 1
    You should get compiler warnings at `scanf("%s", (list+i))` and `printf("%s\n", (list+i))`. – Bodo Jan 23 '23 at 12:30
  • 1
    Ask yourself this: When you read string characters into your list, where is the memory where those characters are supposed to go? – user694733 Jan 23 '23 at 12:30
  • 2
    `list` is an array of two pointers, but you never initialize those pointers before trying to store to them, so your program exhibits undefined behavior. It could do anything. – Tom Karzes Jan 23 '23 at 12:32
  • But my compiler does not give me any warning – Niconsky Jan 23 '23 at 12:33
  • @Niconsky It's a runtime error. C doesn't have the training wheels you're used to. If you want those runtime checks, then use an interpreted language like Python. It will be slower, but it's easier for beginners. – Tom Karzes Jan 23 '23 at 12:34
  • 1
    The compiler is not required to warn you when your program has [undefined behavior](https://stackoverflow.com/q/2397984/212858). If you want maximum hand-holding, turn on all warnings and errors and use an address sanitizer if your compiler has one. – Useless Jan 23 '23 at 12:36
  • Before `scanf` add `list[i] = malloc( 64 );` (max 63 chars in this case) – i486 Jan 23 '23 at 12:37
  • If you don't get a warning you might have to enable warnings. In `scanf("%s", (list+i))`, `(list+i)` is of type `char**` while `%s` requires `char*`. See also https://airbus-seclab.github.io/c-compiler-security/ BTW: Pointer arithmetic is often more difficult to understand than array indexing. I suggest to prefer `list[i]` instead of `*(list+i)` or `&list[i]` instead of `list+i` depending on what you want or what is correct. The generated code will be the same, this is only about readability. – Bodo Jan 23 '23 at 12:44
  • Oh ok now I see but I'm still curious about why even under an undefined behavior it works while it shouldn't. But maybe is it useless to ask why the program does a certain thing under the condition of undefined behavior? My main concern was that soon I'll have to do a C exam and I tought that I was wrong saying this code shouldn't work. – Niconsky Jan 23 '23 at 12:44
  • Undefined behavior is, as the name says, *undefined*. The program may (seem to) work, give unexpected results or crash or do anything. The behavior may depend on the input you use or other circumstances, e.g. what data that happens to be in uninitialized memory from previous programs that have used the same memory before... – Bodo Jan 23 '23 at 12:53

1 Answers1

1
  char *list[2], *list1[] = {"word0", "word1", "word2"};

This line creates 2 arrays of pointers; the second one contains some words, but the first one is never allocated, so it contains garbage data. You need to allocate memory for list.

void function (char **list, char **list1) {
  for(int i=0; i<2; i++) {
    printf("Insert string\n");
    scanf("%s", (list+i));          //adds 2 strings in list
  }

When you calculate (list+1), you get back a char **, the same type as list. Since list was never assigned a value, this could be anywhere. However, scanf expects a char*, so it's not writing into an element of list, but writing into list as if list was a string. You need to allocate memory for each element you write into list.

You can do either:

  for(int i=0; i<2; i++) {
    printf("Insert string\n");
    char *newValue = malloc(...);
    list[i] = newValue;
    scanf("%s", list[i]);
  }

or

  for(int i=0; i<2; i++) {
    printf("Insert string\n");
    char *newValue = malloc(...);
    scanf("%s", newValue);
    list[i] = newValue;
  }

.

  for(int i=0; i<2; i++) {
    printf("%s\n", (list+i));       //reads the strings that I just added but if I place * before (list+i) the code crashes
  }

%s expects a char*; in the previous step you wrote into list as if it were a char* so now when you attempt to read it, it works. If you pass it *(list+i), you're asking printf to "print the string at location 'input text'".

  printf("%s\n", *(list1+1));    //reads the second string of list1 but if I don't place * before (list+1) it reads some weird chars
}

This is the correct approach, the previous one happens to work because of two errors "canceling" each other.

Tordek
  • 10,628
  • 3
  • 36
  • 67
  • 1
    Error handling should be added after `char *newValue = malloc(...);`, and the `scanf` format should limit the string length to one less than the size if the (allocated) memory. – Bodo Jan 23 '23 at 12:58