彻底了解指针数组,数组指针,以及函数指针,以及堆中的分配规则

news/2024/6/29 12:02:14 标签: delete, 编译器, float, pascal, c, 服务器
cle class="baidu_pl">
cle_content" class="article_content clearfix">
content_views" class="htmledit_views">

指针数组和数组指针

content">

下面就简单说说这两个概念:
一:指针数组࿰c;顾名思义࿰c;就是说的首先是一个数组吧࿰c;然后数组的元素是指针而已。
说明形式为:type *pointer_array[constant1][constant2]...[constantn];
例如:int *pai[3];
由于‘*’是自右向左结合࿰c;因此从右向左看࿰c;首先看到[4]说明是一个数组࿰c;是一个包含4个元素的数组࿰c;然后看到‘*’࿰c;显然是指针类型࿰c;由此可以看出数组中存放的是指针而不是一般的类型。同理࿰c;char *pac[2][3]是包含有6个元素࿰c;每一个元素都是一个字符型指针。再来说说他们的初始化:
int *pai[3];既然是一个包含4个整形指针的数组那么其对应的将是一个二维整形数组࿰c;因为一个整形指针对应一个一维整形数组。
那我就用一个二维整形数组来初始化它࿰c;事实上一般也都是这么做的࿰c;这里有一个二维数组࿰c;int arr[3][2]={{1࿰c;2}࿰c;{3࿰c;4}࿰c;{5࿰c;6}},一个三行两列的整形数组࿰c;注意这里的行必须和你的指针数组的维数一致,否则是不允许的࿰c;不信你可以试试。
这个初始化有很多种选择࿰c;以下只列举常见的两中:
第一种也是很好理解的:
for(int i=0;i<3;i++)
  pai[i]=arr[i];
显然arr[i]是每一行的首地址࿰c;相当于一个一维数组的数组名࿰c;如是把它送给一个整形指针pai[i]是理所当然的了。


第二种方法:
在说明指针数组时就初始化:int (*ap)[2]={{1࿰c;2}࿰c;{3࿰c;4}࿰c;{5࿰c;6}};哈哈这个不用说了吧。

注意:不能将二维数组的数组名赋给指针数组的数组名࿰c;pai=arr(错)࿰c;因为两者的类型不一致,二维数组名的类型是指向int[][]型的指针࿰c;而指针数组的的数组名是指向int *[]类型的指针。

c/c++语言中࿰c;指针数组最常用的场合就是说明一个字符串数组。即说明一个数组࿰c;它的每个元素都是一个字符串。

二:数组指针:指向一个数组的指针。
说明形式为:type (*pointer_array)[constant1][constant2]...[constantn];
注意这里的圆括号是必须就将这是由于方括号[]࿰c;较指针说明符“*”的优先级高࿰c;若无此圆括号࿰c;class="tags" href="/tags/BianYiQi.html" title=编译器>编译器将把上述说明解释成成了一个数组指针。
例如:int (*ap)[2];
这样就说明了一个指向包含有2个元素的整形数组的数组指针࿰c;听起来确实有点别扭。不过仔细分析应该还是能理解的࿰c;就是说ap是一个指针࿰c;而它指向的对象是一个指针࿰c;注意不要将它和一个指向一个整形变量的指针搞混了。
同样以一个二维数组来说明其初始化问题࿰c;
int arr[3][2]={{1࿰c;2}࿰c;{3࿰c;4}࿰c;{5࿰c;6}};注意这里的列数必须和数组指针所指的数组的列数相同。
第一种方法:
ap=arr;
为什么这里能这样将二维数组名送给ap呢࿰c;你可以这样理解࿰c;二维数组不就可以看成是一维数组的数组吗࿰c;而一个数组指针它指向的内容就是一个一维数组࿰c;那么你就可以把这个数组指针当做是一个数组名࿰c;只不过这个数组里的元素不是像int,char之类型的࿰c;而是一个数组࿰c;这样你就可以把它和二维数组的数组名联系在一起了吧。文笔不行࿰c;不知道是否说清楚了。


第二种方法:
ap=&arr[0];
这里arr[0]其实就是一维数组的数组名࿰c;将它的地址给ap是很自然的࿰c;因为ap本来就是指向一个一维数组的。
注意这里不能这样初始化:int (*a)[2]={{1,2},{3,4},{5,6}};大家可以想想为什么。
当然他们也可以动态赋值࿰c;由于篇幅就不探讨了。
 

