[Lập trình C] Mảng - array

Posted on January 5th, 2021

Ở các bài học trước, bạn đã học về những kiểu dữ liệu cơ bản trong ngôn ngữ lập trình C như: int, float, double, char... Bài viết này sẽ tiếp tục giới thiệu với bạn về một kiểu dữ liệu khác, phức tạp hơn, đó là mảng - array.

Lý thuyết cơ bản về mảng trong lập trình C

Mảng là gì?

Mảng là một tập hợp các phần tử có cùng kiểu dữ liệu, được lưu trữ trong một dãy các ô nhớ liên tiếp nhau.

Các phần tử của mảng được truy cập bằng cách sử dụng chỉ số.

Chiều của mảng được xác định bởi số lượng loại chỉ số cần để xác định mỗi phần tử của mảng. Từ nay, quy ước nếu không nói gì thêm thì ngầm hiểu mảng là mảng một chiều.

Cách khai báo mảng tương tự như việc khai báo một biến:

Storage_class data_type array_name[size];

Trong đó:

  • Storage_class: được hiểu là phạm vi của biến. Có 4 loại storage class là: auto, extern, static, register.
  • data_type: kiểu dữ liệu (int, float, double, char,...)
  • array_name: tên của mảng
  • size: kích thước của mảng (số nguyên)

Ví dụ:

int player[11];

Trong ví dụ trên:

  • Storage_class: default là auto
  • data_type: int
  • array_name: player
  • size: 11

Một số đặc điểm của mảng

  • Tất cả các phần tử của mảng đều có cùng kiểu dữ liệu
  • Chỉ số của mảng bắt đầu từ 0 và kết thúc là size - 1. Trong ví dụ ở phần trên, mảng player gồm 11 phần tử với các chỉ số là: 0, 1, 2,..., 10.
  • Để truy cập vào một phần tử của mảng có chỉ số là idx, bạn sử dụng cặp dấu [] với chỉ số ở giữa. Trong ví dụ trên, mảng player gồm các phần tử có giá trị là: player[0], player[1], player[2],..., player[10]
  • Không thể so sánh 2 mảng với nhau. Dù cho chúng có cùng kích thước, giá trị của các phần tử giống nhau, nhưng chúng cũng không bằng nhau.
  • Không thể gán giá trị của cả mảng một lúc, mà phải gán giá trị cho từng phần tử.

Khởi tạo giá trị của mảng

Mảng có thể được khởi tạo ngay tại thời điểm khai báo hoặc sau khi khai báo.

Cú pháp khởi tạo mảng khi khai báo là:

data_type array_name[size] = { value0, value1, value2,..., valueN }

Ví dụ:

int a[3] = {3, 4, 2};

Đối với việc khởi tạo mảng khi khai báo, bạn có thể khởi tạo được nhiều phần tử cùng một lúc. Tuy nhiên, khi khởi tạo giá trị sau khi khai báo, bạn chỉ có thể khởi tạo giá trị cho từng phần tử của mảng.

Ví dụ:

int a[3];
a[0] = 3;
a[1] = 4;
a[2] = 2;

Chú ý:

  • Nếu không khởi tạo giá trị cho các phần tử của mảng thì giá trị đó là ngẫu nhiên.
  • Đối với mảng kiểu extern hoặc static thì mặc định giá trị các phần tử của mảng là 0.

Mảng kí tự - string

String là một mảng kí tự, kết thúc bằng kí tự NULL hay '\0'.

Mỗi kí tự trong string (kể cả kí tự kết thúc string) chiếm 1 byte.

Để lưu string gồm N phần tử, cần khai báo mảng có kích thước N + 1 phần tử. Với N phần tử đầu tiên để lưu nội dung của string. Và phần tử cuối cùng dùng để lưu kí tự kết thúc string '\0'

Để nhập vào một string, bạn có thể dùng hàm scanf() với format nhập vào là %s hoặc dùng hàm gets(), ví dụ:

char str[50];

// Nhập vào string dùng scanf() với %s
scanf("%s", str);

// Hoặc nhập vào string dùng gets()
gets(str);

