There are an extremely large number of errors in your code. Probably the one of the largest impediments to recognizing the problems in your code, while not an error, is the lack of spacing in your code. Scrunching everything together makes your code very difficult to read (especially for older eyes). Open the spacing on your code up a bit.
As mentioned in the comments above, your first show-stopping problem is the use of str1
while it is uninitialized. That invokes undefined behavior and then defined operation of your code is over at that point. Your code could SegFault, appear to work normally, or anything in between.
When you are taking user-input, it is recommended you use a line-oriented input function like fgets()
or POSIX getline()
. That one change avoids a large number of pitfalls associated with attempting to take user-input with a formatted-input function like scanf()
. If you don't know what each of the pitfalls are associated with its use -- don't use it for user input. 9 out of 10 of the user input questions on this site relate to the misuse of scanf()
.
Additionally, Don't Skimp On Buffer Size!!. What happens if the user enters "iskabibble"
instead of "quit"
? How are the characters that do not fit in str1[5]
handled? What if the cat steps on the keyboard and 100 characters are entered? It's better to be 10,000 characters too long, than one character too short. Take input with fgets()
and a sufficiently sized buffer, and then parse the values needed with sscanf()
instead of trying to do both with scanf()
, e.g.
#define MAXC 512 /* if you need a constant, #define one (or more)
* ( don't skimp on buffer size!! )
*/
int main (void) {
char buf[MAXC]; /* buffer to store all user input */
int n; /* if possible, declare variables at beginning of scope */
fputs ("please input n for (n x n array): ", stdout);
if (!fgets (buf, MAXC, stdin)) { /* read all user input with fgets() */
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%d", &n) != 1) { /* validate every conversion */
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
Reusing a single buffer to handle user-input simplifies things greatly.
While it is fine to use a VLA (Variable Length Array) for practice problems, be aware compilers are not required to support them from C11 forward, and none were supported before C99. Better to dynamically allocate for production code.
Your variable declarations should be in the scope where the variables are needed. This helps prevent variable shadowing of common variables such as i
, j
, etc.. 300 lines down in your code. For example, loop variables can be declared as part of the loop declaration so long as they are not needed outside the loop, e.g.
int arr[n][n]; /* VLA's are an 'optional' feature since C11 */
for (int i = 0; i < n; i++) { /* i, j can be decalred with loop scope */
for (int j = 0; j < n; j++) {
arr[i][j] = -1;
}
arr[i][0] = i;
}
When you need to user to enter specific input, better to loop continually until the user provides valid input (respecting their ability to cancel input by generating a manual EOF
with Ctrl + d, or Ctrl + z on windows). For example where str1
, s
, str2
and d
are needed:
while (strcmp (buf, "quit")) { /* loop until quit */
char str1[MAXC] = "", str2[MAXC] = "";
int s = 0, d = 0;
while (1) { /* loop continually */
fputs ("enter str1 s str2 d: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read / validate input */
puts ("(user canceled input)");
return 0;
}
buf[strcspn (buf, "\n")] = 0; /* trim '\n' from end of buf */
if (strcmp (buf, "quit") == 0) /* if "quit", break */
break;
/* parse and validate separate values, always protect array bounds */
if (sscanf (buf, "%511s %d %511s %d", str1, &s, str2, &d) != 4) {
fputs (" error: invalid format or integer input.\n", stderr);
continue;
} /* validate range of integers (negated conditions are confusing) */
if ((0 <= s && s < n) && (0 <= d && d < n))
break; /* exit loop on good input */
else /* otherwise, handle error */
fputs (" error: value for s or d out of range.\n", stderr);
}
(note: fgets()
reads and includes the '\n'
generated by the user pressing Enter, so before comparing for "quit"
you will need to remove the newline with strcspn()
)
Since str1
and str2
are parsed from buf
using sscanf()
there is no '\n'
to remove. However, when using sscanf()
you must use the field-width modifier to protect the array bounds from overrun -- otherwise the use of scanf()
or sscanf()
to fill the character array is no safer than gets()
, see: Why gets() is so dangerous it should never be used!
if (strcmp (str1, "move") == 0) { /* handle move */
if (strcmp (str2, "onto") == 0) { /* onto? */
int i; /* declare i in scope needed */
// empty s
for (i = 0; i < n && arr[s][i] != -1; i++) {
arr[arr[s][i]][0] = arr[s][i];
arr[s][i] = -1;
}
// empty d
for (i = 0; i < n && arr[d][i] != -1; i++){
arr[arr[d][i]][0] = arr[d][i];
arr[d][i] = -1;
}
// now move s to d
i = 1;
while (arr[d][i] != -1) {
i++;
}
arr[d][i] = arr[s][0];
arr[s][0] = -1;
}
else if (strcmp (str2, "over") == 0) {
(void)str2; /* no-op prevents empty scope */
}
else {
continue;
}
}
else if (strcmp (str2, "pile") == 0) {
(void)str2;
}
else {
continue;
}
(note: the final else
is not needed)
Complete Code
While I am still unclear on what your logic is supposed to do, handing the input can be done as shown above. Fixing the logic is left to you.
#include <stdio.h>
#include <string.h>
#define MAXC 512 /* if you need a constant, #define one (or more)
* ( don't skimp on buffer size!! )
*/
int main (void) {
char buf[MAXC]; /* buffer to store all user input */
int n; /* if possible, declare variables at beginning of scope */
fputs ("please input n for (n x n array): ", stdout);
if (!fgets (buf, MAXC, stdin)) { /* read all user input with fgets() */
puts ("(user canceled input)");
return 0;
}
if (sscanf (buf, "%d", &n) != 1) { /* validate every conversion */
fputs ("error: invalid integer input.\n", stderr);
return 1;
}
int arr[n][n]; /* VLA's are an 'optional' feature since C11 */
for (int i = 0; i < n; i++) { /* i, j can be decalred with loop scope */
for (int j = 0; j < n; j++) {
arr[i][j] = -1;
}
arr[i][0] = i;
}
while (strcmp (buf, "quit")) { /* loop until quit */
char str1[MAXC] = "", str2[MAXC] = "";
int s = 0, d = 0;
while (1) { /* loop continually */
fputs ("enter str1 s str2 d: ", stdout); /* prompt */
if (!fgets (buf, MAXC, stdin)) { /* read / validate input */
puts ("(user canceled input)");
return 0;
}
buf[strcspn (buf, "\n")] = 0; /* trim '\n' from end of buf */
if (strcmp (buf, "quit") == 0) /* if "quit", break */
break;
/* parse and validate separate values, always protect array bounds */
if (sscanf (buf, "%511s %d %511s %d", str1, &s, str2, &d) != 4) {
fputs (" error: invalid format or integer input.\n", stderr);
continue;
} /* validate range of integers (negated conditions are confusing) */
if ((0 <= s && s < n) && (0 <= d && d < n))
break; /* exit loop on good input */
else /* otherwise, handle error */
fputs (" error: value for s or d out of range.\n", stderr);
}
if (strcmp (str1, "move") == 0) { /* handle move */
if (strcmp (str2, "onto") == 0) { /* onto? */
int i; /* declare i in scope needed */
// empty s
for (i = 0; i < n && arr[s][i] != -1; i++) {
arr[arr[s][i]][0] = arr[s][i];
arr[s][i] = -1;
}
// empty d
for (i = 0; i < n && arr[d][i] != -1; i++){
arr[arr[d][i]][0] = arr[d][i];
arr[d][i] = -1;
}
// now move s to d
i = 1;
while (arr[d][i] != -1) {
i++;
}
arr[d][i] = arr[s][0];
arr[s][0] = -1;
}
else if (strcmp (str2, "over") == 0) {
(void)str2; /* no-op prevents empty scope */
}
else {
continue;
}
}
else if (strcmp (str2, "pile") == 0) {
(void)str2;
}
else {
continue;
}
}
// print results
for (int i = 0; i < n; i++) {
printf ("%d:\n", i);
for (int j = 0; j < n; j++) {
if (arr[i][j] != -1)
printf (" % 3d", arr[i][j]);
else
fputs (" [ ]", stdout);
}
putchar ('\n');
}
}
Example Use/Output
With intentional errors in input:
$ ./bin/vla_quit
please input n for (n x n array): 5
enter str1 s str2 d: move 2 onto 3
enter str1 s str2 d: move bananas onto gorillas
error: invalid format or integer input.
enter str1 s str2 d: move 1 onto 4
enter str1 s str2 d: move -1 onto 2
error: value for s or d out of range.
enter str1 s str2 d: move 0 onto 2
enter str1 s str2 d: quit
0:
[ ] [ ] [ ] [ ] [ ]
1:
[ ] [ ] [ ] [ ] [ ]
2:
[ ] [ ] [ ] [ ] [ ]
3:
[ ] [ ] [ ] [ ] [ ]
4:
[ ] [ ] [ ] [ ] [ ]
Always compile with warnings-enabled, and do not accept code until it compiles without warning. To enable warnings add -Wall -Wextra -pedantic
to your gcc/clang
compile string (also consider adding -Wshadow
to warn on shadowed variables). For VS (cl.exe
on windows), use /W3
. All other compilers will have similar options. Read and understand each warning -- then go fix it. They will identify any problems, and the exact line on which they occur. You can learn a lot by listening to what your compiler is telling you.
Let me know if you have further questions.