class="postTitle">color:#1a8bc8">[转] 彻底了解指针数组࿰c;数组指针࿰c;以及函数指针࿰c;以及堆中的分配规则

一 :关于指针和堆的内存分配

先来介绍一下指针: 指针一种类型࿰c;理论上来说它包含其他变量的地址࿰c;因此有的书上也叫它:地址变量。既然指针是一个类型࿰c;是类型就有大小࿰c;在达内的服务器上或者普通的PC机上࿰c;都是4个字节大小,里边只是存储了一个变量的地址而已。不管什么类型的指针࿰c;char * ,int * ,int (*) ,string * ,class="tags" href="/tags/FLOAT.html" title=float>float * ࿰c;都是说明了本指针所指向的地址空间是什么类型而已࿰c;了解了这个基本上所有的问题都好象都变的合理了。

在C++中࿰c;申请和释放堆中分配的存贮空间࿰c;分别使用new和class="tags" href="/tags/DELETE.html" title=delete>delete的两个运算符来完成:

指针类型 指针变量名 = new 指针类型 (初始化);

        class="tags" href="/tags/DELETE.html" title=delete>delete 指针名;

例如:

1、 int *p = new int(0);

     它与下列代码序列大体等价:

2、int tmp = 0, *p = &tmp;

区别:p所指向的变量是由库操作符new()分配的࿰c;位于内存的堆区中࿰c;并且该对象未命名。

  

下面是关于new 操作的说明 : 部分引自《C++面向对象开发》

1、new运算符返回的是一个指向所分配类型变量(对象)的指针。对所创建的变量或对象࿰c;都是通过该指针来间接操作的࿰c;而动态创建的对象本身没有名字。

2、一般定义变量和对象时要用标识符命名࿰c;称命名对象࿰c;而动态的称无名对象(请注意与栈区中的临时对象的区别࿰c;两者完全不同:生命期不同࿰c;操作方法不同࿰c;临时变量对程序员是透明的)。

3、堆区是不会在分配时做自动初始化的(包括清零)࿰c;所以必须用初始化式(initializer)来显式初始化。new表达式的操作序列如下:从堆区分配对象࿰c;然后用括号中的值初始化该对象。

下面是从堆中申请数组

1、申请数组空间:

指针变量名=new 类型名[下标表达式];

注意:“下标表达式”不是常量表达式࿰c;即它的值不必在编译时确定࿰c;可以在运行时确定。这就是堆的一个非常显著的特点࿰c;有的时候程序员本身都不知道要申请能够多少内存的时候࿰c;堆就变的格外有用。

2、释放数组空间:

class="tags" href="/tags/DELETE.html" title=delete>delete [ ]指向该数组的指针变量名;

注意:方括号非常重要的࿰c;如果class="tags" href="/tags/DELETE.html" title=delete>delete语句中少了方括号࿰c;因class="tags" href="/tags/BianYiQi.html" title=编译器>编译器认为该指针是指向数组第一个元素的࿰c;会产生不彻底的问题(只了第一个元素所占空间)࿰c;我们通常叫它“内存泄露”࿰c;加了方括号后就转化为指向数组的指针࿰c;整个数组。class="tags" href="/tags/DELETE.html" title=delete>delete [ ]的方括号中不需要填数组元素数࿰c;系统自知。即使写了࿰c;class="tags" href="/tags/BianYiQi.html" title=编译器>编译器也忽略。<<Think in c++>>上说过以前的class="tags" href="/tags/DELETE.html" title=delete>delete []方括号中是必须添加个数的࿰c;后来由于很容易出错࿰c;所以后来的版本就改进了这个缺陷。

下面是个例子࿰c;VC上编译通过

#include

using namespace std;

//#include  //for VC

#include

void main(){

     int n;

     char *p;

     cout<<"请输入动态数组的元素个数"<<endl;

     cin>>n; //n在运行时确定࿰c;可输入17

     p=new char[n]; //申请17个字符(可装8个汉字和一个结束符)的内存空间strcpy(pc,“堆内存的动态分配”);//

     cout<<p<<endl;

     class="tags" href="/tags/DELETE.html" title=delete>delete []p;//释放pc所指向的n个字符的内存空间return ; 

}

