10static关键字

关键字static的作用经常被作为面试题考查。本人在面试微软工程院的时候就曾经遇到过这个问题的考查。 那么,static关键字的含义有哪些呢?现在总结如下:

1.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;
}

strtok()函数与static变量

实际上,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修饰变量的介绍,您还可以参考:《变量类型,作用域,存储空间,生命周期 》

2.static修饰函数

static关键字还可以修饰函数。在C语言里,static修饰函数,与static修饰全局变量作用是一样的,表示该函数仅在本文件中使用,其他文件中的函数不能调用此函数。其他文件中不能引用该函数,以避免名字冲突。

3.static在C++中

在C++里,static修饰函数和变量,表示该函数或变量属于该C++类的静态成员,为所有对象共同所有。在类中,静态成员可以实现多个对象之间的数据共享,并且使用静态数据成员还不会破坏隐藏的原则,即保证了安全性。因此,静态成员是类的所有对象中共享的成员,而不是某个对象的成员。 使用静态数据成员可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。

静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。

静态成员函数不接受隐含的this自变量。所以,它就无法访问自己类的非静态成员。

本页共45段,1634个字符,3673 Byte(字节)