在 C 中用省略号传递参数

Parameter passing with ellipsis in C

我需要在 C 中实现我自己的 printf 函数。但是我在将省略号(“...”)功能与我的函数一起使用时遇到了问题(我们需要使用省略号)。

根据我的教科书,传递给带有省略号的函数的参数应该位于存储第一个参数的正上方的堆栈中。不过,我在堆栈的任何地方都找不到我传递的任何附加参数!这是我用来测试函数和输出的一些代码:

#include<stdio.h>
#include<stdlib.h>

int myprintf(char *str, ...)
{
    //test code to view data around str on the stack:
    int i;
    for (i = -8; i <= 8; i++)
    {
        char *ptr = str + (i * 4); // ptr will iterate through the 8 bytes above and
                                   // below str[0] on the stack
        printf ("Bytes from str: %+03d \t
                 Ptr Addr: %x \t
                 Val int: %d \t
                 Val char: %c \n",
                 (i*4), ptr, *ptr, *ptr);
    }

    //my implementation of printf...
}

int main(void)
{
    myprintf("test string", 1111, 2222, 3333);
}

这是 myprintf 函数的输出(堆栈的打印输出):

Bytes from str: -32    Ptr Addr: 8048922    Val int:         48    Val char: 0
Bytes from str: -28    Ptr Addr: 8048926    Val int:         97    Val char: a
Bytes from str: -24    Ptr Addr: 804892a    Val int:        104    Val char: h
Bytes from str: -20    Ptr Addr: 804892e    Val int:         32    Val char:  
Bytes from str: -16    Ptr Addr: 8048932    Val int:          0    Val char: 
Bytes from str: -12    Ptr Addr: 8048936    Val int:        118    Val char: v
Bytes from str: -08    Ptr Addr: 804893a    Val int:         93    Val char: ]
Bytes from str: -04    Ptr Addr: 804893e    Val int:         37    Val char: % <- my other variables should be here and above
Bytes from str: +00    Ptr Addr: 8048942    Val int:        116    Val char: t <-"test string"
Bytes from str: +04    Ptr Addr: 8048946    Val int:         32    Val char:   <-" string"
Bytes from str: +08    Ptr Addr: 804894a    Val int:        105    Val char: i <-"ing"
Bytes from str: +12    Ptr Addr: 804894e    Val int:          0    Val char: 
Bytes from str: +16    Ptr Addr: 8048952    Val int:          3    Val char: 
Bytes from str: +20    Ptr Addr: 8048956    Val int:          0    Val char: 
Bytes from str: +24    Ptr Addr: 804895a    Val int:          0    Val char: 
Bytes from str: +28    Ptr Addr: 804895e    Val int:         -1    Val char: �
Bytes from str: +32    Ptr Addr: 8048962    Val int:          0    Val char:

据我了解,值 1111、2222 和 3333 应该位于此堆栈打印输出中的某个位置。当我进行函数调用时,这些值会去哪里?

编辑:我无法在我的实现中使用 "stdarg.h" 库。

您正在尝试编写可变参数(有时称为可变参数)函数。幸运的是,有标准的 C 库函数可以帮助您管理在堆栈上传递的参数。

您需要 va_start va_endva_nextva_copy 来帮助您。给出的典型例子是:

#include <stdio.h>
#include <stdarg.h>

void
foo(char *fmt, ...)
{
    va_list ap;
    int d;
    char c, *s;

   va_start(ap, fmt);
    while (*fmt)
        switch (*fmt++) {
        case 's':              /* string */
            s = va_arg(ap, char *);
            printf("string %s\n", s);
            break;
        case 'd':              /* int */
            d = va_arg(ap, int);
            printf("int %d\n", d);
            break;
        case 'c':              /* char */
            /* need a cast here since va_arg only
               takes fully promoted types */
            c = (char) va_arg(ap, int);
            printf("char %c\n", c);
            break;
        }
    va_end(ap);
}

在此处查看文档:http://linux.die.net/man/3/va_start