通过指针使堆空间࿰c;编程中的几个可能问题

1.动态分配失败。返回一个空指针(NULL)࿰c;表示发生了异常࿰c;堆资源不足࿰c;分配失败。

   data = new double [m]; //申请空间

   if ((data ) == 0)…… //或者==NULL

2.指针删除与堆空间释放。删除一个指针p(class="tags" href="/tags/DELETE.html" title=delete>delete p;)实际意思是删除了p所指的目标(变量或对象等)࿰c;释放了它所占的堆空间࿰c;而不是删除p本身࿰c;释放堆空间后࿰c;p成了空悬指针࿰c;不能再通过p使用该空间࿰c;在重新给p赋值前࿰c;也不能再直接使用p。

3.内存泄漏(memory leak)和重复释放。new与class="tags" href="/tags/DELETE.html" title=delete>delete 是配对使用的࿰c; class="tags" href="/tags/DELETE.html" title=delete>delete只能释放堆空间。如果new返回的指针值丢失࿰c;则所分配的堆空间无法࿰c;称内存泄漏࿰c;同一空间重复释放也是危险的࿰c;因为该空间可能已另分配࿰c;而这个时候又去释放的话࿰c;会导致一个很难查出来的运行时错误。所以必须妥善保存new返回的指针࿰c;以保证不发生内存泄漏࿰c;也必须保证不会重复释放堆内存空间。

4.动态分配的变量或对象的生命期。无名变量的生命期并不依赖于建立它的作用域࿰c;比如在函数中建立的动态对象在函数返回后仍可使用。我们也称堆空间为自由空间(free store)就是这个原因。但必须记住释放该对象所占堆空间࿰c;并只能释放一次࿰c;在函数内建立࿰c;而在函数外释放是一件很容易失控的事࿰c;往往会出错࿰c;所以永远不要在函数体内申请空间࿰c;让调用者释放࿰c;这是一个很差的做法。你再怎么小心翼翼也可能会带来错误。

类在堆中申请内存 :

通过new建立的对象要调用构造函数࿰c;通过class="tags" href="/tags/DELETE.html" title=delete>deletee删除对象要调用析构函数。

CGoods *pc;

pc=new CGoods;      //分配堆空间࿰c;并构造一个无名对象

                              //的CGoods对象;

…….

class="tags" href="/tags/DELETE.html" title=delete>delete pc;  //先析构࿰c;然后将内存空间返回给堆;        堆对象的生命期并不依赖于建立它的作用域࿰c;所以除非程序结束࿰c;堆对象(无名对象)的生命期不会到期࿰c;并且需要显式地用class="tags" href="/tags/DELETE.html" title=delete>delete语句析构堆对象࿰c;上面的堆对象在执行class="tags" href="/tags/DELETE.html" title=delete>delete语句时࿰c;C++自动调用其析构函数。

正因为构造函数可以有参数࿰c;所以new后面类(class)类型也可以有参数。这些参数即构造函数的参数。

但对创建数组࿰c;则无参数࿰c;并只调用缺省的构造函数。见下例类说明:

class CGoods{

          char Name[21];

          int  Amount;

          class="tags" href="/tags/FLOAT.html" title=float>float Price;

          class="tags" href="/tags/FLOAT.html" title=float>float Total_value;

public:

 CGoods(){}; //缺省构造函数。因已有其他构造函数࿰c;系统不会再自动生成缺省构造࿰c;必须显式声明。  

 CGoods(char* name,int amount ,class="tags" href="/tags/FLOAT.html" title=float>float price){

           strcpy(Name,name);

           Amount=amount;

           Price=price;

           Total_value=price*amount;  }

           ……};//类声明结束

下面是调用机制 :

void main(){

      int n;

      CGoods *pc,*pc1,*pc2;

      pc=new CGoods(“hello”࿰c;10࿰c;118000);

      //调用三参数构造函数   pc1=new CGoods();  //调用缺省构造函数  cout<<”输入商品类数组元素数”<<endl;

      cin>>n;

      pc2 = new CGoods[n];

     //动态建立数组࿰c;不能初始化࿰c;调用n次缺省构造函数  

      ……

      class="tags" href="/tags/DELETE.html" title=delete>delete pc;

      class="tags" href="/tags/DELETE.html" title=delete>delete pc1;

      class="tags" href="/tags/DELETE.html" title=delete>delete []pc2;  

}

