Download as pdf or txt
Download as pdf or txt
You are on page 1of 79

KỸ THUẬT LẬP TRÌNH

Mảng, Struct, Con trỏ

1
Thông tin môn học

 Ngôn ngữ : C.

 Công cụ : Dev-C, Notepad++, Visual Studio


Code.., Sách Các kỹ thuật lập trình, vở, giấy,
bút, máy tính.

 Kiến thức : Tin học cơ sở 2 (Lập trình cơ bản),


các kiến thức hoán vị, tổ hợp, chỉnh hợp, đồ thị
(đồ thị có hướng, vô hướng, đỉnh, cạnh..), cây
(lá, duyệt ).
2
Bài tập 1
 Bài tập 1: Thông tin của một sinh viên như sau:

• Nhập thông tin sinh viên từ bàn phím và hiện thông tin ra
màn hình.
VD: 1234-Nguyen Van A

3
Bài tập 1

4
Các bước chuẩn bị

 Kiểm tra unikey.

 Mở Dev- C và kiểm tra vị trí lưu bài


 Code các thư viện và hàm main()

5
C
Khai báo sử dụng thư viện

 #include<stdio.h> : Thư viện chứa các hàm vào/ ra chuẩn


(standard input/output) :printf(), scanf(), getc(), putc(), gets(),
puts(), fflush() ,fopen(), fclose(), fread(), fwrite(), getchar(),
putchar(), getw(), putw(),…
 #include<conio.h> : Thư viện chứa các hàm vào ra trong chế độ
DOS (DOS console) :clrscr(), getch(), getche(), getpass(), cgets(),
cputs(), putch(), clreol(),…
 #include<math.h>: Thư viện chứa các hàm tính toán :abs(), sqrt(),
log(). log10(), sin(), cos(), tan(), acos(), asin(), atan(), pow(),
exp(),…
 #include<stdlib.h>: Dùng để xúc tiến nhiều phép toán,bao gồm sự
chuyển đổi, các số giả ngầu nhiên, cấp phát vùng nhớ, kiểm soát
quá trình, môi trường, tín hiệu, tìm kiếm, và xếp thứ tự.
 #include<string.h>: Dùng để điều chỉnh dãy kí tự: strcpy(), strncpy,
strlen, strcat, strncat, strcmp, strlwr, strupr, strrev, strchr, stricmp
6
Lập trình

7
Nhập/xuất

 Trong C hay bất cứ ngôn ngữ lập trình nào khác, việc nhập, xử
lý và xuất dữ liệu có thể được thực hiện theo hai cách:
 Thông qua phương tiện nhập/xuất chuẩn (Standard
Input/Output).
 Thông qua tập tin (File)

8
Nhập/xuất chuẩn

 C cung cấp 4 hàm sau đây để thực hiện nhập dữ liệu.

scanf(), getchar(), getch() và getche()

 Với thao tác xuất dữ liệu.

printf(), puts(), putch(), putchar()

9
Hàm scanf()

 Cú pháp:
scanf(“Chuỗi định dạng”, địa chỉ của các biến);
 Chuỗi định dạng: dùng để qui định kiểu dữ liệu, cách biểu
diễn, độ rộng, số chữ số thập phân... Một số định dạng khi
nhập kiểu số nguyên, số thực, ký tự.
 Địa chỉ của các biến: là địa chỉ (&) của các biến mà chúng
ta cần nhập giá trị cho nó. Được viết như sau: &<tên biến>
 Khi nhập giá trị cho một biến thuộc kiểu dữ liệu dẫn xuất
(không phải thuộc bốn kiểu dữ liệu cơ bản char, int, float,
double), không sử dụng & trước tên biến.

10
Hàm scanf()

%d kiểu int
%ld kiểu long int
%c ký tự đơn
%s chuỗi ký tự không kết thúc
%f float hoặc double
%e tương tự như %f nhưng phần đánh dấu lũy thừa được sử
dụng
%g sử dụng cho %f hoặc %e
%x giá trị hệ cơ số 16 (base 16)
%o giá trị hệ cơ số 8 (base 8)