Để in string ra màn hình, bạn có thể dùng hàm printf() với format là %s hoặc dùng hàm puts(), ví dụ:

char str[50];

// In ra string dùng printf() với %s
printf("%s", str);

// In ra string dùng puts()
puts(str);

Một số hàm (có trong string.h) giúp xử lý string là:

  • strcpy(s1, s2): copy s2 vào s1
  • strcat(s1, s2): ghép s2 vào s1
  • strlen(s1): trả về kích thước của s1 (không bao gồm kí tự kết thúc string)
  • strcmp(s1, s2): so sánh s1 và s2. Hàm này trả về 0 nếu s1 giống s2, trả về < 0 nếu s1 < s2 và trả về > 0 nếu s1 > s2
  • strchr(s1, ch): trả về con trỏ trỏ vào vị trí đầu tiên của kí tự ch trong s1
  • strstr(s1, s2): trả về con trỏ trỏ vào vị trí đầu tiên của chuỗi s2 trong s1

Các thao tác với mảng

Duyệt qua các phần tử của mảng:

int a[10], i;
for (i = 0; i < 10; i++) {
	// do something with a[i]
}

In ra các phần tử của mảng:

int a[10], i;
for (i = 0; i < 10; i++) {
	printf("%d ", a[i]);
}

Nhập giá trị cho các phần tử của mảng:

int a[10], i;
for (i = 0; i < 10; i++) {
	scanf("%d", &a[i]);
}

Mảng 2 chiều

Mảng 2 chiều là mảng sử dụng 2 loại chỉ số để xác định 1 phần tử.

Ví dụ:

int a[2][10];

Một mảng 2 chiều có thể hiểu là một mảng 1 chiều của các mảng 1 chiều. Cụ thể trong ví trên, bạn có thể hiểu a bao gồm 2 mảng một chiều, mỗi mảng là 1 mảng 1 chiều có 10 phần tử.

Để khởi tạo giá trị cho các phần tử của mảng 2 chiều, bạn cũng có thể sử dụng giống như khai báo mảng một chiều. Đó là khởi tạo lúc khai báo và khởi tạo sau khi khai báo.

Khởi tạo mảng lúc khai báo (2 cách):

int a[2][3] = {1, 2, 3, 4, 5, 6};

// Hoặc

int b[2][3] = {
	{1, 2, 3},
	{4, 5, 6}
};

Khởi tạo mảng sau khi khai báo:

int a[2][3];
a[0][0] = 1;
a[0][1] = 2;
a[0][2] = 3;
a[1][0] = 4;
a[1][1] = 5;
a[1][2] = 6;

Duyệt qua các phần tử của mảng 2 chiều:

int a[2][3];

for (i = 0; i < 2; i += 1) {
  for (j = 0; j < 3; j += 1) {
    // do something with a[i][j]
  }
}

In ra các phần tử của mảng 2 chiều:

int a[2][3];

for (i = 0; i < 2; i += 1) {
  for (j = 0; j < 3; j += 1) {
    printf("%d ", a[i][j]);
  }
}

Nhập vào giá trị của mảng 2 chiều:

int a[2][3];

for (i = 0; i < 2; i += 1) {
  for (j = 0; j < 3; j += 1) {
    scanf("%d", &a[i][j]);
  }
}

Bài tập thực hành về mảng trong lập trình C

Bài 1

Nhập vào giá trị của mảng số nguyên bao gồm N phần tử (N <= 50).

  • In ra các phần tử mảng từ đầu đến cuối
  • In ra các phần tử mảng từ cuối lên đầu
#include <stdio.h>

int main() {
	int a[50], i, N;
	
	printf("Nhap vao N: ");
	scanf("%d", &N);
	
	if (N <= 0 || N > 50) {
		printf("Ban da nhap sai");
		return 0;
	}
	
	printf("\nNhap vao mang: ");
	for(i = 0; i < N; i++) {
		scanf("%d", &a[i]);
	}
	
	printf("\nMang tu dau den cuoi: ");
	for(i = 0; i < N; i++) {
		printf("%d ", a[i]);
	}
	
	printf("\nMang tu cuoi len dau: ");
	for(i = N - 1; i >= 0; i--) {
		printf("%d ", a[i]);
	}
	
	return 0;
}

