いくつの引数が渡されましたか?


33

選択した言語を使用して、可変数の引数を取り、呼び出された引数の数を返す関数を作成します。

詳細:

  • あなたの言語は可変引数関数をサポートする必要があります:任意の数の引数を取り値を返す呼び出し可能なもの。
  • パラメータは個別に渡すことができる必要があります。これは、配列を渡すと1つのパラメーターしかカウントされないことを意味します。言語でサポートされている場合は、「すべての引数を渡す」配列を使用できます。関数の呼び出し方法に制限があります。
  • この関数を呼び出すコードは、ソース内の引数の数を渡す必要はありません。コンパイラーが呼び出し規約の一部として引数の数を挿入する場合、それは許可されます。
  • 引数は任意のタイプにすることができます。サポートできるのは、単一のタイプ(たとえば、サポートのみint有効)、任意のタイプ(任意のタイプの引数が許可されます)、または任意の組み合わせの引数タイプ(たとえば、最初の引数がint、残りが文字列)です。
  • 関数には最大数の引数がある場合があります(特にリソースが有限であるため)が、少なくとも2つの引数をサポートする必要があります。

サンプル:

  • f() 返す 0
  • f(1)またはf("a")返す1
  • f([1, 2, 3])13つの引数ではなく配列が渡されたときに戻ります
  • f(1, 10)またはf(1, "a")返す2

これはコードゴルフであるため、最も優れたソリューションは、使用するバイト数が最も少ないソリューションです。


4
「客観的に」「関数」、「戻り値」、または「変数引数」とは何であるかは完全には明らかではありません。たとえば、Dodos関数は単項または可変と見なされますか?
user202729

24
@ user202729言語が機能をサポートしていない場合は、別の言語を使用してください。すべての言語が競争できることは必須ではありません。コードゴルフの一部は、仕事に適したツールを見つけることです。
-Sanchises

5
@ user202729珍しい言語でしかできない時折の挑戦があるのと同じように、私は伝統的/高レベルの言語を対象とした時折の挑戦に問題はありません。
-Sanchises

6
私たちは、言語特性が明確に挑戦....持っているため停止問題を解決しなければならなかった知らなかった
コナー・オブライエン

5
言語に引数/呼び出し規約の概念がない場合、任意の数の引数をサポートする基準に適合しません。
グレンスミス

回答:


15

BASICからのAmstrad CPC Z80バイナリ呼び出し、1バイト、16進数エンコード

C9          : RET

(2および5バイトバージョンも、以下を参照)

呼び出しに入ると、渡されたパラメーターの数がAレジスターに記録されます。コードはすぐに戻ります。Z80には戻り値の概念はなく、エントリ状態と終了状態だけがあります。PC(プログラムカウンター)とSP(スタックポインター)を除いて、コードは入力条件を変更しないため、値はレジスタ内に「そこに」アクセスできます。ただし、の値AはBASICにアクセスできず、ほとんどすぐに上書きされます。

例:

CALL &8000, "Hello", "World"

A = 2

CALL &8000, 42

A = 1

CALL &8000

A = 0


要求に応じて、BASICで値にアクセスできるようにするコードを次に示します。わずか5バイトで実現できることに驚いた!:

マシンコード:

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

入場時:

  • AF -アキュムレータおよびフラグレジスタ(2つの8ビットレジスタとして扱われます)
    • A 渡されたパラメーターの数が含まれ、最大32個のパラメーターまで
    • 何が入っているのかわかりませんF。それ0は両方である2つの未定義の旗を除いて、すべての旗がRESETするように見えます1Zフラグ(ゼロ)に設定されて1渡されないパラメータが存在しない場合
  • BC
    • B-32-パラメーターの数(A+ B= 32)
    • C - &FF
  • DE -最後のパラメーターのアドレス、またはパラメーターが渡されなかった場合の呼び出しアドレス
  • HL -現在実行されているトークン化されたBASICコマンドの後の最初のバイトのアドレス(プログラムとして、または即時コマンドモードで)
  • IX -最後のパラメーターへのポインターのスタックアドレス
  • IY - &0000