11
Hàm getchar(), getch() và getche()

 getchar(): nhập vào một ký tự từ bàn phím và đợi ấn enter


 getch(): nhập vào một ký tự từ bàn phím,nhưng không hiển
thị ký tự lên màn hình, không cần phải ấn enter.
 getche(): nhập vào một ký tự từ bàn phím và hiển thị ký tự
lên màn hình (không đợi ấn enter).

12
So sánh scanf() và gets()

 Hàm scanf khi nhập vào một chuỗi thì nó sẽ kết thúc khi
gặp một khoảng trắng, vì vậy hàm scanf rất khó lưu chuỗi
mà có chứa khoảng trắng.
 Hàm gets sẽ chỉ kết thúc khi ta nhấn enter, có nghĩa là hàm
gets dễ dàng lưu được một chuỗi mà có chứa khoảng trắng.
fflush(stdin);
 Lệnh xóa này sẽ cho phép tránh được cách ảnh hưởng do
quá trình nhập liệu bởi hàm gets() và scanf()

13
Hàm printf()

 Cú pháp:
printf(“Chuỗi định dạng ”, Các biểu thức);
 Chuỗi định dạng: dùng để qui định kiểu dữ liệu, cách biểu
diễn, độ rộng, số chữ số thập phân... Một số định dạng khi
đối với số nguyên, số thực, ký tự.
 Các biểu thức: là các biểu thức mà chúng ta cần xuất giá trị
của nó lên màn hình, mỗi biểu thức phân cách nhau bởi dấu
phẩy (,).

14
Hàm puts(), putch(), putchar()

 Hàm puts(): In một xâu kí tự S ra màn hình, sau đó đưa


con trỏ xuống dòng
Ví dụ: puts(“Mua he da qua”);
 Hàm putch(): In một kí tự ra màn hình
 Hàm putchar(): In một kí tự lên màn hình
Ví dụ: putchar(“\n A”) ;
Đưa chữ A lên 1 dòng mới
Chuyển con trỏ xuống dòng dưới putchar( \n A )

15
Ví dụ

16
Ví dụ

17
Bài tập 2
 Bài tập 2: File input.txt chứa thông tin như sau:
Nguyen Van A
• Đọc file input.txt và đưa thông tin ra màn hình.

18
Các bước chuẩn bị đọc/ghi file

 Tại vị trí lưu bài code -> tạo file đầu vào.

 Mở file input điền thông tin “Nguyen Van A”, sau đó save
và đóng lại
 Tại cửa sổ code -> Code các thư viện và hàm main()

19
Cấu trúc đọc/ghi file

 Cú pháp:
FILE *f = fopen(“file”,“kiểu xử lý file”);
// Các câu lệnh xử lý khác
fclose(f);
 "w": chế độ ghi text

 "r" : chế độ đọc

 "w+","r+": chế độ vừa ghi vừa đọc

20
Cấu trúc đọc/ghi file

 Cú pháp đọc :
FILE *fi = fopen(“input.txt”,“r”); // mở file để đọc
// Các câu lệnh đọc nội dung file
fclose(fi);

 Cú pháp ghi :
FILE *fo = fopen(“output.txt”,“w”); // mở file để ghi
// Các câu lệnh ghi nội dung file
fclose(fo);

21
Đọc/ghi file

 C cung cấp 4 hàm sau đây để thực hiện đọc file.

fscanf(), fgets(), fgetc(), fread()

 Với thao tác ghi file.

fprintf(), fputs(), fputc(), fwrite()


 Theo định dạng: fprintf, fscanf
 Từng ký tự hay chuỗi: fputc, fputs, fgetc, fgets
 Trực tiếp từ bộ nhớ: fwrite, fread - Chỉ dùng với tập tin
kiểu nhị phân.

22
Đọc/ghi file

 fscanf(), fgets(), fgetc()

Hàm fscanf(<FILE *>, <định dạng>, <các tham biến>);

Ý nghĩa Đọc dữ liệu từ một tập tin theo định dạng.