申请堆空间之后构造函数运行;

释放堆空间之前析构函数运行;

再次强调:由堆区创建对象数组࿰c;只能调用缺省的构造函数࿰c;不能调用其他任何构造函数。如果没有缺省的构造函数࿰c;则不能创建对象数组。

---------------------下面我们再来看一下指针数组和数组指针―――――――――――――

如果你想了解指针最好理解以下的公式 :

     (1)int*ptr;//指针所指向的类型是int

   (2)char*ptr;//指针所指向的的类型是char

   (3)int**ptr;//指针所指向的的类型是int* (也就是一个int * 型指针)

   (4)int(*ptr)[3];//指针所指向的的类型是int()[3] //二维指针的声明

(1)指针数组:一个数组里存放的都是同一个类型的指针࿰c;通常我们把他叫做指针数组。

比如 int * a[10];它里边放了10个int * 型变量࿰c;由于它是一个数组࿰c;已经在栈区分配了10个(int * )的空间࿰c;也就是32位机上是40个byte,每个空间都可以存放一个int型变量的地址࿰c;这个时候你可以为这个数组的每一个元素初始化࿰c;在࿰c;或者单独做个循环去初始化它。

例子:

int * a[2]={ new int(3),new int(4) };     //在栈区里声明一个int * 数组࿰c;它的每一个元素都在堆区里申请了一个无名变量࿰c;并初始化他们为3和4࿰c;注意此种声明方式具有缺陷࿰c;VC下会报错

例如 :

int * a[2]={new int[3],new int[3]};

class="tags" href="/tags/DELETE.html" title=delete>delete a[0];

delet a[10];

但是我不建议达内的学生这么写࿰c;可能会造成歧义࿰c;不是好的风格࿰c;并且在VC中会报错࿰c;应该写成如下 :

int * a[2];

a[0]= new int[3];

a[1]=new int[3];

class="tags" href="/tags/DELETE.html" title=delete>delete a[0];

delet a[10];

这样申请内存的风格感觉比较符合大家的习惯;由于是数组࿰c;所以就不可以class="tags" href="/tags/DELETE.html" title=delete>delete a;编译会出警告.class="tags" href="/tags/DELETE.html" title=delete>delete  a[1];

注意这里 是一个数组࿰c;不能class="tags" href="/tags/DELETE.html" title=delete>delete [] ;

( 2 ) 数组指针 : 一个指向一维或者多维数组的指针;

int * b=new int[10]; 指向一维数组的指针b ;

注意࿰c;这个时候释放空间一定要class="tags" href="/tags/DELETE.html" title=delete>delete [] ,否则会造成内存泄露࿰c; b 就成为了空悬指针.

int (*b2)[10]=new int[10][10]; 注意࿰c;这里的b2指向了一个二维int型数组的首地址.

注意:在这里࿰c;b2等效于二维数组名࿰c;但没有指出其边界࿰c;即最高维的元素数量࿰c;但是它的最低维数的元素数量必须要指定!就像指向字符的指针࿰c;即等效一个字符串,不要把指向字符的指针说成指向字符串的指针。这与数组的嵌套定义相一致。

int(*b3) [30] [20];  //三级指针――>指向三维数组的指针;

int (*b2) [20];     //二级指针;

b3=new int [1] [20] [30];

b2=new int [30] [20];

      两个数组都是由600个整数组成࿰c;前者是只有一个元素的三维数组࿰c;每个元素为30行20列的二维数组࿰c;而另一个是有30个元素的二维数组࿰c;每个元素为20个元素的一维数组。

      删除这两个动态数组可用下式:

class="tags" href="/tags/DELETE.html" title=delete>delete [] b3;  //删除(释放)三维数组;

class="tags" href="/tags/DELETE.html" title=delete>delete [] b2;  //删除(释放)二维数组;

再次重申:这里的b2的类型是int (*) ࿰c;这样表示一个指向二维数组的指针。

b3表示一个指向(指向二维数组的指针)的指针࿰c;也就是三级指针.

( 3 ) 二级指针的指针

看下例 :