コード

  1. Loa DsのDE値を持つが指すアドレスA
  2. INCリメント DE
  3. XORs A(with A)、与える&00
  4. Loa DsがA指すアドレスへの値DE
  5. RETns

終了時:

  • A破棄されます(常に&00
  • DE 破棄されます(エントリ時より常に1つ高い)
  • 他のすべてのレジスタは保持されます

ベーシック

Amstrad basicには、3つのデータ型と単純な配列のみがあります。デフォルトでは、すべてのBASIC変数はREAL(符号付き、32ビット仮数、8ビット指数)であり、で明示的にすることができます!。INTEGER(符号付き、16ビット)を使用%し、STRING(1バイト文字列の長さ、最大255バイトの文字データ、バイナリセーフ)を使用する場合$

  • x -REAL(暗黙)
  • x! -REAL(明示的)
  • x% -整数
  • x$ -STRING

また、使用することができDEFINTDEFREALかつDEFSTR単一の文字、または2つの文字の範囲でFORTRANに似て、その文字で始まるすべての変数のデフォルトのタイプを指定します。

  • DEFSTR a
  • DEFINT x-z

今:

  • a -STRING(暗黙)
  • i -REAL(暗黙)
  • x -INTEGER(暗黙的)
  • x$ -STRING(明示的)

使用する最も簡単なタイプは整数です。マシンコードは、値ではなくアドレスによって最後のパラメーターが渡されることを想定しています。これが@変数の前に付けられる理由です。戻り変数は、CALLsパラメーターの1つとしてカウントされます。

マシンコードは、BASICから次のように呼び出されます(アドレスのメモリにロードされていると仮定&8000):

CALL &8000, "Hello", "World", 42, @n%

n% = 4

これにより、の初期値に関係なく、常に正しい結果が得られますn%

すべての入力レジスタを保持する2バイトバージョンの場合:

CALL &8003, "Hello", "World", 42, @n%

n% = 4

これにより、最初の3バイトがスキップされ、初期値n%0-の場合にのみ正しい結果が得られます255。これは、Z80がリトルエンディアンであるため機能します。

戻りパラメーターは、渡される前に初期化する必要がありImproper argumentます。そうしないと、BASICはエラーをスローします。次の画像では?、呼び出しの直前と直後に戻り値を印刷して(ショートカットも使用してデモを行っています!)、値の変化を示しています。値を使用して&FFFFいるのは、それが-1符号付き整数のバイナリ表現であるためです。これは、5バイトプログラムが両方のバイトを正しく書き込むのに対し、2バイトプログラムは下位バイトのみを書き込み、上位バイトがすでにあると想定していることを示してい&00ます。

enter image description here


では、戻り値を使用している呼び出し規約はどのようになっていますか?それがアキュムレータに返されない場合、またはまったく返されない場合、あなたの答えは基本的にあなたのために問題を解決するカスタム呼び出し規約を発明することです(保存できる場所にポインタを渡す代わりに、リターンレジスタを追加することによりA、 BASICから実際にそれを行う方法)。それに何か問題があるわけではありませんが、既存の呼び出し規約に従う方が興味深い答えかもしれません。
ピーター

@PeterCordes Amstrad BASICもZ80にもスコープの概念はありません。すべての値はグローバルであり、破棄されるまですぐにアクセスできます。の値はARET命令の直後と同じです。値がAアキュムレータであるため、値の寿命は非常に短くなります。のようなものはありませんx = CALL &8000, 42。それはCALL &8000, x, 42であり、追加のZ80コードである必要がありますが、xそうで2はありません1
CJデニス

出力引数をカウントに含めれば問題ないと思いますが、そうでない場合は1バイトのデクリメント命令がありませんか?些細なものではなく、実際にBASICから使用できるバージョンを見てみたいと思います。
ピーター

1
@PeterCordes完了!ああ、ちなみに、パラメータなしで呼び出すことを忘れてしまいました。最初の2つの命令を&00s- NOPno-opsで上書きするためです。別のバイトを追加してより安全にすることもできますが、もちろん戻りパラメーターがなければ何も設定できません。
CJデニス

32

29
ジャワの鼓動Javscriptは気づいする珍しいenouthある
ランダム男

3
@Therandomguyこれはinterface x{void f(Object...a);}定義されるようなものを必要とし、このラムダはそのインターフェース型の変数に保存されるか、そのインターフェース型を期待するメソッドに渡されなければならないので、それが本当にこのチャレンジにカウントされるかわかりません通常、Javaラムダはcodegolfチャレンジで許可されています)
-SamYonnou