Ví dụ fscanf(fi, “%d”, &x);

23
Đọc/ghi file

 fscanf(), fgets(), fgetc()

Hàm fgets(<vùng nhớ>, <kích thước tối đa>, <FILE *>);

Đọc một chuỗi ký tự từ một tập tin với kích thước tối đa cho
Ý nghĩa
phép, hoặc gặp ký tự xuống dòng.

char s[80];
Ví dụ
fgets(s, 80, fi);

24
Đọc/ghi file

 fscanf(), fgets(), fgetc()

Hàm fgetc(< FILE * >);

Ý nghĩa Đọc một ký tự từ tập tin đang mở.

char c;
Ví dụ
c = fgetc(fi);

25
Đọc/ghi file

 fprintf(), fputs()

Hàm fprintf(<FILE *>, <định dạng>[, <các tham biến>]);

Ý nghĩa Ghi dữ liệu theo một định dạng nào đó vào tập tin.

Ví dụ fprintf(fo,“%d”,x);

26
Đọc/ghi file

 fprintf(), fputs()

Hàm fputs(<chuỗi ký tự>, <FILE*>);

Ý nghĩa Ghi một chuỗi ký tự vào tập tin đang mở.

Ví dụ fputs(“Nguyen Van A”, fo);

27
Đọc/ghi file

 fread(), fwrite()

Hàm fread(<&ptr>, <size>, <len>, <FILE *>);

• ptr: vùng nhớ để lưu dữ liệu đọc.


• size: kích thước mỗi ô nhớ (tính bằng byte).
Ý nghĩa
• len: độ dài dữ liệu cần đọc.
• FILE: đọc từ tập tin nhị
int a[30], b, n;
Ví dụ fread(a,sizeof(int), n , f);
fread(&b, sizeof(int), 1 , f);

28
Đọc/ghi file

 fread(), fwrite()

Hàm fwrite(<&prt>, <size>, <len>,<FILE *> );

Ý nghĩa Tham số tương tự như hàm fread.

Ví dụ fwrite(a, sizeof(int), n , f);

29
Bài tập 2

30
Bài tập 2

31
Đa dữ liệu

 Mảng: Nhiều dữ liệu có chung 1 kiểu


Ví dụ: Tập các số tự nhiên (int), tên các sinh viên của lớp
hoc (char)
 Kiểu cấu trúc: Nhiều dữ liệu, 1 dữ liệu có nhiều kiểu
Ví dụ: Thông tin sinh viên của lớp học gồm mã sinh viên,
họ tên, điểm….

32
Mảng 1 chiều

 Định nghĩa: Mảng là một tập hợp tuần tự các phần tử có cùng
kiểu dữ liệu và các phần tử được lưu trữ trong một dãy các ô
nhớ liên tục trên bộ nhớ. Các phần tử của mảng được truy cập
bằng cách sử dụng “chỉ số”. Mảng có kích thước N sẽ có chỉ
số từ 0 tới N – 1.
 Ví dụ: Ví dụ, với N = 5, khi đó chỉ số mảng(tiếng anh là
index) sẽ có giá trị từ 0 tới 4(5-1) tương ứng với 5 phần tử.

33
Khai báo mảng 1 chiều

 Việc khai báo mảng cần 2 tham số sau


 Kích thước của mảng: Việc này xác định số lượng phần tử có thể
được lưu trữ trong mảng.
 Kiểu dữ liệu của mảng: Việc này chỉ định kiểu dữ liệu của các phần tử
trong mảng; là số nguyên, số thực, ký tự hay là kiểu dữ liệu nào đó.
 Cú pháp : Kiều_dữ_liệu Tên_mảng[Số_phần_tử]
 Ví dụ: int a[10];

34
Nhập/xuất các phần tử mảng

 Để nhập dữ liệu cho các phần tử trong mảng ta cần duyệt tới
từng phần tử trong mảng và tiến hành nhập bằng một vòng for

 Việc xuất các phần tử trong mảng cũng được tiến hành tương
tự:

35
Bài tập 3
 Bài tập 3: Dãy số nguyên a gồm 5 phần tử như sau:
5
13579
• Nhập thông tin từ bàn phím và hiện thông tin ra màn hình.
• Đọc thông tin từ file (tạo file chứa thông tin trên) và hiện
thông tin ra màn hình.

36
Bài tập 3

37
Bài tập

38
Kiểu cấu trúc - struct

 Định nghĩa: Đối với mảng, chỉ có thể lưu nhiều thông tin có
cùng kiểu dữ liệu. Nhưng với kiểu cấu trúc ta có thể lưu thông
tin có nhiều kiểu dữ liệu khác nhau.
 Mảng:

 struct:

39
Kiểu cấu trúc - struct

 Cấu trúc:
 C1:

 Ví dụ:

40
Kiểu cấu trúc - struct

 Khai báo biến:

41
Kiểu cấu trúc - struct

 Truy xuất các thuộc tính của struct:


 Sử dụng . => Toán tử truy xuất tới thành viên khi khai báo
biến bình thường.
 Sử dụng -> => Toán tử truy xuất tới thành viên khi biến là
con trỏ.
 Ví dụ:

42
Kiểu cấu trúc - struct

 Cấu trúc:
 C2:

 Ví dụ:

43
Bài tập 4
 Bài tập 4: Thông tin của một sinh viên như sau:

• Nhập thông tin n sinh viên từ bàn phím và hiện thông tin ra
màn hình.

44
Bài tập 4

45
Bài tập 4

46
Ví dụ

47
Ví dụ

 Trả lời
 Mỗi biến mà bạn khai báo đều có địa chỉ riêng của nó và
giá trị mà nó đang lưu trữ. Để xem được địa chỉ của biến,
bạn thêm dấu & vào trước tên biến.
 Để nhận giá trị địa chỉ là hexa, thay %d bằng %x là được.

48
Con trỏ

 Định nghĩa:
 Con trỏ cũng là một kiểu dữ liệu, dữ liệu này không phải số
hay kí tự, nó là kiểu dữ liệu địa chỉ
 Về bản chất con trỏ cũng như một biến bình thường: nó
cũng có tên biến, giá trị của biến, địa chỉ của biến.Nhưng
có điểm khác là:
 Những biến bình thường thì nó chỉ nằm cố định trong 1 ô
nhớ, còn biến con trỏ thì nó còn có thể trỏ đến các ô nhớ
khác nhau.

49
Con trỏ

 Sử dụng để làm gì?


 Vì biến con trỏ có thể trỏ đi lung tung trong bộ nhớ nên
việc sử dụng bộ nhớ sẽ linh hoạt hơn.
 Áp dụng cho mảng động . Tức là : khi chúng ta sử dụng
mảng tĩnh với số lượng phần tử của mảng là cố định chẳng
hạn mảng có 100 phần tử , chúng ta chỉ sử dụng 5-10 phần
tử bộ nhớ cũng cấp 100 ô nhớ ->lãng phí, nếu sử dụng con
trỏ thì sử dụng bao nhiêu cập phát bao nhiêu.

50
Con trỏ

 Cách khai báo con trỏ:


<kiểu dữ liệu> * <tên con trỏ>
 <kiểu dữ liệu>: bao gồm các kiểu dữ liệu có sẵn (int, float,
char, void) và kiểu dữ liệu do ng dùng định nghĩa
(SinhVien, PhanSo…).
 Dấu *: Biểu thị đây là biến con trỏ.
 <Tên của con trỏ>: Tuân theo quy tắc đăt tên biến trong
lập trình.
Ví dụ: int *p; int* p; int * p;

51
Con trỏ

 3 cách khai báo trên là như nhau, nhưng để cho rõ ràng


code thì lời khuyên là:
 Dấu * để sát tên biến ( khai báo 1 biến) .
 Dấu * để sát kiểu dữ liệu ( khai báo 1 hàm).
 Có thể khai báo 2 con trỏ trên cùng một dòng
Ví dụ: int *a, *b;

52
Con trỏ

 Khởi tạo giá trị:


 Không giống như một biến thông thường, một biến con trỏ
