关键字static的作用经常被作为面试题考查。本人在面试微软工程院的时候就曾经遇到过这个问题的考查。 那么,static关键字的含义有哪些呢?现在总结如下:
static可以修饰变量。当static修饰全局变量时,表示该变量的作用域只存在于本文件。其他文件就不能引用。比如:
在a.c文件里定义了2个全局变量:
static int x = 10; int y = 0;
其中x前面有static关键字修饰,而y没有。那么在文件b.c里,如果想引用x,y,那么:
extern int y;//正确 extern int x;//错误
也就是说,x是不能再b.c文件里访问的,但y是可以的。这样做,其实是为了防止模块与模块之间因为全局变量而造成的命名冲突。
当static修饰函数内的局部变量时,表示该变量存储在静态存储区,而不是存储在栈上。因此该变量具有记忆功能。函数每次执行后,它的值都不会丢失。
问题:下面的函数实现在一个数上加一个数,有什么错误? 如何改正?
int add_n ( int n ) { static int i = 100; i += n; return i; }
分析:由于i声明为static的类型,它存储在静态存储区,生命期为整个程序执行期间。因此,它的值在每次函数调用之后,都不会丢失。该函数原意是想对n加上100。但是实际执行的是,对n加上的并不是100而是函数上次执行后i的值。 因此,应该把代码做如下改正:
int add_n ( int n ) { int i = 100; i += n; return i; }
实际上,C库中的strtok()函数就使用了static变量来计算。
char *strtok( char *str1, const char *str2 );
str1为需要分割的字符串,首次传入的是源字符串,后面就是NULL
str2为分隔符字符串,此字符串中的每个字符都是分割符
返回值:如果不存在分隔符,则返回NULL;存在则返回分割出的字符串的首地址,字符串首部不含分隔符,尾部是字符串结束符+余下的带分割的字符串。
注意:此函数是在源字符串中进行分割操作,如不想修改源字符串,最好copy一份。
处理思想:
1、在原字符串中进行分隔,找到第一个匹配的分隔符,将此字符替换为结束符0
2、tok为首个不是分隔符的字符,last为找到的第一个匹配的分隔符字符下一位字符位置(也就是下次循环开始位置)
3、下一次循环开始忽略所有分隔符的字符。找到首个不是分隔符的字符赋值给tokn
/* Copyright (C) 1994 DJ Delorie, see COPYING.DJ for details */ #include <string.h> char* strtok(char *s, const char *delim) { const char *spanp; int c, sc; char *tok; static char *last; if (s == NULL && (s = last) == NULL) return (NULL); /* * Skip (span) leading delimiters (s += strspn(s, delim), sort of). * 跳过字符串首部的分隔符 */ cont: c = *s++; for (spanp = delim; (sc = *spanp++) != 0;) { if (c == sc) goto cont; } /* *分割符后面没有字符串了 */ if (c == 0) { /* no non-delimiter characters */ last = NULL; return (NULL); } tok = s - 1; /*分割符后面还有字符串,将tok指向字符串首部(不包括分隔符)*/ /* * Scan token (scan for delimiters: s += strcspn(s, delim), sort of). * Note that delim must have one NUL; we stop if we see that, too. * 循环字符串中的字符,直到找到分隔符或者结束符,并替换成结束符 */ for (;;) { c = *s++; spanp = delim; /* *判断字符串中的某字符是否是分割符中的字符 *如果是,将分隔符替换成结束符并返回tok; *如果不是,继续判断下一个字符 */ do { if ((sc = *spanp++) == c) { if (c == 0) s = NULL; else s[-1] = 0; last = s; return (tok); } } while (sc != 0); } /* NOTREACHED */ }
// 哈希实现 char* strtok_r(char* string_org,const char* demial) { static unsigned char* last; unsigned char* str; const unsigned char* ctrl = (const unsigned char*)demial; unsigned char map[32]; int count; for (count =0; count <32; count++){ map[count] = 0; } do { map[*ctrl >> 3] |= (1 << (*ctrl & 7)); } while (*ctrl++); if (string_org){ str = (unsigned char*)string_org; } else{ str = last; } while ((map[*str >> 3] & (1 << (*str & 7))) && *str){ str++; } string_org = (char*)str; for (;*str; str++){ if ( map[*str >> 3] & (1 << (*str & 7))){ *str++ = '\0'; break; } } last =str; if (string_org == (char*)str){ return NULL; }else{ return string_org; } }
现在来看一道Intel的面试题:
问题:A.c 和B.c两个c文件中使用了两个相同名字的static变量,编译的时候会不会有问题?这两个static变量会保存到哪里(栈还是堆或者其他的)? 答案:用关键字static的修饰的全局变量,表明这个变量仅在本文件(模块)中有意义,不会影响其他模块。这两个static变量保存在静态存储区。
关于更多static修饰变量的介绍,您还可以参考:《变量类型,作用域,存储空间,生命周期 》
static关键字还可以修饰函数。在C语言里,static修饰函数,与static修饰全局变量作用是一样的,表示该函数仅在本文件中使用,其他文件中的函数不能调用此函数。其他文件中不能引用该函数,以避免名字冲突。
在C++里,static修饰函数和变量,表示该函数或变量属于该C++类的静态成员,为所有对象共同所有。在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象中共享的成员,而不是某个对象的成员。 使用静态数据成员可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。
静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。
静态成员函数不接受隐含的this自变量。所以,它就无法访问自己类的非静态成员。
本页共45段,1634个字符,3673 Byte(字节)