Note that C23 changes this behaviour to match C++, that is, an empty set of parentheses means exactly the same thing as (void). To be exact, C23 removed old-style/K&R function definitions/declarations of which the empty parentheses are a special case. Of course, the advice given in the video is not outdated since C23 won't be adopted for a very long time (at the time this video came out, it is several months away from being published), and (void) is more explicit anyway. EDIT: An empty set of parentheses actually meant two different things depending on whether it appeared in a function definition or a function declaration (that isn't a definition) before C23. You can find the relevant part of the standard in a reply. The idea is that the video is technically mistaken in saying that a function defined with empty parens takes zero or more arguments. It takes exactly zero but calling it with more causes UB. Compare that with using void which causes a compile-time error instead. On the other hand, using empty parens in a declaration actually means the function could take zero or more arguments.
@@nngnnadasYou're right. The worst part is that a)this was voted into C23 a while ago and b)one doesn't even need to read the standard to learn this, they could just go to cppreference. Still, in practice it will take a while before this video truly becomes dated, but it's too bad the creator didn't future-proof it.
I think the other, and perhaps more general take-away is don't ignore warnings from a compiler or assembler. If you don't investigate there is a good chance it is something that will bite you down the line.
This video actually helped me even though I wasn't getting any compiler warnings (and I have all warnings set to on). In my code I was setting a function pointer of type (void) to a function with () arguments. Hadn't caused any bugs for me yet but happy to have picked up the problem before it became a problem!
It's worth noting that in C++ the two function signatures are equivalent and mean "function with no parameters". Just one of those small differences between C and C++.
@@gregoryfenn1462 C++ doesn't allow old-style declarations even when using extern "C" -(which- -only- -affects- -linkage- ). So, no, an empty set of parentheses means exactly zero arguments in C++ no matter what.
@@Tachi107 Yeah, I think you're right. In any case, the meaning of () stays the same. I just tested it on compiler explorer to make sure. EDIT:cppreference says:"Language linkage of function types (which represents calling convention)..." on the language linkage page, so I wasn't entirely wrong.
By the way, in "The C programming language" (best programming textbook) they explain why this is the way it is. Function arguments were declared inside the body before the standard used in the book was adopted, so that's another relic of backwards compatibility
@@riteshbhartiya6155 No, it will give a compilation error. The "ancient" way to do it was to leave parentheses empty and declare the arguments in the function body
Fifty years ago I had to make a career choice: Study law and become a lawyer, OR study computer science and become a programmer. Luckily, I discovered C and I've been a programmer and a lawyer ever since. Another insightful one, Jacob.
@@INeedAttentionEXE Ya, I had fantasy about that too. I really would have liked to have been in on the Apple vs Microsoft suit re stealing the publicly funded development of the GUI/mouse paradigm.
The problem with your recommendation to use (void) is that it's just visual clutter; it does not help reading the code. A better recommendation is to use option '-std=gnu2x' (with both gcc and clang); then you can simplify all the (void) clutter to () AND get decent diagnostics at the same time.
In languages as C ALWAYS be as explicit as possible. Basically every bug can be tracked to not being explicit. -pedantic is your best friend. Leaving a (void) can lead to memory-bugs that are really, really serious - just like doing a (void).
This video actually helped me even though I wasn't getting any compiler warnings (and I have all warnings set to on). In my code I was setting a function pointer of type (void) to a function with () arguments. Hadn't caused any bugs for me yet but happy to have picked up the problem before it became a problem!
How would you access those arguments then, in case you define your function as "accepting zero or more", when you don't have the ’void’, without also providing parameter definitions?
originally C had no function prototypes, only forward declarations, so e.g. main would be declared as `int main();` and defined as `int main() int argc; char **argv; { /* code */ }` (yeah, arguments were specified like this), so for the call site compiler just placed arguments that were passed to the function on the stack (or in registers), it did not care about actual function argument types.
I would guess you can take the address of the function and then add an offset to get a pointer to the stack parameter depending on the calling convention used.
@@felps3213 Your code wouldn't compile, you still need to name the arguments between the parentheses. The correct "old-style" definition of main would be: int main(argc,argv) int argc; char** argv; { /* code */ } Otherwise, the compiler doesn't know the order of the arguments, because this is also valid: int main(argc,argv) char** argv; int argc; { /* code */ }
All companies I have been have a rule that a function can’t have an empty arguments parameter list. It shall always have a void or the actual signature of the function
Forty years ago, I would have given a lot to have the answers to all the questions you address in your C videos. I had to guess, try, fail, try again and again. Documentation and compilers were not as good as today, and english is not my first language. Young generations are spoilt with such good content of yours, Jacob !!! Peace & Prosper 👍❤🖖
So does that mean all C functions can take varargs by default unless they have explicitly stated arguments, it's just that you can't access the arguments because you have no variable name to identify them by? Could you still access them with inline assembly assuming the compiler will still pass the arguments in to the function in the registers as usual?
If you try it you will know it .. but i will ruin the fun for you: yes, the arguments are pushed to the stack / moved to registers (depending on your architecture), so you can access them inside your function() if you really want.
When using GCC, why not use -Werror=deprecated-non-prototype to force an error? I guess using void is only good when you write your library and to avoid warning generation upon including your header files by end users, in case they have -Wstrict-prototypes, you should forcibly use VOID
If there's no arguments, but passing them still compiles, does that mean you could use vargs with it and just have them optional while (...) requires at least one?
The video doesn't mention that a function defined with empty parentheses still must be called with 0 arguments. Calling it with the wrong number is undefined behavior. Back in the day, you could use varargs.h instead of stdarg.h which allowed you to define a variadic function that can take 0 arguments. Now, varargs is not supported by any compiler as far as I know, but in C23 you'll be able to just write (...) without any parameters before the ellipsis.
One of the big reasons I shy away from C is that it often lacks idiomatic ways of doing things. I prefer it when languages only let you do things one way. Thanks for the video.
Perl must be your Kryptonite :) If you program C professionally you often have a style guide that establishes what "idiomatic C" is for your company. If you're a hobbyist or work for a company that doesn't have a style guide (like I do), you can just pick one and run with it. I personally like the Linux kernel style guide, but really it doesn't matter as long as you're consistent.
If you don't put void as the input to the function, and you just put 12, yet it still compiles and runs even though it's just a warning, I wonder what happens to that 12? does it just get discarded? is it actually put somewhere on stack frame? Does it depend on the sys architecture? Does it depend on the compiler? Maybe both?
It's not put on the stack frame. I just tested it. But if you put an expression like `++x` as an argument of the void function, it will be evaluated, so you get the "side effect", but the result is not put on the stack frame. It's like it was not there. Compiler ignores passing of the argument.
@@Adam_Lyskawa does this depend on optimisation level? My intuition would tell me that the argument would be placed into AX in the same way that evaluating an expression and then ending the function without a return statement can pass a value if at O0 (`int double(int x){x*2;}` should function as its name implies under O0)
@@Starwort I just used online compiler. They don't show optimization level. Anyway, if we're not immediately sure what is happening - I'd bet a possibly undefined behavior. And a code smell for sure. The reason such things happen in code at all is you change a function to not accept an argument, then you forget to change some invocations hoping the compiler will throw you at least a warning. The online one - didn't. So - when refactoring like this - it's best to specify explicit `void` in signature, then just follow the error locations.
In many (most) other C-looking languages an empy parameter list implies `void` and there is no explicit `void` which doesn't make it any easier to put it in. :)
Where are your clips that give us guidance on how to achieve software pipelining by using the restrict keyword? I'm thinking of making the compiler prepare for vector instructions.
it would not cause any problems actually. in fact variadic arguments (`...`) and regular arguments are implemented in the same way in C (at least in gcc and clang) that is, compiler just puts the args in registers or on the stack, so just as passing extra arguments to vararg function in C does not causes problems (passing less arguments than expected does), passing extra arguments to a function does not too.
Begs the question: is there a use case for ommitting the explicit `void`? Maybe for calling a function via pointer, where args are optional depending on what function is called at runtime? Suppose the compiler couldn't warn against that anyway, though
Funny that the first time I saw code use that was about 2 weeks ago and here you are making a video about it. It seems like you always address the stuff that I'm wondering about... mhm...
It technically depends on the system you're working on. However, a common practice is to push the arguments onto the stack (i.e. memory) and then the arguments get pulled from the stack inside the function.
@@teaguehall17 that's totally platform dependent and really annoying. I programmed programs for both linux and windows x86 assembly and having to change all functions because of stupid difference in calling conventions is a nightmare. I get why someone thought "f that let the Maschine do this (c)" 😂
always a pleasure to watch one of your videos. can you give an example of when this behavior of passing zero or more arguments is valid? I remember 'main' being special in that matter, but are there more cases?
Besides omitting the 'void', I never put the 'int' before a function. I just do a function as: function() or function(int var1, unsigned char var2). Has never been an issue in the many C compilers I have used in over 30 years. What am I at risk for here?
Just for clarity, does this mean that all parameters passed, whether expected or not, still get pushed to the stack in c? And as such, could still be "interpreted" through some potentially hairy stack pointer magic/algorithm?
My question is why doesn't no arguments imply a void argument since that's what everyone expects anyway? Can you even use the number argument you gave it for anything?
ok, now you need a video on how to do a function like printf. something that uses variable number of arguments. why? because sooner or later going to run into a situation where you don't have a choice of what tools you have or can pay for or administratively approved of. cason point: had a rad tool (rapid application development) found the opening and closing the files would sometimes not close the file properly. this led to problems with trying to delete a file that it thought was already opened thus leading to a failure (Windows platform) The bad party is it would randomly not close a file for some reason unknown to us and we couldn't figure out what the issue was. fortunately said rad tool, could make calls to the windows API functions. so I found C type fopen() and freed() were in those API functions.I included our own reads and rights using that in the RAD tool. fortunately they did open and close the files properly.
"foo() means 0 or more arguments." Can the function in any way make use of those 'optional' parameters? Or does the compiler simply ignore those parameters? And if not, what happens to the call-stack in this scenario?
no it can't. it doesn't really mean "0 or more arguments" it's just that C originally did not have function prototypes (you couldn't specify parameters in parentheses when declaring a function), and even in function definition the syntax for parameters was different: `int main() int argc; char * * argv; { return 0; }` you couldn't specify parameters in parentheses.
I was unaware of the difference, but let's be real: any decent compiler will give you that warning, which you should read. And even if it didn't, it should change absolutely nothing. If anything, the extra arguments you pass in will be copied to registers that the function will completely ignore. If you get lucky, the compiler will see that and just remove them for you altogether, I have no reason to believe something could possibly go wrong here
How would you use the "zero or more arguments" in an empty set of parentheses? Is there a way to capture an arbitrary set of arguments not defined in the parentheses? If there's no way to capture the arguments, then why is it a thing?
Pro tips: Since C23, the void is no longer needed. Next: Use compiler warnings and don't ignore them. Next: Compile your C code by a C++ compiler, it will find bugs in your code (and write only C code that is compatible with both C and C++).
c doesnt support overloading, so i assume that exactly one quantity of arguments and one specific type per argument is the only valid input. should i spend an infinite number of days per function explicitly declaring that my function(int) should not accept an infinite quantity of an infinite number of types?
I think if the declaration of the function had void in the params, then it will enforce the 0 args prototype? But yeah in C I always use void params explicitly for both the declaration and definition of a function thay takes no params.
but why initially C was designed to have this option of 0 or more arguments? As far as I know C has an implementation of variadic function using ...notation.
K&R originally used it as a convention for headers to not actually declare the arguments to functions, so it fit for not just 0 arguments but as many as the function took. I think the point was to be lazy with evaluating errors which they eventually realized was a bad idea and C89 gave us function declarations with types and named parameters.
no void arguments means it has variadic arguments. so you'd use va_list to read the arguments passed to the function in C23 I think they've removed this so both func() and func(void) is correct and the new variadic is func(...)
@@FlanPoirot That's incorrect, passing arguments to f() is undefined behavior. The correct notation for variadic functions prior to C23 is f(type t, ...). Note that the ellipsis [...] must come after both a named parameter and a comma.
It won’t compile in C or C++. The compiler will try to find a signature of foo that matches three integer parameters. If it doesn’t, it will not compile. This is different in JavaScript where adding or omitting arguments is perfectly legal (with the latter setting the argument to *undefined*).
That was helpful, thanks for the video. On a related note, should functions always return a value? Or is it ok, or perhaps even preferred, to have: void fn(void) { // some code }?
My question is , is following program thread safe? #include #include using namespace std; int a = 0; bool alive = 1, start = 0; void detector() { int local_a = 0; while (alive) { if (start) { if (a != local_a) { cout
2:20 You're misleading us: No version of gcc produces this warning. Your "gcc" must be clang (version 7 to 14). In order for GCC to diagnose this situation, you need "gcc -std=gnu2x", with GCC version 10 or newer; it then produces an error, not a warning.
main is special. If you don’t return anything, the C spec says it’s as if you returned 0. No undefined or implementation-defined behavior here. But do write it out explicitly in non-trivial programs to help others understand your code!
main is the only c function that returns 0 upon successful execution of last line of it's code.. returns some other int value for unsuccessful exit from main.
@@ScienceD9000 I know, but you seemed to miss, that he used MacOS. And I know that he used clang, because GCC doesn't give an error at all(even with "-Wall -Wextra").
This is one of the design choices that just doesn't make sense to me. Why would a function with no parameters be able to accept parameters if it wouldn't be able to do anything with them?
Personally, I hate K&R style and I avoid it even when I write C++. In other words, I explicitly use void in the function declarations and definitions of any function that takes no arguments, with the only exceptions being constructors because the stupid compiler gives me guff there. If the committee wants to formalize that C++ idiom for C23 then they can get wrecked.
Anyone with sense already uses -Werror, so they're the same after all. And anyone with weird warnings he has to ignore, preventing him from doing this, I'm sorry.
Asymmetry fascist! Seriously though, been programming as nature (and Dennis M Ritchie) intended my whole life. I think the move to asymmetrical braces was a mistake.
f() means you can pass ANY unspecified list of arguments to a functions and it will work, at least for some time, until you stop function args polymorphism. Do not shoot yourself in your foot
@@JacobSorber As I've said on another video, where you implied that the return type of a function is part of the function's signature...: _Especially_ _for_ _beginners_ and in a complex field such as CS, it's crucial to be painfully pedantic about language. It's like not differentiating between socialists and "nashonnall" socialists. Also, language in a UA-cam video does not reflect spoken language. You won't hear any UA-camrer say "uhm" like ever or hear them misspeak - heck, you won't even hear them breathing in (which is crazy). You have the opportunity to fix stuff in post. Do it.
The difference between a function declared with an empty parameter list and a function declared with a parameter list consisting of the keyword void will cease to exist in C23. The behaviour of the former will change to match that of the latter, just like in C++.
The fact that stuff like that is even allowed to compile is ridiculous. That's why at the very least I use -Wall -Wpedantic -Werror, or just use a better programming language like Zig.
This is EXACTLY why I use C. It does what I want, when I want, and how i want. God I HATE training wheels... but for those who need them, well that's why we have Python.
Why does it matter? There seems to be a glaring omission in this video, which is a description of the risks of letting that bug pass unfixed. How can the zero or more "invisible" arguments ever be addressed without a name?
Note that C23 changes this behaviour to match C++, that is, an empty set of parentheses means exactly the same thing as (void). To be exact, C23 removed old-style/K&R function definitions/declarations of which the empty parentheses are a special case. Of course, the advice given in the video is not outdated since C23 won't be adopted for a very long time (at the time this video came out, it is several months away from being published), and (void) is more explicit anyway.
EDIT: An empty set of parentheses actually meant two different things depending on whether it appeared in a function definition or a function declaration (that isn't a definition) before C23. You can find the relevant part of the standard in a reply. The idea is that the video is technically mistaken in saying that a function defined with empty parens takes zero or more arguments. It takes exactly zero but calling it with more causes UB. Compare that with using void which causes a compile-time error instead. On the other hand, using empty parens in a declaration actually means the function could take zero or more arguments.
Makes sense though because there's no way to use the arguments in the first place if the parenthesis are empty. That shouldn't even be a thing tbh
@@w花bthat's what I was thinking: why allow arguments you cannot have access to?
@@wendolinmendoza517 it's interesting question, are you sure it's not possible, because if it isn't so than richie was a crazy 😜!
Still kind of immedietly dating the video by not at least mentioning it.
@@nngnnadasYou're right. The worst part is that a)this was voted into C23 a while ago and b)one doesn't even need to read the standard to learn this, they could just go to cppreference. Still, in practice it will take a while before this video truly becomes dated, but it's too bad the creator didn't future-proof it.
Perfect, no argument from me!
i have nothing in return
Any arguments are null and void
@@obinator9065 I wanted to make a pointer joke but some might not get the reference
I think the other, and perhaps more general take-away is don't ignore warnings from a compiler or assembler. If you don't investigate there is a good chance it is something that will bite you down the line.
This video actually helped me even though I wasn't getting any compiler warnings (and I have all warnings set to on). In my code I was setting a function pointer of type (void) to a function with () arguments.
Hadn't caused any bugs for me yet but happy to have picked up the problem before it became a problem!
Always compile with Wall Wextra Werror
Any necessary exceptions can be handled at the smallest scope reasonable.
our teacher has forced us to always compile with errors on warnings. It has indeed helped me many times.
@@khatdubell don't forget -pedantic
@@MaximumBloop I’m actually not a fan of pedantic. Though I suppose it would depend on the use case.
It's worth noting that in C++ the two function signatures are equivalent and mean "function with no parameters". Just one of those small differences between C and C++.
That’s so much more intuitive. Makes sense why the video premise shocked me coming from C++-only background.
Unless you do the weird "extern C" thing in C++ to make it more C like for part of your project
@@gregoryfenn1462 C++ doesn't allow old-style declarations even when using extern "C" -(which- -only- -affects- -linkage- ). So, no, an empty set of parentheses means exactly zero arguments in C++ no matter what.
@@NickpsI believe that extern "C" also affects stuff like calling conventions, no?
@@Tachi107 Yeah, I think you're right. In any case, the meaning of () stays the same. I just tested it on compiler explorer to make sure.
EDIT:cppreference says:"Language linkage of function types (which represents calling convention)..." on the language linkage page, so I wasn't entirely wrong.
By the way, in "The C programming language" (best programming textbook) they explain why this is the way it is. Function arguments were declared inside the body before the standard used in the book was adopted, so that's another relic of backwards compatibility
but that means you can also pass 3 arguments to a function with 2 parameter for backward compatibility, right?
@@riteshbhartiya6155 No, it will give a compilation error. The "ancient" way to do it was to leave parentheses empty and declare the arguments in the function body
@@postmodernist1848 Not really in the function body, but after the function header and before the opening brace that denotes the function body.
@@anon_y_mousse yeah, right, it's the function header before the body
Fifty years ago I had to make a career choice: Study law and become a lawyer, OR study computer science and become a programmer.
Luckily, I discovered C and I've been a programmer and a lawyer ever since.
Another insightful one, Jacob.
Would have liked to see you litigate in Oracle vs Google, assuming you're a copyright lawyer
@@INeedAttentionEXE Ya, I had fantasy about that too. I really would have liked to have been in on the Apple vs Microsoft suit re stealing the publicly funded development of the GUI/mouse paradigm.
haha that’s a GOOD one ❤ love it 😂😂
I am a pure Programmer ^ Lawyer.
C was my first love. Wish I still got to use it
The problem with your recommendation to use (void) is that it's just visual clutter; it does not help reading the code. A better recommendation is to use option '-std=gnu2x' (with both gcc and clang); then you can simplify all the (void) clutter to () AND get decent diagnostics at the same time.
I completely agree. That's why I always compile with -Werror, even when C99 was still modern ;)
In languages as C ALWAYS be as explicit as possible. Basically every bug can be tracked to not being explicit. -pedantic is your best friend. Leaving a (void) can lead to memory-bugs that are really, really serious - just like doing a (void).
Thank you for making these videos. I was one of the people who tought it did not matter whether I used 'void' or not.
This video actually helped me even though I wasn't getting any compiler warnings (and I have all warnings set to on). In my code I was setting a function pointer of type (void) to a function with () arguments.
Hadn't caused any bugs for me yet but happy to have picked up the problem before it became a problem!
Jacob, I'm pretty sure you NEVER thought this little topic would inspire so much excitement.
How would you access those arguments then, in case you define your function as "accepting zero or more", when you don't have the ’void’, without also providing parameter definitions?
I'm guessing you would use inline assembly to grab stuff from the stack.
it's probably for when you define the function in a header file and write the code for it in another file
originally C had no function prototypes, only forward declarations, so e.g. main would be declared as `int main();` and defined as `int main() int argc; char **argv; { /* code */ }` (yeah, arguments were specified like this), so for the call site compiler just placed arguments that were passed to the function on the stack (or in registers), it did not care about actual function argument types.
I would guess you can take the address of the function and then add an offset to get a pointer to the stack parameter depending on the calling convention used.
@@felps3213 Your code wouldn't compile, you still need to name the arguments between the parentheses. The correct "old-style" definition of main would be:
int main(argc,argv) int argc; char** argv; { /* code */ }
Otherwise, the compiler doesn't know the order of the arguments, because this is also valid:
int main(argc,argv) char** argv; int argc; { /* code */ }
You could also have showed if generated assembly/binary is different.
Yeah, I was wondering if the extra argument changes anything when it's put in there and it compiles, or if the compiler just ignores it.
All companies I have been have a rule that a function can’t have an empty arguments parameter list. It shall always have a void or the actual signature of the function
Forty years ago, I would have given a lot to have the answers to all the questions you address in your C videos. I had to guess, try, fail, try again and again. Documentation and compilers were not as good as today, and english is not my first language. Young generations are spoilt with such good content of yours, Jacob !!! Peace & Prosper 👍❤🖖
So does that mean all C functions can take varargs by default unless they have explicitly stated arguments, it's just that you can't access the arguments because you have no variable name to identify them by? Could you still access them with inline assembly assuming the compiler will still pass the arguments in to the function in the registers as usual?
If you try it you will know it .. but i will ruin the fun for you: yes, the arguments are pushed to the stack / moved to registers (depending on your architecture), so you can access them inside your function() if you really want.
When using GCC, why not use -Werror=deprecated-non-prototype to force an error? I guess using void is only good when you write your library and to avoid warning generation upon including your header files by end users, in case they have -Wstrict-prototypes, you should forcibly use VOID
I would love to see a video on how you have vscode set up.
If there's no arguments, but passing them still compiles, does that mean you could use vargs with it and just have them optional while (...) requires at least one?
vargs usage requires at least one argument
The video doesn't mention that a function defined with empty parentheses still must be called with 0 arguments. Calling it with the wrong number is undefined behavior. Back in the day, you could use varargs.h instead of stdarg.h which allowed you to define a variadic function that can take 0 arguments. Now, varargs is not supported by any compiler as far as I know, but in C23 you'll be able to just write (...) without any parameters before the ellipsis.
Beating video, thanks, one interesting thing that is missing is to how can we leverage "no or more arguments" in their function?
One of the big reasons I shy away from C is that it often lacks idiomatic ways of doing things. I prefer it when languages only let you do things one way. Thanks for the video.
Perl must be your Kryptonite :)
If you program C professionally you often have a style guide that establishes what "idiomatic C" is for your company. If you're a hobbyist or work for a company that doesn't have a style guide (like I do), you can just pick one and run with it.
I personally like the Linux kernel style guide, but really it doesn't matter as long as you're consistent.
great tip! great to know this nuance... thanks!
If you don't put void as the input to the function, and you just put 12, yet it still compiles and runs even though it's just a warning, I wonder what happens to that 12? does it just get discarded? is it actually put somewhere on stack frame? Does it depend on the sys architecture? Does it depend on the compiler? Maybe both?
It's not put on the stack frame. I just tested it. But if you put an expression like `++x` as an argument of the void function, it will be evaluated, so you get the "side effect", but the result is not put on the stack frame. It's like it was not there. Compiler ignores passing of the argument.
@@Adam_Lyskawa very interesting. Thanks!
Doggo
@@Adam_Lyskawa does this depend on optimisation level? My intuition would tell me that the argument would be placed into AX in the same way that evaluating an expression and then ending the function without a return statement can pass a value if at O0 (`int double(int x){x*2;}` should function as its name implies under O0)
@@Starwort I just used online compiler. They don't show optimization level. Anyway, if we're not immediately sure what is happening - I'd bet a possibly undefined behavior. And a code smell for sure. The reason such things happen in code at all is you change a function to not accept an argument, then you forget to change some invocations hoping the compiler will throw you at least a warning. The online one - didn't. So - when refactoring like this - it's best to specify explicit `void` in signature, then just follow the error locations.
In many (most) other C-looking languages an empy parameter list implies `void` and there is no explicit `void` which doesn't make it any easier to put it in. :)
Where are your clips that give us guidance on how to achieve software pipelining by using the restrict keyword? I'm thinking of making the compiler prepare for vector instructions.
In what situations would passing an unused argument to a function cause problems?
You would overwrite some of the cpu registers with those params, which could corrupt the functionality in code following the function call
it would not cause any problems actually. in fact variadic arguments (`...`) and regular arguments are implemented in the same way in C (at least in gcc and clang) that is, compiler just puts the args in registers or on the stack, so just as passing extra arguments to vararg function in C does not causes problems (passing less arguments than expected does), passing extra arguments to a function does not too.
Begs the question: is there a use case for ommitting the explicit `void`? Maybe for calling a function via pointer, where args are optional depending on what function is called at runtime? Suppose the compiler couldn't warn against that anyway, though
This does not matter if -Wall -Werror flags are used, which should be always used IMO.
Funny that the first time I saw code use that was about 2 weeks ago and here you are making a video about it. It seems like you always address the stuff that I'm wondering about... mhm...
how do you use the multiple arguments that could be passed to the function? Do you read registers?
Maybe with va_list just like (...) variadic functions.
It technically depends on the system you're working on. However, a common practice is to push the arguments onto the stack (i.e. memory) and then the arguments get pulled from the stack inside the function.
@@teaguehall17 that's totally platform dependent and really annoying. I programmed programs for both linux and windows x86 assembly and having to change all functions because of stupid difference in calling conventions is a nightmare. I get why someone thought "f that let the Maschine do this (c)" 😂
@@teaguehall17 I mean, how would u do it in C if the function is declared as function() ? how do you check if any argument was passed or how many?
why a program compiled with int main(void) execute normally sending arguments
Can you make videos on pattern matching algorithms in c
thank you!
I love how this vid feels totally winged
But what do I do about the *void* in my heart? I crash when trying to accept something new...
always a pleasure to watch one of your videos.
can you give an example of when this behavior of passing zero or more arguments is valid? I remember 'main' being special in that matter, but are there more cases?
printf is an example of a method that can take a variable amount of arguments. For example, printf("Hello"),
Please make a video on working of vtables.
Besides omitting the 'void', I never put the 'int' before a function. I just do a function as: function() or function(int var1, unsigned char var2). Has never been an issue in the many C compilers I have used in over 30 years. What am I at risk for here?
Just for clarity, does this mean that all parameters passed, whether expected or not, still get pushed to the stack in c? And as such, could still be "interpreted" through some potentially hairy stack pointer magic/algorithm?
My question is why doesn't no arguments imply a void argument since that's what everyone expects anyway? Can you even use the number argument you gave it for anything?
ok, now you need a video on how to do a function like printf. something that uses variable number of arguments.
why? because sooner or later going to run into a situation where you don't have a choice of what tools you have or can pay for or administratively approved of.
cason point:
had a rad tool (rapid application development)
found the opening and closing the files would sometimes not close the file properly. this led to problems with trying to delete a file that it thought was already opened thus leading to a failure (Windows platform) The bad party is it would randomly not close a file for some reason unknown to us and we couldn't figure out what the issue was.
fortunately said rad tool, could make calls to the windows API functions.
so I found C type fopen() and freed() were in those API functions.I included our own reads and rights using that in the RAD tool. fortunately they did open and close the files properly.
I have a video about variadic functions from 2017-ish. That might be helpful.
"0 or more arguments" how exactly would those "more" be accessed? I'm assuming it's the precursor to ... for va_list so how?
It should give an error with the empty parenthesis case lool
"foo() means 0 or more arguments." Can the function in any way make use of those 'optional' parameters? Or does the compiler simply ignore those parameters? And if not, what happens to the call-stack in this scenario?
no it can't. it doesn't really mean "0 or more arguments" it's just that C originally did not have function prototypes (you couldn't specify parameters in parentheses when declaring a function), and even in function definition the syntax for parameters was different: `int main() int argc; char * * argv; { return 0; }` you couldn't specify parameters in parentheses.
I was unaware of the difference, but let's be real: any decent compiler will give you that warning, which you should read. And even if it didn't, it should change absolutely nothing. If anything, the extra arguments you pass in will be copied to registers that the function will completely ignore. If you get lucky, the compiler will see that and just remove them for you altogether, I have no reason to believe something could possibly go wrong here
So how do you get access to arguments if they are not declared?
How would you use the "zero or more arguments" in an empty set of parentheses? Is there a way to capture an arbitrary set of arguments not defined in the parentheses?
If there's no way to capture the arguments, then why is it a thing?
Pro tips: Since C23, the void is no longer needed. Next: Use compiler warnings and don't ignore them. Next: Compile your C code by a C++ compiler, it will find bugs in your code (and write only C code that is compatible with both C and C++).
Helpful tip!
... so what's the use case for "zero or more parameters"?
You can also just compile with -Werror, than you got the warning as errors
c doesnt support overloading, so i assume that exactly one quantity of arguments and one specific type per argument is the only valid input. should i spend an infinite number of days per function explicitly declaring that my function(int) should not accept an infinite quantity of an infinite number of types?
Thanks!
I think if the declaration of the function had void in the params, then it will enforce the 0 args prototype? But yeah in C I always use void params explicitly for both the declaration and definition of a function thay takes no params.
but why initially C was designed to have this option of 0 or more arguments? As far as I know C has an implementation of variadic function using ...notation.
K&R originally used it as a convention for headers to not actually declare the arguments to functions, so it fit for not just 0 arguments but as many as the function took. I think the point was to be lazy with evaluating errors which they eventually realized was a bad idea and C89 gave us function declarations with types and named parameters.
If foo() can accept 0 or more arguments how do I access them
What about main?
Does this apply to main()?
Is there any way to use the argument passed when you don’t use (void)? I.e. using 12 in foo() at 2:44
If there is any easy way to do it, its probably through va_start, etc.
@@khatdubell yeah I lightly looked into it but I think you need to use (…) for vargs
It's probably from the K&R function style declaration
I want that shirt..
Where did ya get it?
It's on my channel's merch store.
Why does it work like that? In what situation does passing something to a function with no arguments make sense?
no void arguments means it has variadic arguments. so you'd use va_list to read the arguments passed to the function
in C23 I think they've removed this so both func() and func(void) is correct and the new variadic is func(...)
@@FlanPoirot That's incorrect, passing arguments to f() is undefined behavior. The correct notation for variadic functions prior to C23 is f(type t, ...). Note that the ellipsis [...] must come after both a named parameter and a comma.
@@tourdesource thanks for the correction. (I did know about the ellipsis after named arguments, I just didn't mention I cause it wasn't relevant)
@@FlanPoirot Thanks for making me look it up, I didn't know that either :)
Suppose a function `foo(int x, int y)`. What protects against `foo(1, 2, 3)`?
The compiler. It will fail to compile the code and output the relevant error.
It won’t compile in C or C++. The compiler will try to find a signature of foo that matches three integer parameters. If it doesn’t, it will not compile. This is different in JavaScript where adding or omitting arguments is perfectly legal (with the latter setting the argument to *undefined*).
If the argument list isn't empty, you would have to add ellipses to make it accept a variable amount of arguments, like so: foo(int x, int y, ...)
That was helpful, thanks for the video. On a related note, should functions always return a value? Or is it ok, or perhaps even preferred, to have: void fn(void) { // some code }?
In my opinion if a function cannot meaningfully fail and doesn’t need a return value for some other reason, it doesn’t need to return a value.
Is it possible to retrieve the passed value in case of a declaration of function with no arguments?
If it's ARM, R0 will be the first param, R1 the second. I assume PowerPC and Intel etc have their own rules too.
My question is , is following program thread safe?
#include
#include
using namespace std;
int a = 0;
bool alive = 1, start = 0;
void detector()
{
int local_a = 0;
while (alive)
{
if (start)
{
if (a != local_a)
{
cout
Correct me if I'm wrong, but as of C23 both signatures are now exactly the same!
Yeah but C in general is not C23..
2:20 You're misleading us: No version of gcc produces this warning. Your "gcc" must be clang (version 7 to 14). In order for GCC to diagnose this situation, you need "gcc -std=gnu2x", with GCC version 10 or newer; it then produces an error, not a warning.
I always aim for zero warnings at compile time. So this is not a big deal for me. 🤷♂
And what about no return statement in your 'main' function?
It symbolizes that there is no returning from this newly discovered wisdom.
main is special. If you don’t return anything, the C spec says it’s as if you returned 0. No undefined or implementation-defined behavior here. But do write it out explicitly in non-trivial programs to help others understand your code!
main is the only c function that returns 0 upon successful execution of last line of it's code.. returns some other int value for unsuccessful exit from main.
*runs clang* "see we get a useful warning, let's look weather clang gives us one as well." *runs clang again*
GCC is just an alias.
GCC and Clang are different compilers for Cand C++. Thy both compile the same code but are not the same program.
Clang has far better error messages. I do prefer GCC though since it's usually ahead in terms of new features.
@@ScienceD9000 I know, but you seemed to miss, that he used MacOS. And I know that he used clang, because GCC doesn't give an error at all(even with "-Wall -Wextra").
And that's why C++ is better and you always compile with warnings as errors.
I mean could do the same -Werror in gcc
if you think people get worked up about foo() vs foo(void), let's talk about char** argv vs. char* argv[]
So... Where did the 12 go??
I cannot believe you don't use gnu style brace placement, smh
you should definitely use int foo(void*){}
😂
C++ refrains from putting in void. Why is that?
I received advice a long time ago to not put void. Bad advice.
f(void)
This is one of the design choices that just doesn't make sense to me. Why would a function with no parameters be able to accept parameters if it wouldn't be able to do anything with them?
Personally, I hate K&R style and I avoid it even when I write C++. In other words, I explicitly use void in the function declarations and definitions of any function that takes no arguments, with the only exceptions being constructors because the stupid compiler gives me guff there. If the committee wants to formalize that C++ idiom for C23 then they can get wrecked.
What else did I expect from the brilliant minds at Bell Labs that brought us things like implicit function declarations and implicit int.
I ALWAYS compile with the -Werror flag, so then it doesn't really make a difference :)
Anyone with sense already uses -Werror, so they're the same after all. And anyone with weird warnings he has to ignore, preventing him from doing this, I'm sorry.
I haven't got any warning either with gcc 11.3.0 and -Wall -Werror flags
cool, now make a vid about declaring all your local variables before the {
f
💗🙌🏽
That's why I switched to c++ :P
Because C was too null and void? 😁
@@NonTwinBrothersNULL and void. What a confusing language!
@@ohwow2074 This is TRUE
OOOOO, opening curly brace on a new line????
like any sane person would
Asymmetry fascist! Seriously though, been programming as nature (and Dennis M Ritchie) intended my whole life. I think the move to asymmetrical braces was a mistake.
As god intended
That's the Allman style for us sane people.
Allman style rulez. Everything else looks just unstructured and dirty.
f() means you can pass ANY unspecified list of arguments to a functions and it will work, at least for some time, until you stop function args polymorphism. Do not shoot yourself in your foot
PARAMETERS NOT ARGUMENTS
Oh, no. The semantics/precise-language police. They found me. I don't know how, but they found me. RUN FOR IT MARTY!!!!
@@JacobSorber As I've said on another video, where you implied that the return type of a function is part of the function's signature...:
_Especially_ _for_ _beginners_ and in a complex field such as CS, it's crucial to be painfully pedantic about language. It's like not differentiating between socialists and "nashonnall" socialists.
Also, language in a UA-cam video does not reflect spoken language. You won't hear any UA-camrer say "uhm" like ever or hear them misspeak - heck, you won't even hear them breathing in (which is crazy). You have the opportunity to fix stuff in post. Do it.
The difference between a function declared with an empty parameter list and a function declared with a parameter list consisting of the keyword void will cease to exist in C23. The behaviour of the former will change to match that of the latter, just like in C++.
wow seriously, C doesn't enforce it until you declare void?? I'm I hallucinating or is this default behaviour in c++
in c++ it doesn't make a difference either way you declare a function with no params. They both mean that it takes exactly 0 params.
so unnecessary info
The fact that stuff like that is even allowed to compile is ridiculous. That's why at the very least I use -Wall -Wpedantic -Werror, or just use a better programming language like Zig.
This is EXACTLY why I use C. It does what I want, when I want, and how i want. God I HATE training wheels... but for those who need them, well that's why we have Python.
Why does it matter?
There seems to be a glaring omission in this video, which is a description of the risks of letting that bug pass unfixed. How can the zero or more "invisible" arguments ever be addressed without a name?
It's undefined behavior, which in theory means anything could happen. In practice, it's quite harmless AFAIK.
I'm the 23rd to mention C23