3
@SamYonnou他のラムダと違いはありません。そして、あなたが述べたように、ラムダは問題ありません。
オリビエグレゴワール

@OlivierGrégoire私はラムダが許可されていることを知っています、例えば、JavaScriptと比較して、REPLのようなものを使用していてメインクラス/メソッドの必要性を避けていても、それを設定するにはより多くの余分なコードが必要です定義するインターフェースの必要性のJavaScriptからそれを区別するものである)
SamYonnou

@OlivierGrégoire:私はJavaを知っていますが、それに追いついていません。私は、実際に短くすることができるJavaの回答で、ボイラープレートが敷物の下に流されているものについてのSamのコメントを見て興味がありました。私はそれが許可されるべきであることに同意します(たとえJava関数では通常得られないものを与えますが、正しいので、それは単なる引数の削減ではなく、組み込みの引数カウントを与えています)。それに加えて、「Java beating JS」への返信としても興味深いものです。
ピーター

25

JavaScript、15バイト

[].push.bind(0)

Array.prototype.push関数は任意の数の引数を取り、それらを配列に追加し、配列のサイズを返します。したがって、push空の配列で使用される関数は、に指定されpushた引数の数を返します。

f = [].push.bind(0)

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

f()
> 0

.bind(0)単に与えるpush関数に固定thisそれを変数に格納することができるように、値。実際、7バイトの識別子[].pushは次のように指定せずにそのまま使用できます(ただし、割り当てられません)bind

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

[].push()
> 0


18

ハスケル108の 107 95 94バイト

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

オンラインでお試しください!

これを動かすのは驚くほど困難でしたが、命令型言語ではささいなことを実装する方法を見つけるのを試してみました。


くそー、あなたはそれに私を打った。バインドのない関数であるfと言う場合はオプションなz 0ので、main = print $ ((z 0) pi 0 () [] :: Int)動作します。
-Angs

そして、それは、匿名関数として使用されたときに型が機能することを意味するため、最後の2行からすべてを削除することができますz 0
-Angs

よかった、ありがとう!無名関数をテストしていたときに、何か間違ったことをしていたことがわかりました。あなたの例を試してみたところ、うまくいきました。
user9549915

のように::Int、回答のタイプを遅かれ早かれ宣言する必要があるため、バイトカウントでカウントする必要があると思いmain = print $ ((z 0 :: Double -> Integer -> () -> [a] -> (Int->Int->Int) -> IO () -> Int) pi 0 () [] (+) main)ます。また、これはコンパイル時にのみ機能するので、機能しないようなものだと思いますfoldl(\a b->a b) (z 0) $ [1..5])::Int。いずれにせよ、これは素晴らしいものです。
-Angs

2
s/imperative/non-curry/
-user202729


12

