10种简单的数字滤波算法(C语言源程序)

1、限幅滤波法(又称程序判断滤波法)

 A、方法:

 根据经验判断,确定两次采样允许的最大偏差值(设为A)

 每次检测到新值时判断:

 如果本次值与上次值之差<=A,则本次值有效

 如果本次值与上次值之差>A,则本次值无效,放弃本次值,用上次值代替本次值

 B、优点:

 能有效克服因偶然因素引起的脉冲干扰

 C、缺点

 无法抑制那种周期性的干扰

 平滑度差

#define A 10
char value;
char filter()
{
  char  new_value;
  new_value = get_ad();
  if ( ( new_value – value > A ) || ( value – new_value > A )
     return value;
  return new_value;
}

2、中位值滤波法

 A、方法:

 连续采样N次(N取奇数)

 把N次采样值按大小排列

 取中间值为本次有效值

 B、优点:

 能有效克服因偶然因素引起的波动干扰

 对温度、液位的变化缓慢的被测参数有良好的滤波效果

 C、缺点:

 对流量、速度等快速变化的参数不宜

#define N  11
char filter()
{
  char value_buf[N];
  char count,i,j,temp;
  for ( count=0;count<N;count++)
  {
     value_buf[count] = get_ad();
     delay();
  }
  for (j=0;j<N-1;j++)
  {
     for (i=0;i<N-j;i++)
     {
        if ( value_buf[i]>value_buf[i+1] )
        {
           temp = value_buf[i];
           value_buf[i] = value_buf[i+1]; 
            value_buf[i+1] = temp;
        }
     }
  }
  return value_buf[(N-1)/2];
    

3、算术平均滤波法

 A、方法:

 连续取N个采样值进行算术平均运算

 N值较大时:信号平滑度较高,但灵敏度较低

 N值较小时:信号平滑度较低,但灵敏度较高

 N值的选取:一般流量,N=12;压力:N=4

 B、优点:

 适用于对一般具有随机干扰的信号进行滤波

 这样信号的特点是有一个平均值,信号在某一数值范围附近上下波动

 C、缺点:

 对于测量速度较慢或要求数据计算速度较快的实时控制不适用

 比较浪费RAM
#define N 12
char filter()
{
  int  sum = 0;
  for ( count=0;count<N;count++)
  {
     sum + = get_ad();
     delay();
  }
  return (char)(sum/N);
}

4、递推平均滤波法(又称滑动平均滤波法)

 A、方法:

 把连续取N个采样值看成一个队列

 队列的长度固定为N

 每次采样到一个新数据放入队尾,并扔掉原来队首的一次数据.(先进先出原则)

 把队列中的N个数据进行算术平均运算,就可获得新的滤波结果

 N值的选取:流量,N=12;压力:N=4;液面,N=4~12;温度,N=1~4

 B、优点:

 对周期性干扰有良好的抑制作用,平滑度高

 适用于高频振荡的系统

 C、缺点:

 灵敏度低

 对偶然出现的脉冲性干扰的抑制作用较差

 不易消除由于脉冲干扰所引起的采样值偏差

 不适用于脉冲干扰比较严重的场合

 比较浪费RAM


#define N 12 
char value_buf[N];
char i=0;
char filter()
{
  char count;
  int  sum=0;
  value_buf[i++] = get_ad();
  if ( i == N )   i = 0;
  for ( count=0;count<N,count++)
     sum = value_buf[count];
  return (char)(sum/N);
}

5、中位值平均滤波法(又称防脉冲干扰平均滤波法)

 A、方法:

 相当于“中位值滤波法”+“算术平均滤波法”

 连续采样N个数据,去掉一个最大值和一个最小值

 然后计算N-2个数据的算术平均值

 N值的选取:3~14

 B、优点:

 融合了两种滤波法的优点

 对于偶然出现的脉冲性干扰,可消除由于脉冲干扰所引起的采样值偏差

 C、缺点:

 测量速度较慢,和算术平均滤波法一样

 比较浪费RAM


#define N 12
char filter()
{
  char count,i,j;
  char value_buf[N];
  int  sum=0;
  for  (count=0;count<N;count++)
  {
     value_buf[count] = get_ad();
     delay();
  }
  for (j=0;j<N-1;j++)
  {
     for (i=0;i<N-j;i++)
     {
        if ( value_buf[i]>value_buf[i+1] )
        {
           temp = value_buf[i];
           value_buf[i] = value_buf[i+1]; 
            value_buf[i+1] = temp;
        }
     }
  }
  for(count=1;count<N-1;count++)
     sum += value[count];
  return (char)(sum/(N-2));
}

6、限幅平均滤波法

 A、方法:

 相当于“限幅滤波法”+“递推平均滤波法”

 每次采样到的新数据先进行限幅处理,

 再送入队列进行递推平均滤波处理

 B、优点:

 融合了两种滤波法的优点

 对于偶然出现的脉冲性干扰,可消除由于脉冲干扰所引起的采样值偏差

 C、缺点:

 比较浪费RAM

  
略 参考子程序1、3

7、一阶滞后滤波法

 A、方法:

 取a=0~1

 本次滤波结果=(1-a)*本次采样值+a*上次滤波结果

 B、优点:

 对周期性干扰具有良好的抑制作用

 适用于波动频率较高的场合

 C、缺点:

 相位滞后,灵敏度低

 滞后程度取决于a值大小

 不能消除滤波频率高于采样频率的1/2的干扰信号


#define a 50
char value;
char filter()
{
  char  new_value;
  new_value = get_ad();
  return (100-a)*value + a*new_value; 
}

8、加权递推平均滤波法

 A、方法:

 是对递推平均滤波法的改进,即不同时刻的数据加以不同的权

 通常是,越接近现时刻的数据,权取得越大。

 给予新采样值的权系数越大,则灵敏度越高,但信号平滑度越低

 B、优点:

 适用于有较大纯滞后时间常数的对象

 和采样周期较短的系统

 C、缺点:

 对于纯滞后时间常数较小,采样周期较长,变化缓慢的信号

 不能迅速反应系统当前所受干扰的严重程度,滤波效果差


#define N 12
char code coe[N] = {1,2,3,4,5,6,7,8,9,10,11,12};
char code sum_coe = 1+2+3+4+5+6+7+8+9+10+11+12;
char filter()
{
  char count;
  char value_buf[N];
  int  sum=0;
  for (count=0,count<N;count++)
  {
     value_buf[count] = get_ad();
     delay();
  }
  for (count=0,count<N;count++)
     sum += value_buf[count]*coe[count];
  return (char)(sum/sum_coe);
}

9、消抖滤波法

 A、方法:

 设置一个滤波计数器

 将每次采样值与当前有效值比较:

 如果采样值=当前有效值,则计数器清零

 如果采样值<>当前有效值,则计数器+1,并判断计数器是否>=上限N(溢出)

 如果计数器溢出,则将本次值替换当前有效值,并清计数器

 B、优点:

 对于变化缓慢的被测参数有较好的滤波效果,

 可避免在临界值附近控制器的反复开/关跳动或显示器上数值抖动

 C、缺点:

 对于快速变化的参数不宜

 如果在计数器溢出的那一次采样到的值恰好是干扰值,则会将干扰值当作有效值导入系统

#define N 12
char filter()
{
  char count=0;
  char new_value;
  new_value = get_ad();
  while (value !=new_value);
  {
     count++;
     if (count>=N)   return new_value;
      delay();
     new_value = get_ad();
  }
  return value;    
}

10、限幅消抖滤波法

 A、方法:

 相当于“限幅滤波法”+“消抖滤波法”

 先限幅,后消抖

 B、优点:

 继承了“限幅”和“消抖”的优点

 改进了“消抖滤波法”中的某些缺陷,避免将干扰值导入系统

 C、缺点:

 
略 参考子程序1、9

字符设备 register_chrdev_region()、alloc_chrdev_re

内核中所有已分配的字符设备编号都记录在一个名为 chrdevs 散列表里。该散列表中的每一个元素是一个 char_device_struct 结构,它的定义如下:

  static struct char_device_struct {
      struct char_device_struct *next;    // 指向散列冲突链表中的下一个元素的指针
      unsigned int major;                 // 主设备号
      unsigned int baseminor;             // 起始次设备号
      int minorct;                        // 设备编号的范围大小
      char name[64];                      // 处理该设备编号范围内的设备驱动的名称
      struct file_operations *fops;       // 没有使用
      struct cdev *cdev;                  // 指向字符设备驱动程序描述符的指针
  } *chrdevs[CHRDEV_MAJOR_HASH_SIZE];

注意,内核并不是为每一个字符设备编号定义一个 char_device_struct 结构,而是为一组对应同一个字符设备驱动的设备编号范围定义一个 char_device_struct 结构。chrdevs 散列表的大小是 255,散列算法是把每组字符设备编号范围的主设备号以 255 取模插入相应的散列桶中。同一个散列桶中的字符设备编号范围是按起始次设备号递增排序的。

注册
内核提供了三个函数来注册一组字符设备编号,这三个函数分别是 register_chrdev_region()、alloc_chrdev_region() 和 register_chrdev()。这三个函数都会调用一个共用的 __register_chrdev_region() 函数来注册一组设备编号范围(即一个 char_device_struct 结构)。

register_chrdev_region(dev_t first,unsigned int count,char *name)
First :要分配的设备编号范围的初始值(次设备号常设为0);
Count:连续编号范围.
Name:编号相关联的设备名称. (/proc/devices);
动态分配:
Int alloc_chrdev_region(dev_t *dev,unsigned int firstminor,unsigned int count,char *name);
Firstminor : 通常为0;
*dev:存放返回的设备号;
释放:
Void unregist_chrdev_region(dev_t first,unsigned int count);
调用Documentation/devices.txt中能够找到已分配的设备号.

所以下面先来看一下 __register_chrdev_region() 函数的实现代码。

static struct char_device_struct * __register_chrdev_region(unsigned int major, unsigned int baseminor, int minorct, const char *name)
{
  struct char_device_struct *cd, **cp;
  int ret = 0;
  int i;

  cd = kzalloc(sizeof(struct char_device_struct), GFP_KERNEL);
  if (cd == NULL)
      return ERR_PTR(-ENOMEM);

  mutex_lock(&chrdevs_lock);

  if (major == 0) {
       for (i = ARRAY_SIZE(chrdevs)-1; i > 0; i–)
          if (chrdevs[i] == NULL)
              break;

      if (i == 0) {
          ret = -EBUSY;
          goto out;
      }
      major = i;
      ret = major;
  }

  cd->major = major;
  cd->baseminor = baseminor;
  cd->minorct = minorct;
  strncpy(cd->name,name, 64);

  i = major_to_index(major);

  for (cp = &chrdevs[i]; *cp; cp = &(*cp)->next)
      if ((*cp)->major > major ||
           ((*cp)->major == major && ( ((*cp)->baseminor >= baseminor) || ((*cp)->baseminor + (*cp)->minorct > baseminor)) ))
          break;

 
  if (*cp && (*cp)->major == major) {
      int old_min = (*cp)->baseminor;
      int old_max = (*cp)->baseminor + (*cp)->minorct – 1;
      int new_min = baseminor;
      int new_max = baseminor + minorct – 1;

     
      if (new_max >= old_min && new_max <= old_max) {
          ret = -EBUSY;
          goto out;
      }

     
      if (new_min <= old_max && new_min >= old_min) {
          ret = -EBUSY;
          goto out;
      }
  }

  cd->next = *cp;
  *cp = cd;
  mutex_unlock(&chrdevs_lock);
  return cd;
out:
  mutex_unlock(&chrdevs_lock);
  kfree(cd);
  return ERR_PTR(ret);
}
函数 __register_chrdev_region() 主要执行以下步骤:
1. 分配一个新的 char_device_struct 结构,并用 0 填充。
2. 如果申请的设备编号范围的主设备号为 0,那么表示设备驱动程序请求动态分配一个主设备号。动态分配主设备号的原则是从散列表的最后一个桶向前寻找,那个桶是空的,主设备号就是相应散列桶的序号。所以动态分配的主设备号总是小于 256,如果每个桶都有字符设备编号了,那动态分配就会失败。
3. 根据参数设置 char_device_struct 结构中的初始设备号,范围大小及设备驱动名称。
4. 计算出主设备号所对应的散列桶,为新的 char_device_struct 结构寻找正确的位置。同时,如果设备编号范围有重复的话,则出错返回。
5. 将新的 char_device_struct 结构插入散列表中,并返回 char_device_struct 结构的地址。

分析完 __register_chrdev_region() 后,我们来一个个看那三个注册函数。首先是 register_chrdev_region()。

register_chrdev_region() 函数用于分配指定的设备编号范围。如果申请的设备编号范围跨越了主设备号,它会把分配范围内的编号按主设备号分割成较小的子范围,并在每个子范围上调用 __register_chrdev_region() 。如果其中有一次分配失败的话,那会把之前成功分配的都全部退回。

alloc_chrdev_region() 函数用于动态申请设备编号范围,这个函数好像并没有检查范围过大的情况,不过动态分配总是找个空的散列桶,所以问题也不大。通过指针参数返回实际获得的起始设备编号。

最后一个 register_chrdev() 是一个老式分配设备编号范围的函数。它分配一个单独主设备号和 0 ~ 255 的次设备号范围。如果申请的主设备号为 0 则动态分配一个。该函数还需传入一个 file_operations 结构的指针,函数内部自动分配了一个新的 cdev 结构。关于这些,在后续讲字符设备驱动的注册时会说明。

注销
和注册分配字符设备编号范围类似,内核提供了两个注销字符设备编号范围的函数,分别是 unregister_chrdev_region() 和 unregister_chrdev() 。它们都调用了 __unregister_chrdev_region() 函数。

struct-timeval 用法

gettimeofday() — 获取当前时间(保存在结构体timeval中)

例子:

#include <stdio.h>
#include <sys/time.h>
#include <time.h>

int main(int argc, char * argv[]){
   struct timeval tv;                //(1)
   while(1){
       gettimeofday(&tv, NULL);      //(2)
       printf(“time %u:%u\n”, tv.tv_sec, tv.tv_usec);
       sleep(2);
   }
   return 0;

}

(1) struct–timeval

struct timeval {
   time_t      tv_sec;    
   suseconds_t tv_usec;   
};
millisecond        毫秒
microsecond        微秒
timeval表示一个时间点,比如:
timeval.tv_sec = 1   (s)
timevat.tv_usec = 500 000 (
μs)
1:500 = 1s500000
μs = 1.5s
(2) gettimeofday()
int gettimeofday(struct timeval *tv, struct timezone *tz); 

 
gettimeofday()会把目前的时间有tv所指的结构返回,当地时区的信息则放到tz所指的结构中。

timezone 结构定义为:
struct timezone{
int tz_minuteswest;
int tz_dsttime;
};

C++中的static用法

简介

C++的static有两种用法:面向过程程序设计中的static和面向对象程序设计中的static。前者应用于普通变量和函数,不涉及类;后者主要说明static在类中的作用。

面向过程设计中的static

1、静态全局变量

在全局变量前,加上关键字static,该变量就被定义成为一个静态全局变量。我们先举一个静态全局变量的例子,如下:

//Example 1

#include <iostream.h>

void fn();

static int n; //定义静态全局变量

void main()

{ n=20;

cout<<n<<endl;

fn();

}

void fn()

{

n++;

cout<<n<<endl;

}

静态全局变量有以下特点:

该变量在全局数据区分配内存;

未经初始化的静态全局变量会被程序自动初始化为0(自动变量的值是随机的,除非它被显式初始化);

静态全局变量在声明它的整个文件都是可见的,而在文件之外是不可见的;

静态变量都在全局数据区分配内存,包括后面将要提到的静态局部变量。对于一个完整的程序,在内存中的分布情况如下图:

代码区 //low address

全局数据区

堆区

栈区 //high address

一般程序把新产生的动态数据存放在堆区,函数内部的自动变量存放在栈区。自动变量一般会随着函数的退出而释放空间,静态数据(即使是函数内部的静 态局部变量)也存放在全局数据区。全局数据区的数据并不会因为函数的退出而释放空间。细心的读者可能会发现,Example 1中的代码中将

static int n; //定义静态全局变量

改为

int n; //定义全局变量

程序照样正常运行。

的确,定义全局变量就可以实现变量在文件中的共享,但定义静态全局变量还有以下好处:

静态全局变量不能被其它文件所用;

其它文件中可以定义相同名字的变量,不会发生冲突;

您可以将上述示例代码改为如下:

//Example 2//File1

#include <iostream.h>

void fn();

static int n; //定义静态全局变量

void main()

{ n=20;

cout<<n<<endl;

fn();

}

//File2

#include <iostream.h>

extern int n;

void fn()

{ n++;

cout<<n<<endl;

}

编译并运行Example 2,您就会发现上述代码可以分别通过编译,但运行时出现错误。 试着将

static int n; //定义静态全局变量

改为

int n; //定义全局变量

再次编译运行程序,细心体会全局变量和静态全局变量的区别。

注意:全局变量和全局静态变量的区别

1)全局变量是不显式用static修饰的全局变量,但全局变量默认是静态的,作用域是整个工程,在一个文件内定义的全局变量,在另一个文件中,通过extern 全局变量名的声明,就可以使用全局变量。

2)全局静态变量是显式用static修饰的全局变量,作用域是所在的文件,其他的文件即使用extern声明也不能使用。

