1.26——C语言数组、函数

一、数组

1.数组越界下标可能导致哪些后果?


程序仍能正常运行
程序会异常终止或崩溃
程序能正常运行,但无法得到正确的结果

其他情况

2.传递数组有哪些方式?

 将数组作为参数传递给函数和将指向数组中第一个元素的指针传递给函数是完全等价的。将数组作为参数传递给函数时可以采用值传递和地址传递两种方式,前者需要完整地复制初始数组,但比较安全;后者的速度要快得多。

数组和指针之间的这种联系会引起一些混乱,例如,以下两种定义是完全相同的。

void func(chare [max])
{
 /**********/
}
void func(char *a)
{
 /**********/
}

3.数组和指针的关系
指针: 一个地址
数组: 连续存储的 n 个相同类型的元素

在绝大多数情况下,当一个数组类型的变量被使用的时候,会被自动的转换为一个指向数组第一元素的指针。
少数数组变量不会转换的情况包括数组变量出现在 sizeof 运算符后时,等。

数组不能作为函数的参数传递。函数参数位置的数组形式的参数声明,实际上是声明了一个指针。在函数参数位置上,数组形式的声明与指针声明是等价的。

4.数组的下标总是从0开始吗?

是的,在C语言中,对数组a[max](max是一个编译时可知的值)来说,它的第一个和最后一个元素分别是a[0]和a[max-1]。在其他一些语言中,情况可能有所不同,例如,在BASIC语言中数组a[max]的元素是a[1]到a[max]。注意,a[max]是一个有效的地址,但该地址中的值并不是数组a的元素。a[max]是数组后面第一个元素的值,可以使用,但不能查看该地址中的值。

5.数组作为参数传递给函数时,可以通过sizeof得到数组的大小吗?

不可以,当把数组作为函数的参数时,无法在程序运行时通过数组本身来告诉函数改数组的大小,因此函数的数组参数相当于指向该数组第一个元素的指针。这意味着把数组传递给函数的效率非常高,也意味着程序员必须通过某种机制告诉数组参数的大小。

为了告诉函数数组参数的大小,人们通常采用一下两种方法。

第一种方法是将数组和表示数组大小的值一起传递给函数,例如,memcpy()函数。

第二种方法是引入某种规则来结束一个数组,例如,在c语言中字符串再试以‘\0’结束的,而一个指针数组总是以空指针结束的。

6.指针或带下标的数组名都可以访问元素,哪一种更好呢?

与使用下标相比,使用指针能使C编译程序更轻易地产生优质的代码。假设你的程序中有这样一段代码:

/*X  la some type*/
X a[max];
X *p;
X x;
int i;

为了遍历数组a中的所有元素,你可以采用这样的一种循环方式(方式a):

//方式a
for(i = 0; i < max; ++i)
{
x = a[i];
/*------*/
}

你也可以采用另一种循环方式(方式b):

//方式b
for(p = a; p < &a[max]; ++p)
{
x = *p;
/*------*/
}

这两种方式中的初始情况和递增运算是相同的,作为循环条件的比较表达式也是相同的,区别在于“x=a[]”和"x=*p",前者要确定a[i]的地址,因此需要将i和类型x的大小相乘后再与数组a中第一个元素的地址相加;后者只需要简介引用指针p。简介引用快,而乘法运算却比较慢。

7.可以把另一个地址赋给一个数组命名吗?

不可以,数组名不能被放在赋值运算符的左边(它不是一个左值,更不是一个可以被修改的左值)。一个数组是一个对象,而它的数组名就是指向这个对象的第一个元素的指针。

加入一个数组是用extern或static说明的,则它的数组名是在链接时克制的一个常量,你不能修改这样一个数组名的值,就像你不能修改7的值一样。

8.字符串和数组有什么不同?

数组的元素可以任意一种类型,而字符串是一种特别的数组,它使用了一种众所周知的、确定长度的规则。

