【问题标题】:C program to find the function name of a c fileC程序查找c文件的函数名
【发布时间】:2012-12-17 06:18:54
【问题描述】:

我正在使用 C(Linux 操作系统)编程。我必须读取一个文件,检查该文件中的函数并打印相应的函数名称。到目前为止,我已经编程使用'{'的深度计算来识别函数。我知道__FUNCTION__ 预处理器指令用于打印当前文件的函数名。同样,是否有任何预处理器指令用于查找我们读取的文件的函数名称?我不关心任何特定的工具。我想让它编程。请指导我。提前致谢。

我已尝试实现此代码。此函数将行(在'{'之前)作为参数。

void ffname(char line[100])
{
    int i,j,m,n,f=0;
    char dt[10],fname[28];
    char s[5][10]={"int","void","struct","char","float"};
    dt = strtok(line," ");
    for(i=0;i<5;i++)
    {
        m=strcmp(dt,s[i]);
        if(m==0)
        {
            f=1;
            n=strlen(dt);
        }
    }
    if(f)
    {
        for(i=n+2,j=0;i<strlen(line);i++,j++)
        {
            if(line[i] == '*')
                i++;
            while(line[i] != '(')
            {
                fname[j]=line[i];
            }  
        }
    }
}

我不知道这段代码是否正确。我要这样使用吗?有没有办法找到函数名?

【问题讨论】:

  • C 预处理器使事情变得相当复杂。您可能想调用预处理器,然后检查删除预处理器指令后生成的文件。
  • 编码标准可以使这项任务变得简单得多。只需要函数的大括号左对齐,其他所有内容都需要缩进,并且您有一种简单的方法来检测函数 - 左对齐左大括号之前的东西:-)如果您还需要一个空行在函数类型和签名之前,您可以收集函数声明和开括号之间的所有内容——也就是说,如果您可以断言编码标准。漂亮的打印机可以将您现有的代码转换为符合标准的代码。

标签: c function c-preprocessor preprocessor-directive


【解决方案1】:

我使用简单的 C 代码来查找函数的名称。

#include <stdio.h>
#include <string.h>

#define SIZE 1024
void ffname(char *line)
{
    int i=1,j=0;
    char *dt; 
    char name[SIZE];
    strtok(line,"("); 
    dt = strchr(line,' '); 
    if(dt[i] == '*')
        i++;
    while(dt[i] != '\0')
    {
        name[j]=dt[i];
        i++;
        j++;
    }
    name[j] ='\0';
    printf("Function name is: %s\n", name);
}

int main(int argc, char **argv)
{
    if(argc < 2)
    {
        printf("Give the filename \n");
        printf("Usage: %s filename\n", argv[0]);
        return -1;
    }
    int i, lines =0, funlines =0,count =0, fn =0, flag =0;
    char c[SIZE],b[SIZE];
    FILE *fd;
    fd = fopen(argv[1],"r");
    while(fgets(c,SIZE,fd))
    {   
        lines++;
        i=0;
        for(i=0;i<strlen(c);i++)
        {
            while( c[i] =='\t' || c[i] == ' ')
            {
                i++;
            }
            if( c[i] == '{')
            {
                count++;
                if(flag)
                {
                    funlines++;
                }
                if(count == 1)
                {
                    fn++;
                    printf("Function %d is Started..............\n", fn); 
                    flag = 1;
                    ffname(b);
                }
                break;
            }
            else if( c[i] == '}')
            {
                count--;
                if(!count)
                { 
                    flag = 0;
                    printf("No of lines in the function %d is: %d\n", fn, funlines);
                    printf("Function %d is finished..........\n", fn);
                    funlines = 0;
                }
                else
                {
                    funlines++;
                }
                break;
            }
            else if(flag)
            {
                funlines++;
                break;
            }
        }
        strcpy(b,c);
    }
    printf("Total no of function%d\n",fn);
    printf("Total no of lines%d\n",lines);
    return 0;
}

