__weak是一个宏,和__packed是同一种东西都是gcc的扩展属性:
#define __packed __attribute__((packed))
#define __weak __attribute__((weak))

如果这个关键字用在函数定义上面,一般情况下和一般函数没有两样。但是当有一个同名函数但是不带__weak被定义时,所有对这个函数的调用都是指向后者(不带__weak那个), 如果有两个一样的函数都用了__weak,那么真正调用那个,就要看连接器了。

一、概述

在C语言中,函数和初始化的全局变量(包括显示初始化为0)是强符号,未初始化的全局变量是弱符号。

对于它们,下列三条规则使用:

① 同名的强符号只能有一个,否则编译器报"重复定义"错误。

② 允许一个强符号和多个弱符号,但定义会选择强符号的。

③ 当有多个弱符号相同时,链接器选择占用内存空间最大的那个。

二、哪些符号是弱符号?

我们经常在编程中碰到一种情况叫符号重复定义。多个目标文件中含有相同名字全局符号的定义,那么这些目标文件链接的时候将会出现符号重复定义的错误。比如我们在目标文件A和目标文件B都定义了一个全局整形变量global,并将它们都初始化,那么链接器将A和B进行链接时会报错:

  1. 1 b.o:(.data+0x0): multiple definition of `global'
  2. 2 a.o:(.data+0x0): first defined here

这种符号的定义可以被称为强符号(Strong Symbol)。有些符号的定义可以被称为弱符号(Weak Symbol)对于C语言来说,编译器默认函数和初始化了的全局变量为强符号,未初始化的全局变量为弱符号(C++并没有将未初始化的全局符号视为弱符号)。我们也可以通过GCC的"__attribute__((weak))"来定义任何一个强符号为弱符号。注意,强符号和弱符号都是针对定义来说的,不是针对符号的引用。比如我们有下面这段程序:

  1. extern int ext;
  2. int weak1;
  3. int strong = 1;
  4. int __attribute__((weak)) weak2 = 2;
  5. int main()
  6. {
  7. return 0;
  8. }

上面这段程序中,"weak"和"weak2"是弱符号,"strong"和"main"是强符号,而"ext"既非强符号也非弱符号,因为它是一个外部变量的引用。

下面一段话摘自wikipedia:

In computing, a weak symbol is a symbol definition in an object file or dynamic library that may be overridden by other symbol definitions. Its value will be zero if no definition is found by the loader.

换句话说,就是我们可以定义一个符号,而该符号在链接时可以不解析。

让我们再看一些例子:

  1. $ cat err.c
  2. int main(void)
  3. {
  4. f();
  5. return 0;
  6. }

很明显,不能编译通过,因为'undefined reference' :

  1. $ gcc err.c
  2. /tmp/ccYx7WNg.o: In function `main':
  3. err.c:(.text+0x12): undefined reference to `f'
  4. collect2: ld returned 1 exit status

那么,如果将符号f声明成弱符号,会怎么呢?

  1. $ cat weak.c
  2. void __attribute__((weak)) f();
  3. int main(void)
  4. {
  5. if (f)
  6. f();
  7. return 0;
  8. }
  9. $ gcc weak.c

居然编译通过了,甚至成功执行!让我们看看为什么?

首先声明了一个符号f(),属性为weak,但并不定义它,这样,链接器会将此未定义的weak symbol赋值为0,也就是说f()并没有真正被调用,试试看,去掉if条件,肯定core dump!

我们可以通过nm来查看符号:

  1. $ nm a.out
  2. ...
  3. w f
  4. 08048324 T main
  5. ...

如果我们在另一个文件中定义函数f,与week.c一起编译链接,那么函数f就会正确的被调用!

  1. $ cat f.c
  2. #include <stdio.h>
  3. void f(void)
  4. {
  5. printf("hello from f\n");
  6. }
  1. $ gcc -c weak.c f.c
  2. $ gcc -o weak weak.o f.o
  3. $ ./weak
  4. hello from f
  5. $ nm weak.o
  6. w f
  7. 00000000 T main
  8. $ nm f.o
  9. 00000000 T f
  10. U puts
  11. $ nm weak
  12. ...
  13. 08048384 T f
  14. 08048354 T main
  15. U puts@@GLIBC_2.0
  16. ...

我们甚至可以定义强符号来override弱符号:

  1. $ cat orig.c
  2. #include <stdio.h>
  3. void __attribute__((weak)) f()
  4. {
  5. printf("original f..\n");
  6. }
  7. int main(void)
  8. {
  9. f();
  10. return 0;
  11. }
  12. $ gcc orig.c
  13. $ ./a.out
  14. original f..
  1. $ cat ovrd.c
  2. #include <stdio.h>
  3. void f(void)
  4. {
  5. printf("overridden f!\n");
  6. }
  7. $ gcc -c orig.c ovrd.c
  8. $ gcc -o ovrd orig.o ovrd.o
  9. $ ./ovrd
  10. overridden f!
  1. $ nm orig.o
  2. 00000000 W f
  3. 00000014 T main
  4. U puts
  5. $ nm ovrd.o
  6. 00000000 T f
  7. U puts
  8. $ nm ovrd
  9. ...
  10. 0804838c T f
  11. 08048368 T main
  12. U puts@@GLIBC_2.0
  13. ...

或者如下:

  1. $ cat orig-obj.c
  2. #include <stdio.h>
  3. int __attribute__((weak)) x = 1;
  4. int __attribute__((weak)) y = 1;
  5. int main(void)
  6. {
  7. printf("x = %d, y = %d\n", x, y);
  8. return 0;
  9. }
  10. $ gcc orig-obj.c
  11. $ ./a.out
  12. x = 1, y = 1
  1. $ cat ovrd-obj.c
  2. int x = 2;
  3. void f(void)
  4. {
  5. }
  6. $ gcc -c orig-obj.c ovrd-obj.c
  7. $ gcc -o ovrd-obj orig-obj.o ovrd-obj.o
  8. $ ./ovrd-obj
  9. x = 2, y = 1
  1. $ nm orig-obj.o
  2. 00000000 T main
  3. U printf
  4. 00000000 V x
  5. 00000004 V y
  6. $ nm ovrd-obj.o
  7. 00000000 T f
  8. 00000000 D x
  9. $ nm ovrd-obj
  10. ...
  11. 08048394 T f
  12. 08048354 T main
  13. U printf@@GLIBC_2.0
  14. 080495c8 D x
  15. 080495c4 V y
  16. ...

那么当出现multiple symbols时,会如何呢?

  1. $ cat mul.c
  2. int main(void)
  3. {
  4. f();
  5. return 0;
  6. }
  7. $ cat s1.c
  8. #include <stdio.h>
  9. void f(void)
  10. {
  11. printf("1st strong f from %s\n", __FILE__);
  12. }
  13. $ cat s2.c
  14. #include <stdio.h>
  15. void f(void)
  16. {
  17. printf("2nd strong f from %s\n", __FILE__);
  18. }
  19. $ cat w1.c
  20. #include <stdio.h>
  21. void __attribute__((weak)) f(void)
  22. {
  23. printf("1st weak f from %s\n", __FILE__);
  24. }
  25. $ cat w2.c
  26. #include <stdio.h>
  27. void __attribute__((weak)) f(void)
  28. {
  29. printf("2nd weak f from %s\n", __FILE__);
  30. }
  31. $ gcc -c mul.c s1.c s2.c w1.c w2.c
  1. $ gcc -o test1 mul.o s1.o s2.o
  2. s2.o: In function `f':
  3. s2.c:(.text+0x0): multiple definition of `f'
  4. s1.o:s1.c:(.text+0x0): first defined here
  5. collect2: ld returned 1 exit status
  6. $ gcc -o test2 mul.o s1.o w1.o w2.o
  7. $ ./test2
  8. 1st strong f from s1.c
  9. $ gcc -o test3-1 mul.o w1.o w2.o
  10. $ ./test3-1
  11. 1st weak f from w1.c
  12. $ gcc -o test3-2 mul.o w2.o w1.o
  13. $ ./test3-2
  14. 2nd weak f from w2.c

关于最后一个例子,我想补充的是:如果我们改变给出的编译顺序会怎么样呢?比如像下面这样编译:

  1. $ gcc -o test2 mul.o w1.o s1.o w2.o
  2. $ ./test2
  3. 1st strong f from s1.c

看,我将w1.o放在最前面,不过链接器依然选择强符号,这是我们所期望的。

不过,如果我们这样做:

  1. $ ar qs libw.a w1.o w2.o
  2. $ ar qs libs.a s1.o s2.o

再编译:

  1. $ gcc -o test2 mul.o -L. -lw -ls
  2. $ ./test2
  3. 1st weak f from w1.c

再改成这样编译:

  1. $ gcc -o test2 mul.o -L. -ls -lw
  2. $ ./test2
  3. 1st strong f from s1.c

看,情况有变!这是为什么?

原因就是GCC(准确地说是链接器)对待库是不一样的 —— 默认的,链接器使用第一个找到的符号,后面的就不搜索了。

不过我们也可以强制链接器搜索所有的库,办法如下:

  1. $ ar qs libw.a w1.o w2.o
  2. $ ar qs libs.a s1.o s2.o
  3. $ gcc -o test2 mul.o -L. -Wl,--whole-archive -lw -ls -Wl,--no-whole-archive
  4. ./libs.a(s2.o): In function `f':
  5. s2.c:(.text+0x0): multiple definition of `f'
  6. ./libs.a(s1.o):s1.c:(.text+0x0): first defined here
  7. collect2: error: ld returned 1 exit status

重新如下操作:

  1. $ rm libw.a libs.a
  2. $ ar qs libw.a w1.o w2.o
  3. $ ar qs libs.a s1.o
  4. $ gcc -o test2 mul.o -L. -Wl,--whole-archive -lw -ls -Wl,--no-whole-archive
  5. $ ./test2
  6. 1st strong f from s1.c

现在可以了,尽管-lw在前!

让我们再来看一个具体的例子:

  1. // main.c
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. extern int fun(void);
  5. int global_var1 = 0xff00ff00;       // 强符号
  6. int global_var2 = 0x00ff00ff;       // 强符号
  7. int main(int argc, const char *argv[])
  8. {
  9. /
  10. printf("in main.c: &global_var1 = %p", &global_var1);
  11. printf(" global_var1 = %x\n", global_var1);
  12. printf("sizeof(global_var1) = %d\n", sizeof(global_var1));
  13. /
  14. printf("in main.c: &global_var2 = %p", &global_var2);
  15. printf(" global_var2 = %x\n", global_var2);
  16. printf("sizeof(global_var2) = %d\n", sizeof(global_var2));
  17. /
  18. fun();
  19. printf("global_var1 = %x\n", global_var1);
  20. printf("global_var2 = %x\n", global_var2);
  21. return 0;
  22. }
  1. // test.c
  2. #include <stdio.h>
  3. #include <stdlib.h>
  4. #include <string.h>
  5. double global_var1;
  6. int fun(void)
  7. {
  8. printf("in test.c: &global_var1 = %p", &global_var1);
  9. printf(" global_var1 = %x\n", global_var1);
  10. printf("sizeof(global_var1) = %d\n", sizeof(global_var1));
  11. memset(&global_var1, 0, sizeof(global_var1));
  12. return 0;
  13. }

在gcc中编译得到如下结果:

我们可以看到,在main.c和test.c都有一个global_var1,在main.c中的为强符号,在test.c中的为弱符号。因为在test.c中global_var1没有初始化,所以根据规则②得知:编译器选择main.c中的global_var1的值初始化那片内存。不要误认为在test.c中使用global_var1时是用的main.c中的global_var1,我之前错误得这样认为。其实是这样的:main.c中的global_var1和test.c中的global_var1引用的时同一块内存区域,只是在两个文件中代表的意义不同  ---- 在main.c中代表一个int型变量,在test.c中代表一个double型变量,它们的起始地址相同,但占用内存空间是不同的, 在main.c中占用4个字节,在test.c中占用8个字节,这点从上图的两个sizeof输出结果中可以得到验证。

  1. (gdb) break main
  2. Breakpoint 1 at 0x804841d: file main.c, line 14.
  3. (gdb) run
  4. Starting program: /home/astrol/c/elf/dynamic/understand_weak_symbol_by_example/main
  5. Breakpoint 1, main (argc=1, argv=0xbffff6d4) at main.c:14
  6. 14              printf("in main.c: &global_var1 = %p", &global_var1);
  7. (gdb) print/x &global_var1
  8. $1 = 0x804a018
  9. (gdb) print/x &global_var2
  10. $2 = 0x804a01c
  11. (gdb) x/8xb &global_var1
  12. 0x804a018 <global_var1>:        0x00    0xff    0x00    0xff    0xff    0x00    0xff    0x00
  13. (gdb)

因为在test.c中的global_var1占用八个字节,memset(&global_var1, 0, sizeof(global_var1))将这块内存区域清零,这也就解释了为什么调用fun之后,global_var1和global_var2都变成0的缘故。

  1. (gdb) break 27
  2. Breakpoint 1 at 0x80484d2: file main.c, line 27.
  3. (gdb) run
  4. Starting program: /home/astrol/c/elf/dynamic/understand_weak_symbol_by_example/main
  5. in main.c: &global_var1 = 0x804a018 global_var1 = ff00ff00
  6. sizeof(global_var1) = 4
  7. in main.c: &global_var2 = 0x804a01c global_var2 = ff00ff
  8. sizeof(global_var2) = 4
  9. in test.c: &global_var1 = 0x804a018 global_var1 = ff00ff00
  10. sizeof(global_var1) = 8
  11. global_var1 = 0
  12. global_var2 = 0
  13. Breakpoint 1, main (argc=1, argv=0xbffff6d4) at main.c:27
  14. 27              return 0;
  15. (gdb) print/x &global_var1
  16. $1 = 0x804a018
  17. (gdb) print/x &global_var2
  18. $2 = 0x804a01c
  19. (gdb) x/8xb &global_var1
  20. 0x804a018 <global_var1>:        0x00    0x00    0x00    0x00    0x00    0x00    0x00    0x00
  21. (gdb)

可见在test.c中对global_var1的改动会影响main.c中global_var1和global_var2的值。当程序很大时,这种错误很难发现,所以尽量避免不同类型的符号在多个文件中

三、如何避免呢?

1、上策:想办法消除全局变量。全局变量会增加程序的耦合性,对他要控制使用。如果能用其他的方法代替最好。

2、中策:实在没有办法,那就把全局变量定义为static,它是没有强弱之分的。而且不会和其他的全局符号产生冲突。至于其他文件可能对他的访问,可以封装成函数。把一个模块的数据封装起来是一个好的实践。

3、下策:把所有的符号全部都变成强符号。所有的全局变量都初始化,记住,是所有的,哪怕初始值是0都行。如果一个没有初始化,就可能会和其他人产生冲突,尽管别人初始化了。(自己写代码测试一下)。

4、必备之策:GCC提供了一个选项,可以检查这类错误:-fno-common。

参考链接:

http://blog.csdn.net/chgaowei/article/details/7173436 (新手小心:c语言的强符号和弱符号)

http://www.embedded-bits.co.uk/2008/gcc-weak-symbols/ (GCC Weak Symbols)

http://write.blog.csdn.net/postedit/8008629 ( 什么是weak symbol?)

http://winfred-lu.blogspot.com/2009/11/understand-weak-symbols-by-examples.html (Understand Weak Symbols by Examples)

http://discusstolearn.blogspot.sg/2012/11/symbol-resolution-weak-symbols-how.html (Symbol Resolution, Weak Symbols, How compiler resolves multiple Global Symbols)

http://wanderingcoder.net/2012/06/30/multiply-defined-symbols/  ( Dealing with multiply defined symbols)

http://www.cnblogs.com/whos/archive/2010/10/20/1856274.html(弱符号与强符号概念)

http://www.searchtb.com/2013/03/compile_problems_about_strong_weak_symbols.html (分享两个强符号,弱符号引起的编译问题)

http://blog.csdn.net/glp_hit/article/details/8788963 (强符号 弱符号)

补充:

最近在看《程序员的自我修养》,知道现在的编译器和链接器支持一种叫COMMOM块(Common Block)的机制,这种机制用来解决 一个弱符号定义在多个目标文件中,而它们的类型又不同(即大小不同) 的情况。

目标文件中,编译器将未初始化的全局变量放在了COMMON段,未初始化的静态变量(包括全局和局部静态变量)放在BSS段。

---------------------------------------------------------------------------------------------------------------------------------------

对于全局变量来说,如果初始化了不为0的值,那么该全局变量存储在.data段;

如果初始化的值为0, 那么将其存储在.BSS;(依然是强符号)

如果没有初始化,那么编译时将其存储在COMMON块,等到链接时再将其放入到.BSS段。(这点不同的编译器会有所不同,有的编译器会直接把没有初始化的全局变量放在.BSS段,而没有COMMON块机制)

---------------------------------------------------------------------------------------------------------------------------------------

为什么这样处理呢?

我们可以想到,当编译器将一个编译单元编译成目标文件的时候,如果该编译单元包含了弱符号(未初始化的全局变量就是典型的弱符号),那么该弱符号最终所占空间的大小此时是未知的,因为有可能其他编译单元中同符号名称的弱符号所占的空间比本编译单元该符号所占的空间要大。所以编译器此时无法为该弱符号在BSS段分配空间,因为所需要的空间大小此时是未知的。但是链接器在链接过程中可以确定弱符号的大小,因为当链接器读取所有输入目标文件后,任何一个弱符号的最终大小都可以确定了,所以它可以在最终的输出文件的BSS段为其分配空间。所以总体来看,未初始化的全局变量还是被放在BSS段。       ------摘自《程序员的自我修养》

来看一个例子:

  1. /* aa.c  */
  2. #include <stdio.h>
  3. int global ;    /* weak symbol */
  4. int main(int argc, const char *argv[])
  5. {
  6. printf("global = %d, sizeof(global) in main = %d\n", global, sizeof(global));
  7. bb();
  8. return 0;
  9. }
  1. /* bb.c  */
  2. #include <stdio.h>
  3. double global ; /* weak symbol */
  4. void bb()
  5. {
  6. printf("global = %f, sizeof(global) in bb = %d\n", global, sizeof(global));
  7. }

编译成目标文件:

  1. gcc -c aa.c bb.c

得到aa.o 和 bb.o两个目标文件

来看看他们的符号表

可以清楚的看到,在两个目标文件中,Ndx数值都是COM,表示此时它们被放在COMMON块。在aa.o中global的大小是4个字节,在bb.o中global的大小是8个字节。

那么这两个目标文件链接生成可执行文件后,global的大小是多少呢? -- 当不同的目标文件需要的COMMON块空间大小不一致时,以最大的那块为准。

  1. gcc aa.o bb.o -o cc

得到可执行文件cc

果然,global最终的大小为8个字节。

所以总体来看,未初始化全局变量最终还是被放在BSS段的。

如果我们给aa.c中的global赋值把它变成强符号呢?如下:

  1. /* aa.c  */
  2. #include <stdio.h>
  3. int global = 100;       /* strong symbol */
  4. int main(int argc, const char *argv[])
  5. {
  6. printf("global = %d, sizeof(global) in main = %d\n", global, sizeof(global));
  7. bb();
  8. return 0;
  9. }
  1. /* bb.c  */
  2. #include <stdio.h>
  3. double global;  /* weak symbol */
  4. void bb()
  5. {
  6. printf("global = %f, sizeof(global) in bb = %d\n", global, sizeof(global));
  7. }

得到两个目标文件后查看符号,aa.o中global放在.data段,bb.o依然放在COMMON块,最终的cc中global大小4字节,这很好的验证了本文一开始的第二条规则。

可是有例外情况,看下面程序:

  1. /* aa.c  */
  2. #include <stdio.h>
  3. int global;     /* weak symbol */
  4. int main(int argc, const char *argv[])
  5. {
  6. printf("global = %d, sizeof(global) in main = %d\n", global, sizeof(global));
  7. bb();
  8. return 0;
  9. }
  1. /* bb.c  */
  2. #include <stdio.h>
  3. double __attribute__ ((weak)) global = 1.0;     /* weak symbol */
  4. void bb()
  5. {
  6. printf("global = %f, sizeof(global) in bb = %d\n", global, sizeof(global));
  7. }

aa.c和bb.c中global都是弱符号,如果按照上面的规则的话,最终的可执行文件中global的大小应该是8个字节,可惜结果并不是我们所期望的:

看到没,最终的可执行文件cc中global所占内存却是4个字节!为什么? 下面是我在ELF文档里找到的一段:

---------------------------------------------------------------------------------------------------------------------------------------

Global and weak symbols differ in two major ways.
(全局符号和弱符号的区别主要在两个方面。)
When the link editor combines several relocatable object files, it does not allow multiple definitions of STB_GLOBAL symbols with the same name. On the other hand, if a defined global symbol exists, the appearance of a weak symbol with the same name will not cause an error. The link editor honors the global definition and ignores the weak ones. Similarly, if a common symbol exists (i.e., a symbol whose st_shndx field holds SHN_COMMON), the appearance of a weak symbol with the same name will not cause an error. The link editor honors the common definition and ignores the weak ones.
(* 当链接器链接几个可重定位的目标文件时,它不允许具有STB_GLOBAL属性的符号以相同名字进行重复定义。另一方面,如果一个已定义的全局符号存在,则即便另一个具有相同名字的弱符号存在也不会引起错误。链接器将认可全局符号的定义而忽略弱符号的定义。与此相似,如果一个符号被放在COMMON块(就是说这个符号的 st_shndx 成员的值为SHN_COMMON),则一个同名的弱符号也不会引起错误。链接器同样认可放在COMMON块符号的定义而忽略其他的弱符号。)

---------------------------------------------------------------------------------------------------------------------------------------

至于为什么这样处理,目前我还不得而知,如果读者知道的话,麻烦告诉我一下^_^!

再来看一种情况!如下:

  1. /* aa.c  */
  2. #include <stdio.h>
  3. int __attribute__((weak)) global = 1;   /* weak symbol */
  4. int main(int argc, const char *argv[])
  5. {
  6. printf("global = %d, sizeof(global) in main = %d\n", global, sizeof(global));
  7. bb();
  8. return 0;
  9. }
  1. /* bb.c  */
  2. #include <stdio.h>
  3. double __attribute__((weak)) global = 1.0;      /* weak symbol */
  4. void bb()
  5. {
  6. printf("global = %f, sizeof(global) in bb = %d\n", global, sizeof(global));
  7. }

结果却是:

看到没,同样都是弱符号,却因为编译顺序的不同,可执行文件中的大小也不同,为什么会这样,目前我也是不得而知!

简而言之,在目标文件中没有将未初始化的全局变量未初始化的静态变量那样放在BSS段,而是放在COMMON块,是因为现在的编译器和链接器允许不同类型的弱符号存在,最本质的原因是链接器无法判断各个符号的类型是否一致。

有了COMMON块之后就可以很好的解决这个问题了。

补充:

编程中我们可以使用GCC的“-fno-common”把所有的未初始化的全局变量不以COMMON块的形式处理,也可以使用“__attribute__ ((nocommon))”,如下:

  1. int global __attribute__ ((nocommon));  /* strong symbol */

一旦一个未初始化的全局变量不是以COMMON块的形式存在,那么它就相当于一强符号,如果其他目标文件中还有同一个变量的强符号定义,链接时就会发生符号重复定义错误

参考链接:

http://blog.chinaunix.net/uid-23629988-id-2888209.html(通过未初始化全局变量,研究BSS段和COMMON段的不同)

http://blog.copton.net/articles/linker/index.html ( C++ and the linker)

http://www.lurklurk.org/linkers/linkers.html ( Beginner's Guide to Linkers)

https://thunked.org/programming/code-obfuscation-with-linker-symbol-abuse-t100.html

c语言之函数或者变量的weak属性 C语言之强化,弱化符号weak相关推荐

  1. C语言之强化,弱化符号weak

    C语言之强化,弱化符号weak 转自:http://blog.csdn.net/astrotycoon/article/details/8008629 一.概述 在C语言中,函数和初始化的全局变量(包 ...

  2. c语言中函数名可不可以由用户命名,C语言中变量名及函数名的命名规则与驼峰命名法...

    在程序设计中,变量名.函数名.数组名等统称为标识 符.简单地说,标识符就是一个名字.除库函数的函数名由系统定义外,其余都由用户自定义.C语言规定,标识符只能由字母(a-z,A-Z).数字 (0-9). ...

  3. c语言中函数static变量,[转]C语言中的 static变量、static函数

    1. static 变量 静态变量的类型 说明符是 static. 静态变量当然是属于静态存储方式,但是属于静态存储方式的量不一定就是静态变量. 例如外部变量虽属于静态 存储方式,但不一定是静态变量, ...

  4. C语言用函数指针变量调用函数

    一.用函数指针变量调用函数 一个函数,在编译的时候 ,系统会给这个函数分配一个入口地址,这个入口地址就称为函数的指针(地址).既然有地址,那么我们可以定义一个指针变量指向该函数,然后,我们就可以通过该 ...

  5. c语言inv函数怎么用,1 怎样学习c语言

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 1  怎样学习c语言 很多人对学习C语言感到无从下手,经常问我同一个问题:究竟怎 样学习C语言? 我和刚刚起步的人一样,学习的第一个计算机语言就是C语言. ...

  6. python语言保留字可以用变量名来命名_Python语言基本语法元素之变量,变量的含义及命名规则...

    教学是对知识的二次加工. 笔者希望给python初学者提供帮助.上一篇文章写了Python语言基本语法元素之格式框架:注释.缩进.续行符 这里继续,Python语言基本语法元素之变量. 我们来看看前4 ...

  7. c语言open函数打开文件方式,Linux中C语言open函数打开或创建文件详细讲解

    Linux中C语言open函数打开或创建文件详细讲解 Linux中C语言open函数打开或创建文件详细讲解 头文件: #include #include #include 函数原型: int open ...

  8. R语言基本操作函数(1)变量的基本操作

    1.变量变换 #转换变量类型:使用如下命令可得到全部列表,methods(as) as.array(x) as.data.frame(x) as.numeric(x) as.logical(x) as ...

  9. go语言中将函数作为变量传递

    在Go中函数也是一种变量,我们可以通过type来定义它,它的类型就是所有拥有相同的参数,相同的返回值的一种类型,函数当做值和类型在我们写一些通用接口的时候非常有用,通过下面这个例子我们可以看到test ...

最新文章

  1. 开放linux下mysql数据库3306端口
  2. 每日英语:Would We Be Healthier With A Vegan Diet?
  3. ecshop仿淘宝加入购物车弹出框【支持任何页面】淡出淡隐固定屏幕-兼容ie
  4. vba oracle 01019,Oracle 客户端连接时报ORA-01019错误总结
  5. 战斗机嵌入式训练系统中的智能虚拟陪练
  6. 回溯法 —— 求解0/1背包问题(剪枝)
  7. 5.企业安全建设指南(金融行业安全架构与技术实践) --- 安全团队建设
  8. C#中IPAddress与域名的使用
  9. AWS SQS, SWF and SNS
  10. android addr2line 用法,Android studio中NDK开发(四)——使用addr2line分析Crash日志
  11. 如何使用PC3000检测硬盘
  12. 心知天气天气状况获取,ESP32获取天气信息(含源码)
  13. PTA 校选拔 7-10 宇航员的寻宝图(BFS)
  14. 有什么助于睡眠的方法,睡眠不好,一定要知道这些方法
  15. Smart movie Java_智能影院下载-smartmovie智能影院【手机端+PC端+教程+工具】-东坡下载...
  16. 常用计算机硬件品牌,介绍几个常用的电脑硬件检测工具
  17. 学习必备的50条非常有趣且实用的Python一行代码,值得收藏
  18. 增值税发票二维码解码内容说明
  19. Spark SQL: Error in query: undefined function错误的解决方法
  20. texmaker中图片过大怎么办_latex 图或表和正文间距过大怎么处理

热门文章

  1. Angular6入门学习
  2. Web 4U[02]-自定义表情包神器:表情锅
  3. 破开C语言暗夜的第四道光(1)—— 函数基础知识
  4. p计算机二级考试考什么,计算机二级考试C++上机考试试题
  5. ajax实现页面跳转并传参,jQuery实现页面跳转的时候Post传参
  6. vue打包如何将单页面应用打包成多页面应用
  7. 【Python机器学习】之 SVM 支持向量机算法(二)
  8. signature=0530fd811a6c6b231d307e5e78a2e12e,[Profound mycoses in AIDS in Abidjan (Cte d'Ivoire)]
  9. 数据挖掘系列3:关联规则评价
  10. 智能仓储立库核心干货|立体化立体仓库类型是如何进行叉车和堆垛机配置?