根据处理字符串的不同,语言可以分为两种,一种是简单地将字符串看作一个字符数组,另一种是将字符串看作一种特别的呃类型。C语言属于前一种,但有一点补充,即C字符是以一个NUL字符结束的。数组的值和数组中第一个元素的地址(或指向该元素的指针)是相同的,因此通常一个C语言字符串和一个字符指针是等价的。

一个数组的长度可以任意的,当数组名用做函数的参数时,函数无法通过数组名本身知道数组的大小,因此必须引入某种规则。对字符串来说,这种规则就是自负车的最后一个字符是ASCII字符“NUL(‘\0’)”。

9.array_name 和 &array_name 有什么不同?

前者是指向数组中第一个元素的指针,后者是指向整个数组的指针。数组是一种类型,它有三个要素,即基本类型、大小、数组的值。你可以用一个指针指向整个数组的值。

char a[max]
char *p;
/*pa is declared below*/
pa = &al
p = a;  /*=&a[0]*/

在运行上述代码后,发现p和pa的打印结果是一个相同的值,即p和pa指向同一个地址。但是,p和pa指向的对象是不同的。

10.为什么用const说明的常量不能用来定义一个数组的初始大小

并不是所有的常量都可以用来定义一个数组的初始大小,在C程序中,只有C语言的常量表达式才能用的定义一个数组的初始大小。然而,在C++中,情况有所不同。一个常量表达式的值在程序运行期间是不变的,并且是编译程序能计算出来的值。在定义数组的大小时,你必须使用常量表达式,例如,可以使用数字、预定义的常量标识符、一个sizeof表达式、一个有常量表达式组成的表达式或者使用枚举常量。

二、函数

1.C语言中main函数与其他的函数有什么不同?

C源程序是由函数组成的,函数是C源程序的基本模块,通过对函数的调用可以实现特定的功能。由于采用了函数模块式的结构,C语言易于实现结构化程序设计,使得程序的层次结构更加清晰,便于程序的编写、阅读、调试。

main函数是主函数,它可以调用其他函数,而不允许被其他函数调用,因此C程序的执行总是从main函数开始的,完成对其他函数的调用后再返回到main函数,最后有main函数结束整个程序。一个C源程序必须有、也只能有一个主函数main。

2.函数的定义与声明有什么区别?

 声明和定义是完全同的概念,声明是告诉编译器“这个函数或者变量可以在哪找到,它的模样像什么”。而定义则是告诉编译器,“在这里建立变量或函数”,并且为它们分配内存空间。

函数声明与定义:
  函数的声明如:int Add(int, int);函数声明就是给函数取名并指定函数的参数类型,返回值类型。值得注意的是,在C语言中,有一点跟C++不同,对于带空参数表的函数如:int func();在C中代表可以带任意参数(任意类型,任意数量),而在C++中代表不带任何参数。
  函数的定义如:int Add(int a, int b){} 函数定义看起来跟函数声明很像,但是它有函数体,如果函数体中使用了参数,就必须为参数命名,这里大括号代替了分号的作用。

3.如何定义一个函数?

一个普通函数的定义形式:

返回类型 函数名(参数列表)

{

     函数体;

 }

如:
(1)带返回值的定义

int sum1(int m)
{
	int i,sum=0; 
	for(i=1;i<=m;i++)     
		sum=sum+i;      
	return sum;
}

(2)不带返回值的定义

void sum2(int m)
{
	int i,sum=0; 
	for(i=1;i<=m;i++)     
		sum=sum+i;      
}

4.形参与实参的关系

形式参数和实际参数 
函数的参数分为形参和实参两种。在本小节中,进一步介绍形参、实参的特点和两者的关系。形参出现在函数定义中,在整个函数体内都可以使用,离开该函数则不能使用。实参出现在主调函数中,进入被调函数后,实参变量也不能使用。形参和实参的功能是作数据传送。发生函数调用时,主调函数把实参的值传送给被调函数的形参从而实现主调函数向被调函数的数据传送。 
函数的形参和实参具有以下特点: 
1. 形参变量只有在被调用时才分配内存单元,在调用结束时,即刻释放所分配的内存单元。因此,形参只有在函数内部有效。函数调用结束返回主调函数后则不能再使用该形参变量。 
2. 实参可以是常量、变量、表达式、函数等,无论实参是何种类型的量,在进行函数调用时,它们都必须具有确定的值,以便把这些值传送给形参。因此应预先用赋值,输入等办法使实参获得确定值。 
3. 实参和形参在数量上,类型上,顺序上应严格一致,否则会发生类型不匹配”的错误。 
4. 函数调用中发生的数据传送是单向的。即只能把实参的值传送给形参,而不能把形参的值反向地传送给实参。 因此在函数调用过程中,形参的值发生改变,而实参中的值不会变化。