int (**p)[2]=new (int(*)[3])[2];

       p[0]=new int[2][2];

       p[1]=new int[2][2];

       p[2]=new int[2][2];

       class="tags" href="/tags/DELETE.html" title=delete>delete [] p[0];

       class="tags" href="/tags/DELETE.html" title=delete>delete [] p[1];

       class="tags" href="/tags/DELETE.html" title=delete>delete [] p[2];

       class="tags" href="/tags/DELETE.html" title=delete>delete [] p;

注意此地方的指针类型为int (*),碰到这种问题就把外边的[2]先去掉࿰c;然后回头先把int ** p=new int(*)[n]申请出来࿰c;然后再把外边的[2]附加上去;

p代表了一个指向二级指针的指针࿰c;在它申请空间的时候要注意指针的类型࿰c;那就是int (*)代表二级指针࿰c;而int (**)顾名思义就是代表指向二级指针的指针了。既然是指针要在堆里申请空间࿰c;那首先要定义它的范围:(int(*)[n])[2]࿰c;n 个这样的二级指针࿰c;其中的每一个二级指针的最低维是2个元素.(因为要确定一个二级指针的话࿰c;它的最低维数是必须指定的࿰c;上边已经提到)。然后我们又分别为p[0],p[1],p[2]…在堆里分配了空间࿰c;尤其要注意的是:在释放内存的时候一定要为p[0],p[1],p[2],单独class="tags" href="/tags/DELETE.html" title=delete>delete[] ,否则又会造成内存泄露࿰c;在class="tags" href="/tags/DELETE.html" title=delete>delete[]p 的时候一定先class="tags" href="/tags/DELETE.html" title=delete>delete p[0]; class="tags" href="/tags/DELETE.html" title=delete>delete p[1]࿰c;然后再把给p申请的空间释放掉 class="tags" href="/tags/DELETE.html" title=delete>delete [] p ……这样会防止内存泄露。

(3)指针的指针;

int ** cc=new (int*)[10]; 声明一个10个元素的数组࿰c;数组每个元素都是一个int *指针࿰c;每个元素还可以单独申请空间࿰c;因为cc的类型是int*型的指针࿰c;所以你要在堆里申请的话就要用int *来申请;

看下边的例子  (vc & GNUclass="tags" href="/tags/BianYiQi.html" title=编译器>编译器都已经通过);

       int ** a= new int * [2];     //申请两个int * 型的空间

       a[1]=new int[3];        //为a的第二个元素又申请了3个int 型空间,a[1]指向了此空间首地址处

       a[0]=new int[4];        为a的第一个元素又申请了4个int 型空间࿰c;a[0] 指向了此空间的首地址处

       int * b;

       a[0][0]=0;

       a[0][1]=1;

       b=a[0];

       class="tags" href="/tags/DELETE.html" title=delete>delete [] a[0]       //一定要先释放a[0]࿰c;a[1]的空间࿰c;否则会造成内存泄露.;

       class="tags" href="/tags/DELETE.html" title=delete>delete [] a[1];

       class="tags" href="/tags/DELETE.html" title=delete>delete [] a;

       b++;

       cout<<*b<<endl;       //随机数

注意 :因为a 是在堆里申请的无名变量数组࿰c;所以在class="tags" href="/tags/DELETE.html" title=delete>delete 的时候要用class="tags" href="/tags/DELETE.html" title=delete>delete [] 来释放内存࿰c;但是a的每一个元素又单独申请了空间࿰c;所以在class="tags" href="/tags/DELETE.html" title=delete>delete [] a之前要先class="tags" href="/tags/DELETE.html" title=delete>delete [] 掉 a[0],a[1],否则又会造成内存泄露.

(4) 指针数组 :

   

我们再来看看第二种 :二维指针数组

int *(*c)[3]=new int *[3][2];

如果你对上边的介绍的个种指针类型很熟悉的话࿰c;你一眼就能看出来c是个二级指针,只不过指向了一个二维int * 型的数组而已࿰c;也就是二维指针数组。

例子 :

 int *(*b)[10]=new int*[2][10];//

b[0][0]=new int[100];

b[0][1]=new int[100];

*b[0][0]=1;

cout <<*b[0][0]<<endl;    //打印结果为1

class="tags" href="/tags/DELETE.html" title=delete>delete [] b[0][0];

class="tags" href="/tags/DELETE.html" title=delete>delete [] b[0][1];

class="tags" href="/tags/DELETE.html" title=delete>delete [] b;

cout<<*b[0][0]<<endl;    //打印随机数

 这里只为大家还是要注意内存泄露的问题࿰c;在这里就不再多说了。

