传递了多少论点?


33

使用您选择的语言,编写一个函数,该函数接受可变数量的参数并返回调用该参数的参数数量。

细节:

  • 您的语言需要支持可变参数函数:可调用的函数,可以接受任意数量的参数并返回值。
  • 参数必须能够单独传递。这意味着传递数组将仅计入一个参数。如果您的语言支持,则可以使用“所有传递的参数”数组;该函数的调用方式受到限制。
  • 调用此函数的代码不必在其源中传递参数数量。如果编译器将参数数量作为调用约定的一部分插入,则允许这样做。
  • 参数可以是您想要的任何类型。您只能支持单一类型(例如,仅支持int仍然有效),任意类型(允许任何类型的参数)或参数类型的任意组合(例如,第一个arg为int,其余为字符串)。
  • 您的函数可能具有最大数量的参数(尤其是由于资源有限),但是必须至少支持2个参数。

样品:

  • f() 退货 0
  • f(1)f("a")返回1
  • f([1, 2, 3])1在传递数组时返回,而不是3个参数
  • f(1, 10)f(1, "a")返回2

因为这是代码高尔夫球,所以胜出的解决方案是使用最少字节数的解决方案。


4
(在客观上)尚不清楚“函数”,“返回值”或“可变参数”是什么。例如,将渡渡鸟功能视为单子函数还是可变函数函数?
user202729 '18

24
@ user202729如果您的语言不支持功能,请使用另一种语言。这并不是所有语言都可以竞争的要求,打高尔夫的一部分代码正在寻找适合该工作的工具。
桑契斯

5
@ user202729我对针对传统/高级语言的偶发挑战没有任何问题,就像我们遇到的偶发挑战只有在不寻常的语言中才有可能一样。
桑契斯

6
不知道我们必须解决停机问题的语言特性有明确的挑战....
康纳尔奥布莱恩

5
如果您的语言没有参数/调用约定的概念,那么它就不符合支持任意数量的参数的标准。
格伦·史密斯

Answers:


15

来自BASIC的Amstrad CPC Z80二进制调用,1字节,十六进制编码

C9          : RET

(还有2和5字节版本,请参见下文)

进入呼叫后,传递的参数数量将在A寄存器中。该代码只是立即返回。Z80中没有返回值的概念,只有进入和退出状态。该值在寄存器中只是“可访问的”,因为除了PC(程序计数器)和SP(堆栈指针)外,代码不改变任何输入条件。但是,ABASIC无法访问in的值,并且几乎立即将其覆盖。

例子:

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 -累加器和标志寄存器(视为两个8位寄存器)
    • A 包含传递的参数数量,最多32个参数
    • 我不确定里面有什么F0除两个未定义的标志都为RESET 之外,似乎所有标志都为RESET 1。该Z标志(零)设定为1如果没有传入参数
  • BC
    • B-32减去参数数量(A+ B= 32)
    • C -- &FF
  • DE -最后一个参数的地址,如果未传入任何参数,则为调用地址
  • HL -当前正在执行标记化的BASIC命令之后的第一个字节的地址(作为程序还是在立即命令模式下)
  • IX -指向最后一个参数的指针的堆栈地址
  • IY -- &0000

代码

  1. Loa D所指向的地址中DE的值为A
  2. INC评论 DE
  3. XORs A(带有A),给出&00
  4. LOA DS IN值A的地址指向DE
  5. RET