Bài 2

Nhập vào giá trị của mảng số nguyên bao gồm N phần tử (N <= 50).

  • Tính giá trị trung bình của các phần tử trong mảng
  • Đếm số lượng các phần tử chẵn và số lượng các phần tử lẻ
#include <stdio.h>

int main() {
	int a[50], i, N;
	int tong = 0, soLuongChan = 0, soLuongLe = 0;
	
	printf("Nhap vao N: ");
	scanf("%d", &N);
	
	if (N <= 0 || N > 50) {
		printf("Ban da nhap sai");
		return 0;
	}
	
	printf("Nhap vao mang: ");
	for(i = 0; i < N; i++) {
		scanf("%d", &a[i]);
	}
	
	// Tinh tong
	for(i = 0; i < N; i++) {
		tong += a[i];
	}
	printf("\nTrung binh cong la: %g\n", (float)tong/N);
	
	// Dem so luong phan tu chan va phan tu le
	for(i = 0; i < N; i++) {
		if (a[i] % 2 == 0) soLuongChan++;
		else soLuongLe++;
	}
	printf("So luong phan tu chan la: %d\n", soLuongChan);
	printf("So luong phan tu le la: %d", soLuongLe);
	
	return 0;
}

Bài 3

Nhập vào giá trị của mảng số nguyên bao gồm N phần tử (N <= 50).

  • Tìm giá trị lớn nhất của các phần tử trong mảng
  • Tìm giá trị nhỏ nhất của các phần tử trong mảng
#include <stdio.h>

int main() {
	int a[50], i, N;
	int min, max;
	
	printf("Nhap vao N: ");
	scanf("%d", &N);
	
	if (N <= 0 || N > 50) {
		printf("Ban da nhap sai");
		return 0;
	}
	
	printf("Nhap vao mang: ");
	for(i = 0; i < N; i++) {
		scanf("%d", &a[i]);
	}
	
	// Tim gia tri min
	min = a[0];
	for(i = 1; i < N; i++) {
		if (a[i] < min) min = a[i];
	}
	printf("Gia tri nho nhat la: %d\n", min);
	
	// Tim gia tri max
	max = a[0];
	for(i = 1; i < N; i++) {
		if (a[i] > max) max = a[i];
	}
	printf("Gia tri lon nhat la: %d", max);
	
	return 0;
}

Bài 4

Cho 1 lớp có N sinh viên (N <= 50). Nhập vào tên của từng sinh viên và điểm môn tin học tương ứng. Hãy tìm và in ra tên của sinh viên có điểm số cao nhất và tên của sinh viên có điểm số thấp nhất.

Giả sử: tên của sinh viên không dấu, không quá 20 kí tự

#include <stdio.h>

int main() {
	int i, N;
	char ten[50][21];
	float diem[50];
	float maxDiem, minDiem;

	printf("Nhap vao N: ");
	scanf("%d", &N);

	if (N <= 0 || N > 50) {
		printf("Ban da nhap sai");
		return 0;
	}

	printf("\nNhap thong tin sinh vien:\n");
	for(i = 0; i < N; i++) {
		printf("* Sinh vien thu %d:\n", i + 1);
		printf("\t- Nhap ten: ");
		scanf("%s", ten[i]);
		printf("\t- Nhap diem tin: ");
		scanf("%f", &diem[i]);
	}

	// Tim diem cao nhat
	maxDiem = diem[0];
	for(i = 1; i < N; i++) {
		if(maxDiem < diem[i]) maxDiem = diem[i];
	}
	
	printf("\nSinh vien co diem cao nhat la %g, bao gom:\n", maxDiem);
	for(i = 0; i < N; i++) {
		if(diem[i] == maxDiem)
			printf("\t- Ten: %s\n", ten[i]);
	}

	// Tim diem thap nhat
	minDiem = diem[0];
	for(i = 1; i < N; i++) {
		if(minDiem > diem[i]) minDiem = diem[i];
	}

	printf("\nSinh vien co diem thap nhat la %g, bao gom:\n", minDiem);
	for(i = 0; i < N; i++) {
		if(diem[i] == minDiem)
			printf("\t- Ten: %s\n", ten[i]);
	}

	return 0;
}

