1

Can anyone explain how the below mentioned scanf part will execute in while loop for getting matrix input without boundary size in C?

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

int main() {
    char s[10000];
    int a[100][100], rows = 0, cols = 0;
    while (scanf("%[^\n]\n", s) == 1) {
        char *num = strtok(s, " ");
        int val = 0;
        while (num != NULL) {
            a[rows][val++] = atoi(num);
            num = strtok(NULL, " ");
        }
        rows++;
        cols = val;
    }
    for (int j = 0; j < cols; j++) {
        for (int i = 0; i < rows; i++)
            printf("%d ", a[i][j]);
        printf("\n)
    }
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189
  • 2
    "How the … `scanf()` part will execute in [the] `while` loop" — Badly: see [What is the effect of trailing white space in a `scanf()` format string?](https://stackoverflow.com/q/19499060/15168) It would be far safer to use `fgets()` (or POSIX `getline()`) to read the lines of data than to try using `scanf()` to do so. You could read the newline accurately using `%c` (or even `%*c`) instead of embedding the trailing newline in the format string. You also don't know how many values are initialized in each row of the matrix. You don't reset `cols` to zero at the end of a line of input. Problems! – Jonathan Leffler Jul 14 '22 at 05:10
  • Your code is also prone to buffer overflow. – Zakk Jul 14 '22 at 07:53
  • @SupportUkraine — looking at the code again in the morning light, I agree. – Jonathan Leffler Jul 14 '22 at 13:00
  • As a rule of thumb, my personal opinion is that if you find yourself using `%[…]` with `scanf`, and especially if you're using it in an attempt to read whole lines, this is a clear indication that it's time to [learn how to use something better than `scanf`](https://stackoverflow.com/questions/58403537/what-can-i-use-to-parse-input-instead-of-scanf). – Steve Summit Jul 14 '22 at 23:10

2 Answers2

1

how the below mentioned scanf part will execute ...

scanf("%[^\n]\n",s) weakly attempts to read a line of input.

Code then uses strtok() to find tokens which are then parsed with atoi().

Code contains weaknesses.

Incorrect reading a a line of user input

"%[^\n]" fails if the first character is '\n'. Else it reads an unlimited number of non-'\n' characters into s, then appending a null character. "\n" then reads in, and discards, 0 to an unlimited number of white-spaces including '\n', ' ', etc.

while(scanf("%[^\n]\n",s)==1){ will stop the loop if the first line is only "\n", but will silently consume "\n" otherwise. Lacking a width, it it worse than gets().

Perhaps scanf("%9999[^\n]%c", s, &char_sentinel) >= 1, yet far better to use fgets() here.

// Basic alternative
while (fgets(s, sizeof s, stdin)) {

10000 - that's a long line

For 100 int in a line, I'd expect a worse case of 11 char (e.g. "-2147483647") and a spacer so 12*100 + an '\n' and '\0'. Being generous, how about a worse case line twice that or char s[(12*100+2)*2];?

Too many columns or rows break code

a[rows][val++]=atoi(num); is also prone to buffer overflow as row, col may exceed valid range [0...99] resulting in undefined behavior (UB).

No detection for uneven amount of columns

The number of columns per row may vary. The final printing cols only reflects the last row.

Integer conversion woes

atoi(num) lacks no conversion and overflow detection. Rather than strtok(), atoi(), use strtol() to march down the line of text and detect problems.

Limited size

Code only handles a matrix up to 100 columns and 100 rows. Code uses excessive sized buffers. More advanced code would be more flexible. (not shown below)

Some improvement ideas

After reading a line ...

if (rows >= 100) {
  break; // TBD code to handle excessive rows
} 
char *str = s;
int column = 0;

while (*str) {
  if (column >= 100) {
    break; // TBD code to handle excessive columns
  } 
  char *endptr;
  errno = 0;
  long number = strtol(str, &endptr, 10);
  // If no conversion or out-of-range ...
  if (str == endptr || errno || number < INT_MIN || number > INT_MAX) {
    break; // Handle errors in some fashion
  }
  a[rows][column++] = (int) number;
  str = endptr; // Start where prior conversion left off.
}
if (row == 0) {
  cols = column;
} else if (cols != column) {
  ; // Handle column mis-match
}
row++;
chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

As explained by @chux, it is much better to read the file one line at a time and parse the line with strtol() or some other safe method.

Also note that the final nested loops enumerate the cols first, then the rows so the matrix is output transposed. This might not be intended.

Here is an alternative using sscanf() to parse the line, displaying the matrix as it is entered:

#include <stdio.h>

int main() {
    char s[10000];
    int a[100][100] = { 0 };
    int rows = 0, cols = 0;

    while (rows < 100 && fgets(s, sizeof s, stdin)) {
        int col = 0, pos = 0, len;
        while (col < 100 && sscanf(s + pos, "%d%n", &s[rows][col], &len) == 1) {
            pos += len;
            col++;
        }
        if (cols < col)
            cols = col;
        rows++;
    }
    for (int i = 0; i < rows; i++) {
        for (int j = 0; j < cols; j++) {
            printf("%d ", a[i][j]);
        }
        printf("\n)
    }
    return 0;
}
chqrlie
  • 131,814
  • 10
  • 121
  • 189