怎样从函数返回多个值

怎样从函数返回多个值

l  怎样从函数返回多个值(利用极坐标到直角坐标的转换做例子)

n  1.传入多个指针指向不同的地址,让函数填入需要返回的值

u  clip_image002

n  2.让函数返回包含需要值的结构

u  clip_image003

n  结合指针和结构,让函数接收结构指针,然后再填入需要的数据

u  clip_image004

n  不得已的时候,可以使用全局变量,但是这个方法不推荐,GOD晓得有没有可能在多线程的其他程序中会修改全局变量,所以这是迫不得已时采用的方法。

4 kinds of pointer to function

向函数传递的指针有4种:

clip_image001

l  指向非常量数据的非常量指针non-constant pointer to non-constant data

n  具有最高的数据访问权限,在这种情况下,程序可以通过解引用指针来改写该指针所指向的存储单元的数据,同时,指针也可以被改写,使其指向其他的数据项;

l  指向非常量数据的常量指针constant pointer to non-constant data

n  在被调函数中,可以处理数据,但是不能修改该数据;

l  指向常量数据的非常量指针non-constant pointer to constant data

n  所指向的内存空间总是不变的,而存储在这个内存空间中的数据可以通过指针来改写

l  指向常量数据的常量指针constant pointer to constant data

n  具有最小的访问权限,这样的指针所指向的内存空间总是不变的,并且该内存空间中过的数据也是不能改写的

数组和指针

数组和指针

数组和指针的统一性C语言的长处之一,用指针可以很方便地访问数组和模拟动态分配的数组。

多数的数组引用都会退化为数组第一个元素的指针,这是C语言中数组和指针等价的基础。因此,数组在C语言中是个二等公民:你永远也不能作为一个整体操作数组(例如:复制或将它们传入数组),因为一旦你提到数组的名字,你所得到的就是一个指针而不是一个数组了。

       关于char a[]=”hello”char *p=”world”的区别:

clip_image002[4]

       其中a为数组预留了6个字符的位置,而p只是提供了一个指针。本质区别为:一旦出现类似a的数组和类似p的指针,以后的操作就会按照不同的方法计算。

       数组和指针的区别

数组是一个由(同一类型的)连续元素组成的预先分配的内存块;

指针是一个对任何位置的(特定类型的)数据元素的引用;

数组自动分配空间,但是不能重分配或改变大小;

指针必须被赋值以指向分配的空间(可能使用malloc),但是可以随意重新赋值(即指向不同的对象),同时除了表示一个内存块的基址之外,还有许多用途;

数组和指针的联系

指针可以模拟数组;

几乎没有所谓数组的东西,下标操作符实际上是个指针操作符;

从更高的层次上看,指向一块内存的指针本质上也就是一个数组;

 

如果a是数组int a[10],那么对a的引用就是“int型的指针,而&a“10int的数组的指针

 

       sizeof操作符如果能够判断出数组的大小,就会返回数组的大小,如果数组的大小未知或者数组已经退化为指针,就不能提供数组的大小了。

 

使用结构和指针

使用结构和指针

         链表linked list就是一些包含数据的独立数据结构(通称为节点)的集合。

clip_image002

         单链表是一种使用指针来存储值的数据结构。链表中的每个节点包含一个字段,用于指向链表的下一个节点。另外有一个独立的根指针指向链表的第一个节点。由于节点在创建时是采用动态分配内存的方式,所以他们可能分散于内存之中。但是,遍历链表时根据指针进行的,所以节点的物理排列无关紧要。单链表只能以一个方向进行遍历。

         单链表添加新节点的两个步骤:

l  新节点的link字段必须设置为指向它的目标后续节点;

l  前一个节点的link字段必须设置为指向这个新节点;

l  对于根节点,可以通过保存一个指向必须进行修改的link字段的指针,而不是保存一个指向前一个节点的指针,来消除对起始位置的影响。

clip_image004

         双链表的每个节点包含两个link字段,其中fwd为指向链表的下一个节点,bwd为指向链表的上一个节点。所以,为了把一个新节点插入到双链表中,我们必须修改4个指针。新节点的前向和后向link字段必须被设置,前一个节点的后向link和后一个节点的前向link也需要进行修改,使它们指向这个新节点。

 