2、静态局部变量

在局部变量前,加上关键字static,该变量就被定义成为一个静态局部变量。

我们先举一个静态局部变量的例子,如下:

//Example 3

#include <iostream.h>

void fn();

void main()

{ fn();

fn();

fn();

}

void fn()

{ static int n=10;

cout<<n<<endl;

n++;

}

通常,在函数体内定义了一个变量,每当程序运行到该语句时都会给该局部变量分配栈内存。但随着程序退出函数体,系统就会收回栈内存,局部变量也相应失效。

但有时候我们需要在两次调用之间对变量的值进行保存。通常的想法是定义一个全局变量来实现。但这样一来,变量已经不再属于函数本身了,不再仅受函数的控制,给程序的维护带来不便。

静态局部变量正好可以解决这个问题。静态局部变量保存在全局数据区,而不是保存在栈中,每次的值保持到下一次调用,直到下次赋新值。

静态局部变量有以下特点:

该变量在全局数据区分配内存;

静态局部变量在程序执行到该对象的声明处时被首次初始化,即以后的函数调用不再进行初始化;

静态局部变量一般在声明处初始化,如果没有显式初始化,会被程序自动初始化为0;

它始终驻留在全局数据区,直到程序运行结束。但其作用域为局部作用域,当定义它的函数或语句块结束时,其作用域随之结束;

