0

I have trouble trying to return the string that is scanned in a function and return the string to the first pointer string argument.

The string is a new thing I just learn this week. I tested passing back arguments as integer and character, and both of them work. The code is as follows. This is just part of the code because the whole project is in 3 files. As the code shows, the string is meant to send as an address (the string does not need a & sign). The function scans the input string, and the string gets sent back as an argument.

To be honest, this is my assignment, using return to send back the string is not an option.

`

char stringValue[7] = { '\0' };
input(stringValue, 4, 6);
printf("The string is %s", stringValue);

void input(char* string, int minimum, int maximum)
{
    char inputs[7];

    scanf("%[^\n]", inputs);

    *string = charInRange;
}

`

When I run it, the printing of the value is random. I tested with other tool, and it said:

"assignment makes from pointer without a cast."

I am sure that the problem is probably the line assigned input to the pointer. I think it's some format error. I tried adding some &, *, or removing them. I spent the whole testing and searching for an answer but no result.

Sunny
  • 3
  • 2
  • 1
    Why not `scanf("%[^\n]", string);`?? Properly `scanf("%6[^\n]", string);` (you must use the *field-width* modifier to protect your array bounds -- that's why `fgets()` is a superior input function) You don't need `inputs[]`. Also `char stringValue[7] = "";` is sufficient to initialize all zero. You cannot assign the contents of a string in C. `*string = charInRange;` won't work. You were thinking `strcpy (string, input);`, but the use of `input` would be superfluous. – David C. Rankin Nov 18 '22 at 02:05
  • Of course you need the `scanf()` a continual loop and reread if `strlen (string) < mininum)` -- *field-width* sets the maximum. Otherwise, your two parameters `minimum` and `maximum` are unused. They should be type `size_t` as `strlen()` returns `size_t`. – David C. Rankin Nov 18 '22 at 02:12

1 Answers1

4

Continuing from my comments, your primary problem in input() is you read into the array char inputs[7]; with scanf("%[^\n]", inputs); and then attempt to assign charInRange (nowhere declared in your code) as the first character in string. This won't compile.

When you attempt to print stringvalue back in main, since charInRange isn't defined (or isn't initialized or is initialized outsize the range for ASCII characters), you get gibberish.

As explained in the comments, the input[] array isn't necessary. You have already declared storage for stringvalue in main() and are passing the address for that storage (the pointer char *string) to the input() function. You simply need to read into string in the input() function. The input[] array is unnecessary.

However, before looking at how you can do that, when you are providing a buffer (a character array) for user-input -- Don't Skimp on Buffer Size. When you use scanf() with "%[ ]" the user can enter any number of characters beyond you array size, writing beyond the end of your array. This is a favorite exploit by buffer overrun.

If using the "%s" or "%[ ]" format specifiers with scanf() you must provide a field-width modifier to limit the number of characters that can be read. Failure to do so renders scanf() no safer than gets(). See Why gets() is so dangerous it should never be used!

This is just one of the scanf() pitfalls, and one of the reasons why new C programmers are encouraged to take all user-input with fgets().

With that said, let's look at how we can write your input() function to only accept a user-input of between min and max chars. As mentioned, whenever you have conditions your input must meet, the approach is the loop continually reading input from the user (your read-loop). Check the length of what the user entered and only if the length is from min to max, break your read-loop and return.

To ensure your read-buffer is sufficiently sized, make it larger than what you anticipate the max string you will accept is. (a lot longer, you have a 4M stack on Linux and a 1M stack on Windows) a 1K buffer is fine. On embedded systems where memory is limited, then adjust the buffer size down. When you need to set constants in your code #define them, don't just throw Magic Numbers like 4 and 6 in as parameters. You can do that with:

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

#define INPUTSZ 1024      /* if you need a constant, #define one (or more) */
#define MAXC 6
#define MINC 4

Now you can write your input() function similar to:

void input (char *string, size_t min, size_t max)
{
  /* loop continually until valid input given */
  for (;;) {
    size_t len;   /* length to test against min */
    
    fputs ("\nenter string: ", stdout); /* always prompt for input */
    
    /* take all user-input with fgets (into a larger buffer - don't skimp)
     * validate the return of every input function used.
     */
    if (!fgets (string, INPUTSZ, stdin)) {
      puts ("(user canceled input)");   /* return is NULL, Ctrl+d (z) used */
      *string = 0;                      /* set string as empty-string */
      return;
    }
    
    len = strcspn (string, "\n");       /* get length of input minus '\n' */
    string[len] = 0;                    /* overwrite '\n' with '\0' */
    
    if (min <= len && len <= max) {     /* condition met, break read loop */
      break;
    }
    
    /* handle invalid input */
    fprintf (stderr, "input must be %zu-%zu characters\n", min, max);
  }
}

(note: you can save the length while trimming the '\n' from string in a single step, e.g. string[(len = strcspn (string, "\n"))] = 0;, but that was split for readability above. Also note 0 and '\0' are equivalent. 0 being the ASCII value for the nul-character, see ASCII Table)

And finally your main(), e.g.

int main (void) {
  
  char stringvalue[INPUTSZ] = "";
  
  input (stringvalue, MINC, MAXC);
  
  if (*stringvalue) {
    puts (stringvalue);
  }
}