【讨论】:

    【解决方案2】:

    我假设你正在阅读的文件是一个 C 源文件。

    如果你想正确地完成它(意味着,如果你想可靠地识别所有功能),这不是一项简单的任务。有关更多信息,请参阅Listing C/C++ functions (Code analysis in Unix)

    我不关心任何特定的工具。我想让它编程。

    这当然是可能的,但你最终会得到一个 C 的扫描器/解析器前端,类似于 DoxygenSynopsis 等工具中已经实现的。您可以稍微简化一下并使用一些启发式方法,例如您不需要解析完整的代码(例如,您可以跳过 {} 之间的任何内容)。

    如果您仍想实现自己的方法,我会按照以下步骤操作:

    • 在任何情况下,您都应该首先通过 C 预处理器运行您的 C 文件,以解析任何宏并使原始 C 代码可用。
    • 然后熟悉基本的Compiler Construction 技术,尤其是扫描和Parsing 源文件,以及C 语法。请注意,有不同的语法,具体取决于您使用的 C 版本。例如,ISO/IEC 9899:TC2, Annex A1 包含 C99 的语法。查看上述工具的源代码也会有所帮助。
    • 实施扫描器以标记您的输入,并实施识别函数名称的解析器。从我之前提到的语法来看,(6.9.1) function-definition 是你应该开始使用的产生式术语。

    【讨论】:

      【解决方案3】:

      这很难正确地做到。基本上,您需要实现一个 c 编译器才能正确执行此操作。这正是 c 编译器所做的,并且需要适当的语法定义和预处理器来执行此操作。

      【讨论】:

        【解决方案4】:

        为 C 编写一个解析器很困难(不是不可能,很困难),仅仅是因为 C 支持这么多语法。

        你可以定义一个函数使用

        1. 标准 C 样式,带有标准返回类型
        2. 标准 C 风格,带有 typedef/enum 等返回类型(不能用简单的解析器轻松识别。您需要在文件中构建用户定义数据类型的数据库)
        3. C 宏(例如参考 Basile 的 answer
        4. Assembly(通过gcc -S解析一个非常简单的test.c来了解语法)我已经用这种方法创建了一些占位符函数。

        因此,您可以更轻松地解析汇编文件,而不是解析 C 文件。

        例如gcc -S 将 C 函数定义翻译如下:

            .globl  someFnName
            .type   someFnName, @function
        someFnName:
            ...function-body related code...
        

        如果您只想要函数名称列表(即不需要参数/返回值等),与 C 文件相比,您可以轻松地在汇编中解析上述 3 行代码。
        如果您还添加-g 开关和-s,您还将获得一些行号信息。

        优点:

        1. 比 C 文件更容易解析
        2. 负责定义函数的大多数(如果不是全部)方法。
        3. 根据“.globl someFnName”行是否存在,您可以隔离静态函数。

        缺点:

        1. 需要外部解析器 - gcc 或其他一些
        2. 需要编译器 (gcc) 依赖的辅助解析器
        3. 可能会出现一些误报

        【讨论】:

          【解决方案5】:

          我认为flexbison 可以帮助您解决问题,这里有一些链接:c grammar(lex),c grammar(bison)

          【讨论】:

          • 构建词法分析器和解析器是最好的方法,但需要相当多的知识。此处插入了更多详细信息,以解释如何使用语法注释来发出函数名称、返回类型和签名。
          【解决方案6】:

          简单的方法,如果你愿意做一些假设,请阅读源代码,然后:

          • 删除任何预处理指令(假设您不希望包含文件中的函数,并且不想处理任何可能与函数相关的不稳定的 #define 宏,请注意多行 #defines 继续 @987654323 @ 在行尾)。

          • 删除所有 cmets(注意嵌套的 /* cmets)。

          • 将任何字符串转换为""(注意转义的\" 和多行字符串)。

          • 将任何字符转换为' ' 或其他字符(要摆脱'{' 等,请注意转义的\' 以及其他转义)。

          • 将所有(嵌套、多行)代码块转换为“顶级”{} 对。

          • 将文本重新格式化为仅在 ;} 之后有换行符,除非在一行中加入一个单独的 ;,以防它实际上是 }; 的一部分,它们不起作用定义。

          • 删除所有以;结尾的行

          除非我遗漏了什么,现在你应该留下所有的函数定义,每行一个,函数体替换为{}

          【讨论】:

          • 谢谢。你的算法真的很有用。
          • @Dhasneem 谢谢。稍作修改,最重要的是倒数第二个项目符号。
          【解决方案7】:

          我觉得你可以试试 正则表达式查找目标函数名是否存在。

          您可以在这篇文章中找到更多关于正则表达式的信息。 Regular expressions in C: examples?

          【讨论】:

          • 不,您不能使用正则表达式在任意 c 文件中查找函数。正则表达式不擅长寻找匹配的打开/关闭符号。你需要某种语法……比如 c 编译器。
          【解决方案8】:

          你读什么类型的文件?它是一些任意的 C 源文件吗?如果是,它可以以许多不同的方式定义函数,例如通过预处理器宏。例如与

          #define DF(Nam) void Nam##print(void) {puts(#Nam);}
          

          一个 C 文件可能有 DF(foo) 并定义了函数 fooprint(在源代码中没有出现任何 fooprint)。

          如果您想处理编译器看到的一组函数名称,最好开发一个编译器扩展或插件。使用 GCC,您可以为此目的使用 MELT(一种扩展 GCC 的领域特定语言)。

          如果您想查找某个目标文件*.o 定义的[全局] 函数,您可以在Linux 上使用nm 命令。或许还可以考虑dlopen(3)-ing 共享对象文件*.so

          当然,所有这些都可能是编译器和系统特定的。

          【讨论】:

          • 就我而言,我将读取任何类型的 C 文件,这些文件可能被定义为预处理器宏,也可能不被定义为预处理器宏。有没有查找函数名的伪代码?
          • 不......因为预处理器技巧可能会做一些奇怪的事情,正如我所展示的。如果你想读取任何 C 文件,你最好扩展编译器来处理它。
          【解决方案9】:

          如果可以使用 gcc:

          gcc -nostdinc -aux-info output demo.c
          

          仅输出文件函数(不包括标准库)

          注意:-nostdinc 导致编译错误

          您可以使用 sed 避免编译错误

          gcc -aux-info output demo.c
          sed '/include/d' output
          

          【讨论】:

            猜你喜欢
            • 2012-12-03
            • 1970-01-01
            • 2012-02-11
            • 2015-06-09
            • 2017-02-22
            • 1970-01-01
            • 1970-01-01
            • 2010-11-26
            • 1970-01-01
            相关资源
            最近更新 更多