-1

I'm trying to read the following type of input

2
string1
string2

where the first line indicates the amount of strings following below, and the strings are all of (some) same length. Next, I'd like to print these strings in my program, which I was thinking of doing as follows.

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

void print_string_array(char** a, int size){
    for (int i=0; i<size; i++){
        printf("%s\n", a[i]);
    }
}

int main()
{
    int n;
    scanf("%d", &n);
    char* s;
    scanf("%s", s);
    int l = strlen(s);
    char input[n][l];
    strcpy(s, input[0]);
    for (int i = 1; i < n; i++) {
        scanf("%s", s);
        strcpy(s, input[i]);
    }
    print_string_array(input, n);
}

But I get the following warnings and error.

main.c: In function ‘main’:
main.c:24:24: warning: passing argument 1 of ‘print_string_array’ from incompatible pointer type [-Wincompatible-pointer-types]
   24 |     print_string_array(input, n);
      |                        ^~~~~
      |                        |
      |                        char (*)[(sizetype)(l)]
main.c:5:32: note: expected ‘char **’ but argument is of type ‘char (*)[(sizetype)(l)]’
    5 | void print_string_array(char** a, int size){
      |                         ~~~~~~~^

Segmentation fault (core dumped)

Why isn't my array input recognized as a char** type? And how would I go about fixing this without removing the general properties of my current program (for example, adaptability to any length of input strings)?

EDIT:

I've corrected some mistakes from not having coded in C for years (which may be a war crime from what I've seen in the comments):

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

void print_string_array(char** a, int size){
    for (int i=0; i<size; i++){
        printf("%s\n", a[i]);
    }
}

int main(){
    int n;
    scanf("%d", &n);
    char s[100]; // large enough buffer 
    scanf("%s", s);
    int l = strlen(s);
    char input[n][l+1]; // +1 to leave room for ‘\0’ character?
    strcpy(input[0], s);
    for (int i=1; i<n; i++){
        scanf("%s", input[i]);
    }
    print_string_array(input, n);
    for (int i=0; i<n; i++){
        free(input[i]);
    } 
    free(input);
}

But I still get some errors and warnings which I would like to solve.

main.c: In function ‘main’:
main.c:22:24: warning: passing argument 1 of ‘print_string_array’ from incompatible pointer type [-Wincompatible-pointer-types]
   22 |     print_string_array(input, n);
      |                        ^~~~~
      |                        |
      |                        char (*)[(sizetype)(n)]