数组

数组

一维数组

       数组名的值是一个指针常量,也就是数组第一个元素的地址。

       注意数组名为指针常量,而不是指针变量,不能改变。

下标引用

       对于已知的数组长度,编译器将不检查它的下标,因为涉及到位置和长度方面的信息,将会影响程序的执行效率。

       2[array]在有些编译器中的含义为*(array + 2)

指针与下标

       下标绝不会比指针更有效率,但指针有时会比下标更有效率

       当然,指针的高效率前提是被正确地使用。

指针的效率

clip_image002

clip_image004

       从上面的一个数组复制到另一个数组的总结为:

l  当你根据某个固定数目的增量在一个数组中移动时,使用指针变量将比使用下标产生更高的代码,当这个增量是1并且机器具有地址自动增量模型时,这点表现更为突出;

l  声明为寄存器变量的指针通常比位于静态内存和堆栈中的指针效率更高(具体提高的幅度取决于你所使用的机器);

l  如果你可以通过测试一些已经初始化并经过调整的内容来判断循环是否应该终止,那么你就不需要使用一个单独的计数器;

对于使用指针或者数组的判断:

Ø  程序的维护是软件产品的主要成本所在,所以要多考虑一下维护方便;

Ø  有些机器在设计时使用了特殊的指令,用于执行数组下标的操作,目的就是为了使这种极为常用的操作更加快速,在这种机器上,编译器将使用这些特殊的指令来实现下标表达式,但编译器并不一定会用这些指令来实现指针表达式,所以在这种机器上,下标表达式可能会比指针更有效率;

Ø  关于使用哪种方式,最主要的还是要先确认程序中到底是那段代码占用了绝大部分的运行时间,然后把精力用在这些代码上,努力改进它们。

数组和指针

 

 

 

 

 

int a[5];       

      

       会直接分配内存空间。

 

 

?

int *b;       

 

并未被初始化为指向任何现有的内存空间,甚至根本不会被初始化。

作为函数参数的数组名

void strcpy(char *buffer, char const *string);

       该函数中const的优点:

Ø  这是一个良好的文档习惯,通过函数原型就可以发现该数据不会被修改,而不用阅读完整的函数定义;

Ø  编译器可以捕捉到任何试图修改该数据的意外错误;

Ø  这类声明允许向函数传递const参数。

声明数组参数

int strlen(char *string);

int strlen(char string[]);

这两个其实含义是一样的,调用函数时实际传递的是一个指针,所以函数的形参实际上是个指针。但是为了使程序员新手更容易上手一些,编译器也接受数组形式的函数形参。

       但是两个定义中,指针的声明更加准确一些

字符数组的初始化

       当用于初始化一个字符数组时,字符串为一个初始化列表,在其他任何地方,表示一个字符串常量

指向数组的指针

       int matrix[3][10];

       int (*p)[10] = matrix;–指向数组的指针

 

作为函数参数的多维数组

void func(int (*mat)[10]);         指向整型数组的指针

void func(int **mat);             指向整型指针的指针

初始化

       多维数组的初始化最好加上花括号,比如:

int a[2][3][4] =

{

{

{},

{1,2,3,4},

{}

},

{

{},

{1,1,1,1},

{}

}

};

原因如下;

l  利于显示数组的结构;

l  对于不完整的初始化列表,相当有用,比如上面的只初始化其中的部分数据,如果不加花括号,要好好数对位置才行;

数组长度自动计算

       要求是只有第一维可以缺省提供,其余的几维必须显示写出。但其实也可以缺省,主要是由于必须要把子维的数组要达到最长要求才行(即子初始化列表中必须有一个完整的形式),否则还是不对。

总结

       在绝大多数表达式中,数组名的值是指向数组第一个元素的指针。这个规则只有两个例外

l  sizeof返回整个数组占用的字节而不是一个指针所占用的字节;

l  单目操作符&返回一个指向数组的指针,而不是一个指向数组第一个元素的指针的指针。

 

指针和数组并不相等:

l  当我们声明一个数组时,它同时也分配了一些内存空间,用于容纳数组元素;

l  当我们声明一个指针时,它只分配了用于容纳指针本身的空间。

 

只要有可能,函数的指针形参都应该声明为const