3、静态函数

在函数的返回类型前加上static关键字,函数即被定义为静态函数。静态函数与普通函数不同,它只能在声明它的文件当中可见,不能被其它文件使用。

静态函数的例子:

//Example 4

#include <iostream.h>

static void fn();//声明静态函数

void main()

{

fn();

}

void fn()//定义静态函数

{ int n=10;

cout<<n<<endl;

}

定义静态函数的好处:

静态函数不能被其它文件所用;

其它文件中可以定义相同名字的函数,不会发生冲突;

面向对象的static关键字

(类中的static关键字)

1、静态数据成员

在类内数据成员的声明前加上关键字static,该数据成员就是类内的静态数据成员。先举一个静态数据成员的例子。

//Example 5

#include <iostream.h>

class Myclass

{

public:

Myclass(int a,int b,int c);

void GetSum();

private:

int a,b,c;

static int Sum;//声明静态数据成员

};

int Myclass::Sum=0;//定义并初始化静态数据成员

Myclass::Myclass(int a,int b,int c)

{ this->a=a;

this->b=b;

this->c=c;

Sum+=a+b+c;}

void Myclass::GetSum()

{ cout<<“Sum=”<<Sum<<endl;

}

void main()

{ Myclass M(1,2,3);

M.GetSum();

Myclass N(4,5,6);

N.GetSum();

M.GetSum();}