Bài 5

Nhập vào giá trị của mảng số nguyên bao gồm N phần tử (N <= 50).

  • Sắp xếp và in ra mảng theo thứ tự tăng dần
  • Sắp xếp và in ra mảng theo thứ tự giảm dần
#include <stdio.h>

int main() {
	int a[50], i, j, N, tmp;

	printf("Nhap vao N: ");
	scanf("%d", &N);

	if (N <= 0 || N > 50) {
		printf("Ban da nhap sai");
		return 0;
	}

	printf("\nNhap vao mang: ");
	for(i = 0; i < N; i++) {
		scanf("%d", &a[i]);
	}

	// Sap xep theo thu tu tang dan
	for(i = 0; i < N - 1; i++) {
		for(j = i + 1; j < N; j++) {
			if (a[i] > a[j]) {
				// Hoan doi
				tmp = a[i];
				a[i] = a[j];
				a[j] = tmp;
			}
		}
	}

	printf("\nMang da sap xep theo thu tu tang dan la: ");
	for(i = 0; i < N; i++) {
		printf("%d ", a[i]);
	}

	// Sap xep theo thu tu giam dan
	for(i = 0; i < N - 1; i++) {
		for(j = i + 1; j < N; j++) {
			if (a[i] < a[j]) {
				// Hoan doi
				tmp = a[i];
				a[i] = a[j];
				a[j] = tmp;
			}
		}
	}
	
	printf("\nMang da sap xep theo thu tu giam dan la: ");
	for(i = 0; i < N; i++) {
		printf("%d ", a[i]);
	}

	return 0;
}

Bài 6

Triển khai lại các hàm thao tác với string sau:

  • strcpy(s1, s2) : giả sử s1 có kích thước lớn hơn s2
  • strcat(s1, s2) : giả sử s1 có kích thước đủ để ghép thêm s2
  • strlen(s1)
  • strcmp(s1, s2)
#include <stdio.h>

int main() {
	// strcpy(s1, s2)
	{
		char s1[51], s2[11];
		int i;

		printf("Nhap s1: ");
		scanf("%s", &s1);

		printf("Nhap s2: ");
		scanf("%s", &s2);

		i = 0;
		while(s2[i] != '\0') {
			s1[i] = s2[i];
			i++;
		}
		s1[i] = '\0';

		printf("\nTrien khai strcpy:\n");
		printf("- s1=%s\n", s1);
		printf("- s2=%s\n\n", s2);
	}

	// strcat(s1, s2)
	{
		char s1[51], s2[11];
		int i, j;

		printf("Nhap s1: ");
		scanf("%s", &s1);

		printf("Nhap s2: ");
		scanf("%s", &s2);
		
		i = 0;
		while(s1[i] != '\0') {
			i++;
		}
		j = 0;
		while(s2[j] != '\0') {
			s1[i + j] = s2[j];
			j++;
		}
		s1[i + j] = '\0';

		printf("\nTrien khai strcat:\n");
		printf("- s1=%s\n", s1);
		printf("- s2=%s\n\n", s2);
	}
	
	// strlen(s1)
	{
		char s1[51];
		int i;

		printf("Nhap s1: ");
		scanf("%s", &s1);

		i = 0;
		while(s1[i] != '\0') {
			i++;
		}

		printf("\nTrien khai strlen:\n");
		printf("- strlen(s1)=%d\n\n", i);
	}
	
	// strcmp(s1, s2)
	{
		char s1[51], s2[51];
		int i, t;

		printf("Nhap s1: ");
		scanf("%s", &s1);

		printf("Nhap s2: ");
		scanf("%s", &s2);
		
		i = 0;
		t = 0;
		while(s1[i] != '\0' || s2[i] != '\0') {
			if (s1[i] < s2[i]) {
				t = -1;
				break;	
			}
			if (s1[i] > s2[i]) {
				t = 1;
				break;
			}
			i++;
		}

		printf("\nTrien khai strcmp:\n");
		printf("- strcmp(s1,s2)=%d\n\n", t);
	}


	return 0;
}