5 名學生,3 門課程(英語、高數、C語言)。輸入所有成績,計算並輸出每位學生的總分與平均分。核心考點:二維數組行主序存儲、嵌套循環遍歷、函數參數傳遞二維數組。
#include <stdio.h>
#define NUM_STUDENTS 5
#define NUM_COURSES 3
void calculate(const float score[][NUM_COURSES]);
int main() {
float result[NUM_STUDENTS][NUM_COURSES];
int i, j;
printf("請輸入5個學生3門課分數(每行3個):\n");
for (i = 0; i < NUM_STUDENTS; i++) {
for (j = 0; j < NUM_COURSES; j++) {
scanf("%f", &result[i][j]);
}
}
calculate(result);
return 0;
}
void calculate(const float score[][NUM_COURSES]) {
float sum[NUM_STUDENTS] = {0};
float avg[NUM_STUDENTS] = {0};
int i, j;
for (i = 0; i < NUM_STUDENTS; i++) {
for (j = 0; j < NUM_COURSES; j++) {
sum[i] += score[i][j];
}
avg[i] = sum[i] / NUM_COURSES;
}
for (i = 0; i < NUM_STUDENTS; i++) {
printf("第%d人: 總分=%.2f 平均=%.2f\n", i+1, sum[i], avg[i]);
}
}
// 正確:第二維必須指定,第一維可省略
void calculate(const float score[][NUM_COURSES]);
void calculate(const float (*score)[NUM_COURSES]); // 等價指針寫法
// 錯誤:兩維都不可省略
void calculate(float score[][]); // 編譯錯誤
void calculate(float **score); // 語義不同!指向指針的指針,非連續內存
原理:C 語言二維數組在內存中連續存儲(行主序),編譯器需知道每行長度(第二維)才能計算偏移:score[i][j] ≡ *(score + i*NUM_COURSES + j)。
const float score[][NUM_COURSES] 承諾函數不修改輸入數組,編譯器會檢查違規寫入,體現防禦性編程。
原代碼將 i, j 定義為全局變量——極差實踐:
正確做法:在最小作用域(函數內、循環初始化句)聲明:for (int i = 0; ...) (C99)。
typedef struct {
char name[20];
float scores[NUM_COURSES];
float sum;
float avg;
} Student;
void process_students(Student stu[], int n) {
for (int i = 0; i < n; i++) {
stu[i].sum = 0;
for (int j = 0; j < NUM_COURSES; j++) {
stu[i].sum += stu[i].scores[j];
}
stu[i].avg = stu[i].sum / NUM_COURSES;
}
}
結構體版本將數據與操作封裝,擴展性強(易添加姓名、學號、排名等),是大型項目首選。
int read_scores(float arr[][NUM_COURSES], int rows, int cols) {
for (int i = 0; i < rows; i++) {
printf("第%d位學生 %d 門課分數: ", i+1, cols);
for (int j = 0; j < cols; j++) {
if (scanf("%f", &arr[i][j]) != 1) {
fprintf(stderr, "輸入錯誤: 第%d學生第%d科\n", i+1, j+1);
return 0;
}
if (arr[i][j] < 0 || arr[i][j] > 100) {
fprintf(stderr, "分數範圍錯誤 [0,100]: %.2f\n", arr[i][j]);
return 0;
}
}
}
return 1;
}
學生成績統計是二維數組、嵌套循環、函數參數傳遞、模塊化設計的綜合練習。關鍵收穫:
const 保護輸入數據不被修改;