phải được khởi tạo với một địa chỉ cụ thể trước khi sử dụng
chúng.
 Nếu con trỏ được sử dụng mà không được khởi tạo, giá trị
của nó sẽ là giá trị rác, điều này sẽ làm chương trình của
bạn chạy không đúng, thậm chí là nguy hiểm nếu giá trị rác
đó chẳng may lại chính là địa chỉ của 1 biến nào đó bạn
đang dùng
 Cú pháp: <tên biến con trỏ>=&<Tên biến có cùng kiểu
dữ liệu với biến con trỏ>

53
Ví dụ

54
Ví dụ

55
Ví dụ

 int *p; p=10;


 Sử dụng số một cách trực tiếp không phải là một lựa chọn
đúng đắn. Trình biên dịch sẽ không cho phép bạn viết một
cái gì đó như thế.
 Có một ngoại lệ đặc biệt. Bạn có thể gán zero cho biến con
trỏ(p=0). Trình biên dịch sẽ không báo lỗi. Một con trỏ
được gán một giá trị bằng 0 được gọi là một con trỏ null.
 Không nên gán các con trỏ bằng 0. Thay vào đó, họ sẽ gán
nó bằng NULL
 p = NULL;

56
Vùng nhớ
Code Segment: Lưu trữ mã máy
dạng nhị phân.Chỉ chịu sự chi phối
của HĐH, người lập trình không can
thiệp trực tiếp vào phân vùng này.

Data Segment: Nơi chứa các biến


kiểu static, biến toàn cục.

BSS Segment: Nơi chứa các biến


kiểu static, biến toàn cục nhưng
chưa được khởi tạo giá trị cụ thể.

Heap: Cấp phát bộ nhớ động dùng


cho con trỏ.CPU không quản lý vùng
này mà do người lập trình quản lý.

Stack: Cấp phát bộ nhớ cho tham số


hàm và biến cục bộ.Người lập trình
khi can thiệp vào vùng này sẽ báo
lỗi.

57
Bài tập
 Chạy trình biên dịch sau:

58
Con trỏ và mảng

59
Con trỏ và mảng

60
Con trỏ và mảng

 Địa chỉ của biến mảng chính là địa chỉ của phần tử đầu tiên
của mảng
 Các phần tử liên tiếp có địa chỉ cách đều nhau, nên ta chắc
chắn các phần tử mảng được xếp cạnh nhau trong bộ nhớ.
 Tóm lại, &a[i] tương đương với a+i và a[i] tương đương
với *(a+i).

61
Con trỏ và mảng

62
Cấp phát bộ nhớ
 Trong lập trình C có 3 cơ chế cấp phát:

• malloc.

• calloc.

• realloc.

 Khi sử dụng phải khai báo thư viện stdlib.h

63
Cấp phát bộ nhớ - malloc.
 malloc.

tên con trỏ = (kiểu con trỏ *) malloc (n*sizeof(kiểu con trỏ));

• Dùng hàm malloc() phải xác định rõ số byte của bộ nhớ cần cấp
phát. Hàm này trả về con trỏ kiểu void cho phép chúng ta có thể ép
kiểu về bất cứ kiểu dữ liệu nào.

• Giá trị khởi tạo : Giá trị rác.

• Kết quả trả về :Trả về địa chỉ byte đầu tiên của vùng nhớ đc cấp
phát, con trỏ null nếu cấp phát thất bại

64
Cấp phát bộ nhớ - malloc.
int *p;

p = (int*) malloc(100 * sizeof(int));

• Trong vd trên thực hiện cấp phát cho việc lưu trữ 100 số nguyên.
Giả sử sizeof int là 4, khi đó lệnh dưới đây thực hiện cấp phát 400
bytes. Khi đó, con trỏ p sẽ có giá trị là địa chỉ của byte dữ liệu
đầu tiên trong khối bộ nhớ vừa cấp phát.

65
Cấp phát bộ nhớ - calloc.
 calloc.

tên con trỏ = (kiểu con trỏ *) calloc (n, sizeof(kiểu con trỏ));

