C 语言迷思 - 最短的函数声明调用?
这篇文章源于 V2EX 上的一个 [c 语言谜题] 程序填空,老程序员来做做。原题是这样的
#include <stdio.h> int main() { [] printf("%p\n", *********************************p); return 0; }
在上面的 C 语言代码 []
标记内加入一条语句,使得:
- 程序编译通过,无错误(警告随意);链接通过,无错误(警告随意)
- 程序能够运行,无崩溃和运行时错误
- 源代码为
.c
文件 - 如果用
vs,
则使用 Release 配置;用 gcc 也行 - 加入的语句不能忽略分号
printf
一句必须能够正常输出内容
问:
- 要加入的语句的长度最小是几(含分号、空白、字符、符号等)?
- 这个最小长度的语句的 sha256 哈希值是多少?
ps:为了确保为数不多的趣味性,请勿直接贴出语句
看到这个题目,映入眼帘的第一个答案就是能否声明一个简短的变量,要做到字符最短,肯定使用最短的关键字,那么,肯定是 int
。因为输出是 ***p
,所以变量名必须是 p
。所以,我们第一次给的答案就是下面这样
#include <stdio.h> int main() { int p; printf("%p\n", *********************************p); return 0; }
编译肯定报错,不行你看
[root@localhost ~]# gcc main.c main.c:5:81: error: indirection requires pointer operand ('int' invalid) printf("%p\n", *********************************p); ^~ 1 error generated
赋予一个值编译也会报错,因为我们并没有修正这个错误
#include <stdio.h> int main() { int p=9; printf("%p\n", *********************************p); return 0; }
声明一个指针也会报错
#include <stdio.h> int main() { int*p=9; printf("%p\n", *********************************p); return 0; }
声明一个指针然后赋值也会报错
#include <stdio.h> int main() { int*p=9; printf("%p\n", *********************************p); return 0; }
如果声明为一个函数呢?最短的函数肯定是没有返回值的,所以,我们定义如下
#include <stdio.h> int main() { p(){}; printf("%p\n", *********************************p); return 0; }
编译结果如下
main.c:4:5: warning: implicit declaration of function 'p' is invalid in C99 [-Wimplicit-function-declaration] p(){}; ^ main.c:4:8: error: expected ';' after expression p(){}; ^ ; 1 warning and 1 error generated.
注意那个 main.c:4:8: error: expected ';' after expression
导致这个错误的发生,是因为我们没有给函数添加返回值类型。那就加上试一试呗
#include <stdio.h> int main() { int p(){}; printf("%p\n", *********************************p); return 0; }
然而,在最新的编译器上,这是不通过的,因为这是一个函数定义,C 语言是不允许在函数内再定义一个函数的。
main.c:4:12: error: function definition is not allowed here int p(){}; ^ main.c:5:82: error: use of undeclared identifier 'p' printf("%p\n", *********************************p); ^ 2 errors generated.
我的 gcc 版本如下
gcc --version Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1 Apple LLVM version 9.1.0 (clang-902.0.39.1) Target: x86_64-apple-darwin17.7.0 Thread model: posix InstalledDir: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin
既然不能声明一个函数,那我们可以声明一个指向函数的指针对吧
#include <stdio.h> int main() { int(*p)(); printf("%p\n", *********************************p); return 0; }
然后,使用编译和运行都通过了。
其实,还有比这个长的方法,等待大家自己去挖掘吧,我就不一一列举了。
如何调用一个函数
你是不是很疑惑,为什么一个指向函数的指针可以无限的解引用呢 ?
*********************************p;
说到这里,不得不吐槽下 C 语言坑爹的知识点,
「 对于函数指针 p,直接 p()
可以调用,(*p)()
也可以调用 」
所以,看起来,其实 p === *p
啊,按照某个定律 (等等,哪个定律来的,)*p === **p
,以此类推,多少个 *
也是可行的。
我们可以写一个程序实验下
#include <stdio.h> int main() { int(*p)(); printf("%p\n",p); printf("%p\n",*p); printf("%p\n",**p); printf("%p\n",***p); return 0; }
运行的结果如下
0x7ffee14918f8 0x7ffee14918f8 0x7ffee14918f8 0x7ffee14918f8
因为
*p
指向的是地址,所以,你的值可能和我不一样,但是,四个值肯定是一样的。
后记
如果你是初学者,不知道如何定义一个函数指针,可以访问我们的 C 语言基础教程:函数指针