4.C语言之数组

本文深入探讨C语言中数组的各种操作技巧,包括数组大小确定、初始化、复制及作为函数参数的用法等,并介绍C99引入的新特性如变长数组等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 数组大小

我相信,在C#/Java中,更多的人愿意用List<T>来取代数组,一方面是List提供了较多的方法,另一方面也无需我们去指定数组的大小。

那么在C语言中,我们既然需要必须指定数组的大小,而一般来讲,很多数组大小事我们无法确定并且经常会发生变化的,那么我们最好的方式就是用宏定义来限定数组的大小。

 
  1. #define SIZE 10  
  2.  
  3. int main (void)  
  4. {  
  5.     int a[SIZE];  

 

如果包含多个数组的话,用宏就很难记忆,那么我们就可以利用sizeof运算符。

 
  1. int main (void)  
  2. {  
  3.     int a[]={1,3,4,55,6,7,89,9,0};  
  4.     int i ;  
  5.     printf("%d",(int)sizeof(a)/(int)sizeof(a[0]));  
  6.     for(i=0;i<(int)sizeof(a)/(int)sizeof(a[0]);i++)  
  7.     {  
  8.         a[i]=0;  
  9.     }  
  10.     for(i=0;i<(int)sizeof(a)/(int)sizeof(a[0]);i++)  
  11.     {  
  12.         printf("%d\n",a[i]);  
  13.     }  

注意,我们之前说过,sizeof返回的值是size_t,因此,我们在计算时,最好将其先强制类型转换为我们可以控制的类型。

 

2. 数组初始化

一般情况下,我们初始化数组都是把整数数组初始化为0,那么我们一般会怎么做呢?

 
  1. #define SIZE 5  
  2.  
  3. int main (void)  
  4. {  
  5.     int a[SIZE]={0,0,0,0,0};  

 

那么如过SIZE=100怎么办,那么很多人都会这样去做。

 
  1. #define SIZE 100  
  2.  
  3. int main (void)  
  4. {  
  5.     int a[SIZE];  
  6.     int i ;  
  7.     for(i=0;i<SIZE;i++)  
  8.     {  
  9.         a[i]=0;  
  10.     }  

 

其实我们完全不用麻烦,这么一句代码就可以搞定了。

 
  1. #define SIZE 100  
  2.  
  3. int main (void)  
  4. {  
  5.     int a[SIZE]={0};  

 

在C99中,提供了一种初始化式,使得我们可以这样来写。

 
  1. #define SIZE 100  
  2.  
  3. int main (void)  
  4. {  
  5.     int a[SIZE]={[5]=100,[50]=49};  

 

而其他的数字就都默认为0。那么我们来考虑这样一段代码:

 
  1. #define SIZE 10  
  2.  
  3. int main (void)  
  4. {  
  5.     int a[SIZE]={1,2,3,4,5,[0]=6,7,8};  

那么在C99中,这段代码的结果究竟是什么呢?这个就需要我们来了解一下数组初始化式的原理。

其实,编译器在初始化式数组列表时,都会记录下一个待初始化的元素的位置,比如说在初始化index=0的元素时,会记录下1,这样以此类推,但是当初始化index=5的时候,首先根据他的初始化式记录下一个待初始化的元素时index=1,然后初始化index=0的元素为6。那么也就是说:最后的结果应该是{6,7,8,4,5,0,0,0,0,0}。

3. 常量数组

当数组加上const就变成了常量数组,常量数组主要有两个好处。

1. 告诉使用者,这个数组是不应该被改变的。

2. 有助于编译器发现错误。

4. C99的变长数组

这是个很爽的东西,我们再也不必担心为数组指定大小而发愁了,指定大了会造成空间的浪费,指定小了又不够用。

在C99中,他的长度会由程序执行时进行计算。

方式如下:

 
  1. int main (void)  
  2. {  
  3.     int size;  
  4.     int a[size];  
  5.     scanf("%d",&size);  

 

5. 数组的复制

很多时候,我们需要把一个数组的元素复制到另一个数组上,我们大多数人第一个想到的就是循环复制。

 
  1. #define SIZE 10  
  2.  
  3. int main (void)  
  4. {  
  5.     int a[SIZE];  
  6.     int b[SIZE];  
  7.     int i ;  
  8.     for(i=0;i<SIZE;i++)  
  9.     {  
  10.         a[i]=i;  
  11.     }  
  12.     for(i=0;i<SIZE;i++)  
  13.     {  
  14.         b[i]=a[i];  
  15.     }  
  16.     for(i=0;i<SIZE;i++)  
  17.     {  
  18.         printf("%d",b[i]);  
  19.     }  

 

其实还有一种更好的方法是使用memcpy方法,这是一个底层函数,它把内存的字节从一个地方复制到另一个地方,效率更高。

  1. #include <stdio.h>  
  2. #include <stdlib.h>  
  3. #include <string.h>  
  4.  
  5. #define SIZE 10  
  6.  
  7. int main (void)  
  8. {  
  9.     int a[SIZE];  
  10.     int b[SIZE];  
  11.     int i ;  
  12.     for(i=0;i<SIZE;i++)  
  13.     {  
  14.         a[i]=i;  
  15.     }  
  16.     memcpy(b,a,sizeof(a));  
  17.     for(i=0;i<SIZE;i++)  
  18.     {  
  19.         printf("%d",b[i]);  
  20.     }  

 

 

5. 数组作为函数参数

函数是我们学习程序设计语言最基本的东西了,我在此不再赘述。只讨论一种特殊情况,就是数组作为函数的参数传递。

我们都知道,其实在传递数组的时候,实际上是传递了数组首元素的指针。明确了这一点之后,我们就可以思考下面的问题。

既然他只是传递了数组首元素的指针,那么他必然无法知道整个数组的大小,因此,我们如果希望在函数中用到数组的长度,必须要进行显式传递。

 

 
  1. int Sum(int a[],int size)  
  2. {  
  3.     int i ,sum=0;  
  4.     for(i=0;i<size;i++)  
  5.     {  
  6.         sum+=a[i];  
  7.     }  
  8.     return sum;  

 

那么既然,函数无法检测传入数组的长度,我们也可以利用这一个特性来计算数组前N个数的和,或者是利用这一特性来告诉函数,实际上,数组的有效长度要小于数组的真实长度。

6. C99中变长数组作为函数参数

首先在数组一节中,我们谈到了C99中的变长数组是个很好的东西。那么我们来看看变长数组作为函数参数的情况。

我们看之前的代码,size和a[]并没有直接的联系,那么当变长数组作为参数就会解决这样的情况。

 

 
  1. int Sum(int size,int a[size])  
  2. {  
  3.     int i ,sum=0;  
  4.     for(i=0;i<size;i++)  
  5.     {  
  6.         sum+=a[i];  
  7.     }  
  8.     return sum;  

 

这个代码,则明确地表示了数组a的长度是size,也就是说在size和a[]之间建立起了直接的联系。

但是在这里我们需要注意一点,就是参数的顺序,长度一定要写在数组之前,否则会出现a[size]找不到size的错误。

在进行函数声明时,我们可以有以下几种方式:

 

 
  1. int Sum(int ,int a[*]);  
  2. int Sum(int n ,int a[n]);  
  3. int Sum(int n, int a[*]);  
  4. int Sum(int ,int a[]);  
  5. int Sum(int n ,int a[]); 

 

个人比较推荐第一种,因为我觉得第一种最为简便,而且可以表明a是一个变长数组。像第四种和第五种,我个人认为是两种很不好的方式。

7. C99中数组参数声明使用static

C99中允许在数组参数声明中使用关键字static。例如:

 

 
  1. int Sum(int a[static 10],int n)  
  2. {  
  3.       

 

从函数本身来讲,static并没有对函数的本身实现造成任何影响。static 10的含义是数组的长度至少是10。那么当函数调用时,编译器会事先从内存中取出10个数,而不是在函数调用的时候才一次次的去取,这样就可以使函数的效率更高。

8. main函数的返回值

 

 

在初学C语言的时候,谭老的书上大部分都是这样的代码:

 

 
  1. void main ()  
  2. {  
  3.     printf("Hello world");  

 

但是实际上,这段函数有两个缺陷:

A. 从编程风格上来看,最好显式地声明main函数没有参数

B. main函数应该返回状态码,在某些操作系统中,程序终止时可以检测到状态码,来监视程序是否正常结束。即使你不需要这个状态码,其他人也可能需要。

因此,这个函数最好这样来实现:

 

 
  1. int main (void)  
  2. {  
  3.     printf("Hello world");  
  4.     return 0;  

 

还记得我们之前说过exit(0)么,我们之前说,在main函数中写return 0和exit(0)是没有区别的。那么我们就来看看return 和 exit的区别。

exit属于&lt;stdlib.h>头文件,我们之前说过,0是状态码中成功的意思,那么为了更直观,C标准库为我们提供了这样的两个宏定义。

 

 
  1. int main (void)  
  2. {  
  3.     printf("Hello world");  
  4.     exit(EXIT_SUCCESS); //成é功|  
  5.     exit(EXIT_FAILURE); //失§败ü  

 

让我们转向定义可以发现:

 

 
  1. /* Definition of the argument values for the exit() function */ 
  2.  
  3. #define EXIT_SUCCESS    0  
  4. #define EXIT_FAILURE    1 

 

 

在<stdlib.h>中的这两个宏定义。但是这两个值并不是固定的,而是由实现定义的。

另外,return 和 exit的一个最典型差异就是,在其他函数中调用return 不会引起程序的终止,但是无论在哪里调用exit都会引起程序终止,我们看一个程序。

 

 
  1. int main (void)  
  2. {  
  3.     printf("Begin\n");  
  4.     BreakTest();  
  5.     printf("End\n");  
  6. }  
  7.  
  8. int BreakTest()  
  9. {  
  10.     return 0;  

 

这段代码不应该产生任何疑问:

image

接下来看下这段代码:

 

 
  1. int main (void)  
  2. {  
  3.     printf("Begin\n");  
  4.     BreakTest();  
  5.     printf("End\n");  
  6. }  
  7.  
  8. int BreakTest()  
  9. {  
  10.     exit(EXIT_SUCCESS);  

 

image

由此可知,exit使整个的程序都被终止了。

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值