阵列
阵列是一群具有相同名称或资料型态的变数集合。
由于整个阵列中的变数均具有相同的名称,因此若要存取阵列中的变数,我们只需要透过阵列的 index 来指定就可以了。
阵列与变数的功能都是用来储存资料,但有所不同的是每一个变数只能储存一项资料,而阵列则是由一连串的主记忆体空间组合而成,所以可以同时连续储放多项资料,亦即一次可以宣告多个变数,而不用一个一个宣告。因此,可以少写许多行程式,并且增加程式的可读性。
阵列定义:
阵列里的每一个元素都必须是同一种资料型态。阵列大小须为常数(constant value)。佔用了连续(contiguous)的记忆体位址。如果我们不会分配任何初始值给阵列,最好在宣告的时候将阵列初始化为零或 null。插入或删除元素时较麻烦,因为需挪移其他元素。用来表示有序串列之一种方式。阵列的应用
让电脑随机给定四十人的分数,并在萤幕显示出来。
#include <stdio.h>#include <stdlib.h>#include <time.h>int main(){ int i; int score[40]; srand( time(NULL) ); for(i = 0; i < 40; i++){ score[i] = rand(() % 41) + 60; // 把分数设定在 100~60 之间 } printf("座号\t分数\n"); for(i = 0; i < 40; i++){ printf("%d\t%d\n", (i+1), score[i]); }}
阵列格式
根据阵列结构不同,我们可以把阵列分为一维阵列二维阵列多维阵列而表示方法如下:dataType arrayName[arraySize];dataType arrayName[arraySize] [arraySize];
int score[40]; // 没有宣告初值的阵列int arr[5] = {3, 4, 5, 6, 7}; // 也可以一併宣告初值int arr[] = {3, 4, 5, 6, 7}; // 或是用这种方式让电脑自动决定阵列长度int arr[5] = {0}; // 初始化阵列,所有元素都设为0
但是要切记,阵列的长度要在编译期间就决定好,也就是阵列长度须为常数。如果想要在执行期间,动态生成阵列,要用动态配置记忆体的方式,这边我们等到指标的单元会再提。
int arr[5] = {3, 4, 5, 6, 7};
#include <stdio.h>#include <stdlib.h>#include <time.h>int main(){ int arr[5] = {0, 10, 100, 1000, 5000}; printf("arr[3] = %d\n\n", arr[3]); // arr[3] = 1000}
阵列的空间分配方式
无论是几维的阵列,C语言都会分配一块连续的记忆体空间来处理。(这个概念到未来的指标单元会用到)
int x[10];
分配 10*sizeof(int)
个 bytes
int x[5][10];
分配 5*10*sizeof(int)
个 bytes
但如果是呼叫函式传递参数时,如:
void fun(int x[]){}
这里的阵列 x 就没有分配空间,这样相当于 int *x
。
这是因为 C语言在呼叫函式传递参数时,无法传递整个阵列,因为阵列可能大的不得了,所以我们传递的是阵列的开头地址,也就是指标。
因此,在参数宣告时,指标和没有宣告大小的阵列可以混用。
範例一 整数阵列
定义一个阵列空间为10
的整数一维阵列,以乱数填入介于 a
、b
(a < b)之间的整数元素。a
、b
为使用者输入的整数。请列出该阵列的所有元素。承上题,计算该整数阵列之平均值(means)。
#include <stdlib.h>#include <stdio.h>#include <time.h>void main(){ int arr[10] = {0}; int a, b; srand(time(NULL)); printf("Please enter two number a and b (a < b):\n"); scanf("%d %d", &a, &b); for(int i = 0; i < 10; i++){ arr[i] = rand() % (b - a + 1) + a; } for(int i = 0; i < 10; i++){ printf("arr[%d] = %d\n", i, arr[i]); } /* 第二小题 */ int sum = 0; float ave = 0; for(int i = 0; i < 10; i++){ sum += arr[i]; } ave = sum / 10.0; print("\nmean of the array: %f", ave);}
Please enter two number a and b (a < b):3 25arr[0] = 25arr[1] = 6arr[2] = 4arr[3] = 12arr[4] = 18arr[5] = 5arr[6] = 3arr[7] = 9arr[8] = 11arr[9] = 21mean of the array: 11.4
多维阵列
一维阵列使用阵列名称与一个 index 来指定存取的阵列元素。
二维阵列使用阵列名称与两个 indices 来指定存取的阵列元素,宣告方式与一维阵列类似。
範例二-泡沫排序法 Bubble Sort
我们先来讲解排序演算法(sorting algorithm),但在这之前,我们又要先来再讲讲何谓演算法?
何谓演算法?
Wiki的定义:为任何良定义的具体计算步骤的一个序列,常用于计算、资料处理和自动推理。精确而言,演算法是一个表示爲有限长列表的有效方法。演算法应包含清晰定义的指令用于计算函式。