在出口:

  • A被摧毁(总是&00
  • DE 被摧毁(总是比进入时高一)
  • 所有其他寄存器均保留

基础的

Amstrad basic仅具有三种数据类型,以及简单的数组。默认情况下,所有BASIC变量都是REAL(有符号,32位尾数,8位指数),可以使用来使其明确!。对于INTEGER(带符号的16位)使用%和对于STRING(1字节字符串长度,最多255字节字符数据,二进制安全)使用$

  • x -真实(隐式)
  • x! -真实(显式)
  • x% - 整数
  • x$ -STRING

您也可以使用DEFINTDEFREALDEFSTR一个字母,或两个字母的范围来指定所有以该字母开头的变量的默认类型,类似于FORTRAN。

  • DEFSTR a
  • DEFINT x-z

现在:

  • a -STRING(隐式)
  • i -真实(隐式)
  • x -整数(隐式)
  • x$ -STRING(明确)

最容易使用的类型是整数。机器码希望最后一个参数按地址而不是值传递,这就是为什么@要在变量前加上前缀。返回变量被视为CALLs参数之一。

机器代码从BASIC中被调用如下(假设它被加载到地址为address的内存中&8000):

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

n% = 4

无论的初始值如何,这将始终给出正确的结果n%

对于保留所有输入寄存器的2字节版本:

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

n% = 4

此跳过前三个字节,并且只给出正确的结果,如果初始值n%0- 255。这是因为Z80是低位优先的。

返回参数必须在传递之前进行初始化,否则BASIC将引发Improper argument错误。在下图中,我正在打印?呼叫(在显示高尔夫之后也使用了快捷方式!),在调用之前和之后立即显示返回值,以显示值的变化。我正在使用该值,&FFFF因为这是-1一个有符号整数的二进制表示形式。这表明5字节程序正确写入了两个字节,而2字节程序仅写入低字节,并假定高字节已写入&00

在此处输入图片说明


那么,您使用的调用约定是如何返回值的呢?如果它没有在累加器中或者根本没有返回它们,那么您的答案基本上就是发明一种自定义调用约定,该约定可以为您解决问题(通过添加返回寄存器,而不是传递一个可以存储的指针A,如果这样)如何从BASIC实际执行此操作)。并不是说有什么问题,但是遵循现有的调用约定可能是一个更有趣的答案。
彼得·科德斯

@PeterCordes Amstrad BASIC和Z80都没有范围的概念。所有值都是全局值,可以立即访问,直到销毁为止。指令A后的值立即相同RET。值的寿命A很短,因为它是累加器。没有这样的东西x = CALL &8000, 42。它将必须是CALL &8000, x, 42,并且需要额外的Z80代码,但随后x将是2,而不是1
CJ丹尼斯

我认为在计数中包含输出arg很好,否则不存在1字节的减量指令吗?我希望看到一个可以从BASIC实际使用而不是琐碎的版本。
彼得·科德斯

1
@PeterCordes完成!哦,顺便说一句,我忘了不要不带任何参数调用它,因为它将用&00s- NOPno-ops 覆盖它自己的前两个指令。可以添加另一个字节以使其更安全,但是当然,如​​果没有返回参数,它将无法进行任何设置。
CJ丹尼斯

32

Java(JDK 10),11个字节

a->a.length

在线尝试!


29
Java击败Javscript的现象鲜为人知
随机家伙

3
@Therandomguy这需要进行一些interface x{void f(Object...a);}定义,并且此lambda必须存储在该接口类型的变量中,或者传递给需要该接口类型的方法,因此,我不确定是否可以应对此挑战(即使虽然通常在代码高尔夫挑战中允许使用Java Lambdas)
SamYonnou

3
@SamYonnou与其他lambda没什么区别,正如您提到的,lambda很好
奥利维尔·格雷戈雷(OlivierGrégoire)

@OlivierGrégoire我知道可以使用lambda,例如,与JavaScript相比,我需要很多额外的代码来设置它,即使您使用的是REPL之类的东西,也避免了使用主类/方法(需要定义接口是它与JavaScript区别的地方)
SamYonnou

@OlivierGrégoire:我知道一些Java,但是根本没有跟上。我很感兴趣地看到Sam对Java答案中的样板正在行之有效的评论,这使得它实际上确实很短。我同意应该允许这样做(即使它确实提供了Java函数通常无法提供的功能,对,因此,它不仅简化了样版,而且还为您提供了内置的arg计数)。除此之外,作为对“ Java击败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值,以便它能够被存储在变量中。实际上,如果没有以下说明,则[].push可以直接使用7个字节的标识符(但不能分配)bind

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

