3
char array1[10];
scanf_s("%9s", array1, (unsigned)_countof(array1);

What's mean of (unsigned)_countof(array1)?

and what's different from use scanf_s("%9s", array1, sizeof(array1);?

both worked, but I want to understand about it more surely

Yoonsik
  • 33
  • 3

3 Answers3

2

sizeof and _countof are different. (edit:) In this case you should use countof.

sizeof is an operator in C that returns the memory size occupied by a variable, in bytes. In this case, array1 is an array of 10 chars, so it's size is ten times the size of a char, and sizeof(char) is 1 byte. So the value of sizeof(array1) is 10 * 1 = 10 bytes.

Note that in C you can use sizeof on any variable (e.g. sizeof(array1)) or on any type (e.g. sizeof(int)).

_countof is a macro (only available on Microsoft platforms) that gives you the number of elements declared in an array variable. In this case your array1 variable has 10 elements, so _countof(array1) will be 10.

Now in your precise case this happens to be the same result because the size of each char is 1, but this wouldn't be the same if, for example, your variable was an array of ints.

int array2[10];

sizeof(array2);  // = 40 
_countof(array2); // = 10

An int is (usually) 4 bytes, so an array of 10 ints will have a size of 10*4 = 40 bytes. On the other hand, _countof will just count how many elements are declared in the array, and still return 10.

In fact, we can see that the definition of _countof is litterally sizeof(array) divided by sizeof(array[0]), that is, the size of an individual element.

Since scanf_s with "%s" expects a number of items, you should be using _countof. (edit).

You might also want to be careful with sizeof (and _countof) on arrays, and avoid using them on pointers. Also be aware that _countof will not work outside of Visual Studio. See these related answers.

Louen
  • 3,617
  • 1
  • 29
  • 49
  • 1
    Thank you for the detailed explanation :) – Yoonsik Feb 09 '22 at 09:20
  • @chux-ReinstateMonica you're right. I've edited the answer. – Louen Feb 11 '22 at 01:23
  • @Louen: you should also expand on the types of `sizeof(array)` and `_countof(array)`, which are `size_t` as expected by `scanf_s` as defined in the C Standard as an optional feature. Yet the Microsoft version of the same function expects a value of type `UNSIGNED` for the size argument that follows the pointer. This explains the cast to `(unsigned)` in the posted code. This inconsistency between Microsoft's and the standard version of the `scanf_s` and the fact that it is optional function are good reasons to avoid using it. – chqrlie Feb 13 '22 at 16:09
2

What's the correct way to use scanf_s in using string arrays?

scanf_s() with "%9s" is not for string arrays. It is for reading text and saving as a single string, which is a character array with a null character.

Its proper use is complicated by MSVC expects a different type in the count argument than C11. In C11 and later, scanf_s() is an optional function of the standard library and lacks wide acceptance.

// MSVC - type unsigned expected, hence the cast.
scanf_s("%9s", array1, (unsigned)_countof(array1);

// C11 - type size_t expected
scanf_s("%9s", array1, sizeof array1 / sizeof array1[0]);

Proper use of either obliges code to check the return value of scanf_s()

if (1 == scanf_s("%9s", ...)) Success();
else Fail();

What's mean of (unsigned)_countof(array1)?

_countof() is an MSVC macro equivalent to (sizeof(array) / sizeof(array[0])). When passed an array, it returns the count of elements in the array as type size_t. Casting to unsigned gets the expected type as with MSVC scanf_s(). The value may truncate due to the cast - a rare issue with huge arrays.


what's different from use scanf_s("%9s", array1, sizeof(array1));?

The expected type of sizeof(array1) is size_t and not unsigned as expected in MSVC - leading to undefined behavior. When size_t is as unsigned, that is OK, else a real problem.

The conceptual value is wrong. It should be the array element count, not the byte size of the array. Either OK here as sizeof(char)==1, yet wrong if code moved to say using wide characters.


both worked

Code is not portable. It may have "worked" in OP's case, but lacks portability.


Consider alternatives to scanf_s(), scanf() like fgets().

chux - Reinstate Monica
  • 143,097
  • 13
  • 135
  • 256
1

This question is compiler based, GCC 11.1.0 on Arch Linux does not have functions like scanf_s and _countof. These functions/macros might be valid in Windows.

I read some docs on Microsoft docs. So, _countof is a macro which calls sizeof() internally and scanf_s is similar to scanf in standard C17.

What's mean of (unsigned)_countof(array1)? and what's different from use scanf_s("%9s", array1, sizeof(array1);? both work, but I want to understand about it more surely

Here the macro _countof is counting the length of the stack stored array, which in this case is 10.

_countof macro expands to:

#define _countof(x) (sizeof(x) / sizeof(x[0]))
// the operation goes for `array1` like this:
// 10 * 1 byte / 1 byte
// 1 byte is the sizeof char
// hence, 10 is returned as the result of the above operation

And (unsigned) is just type casting the value of the above operation.

NOTE: As _countof is calling sizeof() do not use this inside a function the get the length of an array. Because sizeof(arr_ptr) will return the size of pointer which is 8 bytes in my case (x86_64 architecture).

Conclusion:

There no _countof is just calling sizeof(). I would like to suggest you to use sizeof and scanf functions because they are cross-platform and portable throughout the C standards.

Darth-CodeX
  • 2,166
  • 1
  • 6
  • 23
  • 1
    Thank you for the detailed explanation ! – Yoonsik Feb 09 '22 at 09:20
  • Glad you liked it – Darth-CodeX Feb 09 '22 at 11:12
  • 1
    Note that if `_countof` is defined as you wrote, the cast `(unsigned)_countof(array1)` would only apply to the first operand `sizeof(array)` and the resulting type might still be larger than `unsigned`. The definition is probably parenthesized as `#define _countof(x) (sizeof(x) / sizeof((x)[0]))` – chqrlie Feb 13 '22 at 16:14
  • 1
    On the contrary, the type matters as the value is passed to a vararg function. `sizeof()` and `_countof()` both are 64-bit values whereas casting to `(unsigned)` converts the value to a 32-bit one. I commented on *Louen*'s answer about the inconsistency in `scanf_s` leading to the need for a cast in MSVC. – chqrlie Feb 13 '22 at 16:42
  • @chqrlie Now you can typecast it using `(unsigned)` and the whole result will be typecasted – Darth-CodeX Feb 13 '22 at 16:45
  • 1
    Yes, as expected by any programmer reading `(unsigned)_countof(array1)`. It is very important to always parenthesize macro expansions properly and defensively. – chqrlie Feb 13 '22 at 16:50
  • 1
    @chqrlie thanks for your time to improve my answer – Darth-CodeX Feb 13 '22 at 16:52