如果您不必与标准兼容,GCC 甚至有一个函数属性,您可以将其应用于该函数以指示您需要 printf 样式格式说明符。如果你使用它,GCC 甚至可以为你做一些类型检查并发出警告,就好像你的可变参数函数是 printf 一样。它对于编写日志函数等很方便。它已经存在了很长时间。 https://gcc.gnu.org/onlinedocs/gcc-5.3.0/gcc/Function-Attributes.html

你的错误在这里:

char *ptr = str + (i * 4); // ptr will iterate through the 8 bytes above and

这不是您想要的。您正在查看字符串周围的内存,而不是指向字符串的指针周围的内存。将其更改为

char *ptr = &str + (i * 4); // ptr will iterate through the 8 bytes above and

正如@Joe所说,除了非常专业的需求,这是解决问题的错误方法,你应该使用stdarg.h。

这个程序的行为可能不符合规范运行,但是对于这个从堆栈中搜索参数的实验,你必须移动指针指向 参数而不是指针 作为参数传递

#include<stdio.h>
#include<stdlib.h>

int myprintf(char *str, ...)
{
    //test code to view data around str on the stack:
    int i;
    for (i = -8; i <= 8; i++)
    {
        /* add & before str to get the address of the argument */
        char *ptr = (char*)&str + (i * 4); // ptr will iterate through the 4 * 8 bytes above and
                                   // below str on the stack
        printf ("Bytes from str: %+03d \t"
                 "Ptr Addr: %p \t"
                 "Val int: %d \t"
                 "Val char: %02X \n",
                 (i*4), (void*)ptr, *(int*)ptr, (unsigned int)(unsigned char)*ptr);
    }

    //my implementation of printf...
    return 0;
}

int main(void)
{
    myprintf("test string", 1111, 2222, 3333);
    return 0;
}

我本地环境的结果:

Bytes from str: -32     Ptr Addr: 0028FF10  Val int: 213    Val char: D5 
Bytes from str: -28     Ptr Addr: 0028FF14  Val int: 1662423109     Val char: 45 
Bytes from str: -24     Ptr Addr: 0028FF18  Val int: -2     Val char: FE 
Bytes from str: -20     Ptr Addr: 0028FF1C  Val int: 1972834658     Val char: 62 
Bytes from str: -16     Ptr Addr: 0028FF20  Val int: 2130567168     Val char: 00 
Bytes from str: -12     Ptr Addr: 0028FF24  Val int: 0  Val char: 00 
Bytes from str: -08     Ptr Addr: 0028FF28  Val int: 0  Val char: 00 
Bytes from str: -04     Ptr Addr: 0028FF2C  Val int: 4227938    Val char: 62 
Bytes from str: +00     Ptr Addr: 0028FF30  Val int: 4235431    Val char: A7 
Bytes from str: +04     Ptr Addr: 0028FF34  Val int: 1111   Val char: 57 
Bytes from str: +08     Ptr Addr: 0028FF38  Val int: 2222   Val char: AE 
Bytes from str: +12     Ptr Addr: 0028FF3C  Val int: 3333   Val char: 05 
Bytes from str: +16     Ptr Addr: 0028FF40  Val int: 0  Val char: 00 
Bytes from str: +20     Ptr Addr: 0028FF44  Val int: 0  Val char: 00 
Bytes from str: +24     Ptr Addr: 0028FF48  Val int: 2686824    Val char: 68 
Bytes from str: +28     Ptr Addr: 0028FF4C  Val int: 4198992    Val char: 50 
Bytes from str: +32     Ptr Addr: 0028FF50  Val int: 1  Val char: 01 

正如@Joe 所说,使用 va_* 宏来读取可变数字参数。 否则,该程序可能无法在某些环境中运行——例如,x86_64 参数在寄存器中传递,而不是在堆栈中传递。

我做了一些修改

  • 扩大搜索范围运行扩大范围
  • 打印传递的字符串地址

和运行 Wandbox 上的代码。

Result变成了这样:

Bytes from str: -64     Ptr Addr: 0x7fff2308b498    Val int: -1473084125    Val char: 23 
Bytes from str: -60     Ptr Addr: 0x7fff2308b49c    Val int: 0  Val char: 00 
Bytes from str: -56     Ptr Addr: 0x7fff2308b4a0    Val int: 4196088    Val char: F8 
Bytes from str: -52     Ptr Addr: 0x7fff2308b4a4    Val int: 0  Val char: 00 
Bytes from str: -48     Ptr Addr: 0x7fff2308b4a8    Val int: 35     Val char: 23 
Bytes from str: -44     Ptr Addr: 0x7fff2308b4ac    Val int: 0  Val char: 00 
Bytes from str: -40     Ptr Addr: 0x7fff2308b4b0    Val int: -1473063104    Val char: 40 
Bytes from str: -36     Ptr Addr: 0x7fff2308b4b4    Val int: 32682  Val char: AA 
Bytes from str: -32     Ptr Addr: 0x7fff2308b4b8    Val int: -1470881080    Val char: C8 
Bytes from str: -28     Ptr Addr: 0x7fff2308b4bc    Val int: 32682  Val char: AA 
Bytes from str: -24     Ptr Addr: 0x7fff2308b4c0    Val int: 0  Val char: 00 
Bytes from str: -20     Ptr Addr: 0x7fff2308b4c4    Val int: 0  Val char: 00 
Bytes from str: -16     Ptr Addr: 0x7fff2308b4c8    Val int: 4195810    Val char: E2 
Bytes from str: -12     Ptr Addr: 0x7fff2308b4cc    Val int: 0  Val char: 00 
Bytes from str: -08     Ptr Addr: 0x7fff2308b4d0    Val int: 0  Val char: 00 
Bytes from str: -04     Ptr Addr: 0x7fff2308b4d4    Val int: 0  Val char: 00 
Bytes from str: +00     Ptr Addr: 0x7fff2308b4d8    Val int: 4196155    Val char: 3B 
Bytes from str: +04     Ptr Addr: 0x7fff2308b4dc    Val int: 0  Val char: 00 
Bytes from str: +08     Ptr Addr: 0x7fff2308b4e0    Val int: 587773152  Val char: E0 
Bytes from str: +12     Ptr Addr: 0x7fff2308b4e4    Val int: 32767  Val char: FF 
Bytes from str: +16     Ptr Addr: 0x7fff2308b4e8    Val int: -1477724000    Val char: A0 
Bytes from str: +20     Ptr Addr: 0x7fff2308b4ec    Val int: 5  Val char: 05 
Bytes from str: +24     Ptr Addr: 0x7fff2308b4f0    Val int: -1475311104    Val char: 00 
Bytes from str: +28     Ptr Addr: 0x7fff2308b4f4    Val int: 32682  Val char: AA 
Bytes from str: +32     Ptr Addr: 0x7fff2308b4f8    Val int: 1111   Val char: 57 
Bytes from str: +36     Ptr Addr: 0x7fff2308b4fc    Val int: 0  Val char: 00 
Bytes from str: +40     Ptr Addr: 0x7fff2308b500    Val int: 2222   Val char: AE 
Bytes from str: +44     Ptr Addr: 0x7fff2308b504    Val int: 0  Val char: 00 
Bytes from str: +48     Ptr Addr: 0x7fff2308b508    Val int: 3333   Val char: 05 
Bytes from str: +52     Ptr Addr: 0x7fff2308b50c    Val int: 0  Val char: 00 
Bytes from str: +56     Ptr Addr: 0x7fff2308b510    Val int: 4196064    Val char: E0 
Bytes from str: +60     Ptr Addr: 0x7fff2308b514    Val int: 0  Val char: 00 
Bytes from str: +64     Ptr Addr: 0x7fff2308b518    Val int: -1473063104    Val char: 40 
pointer passed = 4196155 0x40073b

如您所见,值在 Bytes from str: +00 上作为指针传递,但其他参数的值不接近该值。