[].push()
> 0

19

JavaScript(ES6),16个字节

(...a)=>a.length


18

Haskell中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)

通过这种方式,我的意思是这些类型在用作匿名函数时可以工作,因此您实际上可以删除最后两行中的所有内容,除了z 0
Angs

很好,谢谢!原来我在测试匿名函数时做错了什么。试过你的例子,它工作得很好。
user9549915 '18

我猜::Int应该在字节数中计数,因为答案的类型迟早必须声明,例如in 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无法正常工作。无论哪种方式,这都是好东西。
昂斯

2
s/imperative/non-curry/
user202729 '18


12

Zsh7 5字节

<<<$#

在线尝试!


虽然应该将其包裹起来:f(){ echo $#; }
大师

8
@muru在我看来,作为一个完整程序,这很好。
尼尔

虽然,我现在看到OP只需要一个功能...
Neil

2
@Neil Shell脚本的行为与功能完全相同。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^{##}]&

在线尝试!

郑焕敏建议。有一些限制(输入必须为矩形),但是我们不需要处理任意输入。

11字节

Length@!##&

在线尝试!

Martin Ender建议的另一个11字节解决方案。当没有一个输入但在所有情况下仍返回正确的值时,这似乎出错。

12字节

Length@{##}&

在线尝试!

我原来的解决方案。

在Mathematica中,##代表函数中各种参数的变数。{并将}它们包装在一个列表中,并Length@获取此列表的长度。&最后将其转换为实际功能。


7

R,30个字节

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

在线尝试!


1
function(...)nargs()是20个字节,但是使用length(...)这种方法是我最初的方法,直到我搜索了类似nargs函数的函数。
朱塞佩

@Giuseppe嗯,我尝试将转换list(...)为逻辑,因此sum()可以使用,但这很棘手:/
JAD

1
哈哈,不要试图让我陷入那种争论;)
RoryT

1
@RoryT哦,实际上,R文档说合并。没关系:D
JAD

2
...length() length(list(...))
Giuseppe

7

Bash,12个字节(感谢paxdiablo节省了4个字节)

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!
Martin Ender '18

您可以说它是脚本“ ./n”而不是函数吗?那么就是: echo $#7个字节。(那么,您将使用它来启动“ ./n”脚本的任何shell。即,您运行bash吗?然后当您: ./n arg1 ... argn它将由bash解释。)
Olivier Dulac

@Olivier Dulac挑战明确说明了一个功能。
Wastrel

7

C ++ 14(gcc),34个字节

作为通用可变参数lambda函数(需要C ++ 14):

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

在线尝试!

上一个(错误)答案:32个字节

它缺少template<class...T>(p)

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

6
C ++ 14,C ++ 11没有通用lambda。
Quentin '18

1
您可以添加允许标志,以便能够删除括号p(并-w关闭警告)。
nwp

@nwp:不过,-fpermissive您不会为此选项花费12个字节吗?如果不是标准的ISO C ++或GNU C ++。
彼得·科德斯

@PeterCordes它可能会这样做,并且旨在通过命令行传递程序来避免对所有内容使用琐碎的0字节解决方案。我只是在这里没有考虑到这一点,因为它似乎不是在辱骂。
nwp

@Quentin已修复-> C ++ 14
Bierpfurz


5

八度,9字节

@()nargin

在线尝试!

匿名函数接受任意数量的参数(并静默丢弃该批次),并通过内建输出参数的数量nargin。这在MATLAB中不起作用,因为您需要varargin允许任意多个参数。



4

Perl 5、9个字节

sub{~~@_}

在线尝试!


在站点范围内快速搜索答案似乎表明您可以省略sub
仅ASCII的

2
Protip:TIO让您以PPCG帖子格式(ESC,S,G)进行复制
仅使用ASCII

仅限@ASCII哦,很好,谢谢!:)至于忽略sub,我不这么认为。没有它就不是一个功能。
克里斯(Chris)

@@ only-ASCII我会考虑答案是否sub无效,因为结果不是您可以调用或分配给变量的东西
Ton Hospel 18-4-10


4

C#.NET,11个字节

a=>a.Length

在线尝试。

说明:

在C#中,.NET object用于多种类型的参数,允许将整数,字符串,字符等作为可能的输入传递。例如:

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

C#.NET也可以具有固定大小的可选参数。例如:

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

还有varargs,它是未定义数量的可选参数(这是我在此答案中使用的参数)。例如:

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

通常,lambda是这样创建的:

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)