可以看出,静态数据成员有以下特点:

对于非静态数据成员,每个类对象都有自己的拷贝。而静态数据成员被当作是类的成员。无论这个类的对象被定义了多少个,静态数据成员在程序中也只有一份拷 贝,由该类型的所有对象共享访问。也就是说,静态数据成员是该类的所有对象所共有的。对该类的多个对象来说,静态数据成员只分配一次内存,供所有对象共 用。所以,静态数据成员的值对每个对象都是一样的,它的值可以更新;

静态数据成员存储在全局数据区。静态数据成员定义时要分配空间,所以不能在类声明中定义。在Example 5中,语句int Myclass::Sum=0;是定义静态数据成员;

静态数据成员和普通数据成员一样遵从public,protected,private访问规则;

因为静态数据成员在全局数据区分配内存,属于本类的所有对象共享,所以,它不属于特定的类对象,在没有产生类对象时其作用域就可见,即在没有产生类的实例时,我们就可以操作它;

静态数据成员初始化与一般数据成员初始化不同。静态数据成员初始化的格式为:

<数据类型><类名>::<静态数据成员名>=<值>

类的静态数据成员有两种访问形式:

<类对象名>.<静态数据成员名> 或 <类类型名>::<静态数据成员名>

如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员 ;

静态数据成员主要用在各个对象都有相同的某项属性的时候。比如对于一个存款类,每个实例的利息都是相同的。所以,应该把利息设为存款类的静态数据成员。这 有两个好处,第一,不管定义多少个存款类对象,利息数据成员都共享分配在全局数据区的内存,所以节省存储空间。第二,一旦利息需要改变时,只要改变一次, 则所有存款类对象的利息全改变过来了;

