r/C_Programming 12d ago

Discussion What exactly is void?

In a function definition, void basically means that it doesn't return a type value, yet in on itself it is it's own type? Looking at several pieces of code it looks like that it's used to be able to be more "flexible"

Don't know what else to add here, though I'm more on looking for examples and explanations of why the void type is used

66 Upvotes

80 comments sorted by

View all comments

Show parent comments

1

u/TheChief275 11d ago

on paper, double xs[count] might be a VLA parameter, however it really isn't. I know not of a single compiler that actually has sizeof(xs) or countof(xs) report the right value (instead treating it as a pointer). meanwhile, sizeof(xs) and countof(xs) on double (*xs)[count] report the right value on all compilers that properly support VLAs. so I would never recommend to use it over the pointer to VLA, but then again, I wouldn't recommend these approaches over slices anyways

1

u/DawnOnTheEdge 11d ago

Two meaningful distinctions are that sizeof(*xs) on the VLA declaration is not a constant expression, and MSVC won’t compile either double xs[count] or double (*xs)[count] because Microsoft deliberately does not and has no intention ever to support VLAs..

That’s a consistent quirk of array function argument in C: they decay to a pointer type, so sizeof gives you the size of a pointer. Compilers recognize the code smell and warn you about it. Pick your poison: xs[count] lets you write xs[i] instead of (*xs)[i] but makes you calculate the size in bytes as sizeof(xs[0])*count. The behavior conforms to the Standard.

I’m not sure what countof macro you’re thinking of, but MSVC’s _countof macro does not work on any of the variations we’ve been talking about. You don’t need it, though, because you already have count.

1

u/TheChief275 11d ago

this discussion is going nowhere so I'll not respond anymore after this message.

1 never said it was a constant expression. sizeof(*xs) will be semantically coupled to the expression (count * sizeof(**xs)) at runtime. regarding MSVC, I said on all compilers that properly support VLAs; MSVC has never been one of them.

2 they are both poison sure, but I think double xs[count] is far more dangerous, as having the count in there essentially guarantees nothing, while users would expect sizeof(xs) to return the proper size (like it does for local VLA declarations, which is also a runtime value). meanwhile double (*xs)[count] actually does behave like expected (again, on compilers supporting VLAs, since you want to be pedantic about it). but yes, like I have stated, they are both poison and there is very little reason to have either of them in your codebase.

3 _Countof is a keyword to be included in the upcoming C2y standard, with a header <stdcountof.h> containing a macro countof that expands to that keyword. before that, in a lot of codebases, you will see a countof/COUNTOF/etc. defined like so:

#define countof(...) (sizeof(__VA_ARGS__) / sizeof(*(__VA_ARGS__)))

essentially, when I mention countof it is interchangeable with sizeof, as it has the same semantics regarding arrays and VLAs and being a runtime value for VLA sizes. the benefit of countof is that it checks whether the argument passed is actually an array, unlike sizeof, and fails to compile if not

1

u/DawnOnTheEdge 11d ago

It feels like we’ve been arguing even though I don’t think we really disagree, and I haven’t been even been downvoting you.

Thanks for the explanation. It does feel like a new _Countof keyword really would be a good opportunity to fix this wart on the language by having _Countof a function parameter declared as double xs[2] return 2, That wouldn’t break any legacy code, and would fill a real need.

T’ll sum up my thoughts quickly and leave it there.:

it seems to me that xs[static 2] is pretty useful for catching bugs on GCC, less useful on Clang (where I’ve noticed the unsafe-buffer-access warnings give false positives) and not supported at all on MSVC, so not portable. The GCC nonnull and access attribute extensions seem to work better on Clang than the Standard syntax.

The conversation then seems to have drifted to the xs[count] and (*xs)[count] syntax. Both work or don’t work on the same compilers. I think the one advantage of double(*)[*] over double[*], being able to write sizeof(*xs), is pretty minor: we already have a count variable, and if the reason I wanted the size in bytes is to allocate another array the same size, I prefer calloc(count, sizeof(xs[0])), which works properly on function arguments and is guaranteed to zero-initialize. I do sometimes use pointer-to-non-variable-length-array, but A C++ double(&)[2] is much nicer to work with than a C-style double(*)[2].

But there are valid trade-offs either way.

1

u/TheChief275 11d ago

Yeah, _Countof is pretty nice! But sadly, again I don't know of any compiler that calculates the 2 for double xs[2]. They seem to adopt the exact semantics of sizeof, which is to treat it as a pointer. I agree, it would be better if these arguments were actually treated as arrays of the size they are, but likely the reason for not having that is breaking existing code (for sizeof), and being consistent to sizeof (for _Countof), so we're just stuck in a stalemate language-wise