【问题标题】:Clarification on function pointers in C [duplicate]C中函数指针的说明[重复]
【发布时间】:2015-09-16 16:37:36
【问题描述】:

以下代码来自 abo3.c 的示例 Insecure Programming — 另请参阅 Why cast extern puts to a function pointer (void(*)(char*))&puts?

int main(int argv,char **argc) {
    extern system,puts; 
    void (*fn)(char*)=(void(*)(char*))&system; // <==
    char buf[256];
    fn=(void(*)(char*))&puts;
    strcpy(buf,argc[1]);
    fn(argc[2]);
    exit(1);
}

特别是这一行:

void (*fn)(char*)=(void(*)(char*))&system;

我认为void (*fn)(char*) 听起来像一个 lambda,但我知道它不是。 那么,也许这只是一个带括号的游戏,其中void *fn(char*) 是一个函数的声明,而这个函数引用了system?但是为什么(char*) 参数没有名字呢?这是否允许?

【问题讨论】:

  • extern system, puts; 这行让我很担心。它声明有两个外部定义的整数(因为没有明确指定类型)。这些声明掩盖了&lt;stdlib.h&gt;&lt;stdio.h&gt; 中的声明。但是,在链接时,这些函数可能会用于满足引用。请注意,extern 行在 C99 及更高版本中是无效的——并且在预标准或 C89/C90 标准 C 中充其量是可疑的。
  • 您的声明 int main(int argv, char **argc) 在技术上并非无效,但 100% 是非正统的。整数参数通常称为argc(参数计数),char 双指针通常称为argv(参数向量)。如果您要使用非标准名称,请不要使用颠倒的名称,为了其他人的理智,请不要使用相反的名称!
  • 使用int (*fn)(char const *)=&amp;system;,最好避免强制转换。
  • @alk 如果您有正确的函数指针,则不需要强制转换。将一个函数转换为另一个函数是不好的。我知道&amp; 是不必要的。
  • @NightSkyCode 绝对不是。这些问题的标题几乎相同,但内容却完全不同。此外,以重复的方式关闭一个坏问题(得分-1)并不是一件好事。关闭作为重复应该指出好的问题和信息在哪里(这就是为什么你不应该在这样做时考虑日期;一个较旧的问题可能比一个问同样问题的新问题更糟糕)。

标签: c


【解决方案1】:

它将变量 fn 声明为函数指针(指向具有 char * 类型的一个参数并且不返回任何内容 (void) 的函数。

这个变量用system的地址初始化——见http://linux.die.net/man/3/system。如本页所述,这将需要给定的演员表

【讨论】:

  • 由于system() 返回int 而不是void,这是UB吗?
  • @Kevin - 为什么你认为这是未定义的行为?
  • 因为您将函数转换为错误的类型。它不是(void(*)(char*))The converse cast is illegal,至少。
  • system地址,不是值。
  • 糟糕 - 错过了 &amp; - 这不是必需的。但是代码又是废话
【解决方案2】:

那么,也许这只是一个带括号的游戏,其中 void *fn(char *) 是一个函数的声明,这个函数是引用系统,我认为

void (*fn)(char *) 不是函数,它是函数指针。 You can refer here to learn about function pointers.。而() 在这里很重要,你不能忽视它们。

但是为什么参数(char*)没有名字呢?这是允许的吗?

是的,可以这样做。它的名字并不重要,但它的类型很重要。

【讨论】:

  • void *fn(char *) 没有定义函数指针。函数指针定义为void (*fn)(char *)
  • @alk 实际上这是他在那一行中问的,他错过了问题代码中存在的()。我会说得更清楚。
【解决方案3】:

它是一个纯粹而简单的函数指针,它被分配给system调用的地址

【讨论】:

    【解决方案4】:
    void (*fn)(char*)=(void(*)(char*))&system;
    

    该行获取错误声明符号system 的地址(应该是int(const char*),而不是隐式int),将其转换为fn 的类型并初始化该新的局部变量。
    类型是 void(*)(char*) 或指向接收单个 char* 且不返回任何内容的函数的指针。


    Well, that code is all kinds of bad:

    1. main 的前两个参数的传统命名被颠倒了:

      int main(int argv,char **argc)
      
    2. 使用“隐式 int”将标准库函数 systemputs 声明为 ints。这甚至不是错误的函数类型,而是数据类型!

      extern system,puts;
      
    3. 将符号地址转换为函数指针几乎是明智的。现在,只要它是正确的类型……无论如何,在一个数据指针和代码指针大小相同的盒子上,它可能不会丢失任何信息。

      void (*fn)(char*)=(void(*)(char*))&system; // <==
      fn=(void(*)(char*))&puts;
      
    4. 检查argv [!] 是否至少为 2 真的应该在这里省略:

      strcpy(buf,argc[1]);
      
    5. 通过错误类型的函数指针调用函数是 UB。不要那样做。此外,检查 argv[!] 在此之前是否至少为 3 不是 可选的。 fn(argc[2]);

    6. 依赖strcpyexit 的隐式声明也很糟糕。两者都没有与此一致的原型! (他们不返回int

    【讨论】:

      【解决方案5】:

      首先你要声明一个指向system的函数指针。

      然后你把它全部扔掉,重新定义它指向puts

      然后您似乎尝试为int 建立索引,但您颠倒了main 的通常命名约定,即

      int main (int argc, char **argv)
      

      【讨论】:

      • Sir argc 被标记为 char ** argc 。这可能令人困惑。
      • 为什么有人会以这样的方式结束 argcargv
      • @ameyCU 很好看,他颠覆了main 的常用参数命名约定。
      • @Tommy 这是菜鸟的错误(非常菜鸟的错误)
      【解决方案6】:

      该行声明了一个函数指针的变量,它接受一个参数并且不返回任何内容(void),这与对相同函数原型的类型转换一起需要,因为 extern 函数的类型类似于 void* 指针,它们在编译期间没有被早期绑定。

      【讨论】:

        【解决方案7】:

        它是一个指向函数的指针。如果您尝试在 C 语言中创建一个使用文本菜单的应用程序,我将向您展示,而 switch 我将使用指向函数的指针:

        #include <stdio.h>
        #include<unistd.h>
        
        void clearScreen( const int x );
        int exitMenu( void );
        int mainMenu( void );
        int updateSystem( void );
        int installVlcFromPpa( void );
        int installVlcFromSource( void );
        int uninstallVLC( void );
        int chooseOption( const int min, const int max );
        void showMenu( const char *question, const char **options, int (**actions)( void ), const int length );
        int installVLC( void );
        int meniuVLC( void );
        void startMenu( void );
        
        int main( void ){
            startMenu();
            return 0;
        }
        
        void clearScreen( const int x ){
            int i = 0;
            for( ; i < x ; i++ ){
                printf( "\n" );
            }
        }
        
        int exitMenu( void ) {
            clearScreen( 100 );
            printf( "Exiting... Goodbye\n" );
            sleep( 1 );
            return 0;
        }
        
        int mainMenu( void ){
            clearScreen( 100 );
            printf( "\t\t\tMain Manu\n" );
            return 0;
        }
        
        int updateSystem( void ) {
            clearScreen( 100 );
            printf( "System update...\n" );
            sleep( 1 );
            return 1;
        }
        
        int installVlcFromPpa( void ) {
            clearScreen( 100 );
            printf("Install VLC from PPA \n");
            sleep( 1 );
            return 0;
        }
        
        int installVlcFromSource( void ) {
            clearScreen( 100 );
            printf( "Install VLC from Source \n" );
            sleep( 1 );
            return 0;
        }
        
        int uninstallVLC( void ) {
            clearScreen( 100 );
            printf( "Uninstall VLC... \n" );
            sleep( 1 );
            return 1;
        }
        
        int chooseOption( const int min, const int max ){
            int option,check;
            char c;
        
            do{
                printf( "Choose an Option:\t" );
        
                if( scanf( "%d%c", &option, &c ) == 0 || c != '\n' ){
                    while( ( check = getchar() ) != 0 && check != '\n' );
                    printf( "\tThe option has to be between %d and %d\n\n", min, max );
                }else if( option < min || option > max ){
                    printf( "\tThe option has to be between %d and %d\n\n", min, max );
                }else{
                    break;
                }
            }while( 1 );
        
            return option;
        }
        
        void showMenu( const char *question, const char **options, int ( **actions )( void ), const int length) {
            int choose = 0;
            int repeat = 1;
            int i;
            int ( *act )( void );
        
            do {
                printf( "\n\t %s \n", question );
        
                for( i = 0 ; i < length ; i++ ) {
                    printf( "%d. %s\n", (i+1), options[i] );
                }
        
                choose = chooseOption( 1,length );
                printf( " \n" );
        
                act = actions[ choose - 1 ];
                repeat = act();
        
                if( choose == 3 ){
                    repeat = 0;
                }
            }while( repeat == 1 );
        }
        
        int installVLC( void ) {
            clearScreen( 100 );
            const char *question = "Installing VLC from:";
            const char *options[10] = { "PPA", "Source", "Back to VLC menu" };
            int ( *actions[] )( void ) = { installVlcFromPpa, installVlcFromSource, mainMenu };
        
            size_t len = sizeof(actions) / sizeof (actions[0]);
            showMenu( question, options, actions, (int)len );
            return 1;
        }
        
        int meniuVLC( void ) {
            clearScreen( 100 );
            const char *question = "VLC Options";
            const char *options[10] = { "Install VLC.", "Uninstall VLC.", "Back to Menu." };
            int ( *actions[] )( void ) = { installVLC, uninstallVLC, mainMenu };
        
            size_t len = sizeof(actions) / sizeof (actions[0]);
            showMenu( question, options, actions, (int)len );
        
            return 1;
        }
        
        void startMenu( void ){
            clearScreen( 100 );
            const char *question = "Choose a Menu:";
            const char *options[10] = { "Update system.", "Install VLC", "Quit" };
            int ( *actions[] )( void ) = { updateSystem, meniuVLC, exitMenu };
        
            size_t len = sizeof(actions) / sizeof (actions[0]);
        
            showMenu( question, options, actions, (int)len );
        }
        

        编译并尝试。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2016-06-09
          • 2020-04-13
          • 2016-02-20
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-03-03
          相关资源
          最近更新 更多