同全局变量相比,使用静态数据成员有两个优势:

静态数据成员没有进入程序的全局名字空间,因此不存在与程序中其它全局名字冲突的可能性;

可以实现信息隐藏。静态数据成员可以是private成员,而全局变量不能;

2、静态成员函数

与静态数据成员一样,我们也可以创建一个静态成员函数,它为类的全部服务而不是为某一个类的具体对象服务。静态成员函数与静态数据成员一样,都是类的内部 实现,属于类定义的一部分。 普通的成员函数一般都隐含了一个this指针,this指针指向类的对象本身,因为普通成员函数总是具体的属于某个类的具体对象的。通常情况下,this 是缺省的。如函数fn()实际上是this->fn()。但是与普通函数相比,静态成员函数由于不是与任何的对象相联系,因此它不具有this指 针。从这个意义上讲,它无法访问属于类对象的非静态数据成员,也无法访问非静态成员函数,它只能调用其余的静态成员函数。 下面举个静态成员函数的例子。

//Example 6

#include <iostream.h>

class Myclass

{public:

Myclass(int a,int b,int c);

static void GetSum();/声明静态成员函数

private:

int a,b,c;

static int Sum;//声明静态数据成员

};

int Myclass::Sum=0;//定义并初始化静态数据成员

Myclass::Myclass(int a,int b,int c)

{ this->a=a;

this->b=b;

this->c=c;

Sum+=a+b+c; //非静态成员函数可以访问静态数据成员

}

void Myclass::GetSum() //静态成员函数的实现

{// cout<<a<<endl; //错误代码,a是非静态数据成员

cout<<“Sum=”<<Sum<<endl;

}

void main()