但是不幸的System.Func是不支持paramsvarargs,所以我不得不创建一个delegate

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

这是我对这一挑战的答案,可以在链接的TIO测试代码中找到。


唯一的限制是,输入一个实际的object[]like f(new object[]{1,2,3})将导致3而不是1 f(new int[]{1,2,3})仍将导致1,因为它将解释int[]为single object。要使object[]参数解释为一个单一的对象,以及它可以被浇铸到这样一个对象:f((object)new object[]{1,2,3})


我不得不说,如果有一个答案能使我支持,包括在C#答案中包含与lambda相关的样板,那就是这个答案……但这绝对是一个有效的解决方案。
卡米尔·达卡里

@KamilDrakari也许确实不很清楚我没有打开TIO链接所做的事情,所以我添加了一个解释。
凯文·克鲁伊森

1
@Taemyr我试图找到一个解决方案,但不幸的是C#.NET没有任何解决方案,除了将任何object[]参数强制转换为object,例如:f((object)new object[]{1,2,3});。有没有办法区分f(new object[]{1,2,3});,并f(1,2,3);就我能找到。
凯文·克鲁伊森

1
这样可以正确处理数组参数,从而损失大量字节。可能会有一个更简洁的结构可以处理它,但是在我的测试中它可以工作。
卡米尔·德拉卡里

1
@KamilDrakari嗯,但是它f(1, new object[]{1,2,3})再次失败了。不知道是否可以找到解决此问题的方法。
凯文·克鲁伊森

4

渡渡鸟32 31字节

f
	dot i f dab
i
	
	dip dot dab

在线尝试!

使用丹尼斯的增量功能。

说明

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

另外,目标函数中没有递归的32个字节(感谢@Leo

	dot i
i
	dip dot dab dot
	i dab

在线尝试!

说明

        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)

这是另一个同等解决方案,请在线尝试! 我似乎无法理解您的工作原因,请您补充说明?
狮子座

嘿,您在我的解决方案中添加了解释!我想要一个给你的,我知道我的工作原理xD
Leo

1
@Leo很抱歉延迟回复,idek我在做什么,只是复制了Dennis的功能,将尝试尽快理解。我不知道渡渡鸟是如何工作的,所以我想通了什么你做了第一
ASCII-仅

不用担心,这只是一个有趣的情况:)
Leo

@Leo好吧,我的解释有意义吗?(注意:我在移动设备上,可以随时对其进行编辑以使其更好地大声笑)
仅限ASCII

3

C ++,72个字节

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

通过仅使用int来节省字节。


您可以使用sizeof...
LF

3

Rust,57个字节

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

说明:

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.
    };
}

测试:

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个字节

<?=$argc-1;

在线尝试:1输入 | 3输入


我不太确定这一点(它的有效性),因为这是传递给调用PHP的参数的数量。
伊斯梅尔·米格尔

@IsmaelMiguel,请参阅此共识
毛茸茸的

1
该问题明确需要一个返回数字的函数,而不显示该数字:“ ...编写一个函数,该函数接受可变数量的参数并返回参数的数量。”
axiac

1
重新引用问题:“使用您选择的语言,编写一个函数,该函数接受可变数量的参数并返回被调用的参数数量。” 您的代码不包含函数。
伊斯梅尔·米格尔