main.c:5:32: note: expected ‘char **’ but argument is of type ‘char (*)[(sizetype)(n)]’
    5 | void print_string_array(char** a, int size){
      |                         ~~~~~~~^

Segmentation fault (core dumped)

Starting with the warnings, obviously I change the type of a from char** to something else. However, should I really give it the type char (*)[(sizetype)(n)]?? Also, there's the problem of segmentaion fault which is happening somewhere in the for loop.

J. Schmidt
  • 419
  • 1
  • 6
  • 17
  • `scanf("%s", s)` - ask yourself what `s` points to when that executes. Your code invokes *undefined behavior*. – WhozCraig Sep 07 '22 at 10:11
  • What do you think that `char *s;` points to? It may point to anything, causing the subsequent `scanf` to potentially overwrite memory anywhere. Don't do that, make sure you pass a buffer that is large enough to hold the data, and consider using the length in the `scanf` specifier. – Cheatah Sep 07 '22 at 10:11
  • Worse (which is difficult to top), you not only repeat the same problem later on, you also `strcpy` from indeterminate data in `input[]` as the source to the same uninitialized pointer. And then you ignore the compiler warning you should be getting when passing `input` to a function expecting `char**`. The only thing in this code seemingly correct is the `scanf` to acquire `n`, and even that is never checked to ensure it didn't fail. The rest looks like code-guesswork. Fyi: `strcpy` takes the *target* as the first argument, the *source* as the second. You seem to believe the opposite. – WhozCraig Sep 07 '22 at 10:18
  • Does this answer your question? [C error expected ‘char \*\*’ but argument is of type ‘char (\*)\[10\]’](https://stackoverflow.com/questions/68298019/c-error-expected-char-but-argument-is-of-type-char-10) ```Segmentation fault (core dumped)``` is due to faulty usage of ```scanf``` as stated in other comments –  Sep 07 '22 at 10:21
  • @WhozCraig you've won the rudest comment award 2022 (which is difficult to top) – J. Schmidt Sep 07 '22 at 11:44
  • I've corrected most obvious errors due to my long period of not having touched C, and have asked more precise questions. – J. Schmidt Sep 07 '22 at 12:44

4 Answers4

2

Besides the problems in your code that are explained in the comments.

input is a char* that points to an array in memory that is of size n*l and not a char**, which is a pointer to a char*.

You would need to allocate an array of char*[] and then add each char* pointer that you get from scanf to that array.

1

Maybe this will help you out. It shows an example of dynamic string allocation

char buffer[101];
printf("Enter your name: ");
// Stores the name into auxiliary memory
scanf(" %100[^\n]", buffer);
// Creates a dynamic string
char* name = (char *) malloc(strlen(buffer) + 1);
//or use char* name = strdup(buffer) as pointed out by @Cheatah in the comments
// Sets the value
strcpy(name, buffer); 

printf("Your name is %s", name);
// Frees the memory
free(name);
Dany
  • 37
  • 5
1

The Variable Length Array input[n][l+1] does not need to be free'd.
The input[n][l+1] array is not a pointer to pointer **a. There are differences in the memory arrangement and they are not compatible.
void print_string_array( int size, int len, char (*a)[len]){ gives the compiler the dimensions of the VLA so the function can access it correctly.
scanf("%99s", s); will limit the input to no more than 99 characters. There is a problem in limiting input in scanf("%s", input[i]); to avoid putting too many characters into input[i].

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

void print_string_array( int size, int len, char (*a)[len]){
    for (int i=0; i<size; i++){
        printf("%s\n", a[i]);
    }
}

int main(){
    int n;
    scanf("%d", &n);
    char s[100]; // large enough buffer
    scanf("%99s", s);
    int l = strlen(s);
    char input[n][l+1]; // +1 to leave room for ‘\0’ character?
    strcpy(input[0], s);
    for (int i=1; i<n; i++){
        scanf("%s", input[i]);
    }
    print_string_array(n, l + 1, input);
}
user3121023
  • 8,181
  • 5
  • 18
  • 16
  • Could you explain what exactly one should understand when reading `char (*a)[len]`? I don't think I have ever seen such a type before, also can it be written in another way, for example `char* a[len]` (doesn't work, tried it)? – J. Schmidt Sep 08 '22 at 11:48
1

If you are just to print the strings one after the other, as you receive them, there's no need to store the full set of strings (or even a complete string) to do the task, you can implement a simple (4 states) automaton to read the number of strings, then print the strings as you are receiving their characters (character by character). This allows you to handle any length strings (like hundreds of megabyte strings) and upto 4,294,967,295 strings. (more if I change the declaration of n) and you'll get a far more efficient program that starts printing as soon as possible, instead of reading everything before printing anything.

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

#define IN_STRING    (0)
#define AFTER_NUMBER (1)
#define IN_NUMBER    (2)
#define PRE_NUMBER   (3)

#define F(_fmt) "%s:%d:%s:"_fmt, __FILE__, __LINE__, __func__

#define ERR_TAIL(E_fmt, ...) do {             \
        fprintf(stderr, _fmt, ##__VA_ARGS__); \
        exit(1);                              \
    } while(0)

#define ERR(ERR_fmt, ...)                     \
    ERR_TAIL(F("ERROR: "_fmt), ##__VA_ARGS__)

int main()
{
    int c,                      /* to hold the char */
        last_char = EOF,        /* to hold the prvious char */
        status    = PRE_NUMBER; /* state of the automaton */
    unsigned n    = 0;          /* number of strings to read */

    while ((c = fgetc(stdin)) != EOF) {
        switch (status) {

        case PRE_NUMBER:
            switch(c) {

            case ' ': case '\n': case '\t':
                break;

            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
                status = IN_NUMBER;
                n = c - '0';
                break;

            default: /* error */
                ERR("Sintax error on input: non"
                    " digit character '%c' before "
                    "first number\n", c);
                return 1;

            } /* switch */
            break;

        case IN_NUMBER:
            switch (c) {

            case '0': case '1': case '2': case '3': case '4':
            case '5': case '6': case '7': case '8': case '9':
                n *= 10;
                n += c - '0';
                break;

            case '\n':
                status = IN_STRING;
                break;

            default:
                status = AFTER_NUMBER;
                break;

            } /* switch */
            break;

        case AFTER_NUMBER:
            switch (c) {

            case '\n':
                status = IN_STRING;
                break;

            case '\t': case ' ': /* ignored */
                break;

            default: /* error */
                ERR("Sintax error on input: extra"
                    " character '%c' after first "
                    "number\n", c);
                exit(1);
            } /* switch */
            break;

        case IN_STRING:
            switch (c) {

            case '\n':
                fputc(c, stdout);
                if (!--n)
                    exit(0);
                break;

            default:
                fputc(c, stdout);
                break;

            } /* switch */
            break;

        } /* switch */
        last_char = c;
    } /* while */

    int exit_code = 0;
    /* decide what to do when we receive a premature EOF */
    switch(status) {

    case PRE_NUMBER: /* before reading a number */
        ERR("No number read before the strings\n");
        exit_code = 1;
        break;

    case IN_NUMBER:    /* in a number or in the spaces */
    case AFTER_NUMBER: /* after the first newline */
        fprintf(stderr,
                "Nothing read after the number of strings\n");
        exit_code = 1;
        break;

    case IN_STRING: /* IN_STRING, but */
        if (last_char != '\n')
            fputc('\n', stdout);

    } /* switch */

    return exit_code;
} /* main */

anyway, in your code:

    char* s;
    scanf("%s", s);

you cannot pass scanf() a pointer value that has not been initialized with enough memory to hold the string to be read.

On other side, beware that variable array declaracions (VAD) are controversial, as many compilers don't implement them and your code will not be portable, when you declare an array as VAD, you reserve space in the stack for the array, this being probably a mess if you end declaring very big arrays. The best and most portable way of declaring an array is to declare it with a constant expression (an expression that doesn't change at run time / an expression that is calculated by the compiler at compilation time) This is not your case, as you want to hold as big lines and as many as you like, with the only restriction that all the strings must have the same length as the first one (which you cannot enforce in any way)

One reason for the VADs being controversial is related to the following warning you have in your code

main.c:5:32: note: expected ‘char **’ but argument is of type ‘char (*)[(sizetype)(n)]’
    5 | void print_string_array(char** a, int size){
      |                         ~~~~~~~^

This informs you that a char ** is not the same type as a pointer to an array of n elements, on pointer decayment when passing it as parameter (the decayment happens only to the outer pointer, not to the inner ones). Decayment goes to the first level only, as the second/third/etc. are pointers to arrays, and so, the referred elements are not pointers, but arrays of n elements. When you increment a pointer char * or you do pointer arithmetic, you increment the address stored in that pointer variable by one because the pointed to type is an array. But when you increment a pointer char (*)[10] (a pointer to a ten element char array) you increment the address stored in that pointer by 10, so you cannot use pointer arithmetic, as you have declared it as a double pointer and it will be increased by sizeof (char *) (which is not what you want, 8 bytes, the size of a pointer)

Almost all the things commented lead you to Undefined Behaviour, and it is normal that you are getting that error. If I hade to write your code (and conserve the strings before doing anything) I would use dynamic memory with malloc(3) and realloc(3) as I parse the input.

Luis Colorado
  • 10,974
  • 1
  • 16
  • 31