$$
程式设计 = 资料结构 + 演算法 \
\text{Programming} = \text{Data Structures} + \text{Algorithm}
$$说穿了,演算法就是一种 解决问题的逻辑思维!
排序演算法
所谓排序法,就是将一堆没有排序过的数字由小到大(或大到小)排列好的演算法。
视觉化15种排序法
其中一种程式入门者最常使用的排序演算法-泡沫演算法 Bubble sort
泡沫演算法 Bubble Sort
首先比较最前面两个数字
如果要排序由小到大,那么将较大的往后排
反之,将较小的数字往后排
它重複地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重複地进行直到没有再需要交换,也就是说该数列已经排序完成。这个演算法的名字由来是因为越小的元素会经由交换慢慢「浮」到数列的顶端。
字串 String
C语言其实并没有「string」这种类型的资料型态,但我们通常不会只用「一个字元」啊,一般来说都会使用「字串」。这种时候我们就只能透过字元阵列char[]
来模拟字串。要注意:字串一定是 char[]
字元阵列;但是 char[]
字元阵列不一定就是字串。在 C 谈到字串的话,一个意义是指字元组成的阵列,最后加上一个空(null)字元 '\0'
,例如底下这个 “hello” 字串:char text[] = {‘h’, ‘e’, ‘l’, ‘l’, ‘o’, ‘\0’};
之后可以直接使用以下 function 显示在萤幕上:printf(“%s”, text);
也可以使用 " "
来包含文字,例如:char text[] = “hello”;
虽然在这个例子没有指定空字元 '\0'
,但是此语法下会自动加上空字元。範例
我们改成使用 gets()
来读取字串。
所以这里我们可以发现,已经解决刚刚的问题了。gets()
只有 Enter 键(回车符)才会结束,在 Enter 键以前都会当成字元(包含空白键)。
gets() vs. scanf()
gets(str)
允许输入的字符含有空格。scanf("%s", str)
不允许输入字符串含有空格。注意:
当你编译包含 gets
的程式时,可能会出现 warning 説 gets
是不安全的,原因是 gets( )
及 scanf( )
都无法知道字串 s
大小,必须遇到换行符或读到文件结尾为止才接收输入,因此容易导致字元阵列 overflow 的情况。
意思就是说,当你输入的字串数量超过接收字串的阵列大小,就很容易发生 overflow。
函式 Function
到目前为止都只使用一个函式(Function),也就是 main() 主函式,若某些程式码经常使用,我们就可以抽出来让它成为一个新函式好让我们重覆呼叫。这也就是所谓的模组化。什么叫模组化?简单来说,就是把特定功能分出来当成一个模组,需要的时候只要呼叫这个模组就好;而需要修正的时候也只要修正模组即可。函式用来将程式组织为一个小的、独立的运行单元,一个函式可以接受资料(传入参数),并执行其中的算法,最后将结果传回去。函式的优点
减少撰写重覆的程式码。将程式码以有意义的方式组织起来。在相同的流程下,可藉由参数调整程式的行为。藉由函式库可组织和分享程式码。做为资料结构 (data structures) 和物件 (objects) 的基础。我们就举几个例子来简单示範如何宣告并且使用函数。
範例
没有输入参数,也没有回传值。
主程式
副程式内容
範例
有输入参数,也有回传值。
主程式 简化了前一页的程式方便讲解
副程式内容
通常我们的副函式会写在整个 program 的最前面(main function 前面),不过也可以写在 main function 的后面,但是一开始要先宣告副函式。