# How many arguments were passed?

Using your language of choice, write a function that takes a variable number of arguments and returns the number of arguments it was called with.

Specifics:

• Your language needs to support variadic argument functions: something callable that takes an arbitrary number of arguments and returns a value.
• Parameters must be able to be passed individually. This means that passing an array would only count for one parameter. You can use an "all passed arguments" array if your language supports it; the restriction is on how the function is called.
• Code that calls this function must not be required to pass the number of arguments in its source. If a compiler inserts the number of arguments as part of a calling convention, that is allowed.
• The arguments can be of any type you want. You can support only a single type (e.g. only supporting int is still valid), arbitrary types (any type of argument is allowed), or any combination of argument types (e.g. first arg is int, rest are strings).
• Your function may have a maximum number of arguments (especially since resources are finite), but must support at least 2 arguments.

Samples:

• f() returns 0
• f(1) or f("a") returns 1
• f([1, 2, 3]) returns 1 as it is passed an array, not 3 arguments
• f(1, 10) or f(1, "a") returns 2

As this is code-golf, the winning solution is the one that uses the fewest number of bytes.

• It's not entirely clear (objectively) what is a "function", "return value" or "variadic arguments". For example, would Dodos function be considered as monadic or variadic? Apr 10 '18 at 5:06
• @user202729 If your language doesn't support functions, use another language. It's not a requirement that all languages can compete, part of code golfing is finding the right tool for the job. Apr 10 '18 at 7:46
• @user202729 I have no problems with the occasional challenge aimed at traditional/high-level languages, just as we have the occasional challenge that is only possible in unusual languages. Apr 10 '18 at 13:58
• didn't know we had to solve the halting problem for language characteristics to have a clear challenge.... Apr 11 '18 at 2:06
• If your language doesn't have the concept of arguments / calling convention then it doesn't fit the criteria of supporting arbitrary numbers of arguments. Apr 11 '18 at 8:23

# Java (JDK 10), 11 bytes

a->a.length

Try it online!

• Java beating Javscript is rare enouth to be noticed Apr 10 '18 at 7:08
• @Therandomguy this requires something like interface x{void f(Object...a);} to be defined, and this lambda must be either stored in a variable of that interface type, or be passed to a method expecting that interface type, so I'm not really sure it counts for this challenge (even though usually java lambdas are allowed in codegolf challenges) Apr 10 '18 at 15:40
• @SamYonnou There is no difference with other lambdas, and as you mentioned, lambdas are fine. Apr 10 '18 at 16:15
• @OlivierGrégoire I know that lambdas are allowed, my point was that compared to JavaScript for example, you need a lot more extra code to set it up, even if you are using something like REPL and avoid the need for a main class/method (the need for the interface to be defined is what distinguishes it from JavaScript) Apr 11 '18 at 18:09
• @PeterCordes That's why the "Try it online" link in my answer exists ;-) Apr 12 '18 at 22:43

# JavaScript, 15 bytes

[].push.bind(0)

The Array.prototype.push function takes any number of arguments, adds them to its array, and returns the size of the array. Therefore, the push function used on an empty array returns the number of arguments supplied to push.

f = [].push.bind(0)

f(10,2,65,7)
> 4

f()
> 0

The .bind(0) simply gives the push function a fixed this value so that it can be stored in a variable. In fact, the 7-byte identifier [].push can be used literally (but not assigned) without bind:

[].push(10,2,65,7)
> 4

[].push()
> 0

# JavaScript (ES6), 16 bytes

(...a)=>a.length

f=
(...a)=>a.length

console.log(f())
console.log(f(1))
console.log(f(1,2))
console.log(f(1,2,3))
console.log(f(1,2,3,4))