如果看了上边的文章࿰c;大家估计就会很熟悉࿰c;这个b是一个二维指针࿰c;它指向了一个指针数组

第二种 :

        int **d[2];表示一个拥有两个元素数组࿰c;每一个元素都是int ** 型࿰c;这个指向指针的指针:)

   d不管怎样变终究也是个数组࿰c;呵呵࿰c;

   如果你读懂了上边的࿰c;那下边的声明就很简单了:

   d[0]=new int *[10];

   d[1]=new int * [10];

     class="tags" href="/tags/DELETE.html" title=delete>delete [] d[0];

     class="tags" href="/tags/DELETE.html" title=delete>delete [] d[1];

具体的就不再多说了 :)

二 : 函数指针 

关于函数指针࿰c;我想在我们可能需要写个函数࿰c;这个函数体内要调用另一个函数࿰c;可是由于项目的进度有限࿰c;我们不知道要调用什么样的函数࿰c;这个时候可能就需要一个函数指针;

int a();这个一个函数的声明;

ing (*b)();这是一个函数指针的声明;

让我们来分析一下࿰c;左边圆括弧中的星号是函数指针声明的关键。另外两个元素是函数的返回类型(void)和由边圆括弧中的入口参数(本例中参数是空)。注意本例中还没有创建指针变量-只是声明了变量类型。目前可以用这个变量类型来创建类型定义名及用sizeof表达式获得函数指针的大小:

unsigned psize = sizeof (int (*) ()); 获得函数指针的大小

// 为函数指针声明类型定义

typedef int (*PFUNC) ();

PFUNC是一个函数指针࿰c;它指向的函数没有输入参数࿰c;返回int。使用这个类型定义名可以隐藏复杂的函数指针语法࿰c;就我本人强烈建议我们大内弟子使用这种方式来定义;

下面是一个例子࿰c;一个简单函数指针的回调(在GNUclass="tags" href="/tags/BianYiQi.html" title=编译器>编译器上通过࿰c;在VC上需要改变一个头文件就OK了)

#include              //GNU class="tags" href="/tags/BianYiQi.html" title=编译器>编译器 g++ 实现

using namespace std;

/*                              //vc 的实现

#include "stdafx.h"

#include

*/

#define DF(F) int F(){  cout<<"this is in function "<<#F<<endl;\

      return 0;       \

}

//声明定义DF(F)替代 int F();函数;

DF(a); DF(b); DF(c); DF(d); DF(e); DF(f); DF(g); DF(h); DF(i);     //声明定义函数 a b c d e f g h i

// int (*pfunc)();              //一个简单函数指针的声明

typedef int(*FUNC)();   //一个函数指针类型的声明

FUNC ff[] = {a,b,c,d,e,f,g,h,i};   //声明一个函数指针数组࿰c;并初始化为以上声明的a,b,c,d,e,f,g,h,i函数

FUNC func3(FUNC vv){    //定义函数func3࿰c;传入一个函数指针࿰c;并且返回一个同样类型的函数指针

      vv();

      return vv;

}

/*FUNC func4(int (*vv)()){      //func3的另一种实现

      vv();

      return vv;

}*/

int main(){

      for(int i=0;i

              FUNC r=func3(ff[ i ]);

              cout<<r()<<endl;                //输出返回值࿰c;只是返回了0

      }

      return 0;

}

到目前为止࿰c;我们只讨论了函数指针及回调而没有去注意ANSI C/C++的class="tags" href="/tags/BianYiQi.html" title=编译器>编译器规范。许多class="tags" href="/tags/BianYiQi.html" title=编译器>编译器有几种调用规范。如在Visual C++中࿰c;可以在函数类型前加_cdecl࿰c;_stdcall或者_class="tags" href="/tags/PASCAL.html" title=pascal>pascal来表示其调用规范(默认为_cdecl)。C++ Builder也支持_fastcall调用规范。调用规范影响class="tags" href="/tags/BianYiQi.html" title=编译器>编译器产生的给定函数名࿰c;参数传递的顺序(从右到左或从左到右)࿰c;堆栈清理责任(调用者或者被调用者)以及参数传递机制(堆栈࿰c;CPU寄存器等)。

