【C语言】带你快速掌握——数组、函数的运用
一维数组多维数组函数的定义和调用函数声明实际参数return 语句程序终止
欢迎来到我的博客!💃💃
🏡🏡推荐博客:August_._一起持续学习,不断总结,共同进步🎊🎊
文章目录
数组
数组是含有多个相同数据值的数据结构,这些数据值称为元素(可以是任何类型),可以根据元素在数组中所处的位置把它们一个个地选出来。
我们声明数组,需要指明数组元素的类型和数量。例如, int a [10];
数组的长度不仅可以用任何(整数)常量表达式指定,还可以用宏来定义数组的长度,因为程序以后可能需要调整数组的长度。
#define N 10
int a[N];
一维数组
为了存取特定的数组元素,可以在写数组名的同时在后边加上一个用方括号围绕的整数值[这被称为对数组取下标或进行索引 ]。数组元素始终从0开始,因此长度为 n 的数组元素的索引是0 ~ n—1。
例如,如果 a 是含有10个元素的数组,那么可以依次标记为 a [0], a [1],…, a [9];
1、数组下标
形如 a [ i ] 的表达式是左值,因此数组元素可以像普通变量一样使用:
a[0] = 1;
printf ("%d \n", a[5]);
++a[i];
数组和 for 循环结合在一起使用。许多程序所包含的 for 循环是为了对数组中的每个元素执行一些操作。下面给出了在长度为 N 的数组 a 上的一些常见操作示例。
for (i=0; i<N; i++) a[i]=0; //数组数据全部为0
for (i=0; i<N; i++) scanf ("%d", &a[i]); //输入数组
for (i=0; i<N; i++) sum += a [i]; //数据读入数组
注意,在调用 scanf 函数读取数组元素时,就像对待普通变量一样,必须使用取地址符号&,或者写出
for ( i = 0; i < N; i ++ ) scanf ( “%d”, &a[ i ] ) ;
2、数组初始化
数组初始化器最常见的格式是一个用花括号括起来的常量表达式列表,常量表达式之间用逗号进行分隔:
int a[10] = {1,2,3,4,5,6,7,8,9,10} ;
如果初始化器比数组短,那么数组中剩余的元素赋值为0;
利用这一特性,可以很容易地把数组初始化为全0:
int a[10] = { 0 };
初始化器完全为空是非法的,所以要在花括号内放上一个0。
初始化器比要初始化的数组长也是非法的。如果给定了初始化器,可以省略数组的长度:
int a[] = {1,2,3,4,5,6,7,8,9,10};
3、指示器
C99中方括号和其中的常量表达式一起,组成一个指示器。
对于数组中只有相对较少的元素需要进行显式的初始化,而其他元素可以进行默认赋值。例如:
int a[15]={0,0,29,0,0,0,0,0,0,7,0,0,0,0,48};
int a[15]={ [2]=29, [9]=7, [14]=48 };
它不仅可以使赋值变得更简短、更易读,而且赋值的顺序不再是一个问题,我们也可以将上述的例子重写为
int a[15]={ [14]=48, [9]=7, [2]=29 };
如果数组的长度是省略的,指示器可以指定任意非负整数;编译器将根据最大的值推断出数组的长度。例如,指示符的最大值为23,因此数组的长度为24:
int b[]={ [5]=10, [23]=13, [11]=36, [15]=29 };
初始化器中可以同时使用逐个元素初始化和指示器:
int c[10]={ 5, 1, 9, [4]=3, 7, 2, [8]=6 }; //没有指定值的元素均赋予默认值0
4、对数组使用sizeof运算符
运算符 sizeof 既可以确定数组的大小(字节数),也可以用计算数组元素的大小。
当然也可以得到数组的长度:
sizeof ( a ) / sizeof (a [0])
有些编译器会对该表达式 给出一条警告消息,因为 i 的类型可能是 int (有符号类型),而 sizeof 返回的值类型为 size_t (无符号类型)。把有符号整数与无符号整数相比较是很危险的,尽管在这里这样做没问题(因为 i 和 sizeof ( a ) / sizeof (a [0])都是非负值)。为了避免出现这一警告,可以将 i 的类型改成 t ,或者强制转换为有符号整数:
for ( i=0; i < (int)(sizeof(a)/sizeof(a[0])); i++)
a[i]=0;
但写起来不太方便,定义一个宏来表示它常常是很有帮助的:
#define SIZE (( int )( sizeof ( a )/ sizeof ( a [0])))
for ( i=0; i< IZE; i++)
a[i]=0;
多维数组
数组可以有任意维数。例如,下面的声明产生一个二维数组:
int m [5][9]; //数组 m 有5行9列
二维数组的行和列下标都从0开始索引,为了访问 i 行 j 列的元素,表达式需要写成 m [ i ][ j ]
嵌套的 for 循环是处理多维数组的理想选择。例如,
#define N 10
double ident [N][N];
int row, col;
for (row=0; row<N; row++) //循环行
for (col=0; col<N; col++) //循环列
if (row==col)
ident[row][col]=1;
else
ident[row][col]=0;
1、多维数组初始化
C99的指示器对多维数组也有效。例如,
double arr [2][2] = { [0][0] = 1, [1][1] =1 } ;
像通常一样,没有指定值的元素都默认值为0。
2、常量数组
无论一维数组还是多维数组,都可以通过在声明的最开始处加上单词 const 而成为"常量":
const char Arr[ ] = { ‘0’ , ‘1’ , ‘2’ , ’ A ’ , ’ B ’ , ’ E ’ , ’ F ');
把数组声明为 const 有两个主要的好处:
- 表明程序不会改变数组,这对以后阅读程序的人可能是有价值的信息
- 有助于编译器发现错误——const 会告诉编译器,我们不打算修改数组
函数
函数是 C 程序的构建块。每个函数本质上是一个自带声明和语句的小程序。可以利用函数把程序划分成小块,这样便于人们理解和修改程序。由于不必重复编写要多次使用的代码,函数可以使编程不那么单调乏味。此外,函数可以复用:一个函数最初可能是某个程序的一部分,但可以将其用于其他程序。
函数的定义和调用
1、函数定义
函数定义格式:
返回类型函数名(形式参数)
复合语句
函数的"返回类型"是函数返回值的类型。下列规则用来管理返回类型。
- 函数不能返回数组,但关于返回类型没有其他限制
- 指定返回类型是 void 类型,说明函数没有返回值
- 如果省略返回类型,C89会假定函数返回值的类型是 int 类型,但在C99中这是不合法的
需要在每个形式参数的前面说明其类型,形式参数间用逗号进行分隔。如果函数没有形式参数,那么在圆括号内应该出现 void 。
注意:
即使几个形式参数具有相同的数据类型,也必须分别说明每个形式参数的类型。
int average(int a, int b)
{
int sum;
sum = a + b;
return sum/2;
}
函数体内声明的变量专属于此函数,其他函数不能对这些变量进行检查或修改。
2、函数调用
函数调用由函数名和跟随其后的实际参数列表组成:
average ( x , y );
ave ();
非 void 函数调用会产生一个值,该值可以存储在变量中,进行测试、显示或者用于其他用途:
avg = average (x, y);
if (avg > 0)
printf (" meng\n ");
printf (" This is %d \n ", avg);
函数声明
事实上, C 语言并没有要求函数的定义必须放置在调用点之前。假设重新编排程序,使 函数的定义放置在 main 函数的定义之后:
#include <stdio.h>
int main (void)
{
double x, y, z;
printf (" Enter:");
scanf ("%lf %lf", &x, &y);
printf (" %lf and %lf :%lf \n ", x, y, average(x, y));
return 0;
}
double average (double a, double b)
{
return (a + b)/2;
}
当遇到 main 函数中的 average 函数调用时,编译器没有任何关于 average 函数的信息,但编译器不会给出错消息,而是假设 average 函数返回 int 型的值,编译器为该函数创建了一个隐式声明。当编译器在后面遇到 average 的定义时,它会发现函数的返回类型实际上是 double 而不是 int ,从而我们得到一条出错消息。
所以,为了避免出错,我们可以在调用前声明每个函数。
函数声明使得编译器可以先对函数进行概要浏览,而函数的完整定义以后再给出。
返回类型 函数名(形式参数);
这类函数声明称为函数原型。
下面是为函数添加了声明后程序的样子:
#include <stdio.h>
double average ( double a, double b );
int main ( void )
{
double x, y, z;
printf (" Enter:");
scanf ("%lf %lf", &x, &y);
printf (" %lf and %lf :%lf \n ", x, y, average(x, y));
return 0;
}
double average ( double a, double b )
{
return ( a + b )/2;
}
函数原型不需要说明函数形式参数的名字,只要显示它们的类型就可以了:
double average ( double , double );
通常最好是不要省略形式参数的名字,因为这些名字可以说明每个形式参数的目的,并且提醒程序员在函数调用时实际参数的出现次序。
实际参数
形式参数出现在函数定义中,它们以假名字来表示函数调用时需要提供的值;
实际参数是出现在函数调用中的表达式。
在 C 语言中,实际参数是值传递的:调用函数时,计算出每个实参的值并且把它赋给相应的形参。
实际参数的值传递既有利也有弊。因为形式参数的修改不会影响到相应的实际参数,所以可以把形式参数作为函数内的变量来使用,这样可以减少真正需要的变量数量。例如:
int power (int x, int n)
{
int i, result = 1;
for (i=1; i<=n; i++)
result = result * x ;
return result ;
}
因为 n 只是原始指数的副本,所以可以在函数体内修改它,也就不需要使用变量 i 了:
int power (int x , int n)
{
int result=1;
while (n-->0)
result = result * x ;
return result ;
}
1、实际参数的转换
C 语言允许在实际参数的类型与形式参数的类型不匹配的情况下进行函数调用。
编译器在调用前遇到原型,就像使用赋值一样,每个实参的值被隐式地转换成相应形参的类型。
编译器在调用前没有遇到原型。编译器执行默认实参提升:
- 把 float 类型的实际参数转换成 double 类型
- 执行整值提升,即把 char 类型和 short 类型的实际参数转换成 int 类型
2、数组型实际参数
数组经常被用作实际参数。 当形式参数是一维数组时,可以不说明数组的长度:
int f (int a[ ])
{
```
}
实参可以是元素类型正确的任何一维数组。如果函数需要传递给它的数组的长度,我们必须把长度作为额外的参数提供出来。例如:
//函数的原型
int sum_array (int a[], int n);
//或者省略形参名字
int sum_array (int[], int);
注意,在把数组名传递给函数时,不要在数组名的后边放置方括号:
total = sum_array ( b[], LEN ); //WRONG
如果形式参数是多维数组,声明参数时只能省略第一维的长度。
3、在数组参数声明中使用static
C99允许在数组参数声明中使用关键字 static
例如,将 static 放在数字3之前,表明数组 a 的长度至少可以保证是3:
int sum_array (int a[static 3], int n)
static 是一个"提示", C 编译器可以据此生成更快的指令来访问数组。
(如果编译器知道数组总是具有某个最小值,那么它可以在函数调用时预先从内存中取出这些元素值,而不是在遇到函数内部需要用到这些元素的语句时才取出相应的值。)
注意:
如果数组参数是多维的, static 仅可用于第一维(例如,指定二维数组的行数)
4、复合字面量
复合字面量的格式:
先在一对圆括号内给定类型名,随后是一个初始化器,用来指定初始值。
total = sum_array ( ( int [ ] ) { 3, 0, 3, 4, 1 } , 5 );
复合字面量可以包含任意的表达式,不限于常量。例如:
total = sum_array ( ( int [ ] ) {2*i , i + j , j * k ) , 3);
其中 i 、 j 、 k 都是变量。复合字面量的这一特性极大地增加了其实用性。
复合字面量为左值,所以其元素的值可以改变。如果要求其值为"只读",可以在类型前加上 const ,例如(const int [ ] ) {5 , 4}。
return 语句
非 void 的函数必须使用 return 语句来指定将要返回的值。
return 语句的格式:
return 表达式;
注意:
(1)如果 return 语句中表达式的类型和函数的返回类型不匹配,那么系统会把表达式的类型隐式地转换成返回类型
(2)如果没有给出表达式, return 语句可以出现在返回类型为 void 的函数中
例如:
void print (void)
printf (" To C , or not to C : that is the question .\n ");
return ;
但是, return 语句不是必需的,因为在执行完最后一条语句后函数将自动返回。
如果非 void 函数最后没有执行 return 语句,那么程序试图使用函数的返回值,其行为是未定义的。
程序终止
终止程序除了执行 return 语句,还可以调用 exit 函数——属于<stdlib.h>头。传递给 exit 函数的实参和 main 函数的返回值具有相同的含义:两者都说明程序终止时的状态。
为了表示正常终止,传递0:
exit (0);
//可以用 EXIT_SUCCESS 来代替(效果是相同的):
exit ( EXIT_SUCCESS );
传递 EXIT_FAILURE 表示异常终止:
exit ( EXIT_FAILURE );
EXIT_SUCCESS 和 EXIT_FAILURE 都是定义在<stdlib.h>中的宏。 EXIT_SUCCESS 和 EXIT_FAILURE 的值都是由实现定义的,通常分别是0和1。
作为终止程序的方法, return 语句和 exit 函数关系紧密。事实上, main 函数中的语句
return 表达式;
等价于
exit(表达式);
俩者的差异:
不管哪个函数调用 exit 函数都会导致程序终止, return 语句仅当由 main 函数调用时才会导致程序终止。
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)