首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >"__builtin_va_list“是如何实现的?

"__builtin_va_list“是如何实现的?
EN

Stack Overflow用户
提问于 2018-04-09 20:37:03
回答 3查看 6.5K关注 0票数 11

我想深入研究在macOS上用C语言实现函数"printf“。"printf“使用<stdarg.h>头文件。我打开<stdarg.h>文件,发现va_list只是一个宏。

所以,我真的很好奇__builtin_va_list是如何实现的?我知道它是特定于编译器的。在哪里可以找到__builtin_va_list的定义?我应该下载clang编译器的源代码吗?

EN

回答 3

Stack Overflow用户

发布于 2018-04-09 22:19:25

如果您查看libc源代码,就会发现这是printf的实现:

整型

__printf (const char *format, ...)
{
  va_list arg;
  int done;

  va_start (arg, format);
  done = vfprintf (stdout, format, arg);
  va_end (arg);

  return done;
}

这不能告诉你太多..。它是一个变量函数,至少有一个参数,格式...它使用va_start生成一个va_list,并将其交给vfprintf来完成这项繁重的工作。

它这样做是为了可以在所有printf流函数之间分担繁重工作,但这里是来自git://sourceware.org/git/glibc.git的libc源代码版本中的vfprintf,但是该文件很难理解,因为它是使用许多宏实现的。

但是,您可以通过扫描一个格式(这里我们不扫描一个格式,但只信任传入的参数编号)函数并对args执行操作来编写您自己的可变变量

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

void printNStrings( int n, ...)
{
    va_list l;
    va_start(l,n);
    for (int i=0; i< n; i++)
    {
        char * arg = va_arg(l, char *);
        puts(arg);
    }
    va_end(l);

}


int main(void)
{
    printNStrings(2,"hello", "world");
    printNStrings(3, "how", "are", "you");
    return 0;

}

应该注意的是,这是一个危险的提议。因为va_arg只是从堆栈中拉出一个参数,并且没有任何状态可以存储有关边界的信息,所以:printNStrings(4, "how", "are", "you");很快就会进入堆栈溢出和UB区域。编译器本身已经对printf家族函数进行了边界检查...您可以使用如下声明在流行的编译器上调用它们:void mylogger(const char *format, ...) __attribute__((format(printf, 1, 2)));,它将打开对传递到记录器中的类型的警告。

我仍然在寻找va_list在glibc中的实际实现,以作为参考。

票数 1
EN

Stack Overflow用户

发布于 2018-07-25 17:28:45

这个答案,对于clang,只是展示了我如何找到一个内置函数的实现。

我对std::atomic<T>的实现很感兴趣。如果T不是一个简单类型,那么clang使用锁来保护它的原子性。首先,我找到了一个名为__c11_atomic_store的内置函数。问题是,这个内置函数是如何在clang中实现的?

在clang代码库中搜索Builtin,在clang/Basic/Builtins.def中查找

// Some of our atomics builtins are handled by AtomicExpr rather than
// as normal builtin CallExprs. This macro is used for such builtins.
#ifndef ATOMIC_BUILTIN
#define ATOMIC_BUILTIN(ID, TYPE, ATTRS) BUILTIN(ID, TYPE, ATTRS)
#endif

// C11 _Atomic operations for <stdatomic.h>.
ATOMIC_BUILTIN(__c11_atomic_init, "v.", "t")
ATOMIC_BUILTIN(__c11_atomic_load, "v.", "t")
ATOMIC_BUILTIN(__c11_atomic_store, "v.", "t")
ATOMIC_BUILTIN(__c11_atomic_exchange, "v.", "t")
...

关键字是AtomicExprCallExpr。然后我检查了AtomicExpr的构造函数的每一个调用者,但是没有发现任何有用的信息,所以我猜测,可能在解析阶段,如果解析器匹配一个内置函数调用,它会构造一个带有内置标志的CallExpr到AST,在代码生成阶段,它会发出实现。

查看CodeGen,我在lib/CodeGen/CGBuiltin.cppCodeGen/CGAtomic.cpp中找到了答案。

你可以查看CodeGenFunction::EmitVAArg,我希望这会对你有帮助。

票数 1
EN

Stack Overflow用户

发布于 2020-03-21 10:35:00

在Clang 9中,这是在

clang\lib\AST\ASTContext.cpp

调用图:

getVaListTagDecl
=>getBuiltinVaListDecl
=>CreateVaListDecl
=>Create***BuiltinVaListDecl
for example:
=>CreateCharPtrBuiltinVaListDecl
=>CreateCharPtrNamedVaListDecl
=>buildImplicitTypedef

当经过预处理的源代码中有__builtin_va_list时,编译器调用getVaListTagDecl来构建TypedefDecl AST节点并将其插入到AST中,TypedefDecl在任何源代码中都不存在,它是在构建过程中动态生成的,就好像源代码中有这样的内容:

typedef *** __builtin_va_list;
//for example
typedef char* __builtin_va_list;
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/49733154

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档