class T r where z::Int->r
instance T Int where z=id
instance T r=>T(a->r)where z n _=z$n+1 z 0 Try it online! This was surprisingly hard to get working, but I had fun trying to find out how to implement something that's trivial in imperative languages. • Damn, you beat me to it. f is optional if you say z 0 is the function without the binding, so main = print$ ((z 0) pi 0 () [] :: Int) works.
– Angs
Apr 10 '18 at 7:40
• And by that I mean that the types work when used as an anonymous function so you can indeed remove everything from the last two rows except z 0
– Angs
Apr 10 '18 at 7:55
• Nice, thanks! Turns out I was doing something wrong when I was testing the anonymous function. Tried your example and it worked just fine.
– user79465
Apr 10 '18 at 8:07
• I guess the ::Int should be counted in the byte count, since the type of the answer has to be declared sooner or later, like in main = print $((z 0 :: Double -> Integer -> () -> [a] -> (Int->Int->Int) -> IO () -> Int) pi 0 () [] (+) main). I also think that this works only during compile time so something like foldl(\a b->a b) (z 0)$ [1..5])::Int can't work. Either way, this is great stuff.
– Angs
Apr 10 '18 at 11:04
• s/imperative/non-curry/ Apr 10 '18 at 15:51

# Python 3, 15 bytes

lambda*a:len(a)

Try it online!

• Also, Python 2 :) Apr 10 '18 at 3:09
• It's not at all surprising that this was the first answer. ;) Apr 14 '18 at 3:38

## Amstrad CPC Z80 binary call from BASIC, 1 byte, hex encoded

C9          : RET

(Also 2 and 5 byte versions, see below)

Upon entry to the call, the number of parameters passed will be in the A register. The code simply returns immediately. There is no concept of return values in the Z80, just entry and exit states. The value is just "there" accessible in the register since the code changes no input conditions, except for PC (the program counter) and SP (the stack pointer). However, the value in A is not accessible to BASIC and gets overwritten almost immediately.

Examples:

CALL &8000, "Hello", "World"

A = 2

CALL &8000, 42

A = 1

CALL &8000

A = 0

By request, here is some code that makes the value accessible in BASIC. I was very surprised to find it could be done in only 5 bytes!:

### The machine code:

12          : LD   (DE), A
13          : INC  DE
AF          : XOR  A
12          : LD   (DE), A
C9          : RET

On entry:

• AF - the accumulator and flags registers (treated as two 8-bit registers)
• A contains the number of parameters passed, up to the maximum of 32 parameters
• I'm not sure what's in F. It appears to have all flags RESET to 0, except the two undefined flags which are both 1. The Z flag (zero) is SET to 1 if there were no parameters passed in
• BC
• B - 32 minus the number of parameters (A + B = 32)
• C - &FF
• DE - The address of the last parameter, or the calling address if no parameters were passed in
• HL - The address of the first byte after the tokenised BASIC command currently being executed (either as a program or in immediate command mode)
• IX - The stack address of the pointer to the last parameter
• IY - &0000

The code

1. LoaDs the address pointed to by DE with the value in A
2. INCrements DE
3. XORs A (with A), giving &00
4. LoaDs the value in A to the address pointed to by DE
5. RETurns

On exit:

• A is destroyed (it's always &00)
• DE is destroyed (it's always one higher than on entry)
• All other registers are preserved

### The BASIC

Amstrad basic has only three data types, plus simple arrays. By default, all BASIC variables are REAL (signed, 32 bit mantissa, 8 bit exponent), which can be made explicit with !. For an INTEGER (signed, 16 bit) use % and for a STRING (1 byte string length, up to 255 bytes character data, binary safe) use $: • x - REAL (implicit) • x! - REAL (explicit) • x% - INTEGER • x$ - STRING

You can also use DEFINT, DEFREAL and DEFSTR with a single letter, or a range of two single letters to specify the default type for all variables starting with that letter, similar to FORTRAN.

• DEFSTR a
• DEFINT x-z

Now:

• a - STRING (implicit)
• i - REAL (implicit)
• x - INTEGER (implicit)
• x$- STRING (explicit) The easiest type to work with is the integer. The machine code expects the last parameter to passed by address, not value, which is why @ is prefixed to the variable. The return variable is counted as one of the CALLs parameters. The machine code is called as follows from BASIC (assuming it's loaded at into memory at address &8000): CALL &8000, "Hello", "World", 42, @n% n% = 4 This will always give the correct result, regardless of the initial value of n%. For a 2-byte version that preserves all input registers: CALL &8003, "Hello", "World", 42, @n% n% = 4 This skips the first three bytes, and only gives the correct result if the initial value of n% is 0-255. This works because the Z80 is little-endian. The return parameter must be initialised before being passed, otherwise BASIC will throw an Improper argument error. In the below image, I am printing (with the shortcut ? since I've golfed the demonstration too!) the return values immediately before and after the call to show the value changing. I'm using the value &FFFF because that is the binary representation of -1 for a signed integer. This demonstrates that the 5-byte program correctly writes both bytes, whereas the 2-byte program only writes the low byte and assumes that the high byte is already &00. • So how does the calling convention you're using return values? If it doesn't return them in the accumulator, or at all, then your answer is basically inventing a custom calling convention that solves the problem for you (by adding a return register, instead of passing a pointer where you can store A, if that's how you might actually do it from BASIC). Not that there's anything wrong with that, but it might be a more interesting answer to follow an existing calliing convention. Apr 12 '18 at 16:03 • @PeterCordes Neither Amstrad BASIC nor the Z80 have the concept of scopes. All values are global and are immediately accessible until destroyed. The value of A is the same immediately after the RET instruction. The lifespan of a value in A is very short since it's the accumulator. There's no such thing as x = CALL &8000, 42. It would have to be CALL &8000, x, 42, and extra Z80 code, but then x would be 2, not 1. Apr 12 '18 at 22:26 • I think it's fine if you include the output arg in the count, otherwise there's a 1-byte decrement instruction isn't there? I'd be interested to see a version that was actually usable from BASIC instead of being trivial. Apr 12 '18 at 22:32 • @PeterCordes Done! Oh, by the way, I forgot to mention not to call it with no parameters as it will overwrite its own first two instructions with &00s - NOP no-ops. Another byte can be added to make it safer, but of course without a return parameter it can't set anything. Apr 13 '18 at 13:44 # Zsh, 7 5 bytes <<<$#

Try it online!

• Though it should probably be wrapped: f(){ echo $#; } – muru Apr 10 '18 at 7:39 • @muru That looks fine as a full program to me. – Neil Apr 10 '18 at 8:14 • Although, I see now that the OP only wants a function... – Neil Apr 10 '18 at 15:50 • @Neil shell scripts act exactly like functions. OP doesn't clear on what a function is, I claim my submission is just a function saved to the disk. Apr 10 '18 at 17:28 # Brain-Flak, 6 bytes My first Brain-Flak solution worthy posting, I guess it's the right tool for this job: ([]<>) Try it online! ### Explanation When executing a Brain-Flak program, initially the left stack contains all arguments. From there it's simply a matter of: ( -- push the following.. [] -- height of the stack (ie. # of arguments) <> -- ..to the other stack (toggles to the other stack) ) -- -- the right stack now contains the # of arguments which -- gets printed implicitly # Wolfram Language (Mathematica), 11 bytes Tr[1^{##}]& Try it online! Suggested by JungHwan Min. Some restrictions (input must be rectangular) but we are not required to handle arbitrary input. 11 bytes Length@!##& Try it online! Another 11 byte solution suggested by Martin Ender. This seems to error when there isn't one input but it still returns the correct value in all cases. 12 bytes Length@{##}& Try it online! My original solution. In Mathematica ## stands for a variadic number of arguments in a function. { and } wraps them in a list and Length@ takes the length of this list. & at the end turns this into an actual function. # R, 30 bytes function(...)length(list(...)) Try it online! • function(...)nargs() is 20 bytes, but using length(...) was my initial approach until I googled a nargs-like function. Apr 10 '18 at 12:15 • @Giuseppe hmm, I tried converting the list(...) to a logical so sum() could be used, but that's tricky :/ – JAD Apr 10 '18 at 12:23 • Haha, don't try and bait me into that argument ;) Apr 11 '18 at 8:43 • @RoryT oh actually, R docs say combine. Nevermind :D – JAD Apr 11 '18 at 8:44 • ...length() does the same thing as length(list(...)) Apr 17 '18 at 16:42 # Bash, 12 bytes (thanks to paxdiablo for saving 4) n()(echo$#)

Copy and paste at a bash prompt. Then run the n function from the prompt:

$n 0$ n 46 gr 3443 dad
4
$n 4fwj23 wrw jdwj 00998 34 eyt q3 vg wq j qw 11 • Welcome to PPCG! Apr 10 '18 at 18:52 • can you just say it is a script "./n" and not a function? then it is just: echo$#, 7 bytes. (will be then any shell you use to launch the "./n" script with. ie, you run bash? then when you: ./n arg1 ... argn it will be interpreted by bash.) Apr 13 '18 at 13:04
• @Olivier Dulac The challenge clearly says a function. Apr 13 '18 at 17:04

# C++14 (gcc), 34 bytes

As generic variadic lambda function (C++14 required):

[](auto...p){return sizeof...(p);}

Try it online!

### Previous (incorrect) answer: 32 bytes

It was missing the template<class...T> and (p)

int f(T...p){return sizeof...p;}
• C++14, C++11 has no generic lambdas. Apr 10 '18 at 12:55
• You could add the permissive flag to be able to remove the parenthesis around p (and -w to turn off the warning).
– nwp
Apr 10 '18 at 16:00
• @nwp: wouldn't -fpermissive cost you the 12 bytes for that option, though? If it's not standard ISO C++ or GNU C++. Apr 12 '18 at 16:05
• @PeterCordes It probably does and is intended to avoid having a trivial 0-byte solution for everything by passing the program via command line. I just didn't think about that here because it seems to not be abusive.
– nwp
Apr 12 '18 at 16:16
• @Quentin fixed -> C++14 Apr 15 '18 at 7:15

# Ruby, 12 bytes

->*a{a.size}

Try it online!

*a is a splat of the arguments, making a consume all arguments passed to the Proc. a.size obtains its size.

# Octave, 9 bytes

@()nargin

Try it online!

Anonymous function taking any number of arguments (and silently discarding the lot), and outputs the number of arguments through the built-in nargin. This does not work in MATLAB, where you would need varargin to allow for arbitrary many arguments.

# Perl 6, 5 bytes

Thanks @Joshua for -5 bytes

{+@_}

Try it online!

• You could just do {+@_} Apr 10 '18 at 4:44
• @Joshua >_> true, thanks Apr 10 '18 at 4:48
• You can remove the sub in Perl 6 (not in Perl 5, though). Apr 10 '18 at 17:47
• @nwellnhof Oh, TIL. Perl 6 is so different from Perl 5 >_> Apr 11 '18 at 0:44

# C++, 72 bytes

int f(){return 0;}template<class...P>int f(int,P...p){return f(p...)+1;}

Saves bytes by only working with ints.

• You can use sizeof.... Jul 23 '19 at 4:02

# Perl 5, 9 bytes

sub{~~@_}

Try it online!

• A quick sitewide search of answers seems to indicate that you can leave out the sub Apr 10 '18 at 5:53
• Protip: TIO let's you copy in PPCG post format (ESC, S, G) Apr 10 '18 at 5:55
• @ASCII-only Oh nice, thanks! :) As for leaving out the sub, I don't think so. It's not a function without it. Apr 10 '18 at 5:59
• @ASCII-only I'd consider answers without sub invalid since the result isn't something you can call or assign to a variable Apr 10 '18 at 5:59

# PHP, 34 bytes

function(...$a){return count($a);}
• Nice one! The alternative, for PHP 5.6 and older, is function(){return func_num_args();} (35 bytes, posted below). Apr 10 '18 at 14:16
• @IsmaelMiguel This one also works in PHP 5.6. Apr 10 '18 at 14:56
• You should mention this works only in PHP 5.6 and newer. Apr 10 '18 at 14:57
• @axiac You're right, my bad. Was thinking about PHP5.5 Apr 10 '18 at 15:12

# C# .NET, 11 bytes

a=>a.Length

Try it online.

Explanation:

In C# .NET object is used for multi-type arguments, allowing one to pass integers, strings, characters, etc. as possible inputs. For example:

// Can be called like: F(2), F("test"), F('a'), etc.
void F(object arg){ ... }

C# .NET can also have a fixed size of optional arguments. For example:

// Can be called like: F(), F(2), F("test"), F('a'), etc.
void F(object arg = null){ ... }

And there are also varargs, which is an undefined amount of optional arguments (which is what I've used in this answer). For example:

// Can be called like: F(), F(2), F(2, "test", 'a'), etc.
void F(params object[] args){ ... }

Usually lambdas are created like this:

System.Func<object[], int> F f = a=>a.Length;
// A call like f(new object[]{2, "test", 'a')) will return 3 (size of the input array)

But unfortunately System.Func doesn't support params varargs, so I'll have to create a delegate instead:

delegate int F(params object[] args);
F f = a=>a.Length;
// A call like f() will return 0, and f(2, "test", 'a') will return 3

Which is my answer for this challenge, and can be found in the linked TIO test code.

The only limitation is that inputting an actual object[] like f(new object[]{1,2,3}) will result in 3 instead of 1. f(new int[]{1,2,3}) will still result in 1, because it interprets the int[] as a single object. To have the object[] parameter be interpret as a single object as well it can be casted to an object like this: f((object)new object[]{1,2,3}).

• I have to say, if there were ever an answer that made me support including lambda-related boilerplate in C# answers it would be this one... but it is definitely a valid solution. Apr 10 '18 at 15:23
• @KamilDrakari Maybe it indeed wasn't very clear what I did without opening the TIO-link, so I've added an explanation. Apr 11 '18 at 9:25
• @Taemyr I tried finding a solution, but unfortunately there is none for C# .NET, except for casting any object[] parameters to object, like this: f((object)new object[]{1,2,3});. There is no way to differentiate between f(new object[]{1,2,3}); and f(1,2,3); as far as I could find. Apr 11 '18 at 14:58
• this handles array parameters correctly for a huge penalty of bytes. There might be a more concise structure that can handle it, but it works in my testing. Apr 11 '18 at 15:10
• @KamilDrakari Hmm, but it fails for f(1, new object[]{1,2,3}) again though. Not sure if a solution for this behavior can be found. Apr 11 '18 at 15:43

# Dodos, 32 31 bytes

f
dot i f dab
i

dip dot dab

Try it online!

Uses Dennis' increment function.

## Explanation

f                     # definition of f - target function
dot i f dab   # sum of j(f(all args but first)). recurses until it has 0 args
i                     # definition of i - returns (arg, 1) given 1 arg
# arg
dip dot dab   # 1 (dot dab on list of length 1 returns 0, dip returns |0 - 1|)

Alternatively, 32 bytes without recursion in target function (thanks @Leo)

dot i
i
dip dot dab dot
i dab

Try it online!

## Explanation

dot i             # anonymous function: sum of i(args)
# here this becomes implicit main
i                         # definition of i - returns a list with all arguments replaced with 1
dip dot dab dot   # 1 (dab dot returns empty list, dot returns 0, dip returns |0 - 1|
i dab             # list concatenated with i(all args but first)
• Here's another same-length solution Try it online! I can't seem to understand why yours works though, could you add an explanation please?
– Leo
Apr 10 '18 at 4:47
• Hey, you added an explanation to my solution! I wanted one for yours, I know how mine works xD
– Leo
Apr 10 '18 at 5:34
• @Leo sorry for late reply, idek what I'm doing, just copied Dennis' function, will try to understand asap. I had no idea how dodos works so I figured out what yours did first Apr 10 '18 at 5:36
• No worries, it was just a funny situation :)
– Leo
Apr 10 '18 at 5:38
• @Leo ok so does my explanation make sense? (note: I'm on mobile so feel free to edit it to make it better lol) Apr 10 '18 at 5:46

# Rust, 57 bytes

macro_rules!f{()=>{0};($($x:expr),+)=>{[$($x),+].len()};}

Explanation:

macro_rules! f {         // define a macro called f
() => {0};           // when called without arguments, expand to 0
($($x:expr),+) => {  // when called with 1 or more comma seperated arguments
[                // rust uses [a, b, c] to make an array
$($x),+      // expand to the arguments seperated with a comma
]
.len()           // take the length of that.
};
}

Test:

fn main() {
println!("{:?}", f!());                // prints 0
println!("{:?}", f!(4));               // prints 1
println!("{:?}", f!(5, 2));            // prints 2
// works with anything, as long as you dont mix things
println!("{}", f!("", "a", "hello"));  // prints 3
}

# PHP, 35 bytes

function(){return func_num_args();}

manual entry

# Common Lisp, 28 bytes

(defun f(&rest a)(length a))

Try it online!

L,L

Try it online!

# PHP, 11 bytes

<?=$argc-1; Try it online: 1 input | 3 inputs • I'm not so sure about this one (and it's validity) since it is the count of arguments passed to call PHP. Apr 10 '18 at 14:20 • @IsmaelMiguel, see this consensus. Apr 10 '18 at 14:31 • The question explicitly requires a function that returns the number, does not display it: "...write a function that takes a variable number of arguments and returns the number of arguments." Apr 10 '18 at 14:59 • Re-quoting the question: "Using your language of choice, write a function that takes a variable number of arguments and returns the number of arguments it was called with.". Your code doesn't contain functions. Apr 10 '18 at 15:11 • @IsmaelMiguel, if that were indeed the case then many other solutions would also be invalidated. The norm is to allow solutions to be programmes or functions. Apr 11 '18 at 9:01 ## Batch, 50 49 bytes set n=0 for %%a in (%*)do set/an+=1 exit/b%n% No builtin in Batch, so we have to go old-school. Saved 1 byte thanks to @IsmaelMiguel. Outputs via exit code, or save 3 bytes if output via global variable is valid. Example of use in a full program: @echo off call:c %* echo %ERRORLEVEL% exit/b :c set n=0 for %%a in (%*)do set/an+=1 exit/b%n% • I believe that this answer is answer goes (somewhat) against the rules. Batch has something somewhat close to functions. You can do something similar to :a|set r=0&for %%a in (%*)do set/ar+=1 (| = windows-style newline). This solution is 38 bytes. To execute it, do call :a <args> with a goto :eof before the function, being the value available inside the variable r. If you want to keep your solution, remove the /a on the first set, and remove those @. Apr 11 '18 at 9:39 • @IsmaelMiguel Like this? (Note: I didn't include the function name in the byte count, but I did include the function return, which seems reasonable, as there needs to be one somewhere.) – Neil Apr 11 '18 at 11:32 • Yes, that's exactly it. Nice catch with the exit code! I was surprised to see that exitcodes can be larger than 255. An example is the list provided by Symantec: symantec.com/connect/articles/… Apr 11 '18 at 12:36 # x86 32-bit (i386) machine code function, 13 bytes Calling convention: i386 System V (stack args), with a NULL pointer as a sentinel / terminator for the end-of-arg-list. (Clobbers EDI, otherwise complies with SysV). C (and asm) don't pass type info to variadic functions, so the OP's description of passing integers or arrays with no explicit type info could only be implemented in a convention that passed some kind of struct / class object (or pointers to such), not bare integers on the stack. So I decided to assume that all the args were non-NULL pointers, and the caller passes a NULL terminator. A NULL-terminated pointer list of args is actually used in C for functions like POSIX execl(3): int execl(const char *path, const char *arg, ... /* (char *) NULL */); C doesn't allow int foo(...); prototypes with no fixed arg, but int foo(); means the same thing: args unspecified. (Unlike in C++ where it means int foo(void)). In any case, this is an asm answer. Coaxing a C compiler to call this function directly is interesting but not required. nasm -felf32 -l/dev/stdout arg-count.asm with some comment lines removed. 24 global argcount_pointer_loop 25 argcount_pointer_loop: 26 .entry: 28 00000000 31C0 xor eax, eax ; search pattern = NULL 29 00000002 99 cdq ; counter = 0 30 00000003 89E7 mov edi, esp 31 ; scasd ; edi+=4; skip retaddr 32 .scan_args: 33 00000005 42 inc edx 34 00000006 AF scasd ; cmp eax,[edi] / edi+=4 35 00000007 75FC jne .scan_args 36 ; dec edx ; correct for overshoot: don't count terminator 37 ; xchg eax,edx 38 00000009 8D42FE lea eax, [edx-2] ; terminator + ret addr 40 0000000C C3 ret size = 0D db$ - .entry

The question shows that the function must be able to return 0, and I decided to follow that requirement by not including the terminating NULL pointer in the arg count. This does cost 1 byte, though. (For the 12-byte version, remove the LEA and uncomment the scasd outside the loop and the xchg, but not the dec edx. I used LEA because it costs the same as those other three instructions put together, but is more efficient, so the function is fewer uops.)

C caller for testing:

Built with:

nasm -felf32 -l /dev/stdout arg-count.asm | cut -b -28,$((28+12))- && gcc -Wall -O3 -g -std=gnu11 -m32 -fcall-used-edi arg-count.c arg-count.o -o ac && ./ac -fcall-used-edi is required even at -O0 to tell gcc to assume that functions clobber edi without saving/restoring it, because I used so many calls in one C statement (the printf call) that even -O0 was using EDI. It appears to be safe for gcc's main to clobber EDI from its own caller (in CRT code), on Linux with glibc, but otherwise it's totally bogus to mix/match code compiled with different -fcall-used-reg. There's no __attribute__ version of it to let us declare the asm functions with custom calling conventions different from the usual. #include <stdio.h> int argcount_rep_scas(); // not (...): ISO C requires at least one fixed arg int argcount_pointer_loop(); // if you declare args at all int argcount_loopne(); #define TEST(...) printf("count=%d = %d = %d (scasd/jne) | (rep scas) | (scas/loopne)\n", \ argcount_pointer_loop(__VA_ARGS__), argcount_rep_scas(__VA_ARGS__), \ argcount_loopne(__VA_ARGS__)) int main(void) { TEST("abc", 0); TEST(1, 1, 1, 1, 1, 1, 1, 0); TEST(0); } Two other versions also came in at 13 bytes: this one based on loopne returns a value that's too high by 1. 45 global argcount_loopne 46 argcount_loopne: 47 .entry: 49 00000010 31C0 xor eax, eax ; search pattern = NULL 50 00000012 31C9 xor ecx, ecx ; counter = 0 51 00000014 89E7 mov edi, esp 52 00000016 AF scasd ; edi+=4; skip retaddr 53 .scan_args: 54 00000017 AF scasd 55 00000018 E0FD loopne .scan_args 56 0000001A 29C8 sub eax, ecx 58 0000001C C3 ret size = 0D = 13 bytes db$ - .entry

This version uses rep scasd instead of a loop, but takes the arg count modulo 256. (Or capped at 256 if the upper bytes of ecx are 0 on entry!)

63                       ; return int8_t maybe?
64                       global argcount_rep_scas
65                       argcount_rep_scas:
66                               .entry:
67 00000020 31C0             xor   eax, eax
68                           ;    lea   ecx, [eax-1]
69 00000022 B1FF             mov   cl, -1
70 00000024 89E7             mov   edi, esp
71                       ;    scasd              ; skip retaddr
72 00000026 F2AF             repne scasd        ; ecx = -len - 2 (including retaddr)
73 00000028 B0FD             mov   al, -3
74 0000002A 28C8             sub   al, cl       ; eax = -3 +len + 2
75                       ;    dec   eax
76                       ;    dec   eax
77 0000002C C3               ret

size =  0D = 13 bytes         db \$ - .entry

Amusingly, yet another version based on inc eax / pop edx / test edx,edx / jnz came in at 13 bytes. It's a callee-pops convention, which is never used by C implementations for variadic functions. (I popped the ret addr into ecx, and jmp ecx instead of ret. (Or push/ret to not break the return-address predictor stack).

# R, 20 bytes

function(...)nargs()

Try it online!

R has a function just for that.

# JavaScript, 35 bytes

f=
function(){return arguments.length}

console.log(f(2,5,4))

(\((@a))(len@a))

Try it online!