(note: the stringvalue is only printed if the user didn't cancel by generating a manual EOF with Ctrl + d (Ctrl + z on windows))

Can you look and determine what line in the input() function allows you to make the test if (*stringvalue)? Note *stringvalue is the same as *(stringvalue + 0) which is the pointer notation for stringvalue[0].

Example Use/Output

Does the code limit input to the proper number of characters?

$ ./bin/inputmaxmin

enter string: 12
input must be 4-6 characters

enter string: 123
input must be 4-6 characters

enter string: 1234567
input must be 4-6 characters

enter string: 1234
1234

What about handling the user canceling input with Ctrl + d?

$ ./bin/inputmaxmin

enter string: one hundred twenty-three
input must be 4-6 characters

enter string: (user canceled input)

Also, you seem a little iffy on pointers. Here are a couple of links that provide basic discussions of pointers that may help. Difference between char pp and (char) p? and Pointer to pointer of structs indexing out of bounds(?)... (ignore the titles, the answers discuss pointer basics)

Look things over and let me know if you have questions.

David C. Rankin
  • 81,885
  • 6
  • 58
  • 85
  • Concerning buffer size, C has "An implementation shall support text files with lines containing at least 254 characters, including the terminating new-line character. The value of the macro `BUFSIZ` shall be at least 256.". IMO, `BUFSIZ` or maybe 2x `BUFSIZ` is reasonable. Beyond `BUFSIZ`, code trends on thinning ice. – chux - Reinstate Monica Nov 18 '22 at 09:08
  • Hmm we can fix that with my trusty `1024`. Thanks for the thinning ice report. – David C. Rankin Nov 18 '22 at 09:12
  • Sure. - me I tend to use max([4K](https://en.wikipedia.org/wiki/Ice_Road_Truckers),BUFSIZ) on non-embedded apps. – chux - Reinstate Monica Nov 18 '22 at 09:15
  • Corner: When a full buffer reads without a final `'\n'`, yet the next available character is a `'\n'`, I think that should be acceptable input. Yet that extra code not needed in this learners case. – chux - Reinstate Monica Nov 18 '22 at 09:16
  • 1
    Agreed. So if, in the changed example, we read `1023` characters and the next, unread just happens to the `'\n'` char, there is no adverse effect. Perhaps a little more learning required, but compared to the `scanf()` which was replaced, everything, all caveats in `fgets()` use can be explained in a fraction of the time a detailed discussion of `scanf()` -- Pitfalls would (and still have time left over for dinner, movies and drinks) – David C. Rankin Nov 18 '22 at 09:58
  • Ah, I got it. BTW, the reason I did not use the width modifier is that there is a clear buffer function that I suppose to use. Of course, I had only dealt with numbers and characters in scanf(). Buffer is a new concept. Anyway, thank you for your help! – Sunny Nov 18 '22 at 16:43
  • @Sunny -- don't overthink `"buffer"` that generally just means character array where you are going to save something. Basically your input exists in a file (`stdin`, a file on disk -- doesn't matter). You read from where the input is storing it in a buffer. The buffer can be a character array for text, or an allocated block of memory, etc.. --- it's just storage -- that you can conveniently access within you program from that point forward whenever the information in the buffer (array) is needed. – David C. Rankin Nov 18 '22 at 18:49
  • @DavidC.Rankin -- I got a question. Since I am only allowed to use scanf to get the input(other library functions are not allowed). After scanning the input, I validate the input within the range(min, max), however, the error message popped up. "Stack around the variable 'stringValue' was corrupted. Only entered value above the maximum. Maybe it is too much for stringValue[7], but did I not rescan the input to make it correct? How do I solve this situation? – Sunny Nov 19 '22 at 20:51
  • @Sunny Rule 1 - Never *Skimp* on buffer size. `stringValue[7]` can only hold a string of six characters + the nul-terminating character `'\0'`. Unless you use `scanf (" %6[^\n]", stringValue)` with `6` as the *field-width* modifier, then `scanf()` can write beyond the end of your array corrupting the stack around `stringValue`. Recall `"%c"`, `"%[ ]"` and `"%n"` do not discard leading whitespace, so if the user presses spacebar incidentally before typing, you are reading extra characters. Without the field-width modifier, `scanf()` into an array is no safer than `gets()` (removed from C11) – David C. Rankin Nov 19 '22 at 23:49
  • The `' '` before the format specifier in `" %6[^\n]"` will ensure leading whitespace is discarded. `scanf()` is so full of pitfalls, just like this example, you are *strongly* encouraged to use `fgets()` instead of `scanf()`. If you must use `scanf()`, spend a couple of hours reading EVERY word of [man 3 scanf](https://man7.org/linux/man-pages/man3/scanf.3.html). Make sure you understand every aspect of the *format string*, *directive* and the *conversion specification*. Learning `scanf()` now will save you days of headache, frustration and debugging later. – David C. Rankin Nov 19 '22 at 23:56
  • 1
    As @chux-ReinstateMonica stated, unless you have reason to reduce the size of the buffer (array), use the standard `BUFSZ` to size `char stringValue[BUFSZ];`. (on Linux, that provides a standard buffer size of `4096` and if I recall correctly `512` bytes on Windows -- may be more now) – David C. Rankin Nov 19 '22 at 23:59