staticvoid和void区别(转)
static关键字
1.作用于变量:
用static声明局部变量-------局部变量指在代码块{}内部定义的变量,只在代码块内部有效(作用域),其缺省的存储方式是自动变量或说是动态存储的,即指令执行到变量定义处时才给变量分配存储单元,跳出代码块时释放内存单元(生命期)。用static声明局部变量时,则改变变量的存储方式(生命期),使变量成为静态的局部变量,即编译时就为变量分配内存,直到程序退出才释放存储单元。这样,使得该局部变量有记忆功能,可以记忆上次的数据,不过由于仍是局部变量,因而只能在代码块内部使用(作用域不变)。
用static声明外部变量-------外部变量指在所有代码块{}之外定义的变量,它缺省为静态变量,编译时分配内存,程序结束时释放内存单元。同时其作用域很广,整个文件都有效甚至别的文件也能引用它。为了限制某些外部变量的作用域,使其只在本文件中有效,而不能被其他文件引用,可以用static关键字对其作出声明。
总结:用static声明局部变量,使其变为静态存储方式,作用域不变;用static声明外部变量,其本身就是静态变量,这只会改变其连接方式,使其只在本文件内部有效,而其他文件不可连接或引用该变量。
2.作用于函数:
使用static用于函数定义时,对函数的连接方式产生影响,使得函数只在本文件内部有效,对其他文件是不可见的。这样的函数又叫作静态函数。使用静态函数的好处是,不用担心与其他文件的同名函数产生干扰,另外也是对函数本身的一种保护机制。
如果想要其他文件可以引用本地函数,则要在函数定义时使用关键字extern,表示该函数是外部函数,可供其他文件调用。另外在要引用别的文件中定义的外部函数的文件中,使用extern声明要用的外部函数即可。
参考资料:
①《 C程序设计(第二版) 》,谭浩强
②《 Pointers on C 》,Kenneth A.Reek
void和void指针
void的含义
void即“无类型”,void *则为“无类型指针”,可以指向任何数据类型。
void指针使用规范
①void指针可以指向任意类型的数据,亦即可用任意数据类型的指针对void指针赋值。例如:
int *pint;
void *pvoid;
pvoid = pint; /* 不过不能 pint = pvoid; */
如果要将pvoid赋给其他类型指针,则需要强制类型转换如:pint = (int *)pvoid;
②在ANSI C标准中,不允许对void指针进行算术运算如pvoid++或pvoid+=1等,而在GNU
中则允许,因为在缺省情况下,GNU认为void *与char *一样。sizeof( *pvoid )== sizeof( char ).
void的作用
①对函数返回的限定。
②对函数参数的限定。
当函数不需要返回值时,必须使用void限定。例如: void func(int, int);
当函数不允许接受参数时,必须使用void限定。例如: int func(void)。
由于void指针可以指向任意类型的数据,亦即可用任意数据类型的指针对void指针赋值,因此还可以用void指针来作为函数形参,这样函数就可以接受任意数据类型的指针作为参数。例如:
void * memcpy( void *dest, const void *src, size_t len );
void * memset( void * buffer, int c, size_t num );
参考资料:《 C/C++语言void及void指针深层探索 》,宋宝华
函数指针
函数指针是什么?
先来看函数调用是怎么回事。一个函数占用一段连续内存。当调用一个函数时,实际上是跳转到函数入口地址,执行函数体的代码,完成后返回。如何到对应的入口地址?这是由函数名来标记的,实际上,函数名就是函数的入口地址。
函数指针是一种特殊类型的指针,它指向一个函数的入口地址。
注意:除了void类型指针是无类型的指针外,其他所有指针都是有对应类型的,例如int *pint、struct studentdata *psdata等,只有指明了指针所指的数据类型,编译器才能为指针分配或预计分配相应大小的存储空间,指针的算术运算如pint++等才是有意义的。因此,定义了某种类型的指针之后,除非使用强制类型转换,那么它只能指向相应数据类型的变量或常量,不同类型的指针或数据之间不可混用。所以指针的类型实际上是一种身份标志的作用。
函数指针如何表明自己的身份呢?为了避免混乱,必须也要作出相应规定,不同函数的函数指针不能混用。例如,int func1(int arg11, char arg12)与int func2(char arg)的函数指针就不能混用,要定义可以指向func1的函数指针应该这样:
int (*pfunc1)(int, char) = func1;
定义可以指向func2的函数指针则该如下:
int (*pfunc2)(char) = func2;
从函数指针的定义可以看出,函数指针的类型实际上是由函数签名决定的。函数签名就象是函数的身份证,一个函数的函数签名是独一无二的,具有相同函数签名的函数实际上就是同一函数。函数签名包括函数名、函数形参类型的有序列表和函数返回值类型。
一个函数指针的定义规定了它只能指向特定类型的函数。如果两个函数的形参列表和返回值类型相同,只有函数名和函数体不同,则可以使用相同类型的函数指针。例如,如果还有一个函数int func3(char arg),则上面定义的可以指向函数func2的函数指针也可以用于指向func3,即:
pfunc2 = func3;
再使用pfunc2(char ARG)就可以调用函数func3,这时指令计数器(PC)指向函数入口,从此开始执行函数体代码。
注意:对函数指针进行算术运算也是没有意义的。
如何使用函数指针?
①定义合适类型的函数指针变量;
int (*pfunc)(int, int);
②给函数指针变量赋值,使它指向某个函数入口;
int example(int, int);
pfunc = example; /*将函数入口地址赋给函数指针变量*/
或者:pfunc = &example; /*函数名总是被编译器转换为函数指针(入口地址)来使用,因
此与上面一句等价 */
③使用函数指针来调用相应的函数;
retval = pfunc(10, 16);
或者:retval = (*pfunc)(10, 16);
static修饰的变量上面两句都与retval = example(10, 16);等价。
理解:一个指针变量p实际上也和普通的变量一样,要占存储空间(通常与平台的虚拟地址一样宽),也有其自身的存储地址&p;不同的是,在指针变量p的值有特殊的意义,它是另外一个变量或常量的地址值,也就是说,在地址为&p的存储单元上存放着另外一个数据的地址。因此,*p实际上是将p看作它指向的数据的地址来使用,*操作符是引用相应地址中的数据,也就是对地址为p的存储单元中存放的数据进行操作。
一个函数指针变量则更为特殊。比如上面的例子,pfunc变量本身的值是函数example()的入口地址。因此pfunc可以代替其所指函数的函数名来使用。至于*pfunc,如果按照上面的
理解,它实际上是地址pfunc的内容,也即函数example()的入口地址的内容,就有点含糊了。不过,从另一方面来理解,如果使用pfunc = &example来初始化pfunc,则*pfunc == *(&example) == example,又与pfunc等价。因此,就有了两种使用函数指针来调用相应函数的形式。
值得注意的是,不可用*pfunc来对pfunc的值初始化。即*pfunc = example的写法是错误的。
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论