• Giá trị khởi tạo : bằng 0.

• Hàm calloc() nhận vào 2 tham số là số ô nhớ (số phần tử) muốn
khởi tạo và kích thước của 1 ô nhớ (kích thước mỗi phần tử).

• Kết quả trả về :Con trỏ sẽ trỏ tới vùng nhớ vừa được cấp phát
thành công, con trỏ null nếu cấp phát thất bại

66
Cấp phát bộ nhớ - calloc.
int *p;

p = (int*) calloc(100, sizeof(int))

• Trong ví dụ trên, hàm calloc() thực hiện cấp phát 100 ô nhớ liên
tiếp và mỗi ô nhớ có kích thước là số byte của kiểu int. Hàm này
cũng trả về con trỏ chứa giá trị là địa chỉ của byte đầu tiên trong
khối bộ nhớ vừa cấp phát.

67
Bài tập malloc - calloc.
Trình biên dịch

68
Bài tập malloc - calloc.
Kết quả

69
Bài tập malloc - calloc.
 Nếu không quan tâm đến giá trị mặc định của vùng nhớ được cấp
thì dùng malloc.

 Nếu muốn tất cả giá trị của toàn bộ ô nhớ sau khi được cấp là 0 thì
dùng calloc.

int *p=(int *)calloc(10,sizeof(int));

Tương đương với:

int *p=(int*)malloc(size(int));

memset(p,0,10*sizeof(int));

70
Cấp phát bộ nhớ - realloc.
 realloc.

tên con trỏ = (kiểu con trỏ *) realloc (tên con trỏ, số lượng cần
cấp phát*sizeof(kiểu con trỏ));

Số lượng cần cấp phát = cũ + mới

• Khi cấp phát cho biến con trỏ 1 số lượng ô nhớ nào đó mà trong
quá trình làm việc ta thiếu hoặc cần cấp phát thêm thì ta sử dụng
realloc.

• Hàm realloc nhận 2 đối số : 1 là con trỏ trả về bởi malloc() hoặc
calloc, 2 là số byte cần cấp phát lại.

71
Bài tập malloc – calloc - realloc.
 realloc:

72
Giải phóng bộ nhớ
 Tại sao cần phải giải phóng bộ nhớ

• Bộ nhớ khi cấp phát cho con trỏ thuộc tâng 4 HEAP, nên nếu chúng
ta không giải phóng thì ô nhớ đó sẽ không bao giờ được giải phóng
-> Tràn bộ nhớ (máy tính mới mạnh ít gặp trường hợp này).

 Nếu giải phóng thì giá trị con trỏ còn hay mất ?

• Mất :Nếu sau khi giải phóng có tiền trình khác chiếm hữu ô nhớ.

• Còn :Không có tiến trình khác chiếm hữu

73
Giải phóng bộ nhớ
 Bản chất của giải phóng bộ nhớ

• Là thông báo cho chương trình biết vùng nhớ đã sử dụng xong,
HĐH có thể dùng cho tiến trình khác.

 Cú pháp

free(tên con trỏ);

74
Con trỏ đến mảng 1 chiều

75
Con trỏ đến mảng 1 chiều

76
Con trỏ và Struct
Struct SinhVien Struct SinhVien
{ {
char hoten[20]; char hoten[20];
int tuoi; int tuoi;
float dtb; float dtb;
}sv; }*sv1;

Truy cập thành viên


Truy cập thành viên hoten sv.hoten sv1->hoten
Truy cập thành viên tuoi sv.tuoi sv1->tuoi
Truy cập thành viên dtb sv.dtb sv1->dtb

77
Con trỏ và Struct
 Để truy cập để lấy dữ liệu các thành phần của con trỏ cấu trúc ta có
3 cách sau:

• Cách 1: Mang[i].dtb;

• Cách 2: (*(Mang+i)).dtb;

• Cách 3: (Mang+i) ->dtb;

 Cả 3 cách trên đều truy cập tới Điem Trung Binh.

78
Bài tập 2

79

You might also like