@IsmaelMiguel,如果确实如此,那么许多其他解决方案也将失效。规范是允许解决方案成为程序或函数。
毛茸茸的

2

批处理,50 49字节

set n=0
for %%a in (%*)do set/an+=1
exit/b%n%

没有内置的批处理功能,因此我们必须改行。感谢@IsmaelMiguel,节省了1个字节。通过退出代码输出,如果通过全局变量输出有效,则节省3个字节。在完整程序中使用的示例:

@echo off
call:c %*
echo %ERRORLEVEL%
exit/b
:c
set n=0
for %%a in (%*)do set/an+=1
exit/b%n%

我认为这个答案是(有点)违反规则的答案。批处理有些接近功能。您可以执行类似于:a|set r=0&for %%a in (%*)do set/ar+=1|= Windows风格的换行符)的操作。此解决方案为38个字节。要执行它,请在函数之前做call :a <args>一个goto :eof变量,即变量中可用的值r。如果你想保持你的解决方案,除去/a第一set,并删除这些@
伊斯梅尔·米格尔

@IsmaelMiguel喜欢这个吗?(注意:我没有在字节数中包括函数名称,但是我确实包括了函数返回,这似乎很合理,因为需要在某个地方。)
Neil

是的,就是这样。退出代码很不错!我很惊讶地看到,exitcodes可以大于255的一个例子是由赛门铁克提供的名单:symantec.com/connect/articles/...
伊斯梅尔·米格尔

2

x86 32位(i386)机器代码功能,13个字节

调用约定:i386 System V(堆栈args),其中NULL指针作为arg-list的末尾/终止符。(Clobbers EDI,否则符合SysV)。

C(和asm)不会将类型信息传递给可变参数函数,因此OP传递不带显式类型信息的整数或数组的描述只能在传递某种struct / class对象(或指向此类的指针)的约定中实现),而不是堆栈上的裸整数。因此,我决定假定所有的args都是非NULL指针,并且调用方传递一个NULL终止符。

NULL终止的args指针列表实际上在C中用于POSIX之类的execl(3)功能int execl(const char *path, const char *arg, ... /* (char *) NULL */);

C不允许int foo(...);没有固定arg的原型,但int foo();含义相同:args未指定。(不同于C ++中的含义int foo(void))。无论如何,这是一个asm答案。哄骗C编译器直接调用此函数很有趣,但不是必需的。

nasm -felf32 -l/dev/stdout arg-count.asm 删除了一些注释行。

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

问题表明该函数必须能够返回0,我决定遵循这一要求,在arg计数中不包括终止NULL指针。不过,这确实花费了1个字节。(对于12字节的版本,删除LEA并取消注释scasd循环外部和xchg,但不要注释dec edx。我使用LEA是因为它的成本与其他三条指令的总和相同,但效率更高,因此功能更少哎呀。)

C调用者进行测试

内置:

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即使在-O0也是必需的,以告诉gcc假设函数edi不保存或恢复它,因为我在一个C语句(printf调用)中使用了那么多调用,甚至-O0使用EDI。对于gcc main,在Linux上使用glibc掩盖来自其自己的调用方的EDI(在CRT代码中)似乎是安全的,但否则,将使用different编译的代码混合/匹配完全是伪造的-fcall-used-reg。没有任何__attribute__版本可以让我们使用不同于通常的自定义调用约定来声明asm函数。

#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);
}

另外两个版本还有13个字节:这个基于的版本loopne返回的值太大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

此版本使用rep scasd而不是循环,但是将arg计数取模256。(如果输入的高字节为ecx0,则上限为256 !)

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

有趣的是,另一个基于inc eax/ pop edx/ test edx,edx/的版本jnz以13个字节出现。这是callee-pops约定,C实现从未将其用于可变函数。(我将ret addr弹出到ecx中,然后将jmp ecx弹出到ret中。(或者通过push / ret不会破坏返回地址预测变量堆栈)。



1

JavaScript,35个字节

f=
function(){return arguments.length}

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


By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.