好了࿰c;先到此为止吧࿰c;写这篇文章耗费了基本上快半天的时间了࿰c;很多事情还没有做࿰c;等改天有时间再回来整理࿰c;所有的源程序都放在openlab3服务器上我的目录下lib/cpp下࿰c;大家可以去拿。不知道的登陆openlab3 然后cd ~chengx/lib/cpp就可以看到了。

还有很复杂的声明可能也是一种挑战 比如<<Think in c++>>里的

int (*(*f4())[10]();的声明,f4是一个返回指针的函数࿰c;该指针指向了含有10个函数指针的数组࿰c;这些函数返回整形值;不是这个函数有特别之处࿰c;而是Bruce Eckel 说的“从右到左的辨认规则”是一种很好的方法࿰c;值得我们去学习࿰c;感谢他:)

最后我想应该跟大家说一下࿰c;写程序应该就象JERRY所说的:简单就是美;我们应该遵循一个原则 : KISS (Keep It Simple,Stupid ,尽量保持程序简单 出自 :《Practical C programming》)࿰c;把自己的程序尽量的简单明了࿰c;这是个非常非常好的习惯。

来源: http://www.eefocus.com/andysun001/blog/10-06/192012_760f9.html#articletop
cle>

http://www.niftyadmin.cn/n/1736429.html

相关文章

linux板级设备的初始化过程

start_kernel() -> setup_arch(&command_line) -> mdesc setup_machine(machine_arch_type) -> list lookup_machine_type(nr) -> return struct machine_desc. 内核在启动的时候就是最先运行start_kernel() , 然后她就会调用体系结构相关的setup_arch(&…

第一讲:2410的初始化流程(设备初始化)

第一讲&#xff1a;2410的初始化流程&#xff08;设备初始化&#xff09;1 在mach-smdk2410.c中先通过MACHINE_START()定义了machine_desc的变量,其中注册了smdk2410_map_io(), s3c2410_init_irq(), smdk2410_init()这3个回调函数. 这3个回调函数会在系统起来的时候setup_arch(…

Lcd帧缓冲设备

帧缓冲(framebuffer)是Linux为显示设备提供的一个接口&#xff0c;是把显存抽象后的一种设备&#xff0c;它允许上层应用程序在图形模式下直接对显示缓冲区进行读写操作&#xff0c;这种操作是抽象的&#xff0c;统一的。用户不必关心物理显存的位置&#xff0c;换页机制等等具…

Frame Buffer

Linux的帧缓冲&#xff08;Frame Buffer&#xff09;之一&#xff1a;原理及控制参数 大家都知道Unix/Linux系统是由命令驱动的。X&#xff0d;Window&#xff0d;System是Unix/Linux上的图形系统&#xff0c;它是通过X&#xff0d;Server来控制硬件的。但有一些Linux的发行版在…

framebuffer 简介

FrameBuffer 是出现在 2.2.xx 内核当中的一种驱动程序接口。Linux 工作在保护模式下&#xff0c;所以用户态进程是无法象 DOS 那样使用显卡 BIOS 里提供的中断调用来实现直接写屏&#xff0c;Linux 抽象出 FrameBuffer 这个设备来供用户态进程实现直接写屏。Framebuffer 机制模…

对FrameBuffer的简单解释和用法示例

大家都知道Unix/Linux系统是由命令驱动的。那么最基本的系统是命令行的&#xff08;就是想DOS一样的界面&#xff09;。X&#xff0d;Window&#xff0d;System是Unix/Linux上的图形系统&#xff0c;它是通过X&#xff0d;Server来控制硬件的。但有一些Linux的发行版在引导的时…

mmap与内存管理

mmap与内存管理- mmap系统调用的编程实例 收藏 1 mmap的使用 mmap地址映射是Linux系统提供的一种功能强大的系统调用&#xff0c;最典型的应用是用于显卡内存的映射。同样&#xff0c;对于普通的硬盘文件也可以进行mmap系统调用。 #include <sys/mman.h> void * m…

linux spi驱动分析

关于spi的学习&#xff0c;我觉得最好的方法还是看Linux的源代码&#xff0c;主要是driver/spi/spi.c(h)&#xff0c;spidev.c(h)。spi dev的示例可以看看at25.c&#xff0c;spi总线的示例可以看omap_uwire或者spi_s3c24xx.c和spi_s3c24xx_gpio.c。在看这些代码之前&#xff0c…