5.传值与传址有什么不同?
在C语言中常会遇到传值和传址问题,特别是在对栈操作的时候常会遇到类似InitStack(Stack &S)、DestroyStack(Stack &S)之类函数的情况,为什么这些函数在传参的时候会使用Stack &S的方式而不是使用Stack S的方式呢?主要原因是一个是传值不会改变原栈里面的值,一个是传址可以改变原栈里的值,我们在使用堆栈进行运算的时候,常用新建栈、删除栈、插入栈这些都需要对栈进行修改,需要用Stack &S,而像查询遍历之类的不需要改变栈里面的东西,则可以使用Stack S,但是使用Stack &S也不会出错。

6.指针变量或数组名作为函数参数的注意问题

指针变量作实参在调用是仍然符合”值传递“的规则,将其“值”赋给形参,相当于复制。此时数据在实参与形参传递仍是单向的,调用函数不会影响实参的“值”(即指针变量中所存的地址)。而与简单变量不同的是,指针变量复制给形参的“值”本身是一个地址,这个地址的形参访问其所致变量创造了可靠条件。

7.数组名作为函数参数注意问题

数组名本身是一个特殊的指针变量,其值是数组的首地址,因此作为实参时其传给形参的是内存中某指定单元的地址,调用过程中形参数组与实参数组占用同一个内存单元,因此对形参数组的操作也就是对实参数组的操作,对实参数组与形参数组来说数据传递表现为“双向”的,而对实参变量与形参变量而言数据的传递仍然是单向的。

8.结构体数组作函数参数的两种情况

用结构体数组作函数参数包含两类情况:结构体数组元素作为实参和结构体数组名作为实参。两类情况仍然服从数据的单向值传递原则,只不过前者传给形参的是某些变量的值,而后者传给形参的是结构体数组的首地址。

1.结构体数组元素作为实参:符合结构体变量作为实参规则,采取单向“值传递”方式将结构体变量所占的内存单元的内容全部顺序复制给形参(函数调用期间形参也要占用内存单元)。

2.结构体数组名作为实参:通整形数组数组名作为实参一样,传递给形参的是内存中指定单元的地址,调用过程中形参数组与实参数组占用同一段内存单元,因此对形参数组的操作也就是对实参数组的操作,对数组的操作表现为双向性。

9.如何编写有多个返回值的C语言函数

1.利用全局变量:全局变量的作用域是从定义变量开始到程序结束,把多个返回值定义成全局变量,当函数被调用时,全局变量被更改,再把更改后的全局变量应用于主调函数中。被更改后的全局变量值即为函数的多个返回值。

2.传递数组指针:把多个返回值作为数组元素定义成一个数组,并使改数组的地址作为函数的形式参数,以传址的方式传递数组参数。函数被调用后,形参数组改变导致实参改变,再从改变后的实参数组元素中获得函数的多个返回值。

3.传递结构体指针:把要求返回的数个值定义成一个结构体,然后同样以传递结构体指针方式把结构体的指针传递给形参结构体指针,那么函数中对形参的结构体的修改也是对实参结构体的修改,函数调用后获取的实参结构体成员即函数的多个返回值。

 

10.什么是回调函数

对指针的应用是C语言编程的精髓所在,而回调函数就是C语言里面对函数指针的高级应用。简而言之,回调函数是一个通过函数指针调用的函数。如果你把函数指针(函数入口地址)传递给另一个函数,当这个函数指针被调用它所指向的函数时,我们就说这个函数是回调函数。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值