それはおそらくラップする必要がありますが:f(){ echo $#; }
-muru

8
@muruそれは私にとって完全なプログラムとしては問題ありません。
ニール

しかし、私は今OPが機能だけを望んでいることがわかります
ニール

2
@Neilシェルスクリプトは、関数とまったく同じように機能します。OPは関数が何であるかを明確にしていません。私の提出はディスクに保存された関数に過ぎないと主張します。
パベル

9

Brain-Flak、6バイト

初めて投稿する価値のあるBrain-Flakソリューションです。この仕事に最適なツールだと思います。

([]<>)

オンラインでお試しください!

説明

Brain-Flakプログラムを実行すると、最初は左側のスタックにすべての引数が含まれます。そこからは、単に次の問題が発生します。

(      -- 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

7

Wolfram言語(Mathematica)、11バイト

Tr[1^{##}]&

オンラインでお試しください!

JungHwan Minによる提案。いくつかの制限(入力は長方形でなければなりません)が、任意の入力を処理する必要はありません。

11バイト

Length@!##&

オンラインでお試しください!

Martin Enderが提案した別の11バイトのソリューション。これは、入力が1つもないときにエラーのように見えますが、すべての場合に正しい値を返します。

12バイト

Length@{##}&

オンラインでお試しください!

私の元のソリューション。

Mathematica ##では、関数内の可変個の引数を表します。{そして}それらをリストにラップし、Length@このリストの長さを取ります。&最後にこれを実際の機能に変えます。


7

R、30バイト

function(...)length(list(...))

オンラインでお試しください!


1
function(...)nargs()は20バイトですがlength(...)nargs-like関数をグーグル検索するまでは、最初のアプローチが使用でした。
ジュゼッペ

@ジュゼッペうーん、私list(...)は論理に変換しようとしたのでsum()使用できますが、それはトリッキーです:/
JAD

1
ハハ、私をその議論に
誘っ

1
@RoryTああ、実際、Rのドキュメントは結合と言います。ネバーマインド:D
JAD

2
...length() と同じことを行うlength(list(...))
ジュゼッペ

7

Bash、12バイト(4を保存してくれたpaxdiabloに感謝)

n()(echo $#)

bashプロンプトでコピーして貼り付けます。次に、プロンプトからn関数を実行します。

$ n
0
$ n 46 gr 3443 dad
4
$ n 4fwj23 wrw jdwj 00998 34 eyt q3 vg wq j qw
11

2
PPCGへようこそ!
マーティンエンダー

関数ではなくスクリプト "./n"であると言えますか?それはちょうどです: echo $#、7バイト。(その後、「./ n」スクリプトを起動するために使用するシェルになります。つまり、bashを実行しますか?次に、 ./n arg1 ... argnbashによって解釈されます。)
Olivier Dulac

@Olivier Dulac課題は明らかに機能を語っています。
ワストレル

7

C ++ 14(gcc)、34バイト

汎用の可変長ラムダ関数として(C ++ 14が必要):

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

オンラインでお試しください!

前の(間違った)回答:32バイト

それは行方不明になったtemplate<class...T>(p)

int f(T...p){return sizeof...p;}

6
C++14, C++11 has no generic lambdas.
Quentin

1
You could add the permissive flag to be able to remove the parenthesis around p (and -w to turn off the warning).
nwp

@nwp: wouldn't -fpermissive cost you the 12 bytes for that option, though? If it's not standard ISO C++ or GNU C++.
Peter Cordes

@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

@Quentin fixed -> C++14
Bierpfurz


5

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.



4

Perl 5, 9 bytes

sub{~~@_}

Try it online!


A quick sitewide search of answers seems to indicate that you can leave out the sub
ASCII-only

2
Protip: TIO let's you copy in PPCG post format (ESC, S, G)
ASCII-only

@ASCII-only Oh nice, thanks! :) As for leaving out the sub, I don't think so. It's not a function without it.
Chris

@ASCII-only I'd consider answers without sub invalid since the result isn't something you can call or assign to a variable
Ton Hospel


4

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.
Kamil Drakari

@KamilDrakari Maybe it indeed wasn't very clear what I did without opening the TIO-link, so I've added an explanation.
Kevin Cruijssen

1
@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.
Kevin Cruijssen

1
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.
Kamil Drakari

1
@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.
Kevin Cruijssen

4

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

Hey, you added an explanation to my solution! I wanted one for yours, I know how mine works xD
Leo

1
@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
ASCII-only

No worries, it was just a funny situation :)
Leo

@Leo ok so does my explanation make sense? (note: I'm on mobile so feel free to edit it to make it better lol)
ASCII-only

3

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....
L. F.

3

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
}




2

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.
Ismael Miguel

@IsmaelMiguel, see this consensus.
Shaggy

1
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."
axiac

1
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.
Ismael Miguel

@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.
Shaggy

2

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 @.
Ismael Miguel

@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

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/…
Ismael Miguel

2

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).



1

JavaScript, 35 bytes

f=
function(){return arguments.length}

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


弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.