3

C (Linux OS) でプログラミングしています。ファイルを読み取り、そのファイル内の関数をチェックして、対応する関数名を出力する必要があります。これまでのところ、「{」の深度計算を使用して関数を識別するようにプログラムしました。__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];
            }  
        }
    }
}

このコードが正しいかどうかわかりません。このまま使おうかな。関数名を見つけるオプションはありますか?

4

9 に答える 9

3

あなたが読んでいるファイルはCソースファイルだと思います。

これを適切に実行したい場合(つまり、すべての機能を確実に認識したい場合)、これは簡単な作業ではありません。追加情報については、C / C ++関数のリスト(Unixでのコード分析)を参照してください。

特定のツールについては心配していません。プログラムしたいです。

それは確かに可能ですが、基本的には、DoxygenSynopsisなどのツールにすでに実装されているものと同様のC用のスキャナー/パーサーフロントエンドになります。{おそらく少し単純化して、いくつかのヒューリスティックを使用できます。たとえば、完全なコードを解析する必要はありません(たとえば、との間はスキップできます})。

それでも独自のアプローチを実装したい場合は、次の手順に従います。

  • いずれの場合も、最初にCファイルをCプリプロセッサで実行して、マクロを解決し、生のCコードを使用できるようにする必要があります。
  • 次に、基本的なコンパイラ構築手法、特にソースファイルのスキャンと解析、およびC文法について理解します。使用しているCバージョンに応じて、文法が異なることに注意してください。ISO / IEC 9899:TC2、Annex A1には、たとえばC99の文法が含まれています。上記のツールのソースコードを確認することも役立つはずです。
  • 入力をトークン化するスキャナーを実装し、関数名を認識するパーサーを実装します。前に述べた文法から、(6.9.1) function-definitionあなたが始めるべき生産用語です。
于 2012-12-17T06:41:10.443 に答える
3

関数の名前を見つけるために、単純な 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;
}
于 2012-12-19T06:53:03.473 に答える
1

Cが非常に多くの構文をサポートしているという理由だけで、Cのパーサーを作成することは困難です(不可能ではなく、困難です)。

を使用して関数を定義できます

  1. 標準のCスタイル、標準の返品タイプ
  2. typedef / enumなどの戻り型を含む標準のCスタイル(単純なパーサーでは簡単に識別できません。ファイル内にユーザー定義のデータ型のデータベースを構築する必要があります)
  3. Cマクロ(たとえば、Basileの回答を参照)
  4. 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. 誤検知が発生する可能性があります
于 2012-12-17T09:11:12.280 に答える
1

あなたの問題を解決するのに役立つと思います。ここにいくつかのリンクがあります:flexc grammar (lex)c grammar(bison)bison

于 2012-12-17T09:26:36.330 に答える
1

これを正しく行うのは非常に困難です。基本的に、これを正しく行うには ac コンパイラを実装する必要があります。これはまさに c コンパイラが行うことであり、これを行うには適切な文法定義とプリプロセッサが必要です。

于 2012-12-17T06:38:23.790 に答える
1

簡単な方法です。いくつかの仮定を行う場合は、ソース コードを読み込んでから、次のようにします。

  • すべてのプリプロセッサ ディレクティブを削除します (インクルード ファイルから関数を使用したくない場合、および関数に関連する可能性のある不安定なマクロを処理したくない場合は、行末でs が続く#define複数行に注意してください)。#define\

  • コメントをすべて削除します (ネストされた/*コメントに注意してください)。

  • 任意の文字列を に変換します""(エスケープされた\"文字列や複数行の文字列には注意してください)。

  • 文字を' 'or 何かに変換します (etc を取り除くには'{'、エスケープやその他のエスケープに注意して\'ください)。

  • すべての (ネストされた複数行の) コード ブロックを「トップ レベル」{}のペアに変換します。

  • ;との後にのみ改行を入れるようにテキストを再フォーマットします。 ただし、実際には関数定義の一部ではない場合に備えて、行内の単独の行を前の行に}結合することを除きます。;};

  • で終わる行をすべて削除します。;

私が何かを見逃していない限り、これで、すべての関数定義が 1 行に 1 つずつ、関数本体が に置き換えられたままになっているはずです{}

于 2012-12-17T13:35:13.060 に答える
0

If you can make use of gcc:

gcc -nostdinc -aux-info output demo.c

outputs only file functions (excluding standard libs)

NOTE: -nostdinc causes compile error

You can avoid compile error using sed

gcc -aux-info output demo.c
sed '/include/d' output
于 2012-12-17T10:11:27.803 に答える
0

正規表現を試して、対象の関数名が存在するかどうかを確認できると思います 。

正規表現の詳細については、この投稿を参照してください。C の正規表現: 例?

于 2012-12-17T06:21:09.047 に答える
0

どのようなファイルを読み取りますか? 任意の C ソース ファイルですか? そうであれば、プリプロセッサ マクロなど、さまざまな方法で関数を定義できます。たとえば、

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

C ファイルはDF(foo)関数を持っている可能性があり、関数を定義している可能性があります (ソース コードに が fooprint出現することはありません)。fooprint

コンパイラから見た一連の関数名を処理したい場合は、コンパイラの拡張機能またはプラグインを開発することをお勧めします。GCC では、その目的のためにMELT (GCC を拡張するためのドメイン固有言語) を使用できます。

オブジェクトファイルで定義された[グローバル]関数を見つけたい場合は、Linuxでコマンドを*.o使用できます。nmおそらく、共有オブジェクトファイルをdlopen(3)することも検討してください*.so

もちろん、これはすべてコンパイラーおよびシステム固有のものである可能性があります。

于 2012-12-17T06:24:16.277 に答える