1指针作为函数形参 可以 没有返回值。 2定义的void函数,无论带不带指针作型参都不会报错。你的是其他错误,尝试去读编译器查的错。 你可以把你写的程序发出来,大家看看就很了然了。
关于如何将C++程序转换为C程序的探讨
[摘要] C++是在C的基础上演变而来的,在我们的实际应用中用C++编的源程序就一定比用C编的源程序可行吗不尽然,因为C++解释器比C语言解释器占用的存储空间要大。在嵌入式系统中为了节省有限的存储空间,降低成本,将用C++语言写的源程序转换成C语言源程序是很有必要的。
[关键词] C++ 程序 C程序 转换探讨
一、C++与C程序概述
C++是在C的基础上演变而来的,C++与C区别最大的就是C++中的类的概念和特性,将C++改为C的问题,就转换成如何将类化去的问题。方法有两种:第一种是将C++中的面向对象特征去掉,先全部理解源代码的逻辑,然后改写;第二种是在C中保留面向对象的部分特征,用结构体实现类的功能。第一种方法,对于类的数目很少的情况还可以,如果类的数目比较多,全部理解源代码,然后重写就很耗时间,而且很容易出错,更甚者,如果遇到大的项目想全部理解源代码几乎是不可能的。这个时候就需要采用第二种方法了,你可以一个类一个类的改没有什么太高的难度,如果没有笔误的话,几乎不会出错,而且根本不需要理解程序逻辑,也许改完后你对程序所要实现的功能还一无所知。这倒不是说一无所知对大家有好处,只是说这种方法的与程序逻辑本身的无关性。所以,在此用第二种方法对C++的一些特性,以及如何在C里实现或者替代,作一些初步的探讨。
二、C++程序到C程序的转换
为了便于下面的讨论先做几点说明:
1函数Ixx为类xx的构造函数的实现。
2原类的成员函数改为前缀为 结构体名+‘_’的函数。
3函数指针U为原类的析构函数的声明;
4U+结构体名称 为原类的析构函数的实现;
5Fun_+结构体名 为对该结构体成员函数指针进行指向。
以后遇到上述情况将不再说明。
(一)类的成员函数和数据成员
由于struct没有对成员的访问权限进行控制,必须加入额外的机制进行访问控制,这样一来就使得程序复杂化了,所以只能放弃访问权限的控制。
1对于类的数据成员可以直接转为C中结构体的数据成员。
2函数则需转化为对应的函数指针,因为struct里不允许出现函数的声明和定义。而函数前如果有virture,inline等修饰符也要去掉,如函数void funca(int a);改为void (funca)(struct B p,int a);大家可以看到函数指针的原型里加了一个指针struct B的指针,这是因为要在函数内部对类的成员进行 *** 作,要靠该指针指定结构体的成员。在类的成员函数里,实际上在参数列里也隐含有一个指向自身的this指针。
3对于静态成员则要定义成全局变量或全局函数,因为结构体中不能有静态成员。
(二)类的构造函数
类在实例化的时候会调用类的缺省构造函数,在struct里,要定义一个同名函数指针指向一个具有构造函数功能的初始化函数,与构造函数不同的是,要在初始化函数里加入进行函数指针初始化的语句。使用的时候在创建结构体变量的时候要用malloc而不是new,并且这个时候要手工调用初始化函数。
(三)类的析构函数
类的析构函数所作的工作是释放所占的资源。
在C中,无论是哪个struct都用函数指针U替代析构函数。之所以所有的struct都用指针U是基于如下情况:
如果将子类指针赋给基类指针,基类指针在释放的时候不必考虑调用哪个函数名的析构函数,只需调用成员函数U即可。成员函数U需要像一般成员函数一样在fun_类名()函数中指定。
类的析构函数是由系统调用的,在C中则要显式调用。至于何时调用,要准确判断。
(四)类的拷贝构造函数
类的拷贝构造函数主要用途是加快以下情况下类的构建速度:
1作为参数传给函数。(additem(Itema))
2作为函数返回值。
3实例化类时作参数。
这三种情况下都是由系统直接调用类的拷贝构造函数而不是构造函数。
注意:C=D;不会调用拷贝构造函数,这种情况下使用的是重载‘=’运算符的方法。(详见运算符重载);
由于C中定义struct变量的时候,使用的全部是指针,不会用到拷贝构造函数,所以暂不考虑。对于原来函数参数或者返回值需要类变量的,要全部转化为类指针的方式。实例化类时作参数的情况,可以通过另外定义一个带参数的构造函数来解决。
(五)类的内联函数和虚函数
内联函数和虚函数的修饰符inline 、virture 要全部去掉。内联函数体则要去掉,将内联函数在外面定义成一个函数。如:
class B
{
…
virture void funb();
inline int add()const
{return a+b;};
private:
int a;
int b;
…
}
改为:
typedef classB B;
struct classB
{
…
void (funb)(struct classB p);
int (add)(struct classB p);
int a;
int b;
}
void classB_funb(B p)
{
…
}
int classB_add(B p)
{
return p->a+p->b;
}
void fun_classB(B p)
{
…
p->funb=classB_funb;
p->add= classB_add;
}
(六)重载
类中重载有函数重载和运算符重载两种:
1函数的重载
函数重载满足的条件是:函数名相同,参数个数或者参数类型不同。这样在调用的时候,会根据你输入的参数不同,调用不同的函数。在C中只好分别起不同的名字,没有别的解决办法。
既然是全局变量就可以,一般全局指针变量初始值赋值为NULL,比如在定义时:int p=NULL;
在使用时先判断是否为NULL,如果是NULL那么还不可用,否则就是函数已经赋值了。
首先看看下面的一些变量的存储问题
int a = 0; 全局初始化区
char p1; 全局未初始化区
main()
{
int b; 栈
char s[] = "abc"; 栈 (这个地方是你第一个函数)
char p2; 栈
char p3 = "hello world"; hello world\0在常量区,p3在栈上(这个地方是你第二个函数)
static int c =0; 全局(静态)初始化区
p1 = (char )malloc(10);
p2 = (char )malloc(20);
分配得来得10和20字节的区域就在堆区。
strcpy(p1, "hello world"); hello world\0放在常量区,编译器可能会将它与p3所指向的"hello world"
优化成一个地方。
}
你如果看完了,我现在说,
char strArray[] = "hello world";strArray[] 是在栈区申请了空间存放
"hello world",他的生存周期就是在本函数内部,此函数一结束,他就释放了,所以你的全局字符指针就找不到了。
相应的:
char strString = "hello world";strString指向了字符串常量的地址,就是说strString的地址就是字符串常量的地址,它是在常量区,由系统维护的,不会在函数结束后释放,所以在主函数中还找得到。
另外,
char strArray[] = "hello world";是在函数运行时赋值的;
char strString = "hello world";是在编译时就指定了的。
OK就说这么多了
#include <stdioh>
void F(int a, int b){printf("hello %d %d\n", a, b);};
typedef void (&pF)(int, int);
int main()
{
int a = 1, b = 3;
pF x = F;
x(a, b);
printf("0x%X 0x%X", F, x);
}
全局变量是被存储在内存中的全局静态区的, 全局变量的声明总是定义, 因为编译器会将他们的值初始化为其默认值, 可以在全局范围内定义变量, 但不能在全局范围内实施 *** 作, 因为 *** 作是在函数中实现的, 你要分清初始化和赋值的不同, 所以比如有一个全局变量a;
int a; // 等价于int a(int());
则以下对a的 *** 作都是违法的:
++a;
a--;
a += 1;
程序是由函数构建的, 而不是文件构建的, 所以以上 *** 作根本执行不到, 所以编译不同过, 而且C/C++编译器貌似只识别全局范围内变量的声明定义, 所以在你试图global = 10的时候它认为你要创建一个不带类型标识符的变量global, 所以给你一个错误; 另外它又发现你企图声明的这个变量与已存在的变量名有冲突, 所以它又给你一个错误
一般对数组初始化可以用以下方法实现:
(1)在定义数组时对数组元素赋以初值。如:
static
int
a[10]={0,1,2,3,4,5,6,7,8,9};
经过上面的定义和初始化后,a[0]=0,a[1]=1,…,a[9]=9。
(2)初始化时可以只对一部分元素赋初值。例如:
static
int
a[10]={0,1,2,3,4};
定义的数组有10个元素,但只对其中前5个元素赋了初值,后5个元素初值为0。
(3)如果想使一个数组的元素值全部为0,可以用下面的方法:
static
int
a[10]={0,0,0,0,0,0,0,0,0,0};
不能用:
static
int
a[10]={010};
如果对static型数组不赋初值,系统会对定义的所有数组元素自动赋以0值。
(4)在对全部数组元素赋初值时,可以不指定数组长度。
用MFC制作的工程由很多文件构成,它不能象一般C++程序那样随意在类外定义全局变量,在这里要想定义能被工程内多个文件共享的全局变量和函数必须用一些特殊方法才行。实际上有多种方法可以实现,这里只介绍两种方法。
一、在应用程序类中定义
用MFC生成的工程中都有一个名为CxxxApp的类,它派生于CWinApp类。这个类主要进行程序的初始化,生成文档、视图对象等工作。可以把需要全局访问的变量和函数定义为这个类的成员变量和成员函数,就可以实现全局访问了。
从严格意义上讲,这种变量和函数并不是全局的,因为它仍然只是类中的成员,只是由于很容易获得CxxxApp类的指针,所以可以在文档、视 图、对话框以及各种自定义类中访问到它们,达到与全局变量类似的效果。访问时用函数“AfxGetApp()”获得CxxxApp类的指针,用 “AfxGetApp()->成员”访问变量或函数。
例:
Testh:(应用程序类头文件)
class CTestApp : public CWinApp + i; Q+ }
public:
int x; //全局变量
int f(int y); //全局函数 ) X s+ s' N+ U
…………
Testcpp:(应用程序类程序文件)
int CTestApp::f(int y) //全局函数定义
{
定义在CTestApp类中的变量和函数可以在其它类中被访问。比如在视图的某函数中要访问变量x和函数f():
void CTestView::xyz()
{
CTestApp app = (CTestApp )AfxGetApp(); //生成指向应用程序类的指针 /app
int z = app->f(1); //访问函数f() 0 R,
}
这样,变量x和函数f()可以视作为全局的
用这种方法实现的全局变量和全局函数虽比较简单,但也有缺点,一是访问不太方便,每次都需要获取应用程序类的指针;再就是把一些与应用程序类本身无关的变量和函数放在里面,使这个类看上去怪怪的,破坏了类的封装。
二、用静态变量和静态函数实现
很喜欢API函数的那种调用方法,不论在哪个类中只要用“::API函数”就可以调用了。合理利用静态类型(static)可以实现与此相似的全局变量和全局函数。
静态变量和静态函数有如下性质:
若在一个类中用关键字static声明数据成员,则这个数据成员就只存在一个拷贝,无论该类创建了多少个实例,它始终只存在一个,即使该类的实例一个也没创建,它也存在。
若在一个类中用关键字static声明函数,该函数可以用“类名::函数名”方式访问,无需引用该类的实例,甚至这个类的实例可以不存在。
利用这个性质实现的全局变量和函数使用起来很方便。
值得注意的是,全局变量和全局函数最好集中封装,不要在文档、视图等类内部定义,这样用起来才有全局的感觉。
例:
1、添加一个没有基类的新类,设类名起为CPublic,姑且称之为公用类
单击“Insert”菜单下的“New Class”命令,选择“Class type”为“Generic Class”,在“Name”栏中填入类名“CPublic”,单击“OK”,则新类建立完毕。
2、包含公用类的头文件,使各个类都能访问它
CPublic的头文件应包含在应用程序类的头文件中,这样在其它类中引用CPublic类时就不需要再包含了。
Testh:(应用程序类头文件))
#include "Publich" //包含公用类头文件
class CTestApp : public CWinApp
{
…………
};
3、在公用类中定义全局变量和全局函数,均使用static修饰,静态变量还必须在类外定义和初始化
Publich:(公用类头文件)
class CPublic
{
public:
CPublic();
virtual ~CPublic();
public:
static int x; //全局变量 % v
static int time; //全局变量 #
static int f(int y); //全局函数
}
在公用类中对静态变量进行初始化和定义函数体:
Publiccpp:(公用类程序文件)
int CPublic::x = 0; //初始化全局变量
int CPublic::time; //定义全局变量
CPublic::CPublic()
{
}
CPublic::~CPublic()
{
}
int CPublic::f(int y) //全局函数,这里不要再加static
{ y++; 4
return y;
} 3 O2 V% Z6 r+ r( k! L
4、全局量的使用
使用变量:CPublic::变量名6 H)
使用函数:CPublic::函数()
如在视图的某函数中访问变量x和函数f():
void CTestView::xyz()
{
CPublic::x = 0; //访问变量x;
CPublic::time = CPublic::f(1); //访问函数f()'
…………
}
在其它类中访问x、time和f()的方法与此相同。
5、几点注意:
① 由于静态量可独立于类存在,不需要生成CPublic类的实例。
② 静态数据成员的定义和初始化必须在类外进行,如例中x的初始化;变量time虽然没有初始化,但也必须在类外进行定义。由于没有生成CPublic类的实例,所以它的构造函数和析构函数都不会被执行,在里面做什么工作都没有什么意义。
③ 如果静态函数需要访问CPublic类内的变量,这些变量也必须为静态的。因为非静态量在不生成实例时都不会存在。 这里x虽为类内成员,但如果不生成CPublic类的实例,就会出现函数f()存在,而变量x不存在的问题。
总之,用没有实例的类管理全局量是一个不错的选择,它具有集中管理,使用方便的好处。当然,除非特别必要,全局量还是少用为好,一个好的编程者决不会随意滥用全局量的,一个封装做得不好的程序,在修改维护时会让吃足苦头。
1、全局静态变量、局部静态变量
2、静态函数。只能在 本源文件 中使用
3、c++中,static关键字可以定义 类 中的静态成员变量。类中static静态数据成员拥有一块独立的单独存储区,而不管创建了多少个该类的对象。这些对象共享这一块静态存储区。
4、c++中,static关键字可以定义 类 中的静态成员函数。静态成员函数也是类的一部分,而不是对象的一部分。这些对象共享这一块静态存储区
1、数组是用来存储多个相同类型的集合。数组名是首元素的地址。
2、指针相当一个变量,它存放的是其他变量在内存中地址。指针名只想内存的首地址。
区别:
1、赋值
2、存储方式:
数组:连续存放、连续内存。不是在静态区就是在栈上。
指针:灵活、内存空间不确定。
3、初始化
4、指针 *** 作
数组名的指针 *** 作:
定义:指向函数的指针。
应用:回调
c语言:全局或者静态变量,初始化发生在任何代码执行之前,属于编译期初始化。
c++:全局或者静态对象当且仅当对象首次用到时才进行构造。
解析:
作用域 :C++里作用域可分为6种: 全局 , 局部 , 类,语句 , 命名空间 和 文件作用域 。
静态全局变量 :全局作用域+文件作用域,所以无法在其他文件中使用。
静态局部变量 :局部作用域,只被初始化一次,直到程序结束。
静态局部变量 :局部作用域,只被初始化一次,直到程序结束
生命周期 :静态全局变量、静态局部变量都在静态存储区,直到程序结束才会回收内存。类静态成员变量在静态存储区,当超出类作用域时回收内存。
答:能。
原因: 因为在 编译时对象 就绑定了 函数地址 ,和指针空不空没关系。
定义 : 野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
产生原因 : 释放内存后指针不及时置空(野指针),依然指向了该内存,那么可能出现非法访问的错误。这些我们都要注意避免。
避免办法:
1、初始化置为NULL;
2、申请内存判空;
3、指针释放后置空;
4、使用智能指针;
1、首先从作用域考虑 :C++里作用域可分为6种: 全局 , 局部 , 类 , 语句 , 命名空间 和 文件 作用域。
全局变量 :全局作用域,可以通过 extern 作用于其他非 定义的源文件
静态全局变量 :全局作用域+文件作用域,所以 无法在其他 文件中使用。
局部变量 :局部作用域,比如函数的参数,函数内的局部变量等等。
静态局部变量 :局部作用域,只被 初始化一次,直到程序结束
2、从所在空间考虑 :除了 局部变量在栈上 外,其他都在静态存储区。因为静态变量都在静态存储区,所以下次调用函数的时候还是能取到原来的值。
3、生命周期 : 局部变量在栈上,出了作用域就回收内存 ;而全局变量、静态全局变量、静态局部变量都在静 态存储区,直到程序结束才会回收内存。
区别:
1、宏不是函数 ,省去压栈退栈过程,提高效率。 内联函数本质是一个函数 , 并且内联函数本身不能直接调用自身。
2、 宏函数 是在预编译的时候把所有的宏名用宏体来替换,简单的说就是字符串替换。 而内联函数 则是在编译的时候进行代码插入,编译器会在每处调用内联函数的地方直接把内联函数的内容展开,这样可以省去函数的调用的开销,提高效率。
3、 宏定义 是没有类型检查的,无论对还是错都是直接替换; 而内联函数 在编译的时候会进行类型的检查,内联函数满足函数的性质,比如有返回值、参数列表等。
1、赋值顺序不同 :++ i 是先加后赋值;i ++ 是先赋值后加;++i和i++都是分两步完成的。
2、效率不同 :后置++执行速度比前置的慢
3、 i++ 不能作为左值,而++i 可以
4、 两者都不是原子 *** 作
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)