{ Myclass M(1,2,3);

M.GetSum();

Myclass N(4,5,6);

N.GetSum();

Myclass::GetSum();

}

关于静态成员函数,可以总结为以下几点:

出现在类体外的函数定义不能指定关键字static;

静态成员之间可以相互访问,包括静态成员函数访问静态数据成员和访问静态成员函数;

非静态成员函数可以任意地访问静态成员函数和静态数据成员;

静态成员函数不能访问非静态成员函数和非静态数据成员;

由于没有this指针的额外开销,因此静态成员函数与类的全局函数相比速度上会有少许的增长;

调用静态成员函数,可以用成员访问操作符(.)和(->)为一个类的对象或指向类对象的指针调用静态成员函数,也可以直接使用如下格式:

<类名>::<静态成员函数名>(<参数表>)

调用类的静态成员函数。

作用

static静态变量声明符。 在声明它的程序块,子程序块或函数内部有效,值保持,在整个程序期间分配存储器空间,编译器默认值0。

是C++中很常用的修饰符,它被用来控制变量的存储方式和可见性。

为什么要引入static

函数内部定义的变量,在程序执行到它的定义处时,编译器为它在栈上分配空间,大家知道,函数在栈上分配的空间在此函数执行结束时会释放掉,这样就产生了一个问题: 如果想将函数中此变量的值保存至下一次调用时,如何实现? 最容易想到的方法是定义一个全局的变量,但定义为一个全局变量有许多缺点,最明显的缺点是破坏了此变量的访问范围(使得在此函数中定义的变量,不仅仅受此函数控制)。

什么时候用static

需要一个数据对象为整个类而非某个对象服务,同时又力求不破坏类的封装性,即要求此成员隐藏在类的内部,对外不可见。

static的内部机制

静态数据成员要在程序一开始运行时就必须存在。因为函数在程序运行中被调用,所以静态数据成员不能在任何函数内分配空间和初始化。

这样,它的空间分配有三个可能的地方,一是作为类的外部接口的头文件,那里有类声明;二是类定义的内部实现,那里有类的成员函数定义;三是应用程序的main()函数前的全局数据声明和定义处。

静态数据成员要实际地分配空间,故不能在类的声明中定义(只能声明数据成员)。类声明只声明一个类的“尺寸和规格”,并不进行实际的内存分配,所以在类声明中写成定义是错误的。它也不能在头文件中类声明的外部定义,因为那会造成在多个使用该类的源文件中,对其重复定义。

static被引入以告知编译器,将变量存储在程序的静态存储区而非栈上空间,静态

数据成员按定义出现的先后顺序依次初始化,注意静态成员嵌套时,要保证所嵌套的成员已经初始化了。消除时的顺序是初始化的反顺序。

static的优势

可以节省内存,因为它是所有对象所公有的,因此,对多个对象来说,静态数据成员只存储一处,供所有对象共用。静态数据成员的值对每个对象都是一样,但它的值是可以更新的。只要对静态数据成员的值更新一次,保证所有对象存取更新后的相同的值,这样可以提高时间效率。

应用格式

引用静态数据成员时,采用如下格式:

<类名>::<静态成员名>

如果静态数据成员的访问权限允许的话(即public的成员),可在程序中,按上述格式来引用静态数据成员。

注意事项

(1)类的静态成员函数是属于整个类而非类的对象,所以它没有this指针,这就导致了它仅能访问类的静态数据和静态成员函数。

(2)不能将静态成员函数定义为虚函数。

(3)由于静态成员声明于类中,操作于其外,所以对其取地址操作,就多少有些特殊,变量地址是指向其数据类型的指针 ,函数地址类型是一个“nonmember函数指针”。

(4)由于静态成员函数没有this指针,所以就差不多等同于nonmember函数,结果就产生了一个意想不到的好处:成为一个callback函数,使得我们得以将C++和C-based X Window系统结合,同时也成功的应用于线程函数身上。

(5)static并没有增加程序的时空开销,相反她还缩短了子类对父类静态成员的访问时间,节省了子类的内存空间。

(6)静态数据成员在<定义或说明>时前面加关键字static。

(7)静态数据成员是静态存储的,所以必须对它进行初始化。

(8)静态成员初始化与一般数据成员初始化不同:

初始化在类体外进行,而前面不加static,以免与一般静态变量或对象相混淆;

初始化时不加该成员的访问权限控制符private,public等;

初始化时使用作用域运算符来标明它所属类;

所以我们得出静态数据成员初始化的格式:

<数据类型><类名>::<静态数据成员名>=<值>

(9)为了防止父类的影响,可以在子类定义一个与父类相同的静态变量,以屏蔽父类的影响。这里有一点需要注意:我们说静态成员为父类和子类共享,但我们有重复定义了静态成员,这会不会引起错误呢?不会,我们的编译器采用了一种绝妙的手法:name-mangling 用以生成唯一的标志。在各通信公司的笔试面试中经常出现的考题就是static的作用及功能。

vc++编译error LNK2001| unresolved external symbol _WinMain@16|symbol __beginthreadex

在编译VC++程序时,出现Linking… /subsystem:windows
LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16
Debug/TestWin.exe : fatal error LNK1120: 1 unresolved externals
Error executing link.exe.

1.原因

解决这个错误首先在新建工程时要分清Win32 Application和Win32 Console Application,因为Win32 Application的入口函数为WinMain,Win32 Console Application的入口函数是main。就是说,如果你编写传统的C程序,必须建立Win32 Console程序,但VC里面默认的是Win32 Application,于是上面提及的链接错误就就经常出现了。

2.解决办法

将[project]-[settings]-[link]的project options里的 /subsystem:windows
改成 /subsystem:console

我发现与前几天出现的libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main(解决办法见另一篇文章http://renrenstudy.com/php/vc-6-0-compile-error/)有几分相似,好多错误之前都会出现error LNK2001,于是我在网上浏览类似错误原因,还有解决办法,这里整理一下,方便亲们以后查询:

1.libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main

解决方法:
见另一篇文章http://renrenstudy.com/php/vc-6-0-compile-error/

2.LIBCD.lib(wincrt0.obj) : error LNK2001: unresolved external symbol _WinMain@16

解决方法:见上文
3.msvcrtd.lib(crtexew.obj) : error LNK2001: unresolved external symbol _WinMain@16

解决方法:MFC项目的程序入口函数是WinMain, 如果编译项目的Unicode版本, 程序入口必须改为wWinMainCRTStartup, 所以需要重新设置程序入口:
[Project] –> [Settings] –> 选择”C/C++”属性页,
在Category中选择Output,
再在Entry-point symbol中填入wWinMainCRTStartup, 即可

4.nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __beginthreadex

nafxcwd.lib(thrdcore.obj) : error LNK2001: unresolved external symbol __endthreadex

解决方法:

线程运行时库设置错误,这是因为MFC要使用多线程时库, 需要更改设置:
[Project] –> [Settings] –> 选择”C/C++”属性页,
在Category中选择Code Generation,
再在Use run-time library中选择Debug Multithreaded或者multithreaded
其中,
Single-Threaded                  单线程静态链接库(release版本)
Multithreaded                    多线程静态链接库(release版本)
multithreaded DLL                多线程动态链接库(release版本)
Debug Single-Threaded            单线程静态链接库(debug版本)
Debug Multithreaded              多线程静态链接库(debug版本)
Debug Multithreaded DLL          多线程动态链接库(debug版本)
单线程: 不需要多线程调用时, 多用在DOS环境下
多线程: 可以并发运行
静态库: 直接将库与程序Link, 可以脱离MFC库运行
动态库: 需要相应的DLL动态库, 程序才能运行
release版本: 正式发布时使用
debug版本: 调试阶段使用

vc 6.0 编译出错| error LNK2001: unresolved external symbol _main|fatal error LNK1120: 1 unresolved externals|解决方法

今天在学习VC++时,把网上下载好的例子,载入进行编译,可是出现error LNK2001: unresolved external symbol _main和fatal error LNK1120: 1 unresolved externals ,刚开始还挪动到安装目录工程下,没有任何效果,于是百度查找许久,把解决方法写下:

一、原因

1, 你用vc建了一个控制台程序,它的入口函数应该是main, 而你使用了WinMain.

2. 你用vc打开了一个.c/.cpp 文件,然后直接编译这个文件,这个文件中使用了WinMian而不是main作为入口函数。vc这时的默认设置是针对控制台程序的。

Windows子系统设置错误, 提示:libcmtd.lib(crt0.obj) : error LNK2001: unresolved external symbol _main,Windows项目要使用Windows子系统, 而不是Console。

二、 解决方法
1.进入【project】->【setting】->c/c++, 在category(类型目录)中选择preprocessor(预处理器),在processor definitions中删除_console, 添加_windows.

2.进入【project】->【setting】->Link(连接), 在Project options中将 /subsystem:console改为/subsystem:windows.

VC++6.0弹出“0x5003eaed”指令引用的“0x00000000”内存不能为“read”|“打开”文件按钮失效退出(附图)

今天在用VC++6.0时点打开文件时提示弹出“0x5003eaed”指令引用的“ox00000000”内存,该内存不能为“read”。我在网上也看了很多解决办法,总是有些模模糊糊,对初学者不太容易弄懂,特写此博客做一些总结:
一、原因
启动“打开文件”对话框,在 Visual C++ 中使用键盘快捷键或从文件菜单中将产生以下错误:
访问冲突 (0xC0000005) 在 0x5003eaed DEVSHL.DLL 中。
DevShl.Dll 引用在 “0x0000000” 内存。无法读取内存。
当您从 项目 菜单中选择 添加到项目 并单击 文件 时,将出现相同错误。
也有说是Miscrofoft office 2007与VC++不兼容,可是卸载后有时还会出错。
二、解决方法
FileTool.exe 是一个示例,用于替换“打开”和“添加”到项目菜单项在Visual C++中使用开发工具 Studio对象模型。
1.首先下载FileTool.exe,并运行。

下载地址:http://support.microsoft.com/kb/241396

自己选在安装路径,这里安装在D:\Program Files\Microsoft Visual Studio。

2.用VC++打开FileTool文件夹里的“FileTool.dsw”编译一下,就会在FileTool文件夹里的Debug文件夹里找到生成的“FileTool.dll”文件,然后将“FileTool.dll”文件复制到你安装VC++的D:\Program Files\Microsoft Visual Studio\Common\MSDev98\AddIns里,然后重启VC++。

3.打开VC++“工具”菜单下“定制”对话框中单击“附加项和宏文件”选项卡,勾选上“FileTool Developer Studio Add-in”,保存,这时会出现

,以后添加就点“A”,打开就点“O”,不要再使用菜单里的这两个按钮以及快捷键。

VC++学习视频第20集 | Hook编程

有些朋友反映视频看不清楚,下面提供两个视频源文件下载地址,非常清晰:

DBANK网盘下载地址:孙鑫C++视频教程(全20讲).rar

VeryCD(电驴)资源地址:《孙鑫VC++视频教程 20集全》[RMVB]

注:有些朋友反映在线观看的视频看不清楚,而提供的DBANK下载地址和电驴下载地址下载速度又太慢。如果大家有相似情况,可以在本文下方评论中留下邮箱地址,注明“需要C++学习视频孙鑫20集教程,邮箱地址xxxx@xxx”。我会在看到评论后将视频给大家发送到邮箱。

VC++学习视频第19集 | 动态链接库程序的编写

有些朋友反映视频看不清楚,下面提供两个视频源文件下载地址,非常清晰:

DBANK网盘下载地址:孙鑫C++视频教程(全20讲).rar

VeryCD(电驴)资源地址:《孙鑫VC++视频教程 20集全》[RMVB]

注:有些朋友反映在线观看的视频看不清楚,而提供的DBANK下载地址和电驴下载地址下载速度又太慢。如果大家有相似情况,可以在本文下方评论中留下邮箱地址,注明“需要C++学习视频孙鑫20集教程,邮箱地址xxxx@xxx”。我会在看到评论后将视频给大家发送到邮箱。

VC++学习视频第18集 | ActiveX控件的应用与工作原理

有些朋友反映视频看不清楚,下面提供两个视频源文件下载地址,非常清晰:

DBANK网盘下载地址:孙鑫C++视频教程(全20讲).rar

VeryCD(电驴)资源地址:《孙鑫VC++视频教程 20集全》[RMVB]

注:有些朋友反映在线观看的视频看不清楚,而提供的DBANK下载地址和电驴下载地址下载速度又太慢。如果大家有相似情况,可以在本文下方评论中留下邮箱地址,注明“需要C++学习视频孙鑫20集教程,邮箱地址xxxx@xxx”。我会在看到评论后将视频给大家发送到邮箱。

VC++学习视频第17集 | 详细讲解进程间通讯的四种方式

有些朋友反映视频看不清楚,下面提供两个视频源文件下载地址,非常清晰:

DBANK网盘下载地址:孙鑫C++视频教程(全20讲).rar

VeryCD(电驴)资源地址:《孙鑫VC++视频教程 20集全》[RMVB]

注:有些朋友反映在线观看的视频看不清楚,而提供的DBANK下载地址和电驴下载地址下载速度又太慢。如果大家有相似情况,可以在本文下方评论中留下邮箱地址,注明“需要C++学习视频孙鑫20集教程,邮箱地址xxxx@xxx”。我会在看到评论后将视频给大家发送到邮箱。

VC++学习视频第16集 | 事件内核对象、关键代码段(临界区)的讲解

有些朋友反映视频看不清楚,下面提供两个视频源文件下载地址,非常清晰:

DBANK网盘下载地址:孙鑫C++视频教程(全20讲).rar

VeryCD(电驴)资源地址:《孙鑫VC++视频教程 20集全》[RMVB]

注:有些朋友反映在线观看的视频看不清楚,而提供的DBANK下载地址和电驴下载地址下载速度又太慢。如果大家有相似情况,可以在本文下方评论中留下邮箱地址,注明“需要C++学习视频孙鑫20集教程,邮箱地址xxxx@xxx”。我会在看到评论后将视频给大家发送到邮箱。