Giáo trình Ngôn ngữ lập trình C

MỤC LỤC

GIỚI THIỆU

Chương 1

CÁC KHÁI NIỆM CƠ BẢN

1.1. Tập ký tự dùng trong ngôn ngữ C

1.2. Từ khoá

1.3. Tên

1.4. Kiểu dữ liệu

1.4.1. Kiểu ký tự (char)

1.4.2. Kiểu nguyên

1.4.3. Kiểu dấu phảy động

1.5. Định nghĩa kiểu bằng TYPEDEF

1.5.1. Công dụng

1.5.2. Cách viết

1.6. Hằng

1.6.1. Tên hằng

1.6.2. Các loại hằng

1.6.2.1. Hằng int

1.6.2.2. Hằng long

1.6.2.3. Hằng int hệ 8

1.6.2.4. Hằng int hệ 16

1.6.2.5. Hằng ký tự

1.6.2.5. Hằng xâu ký tự

1.7. Biến

1.8. Mảng

 

Chương 2

CÁC LỆNH VÀO RA

2.1. Thâm nhập vào thư viện chuẩn

2.2. Các hàm vào ra chuẩn - getchar() và putchar()

2.2.1. Hàm getchar()

2.2.2. Hàm putchar()

2.2.3. Hàm getch()

2.2.4. Hàm putch()

2.3. Đưa kết quả lên màn hình - hàm printf

2.4. Vào số liệu từ bàn phím - hàm scanf

2.5. Đưa kết quả ra máy in

 

Chương 3

BIỂU THỨC

3.1. Biểu thức

3.2. Lệnh gán và biểu thức

3.3. Các phép toán số học

3.4. Các phép toán quan hệ và logic

3.5. Phép toán tăng giảm

3.6. Thứ tự ưu tiên các phép toán

3.7. Chuyển đổi kiểu giá trị

 

Chương 4

CẤU TRÚC CƠ BẢN CỦA CHƯƠNG TRÌNH

4.1. Lời chú thích

4.2. Lệnh và khối lệnh

4.2.1. Lệnh

4.2.2. Khối lệnh

4.3. Cấu trúc cơ bản của chương trình

4.4. Một số qui tắc cần nhớ khi viết chương trình

 

Chương 5

CẤU TRÚC ĐIỀU KHIỂN

5.1. Cấu trúc có điều kiện

5.1.1. Lệnh if-else

5.1.2. Lệnh else-if

5.2. Lệnh nhảy không điều kiện - toán tử goto

5.3. Cấu trúc rẽ nhánh - toán tử switch

5.4. Cấu trúc lặp

5.4.1. Cấu trúc lặp với toán tử while và for

5.4.1.1. Cấu trúc lặp với toán tử while

5.4.1.2. Cấu trúc lặp với toán tử for :

5.4.2. Chu trình do-while

5.5. Câu lệnh break

5.6. Câu lệnh continue

 

Chương 6

HÀM

6.1. Cơ sở

6.2. Hàm không cho các giá trị

6.3. Hàm đệ qui

6.3.3. Mở đầu

6.3.2. Các bài toán có thể dùng đệ qui

6.3.3. Cách xây dựng hàm đệ qui

6.3.4. Các ví dụ về dùng hàm đệ qui

6.4. Bộ tiền sử lý C

 

Chương 7

CON TRỎ

7.1. Con trỏ và địa chỉ

7.2. Con trỏ và mảng một chiều

7.2.1.Phép toán lấy địa chỉ

7.2.2. Tên mảng là một hằng địa chỉ

7.2.3. Con trỏ trỏ tới các phần tử của mảng một chiều

7.2.4. Mảng, con trỏ và xâu ký tự

7.3. Con trỏ và mảng nhiều chiều

7.3.1.Phép lấy địa chỉ

7.3.2. Phép cộng địa chỉ trong mảng hai chiều

7.3.3. Con trỏ và mảng hai chiều

7.4. Kiểu con trỏ kiểu địa chỉ, các phép toán trên con trỏ

7.4.1. Kiểu con trỏ và kiểu địa chỉ

7.4.2. Các phép toán trên con trỏ

7.4.3. Con trỏ kiểu void

7.5. Mảng con trỏ

7.6. Con trỏ tới hàm

7.6.1. Cách khai báo con trỏ hàm và mảng con trỏ hàm

7.6.2. Tác dụng của con trỏ hàm

7.6.3. Đối của con trỏ hàm

 

Chương 8

CẤU TRÚC

8.1. Kiểu cấu trúc

8.2. Khai báo theo một kiểu cấu trúc đã định nghĩa

8.3. Truy nhập đến các thành phần cấu trúc

8.4. Mảng cấu trúc

8.5. Khởi đầu một cấu trúc

8.6. Phép gán cấu trúc

8.7. Con trỏ cấu trúc và địa chỉ cấu trúc

8.7.1. Con trỏ và địa chỉ

8.7.2. Truy nhập qua con trỏ

8.7.3. Phép gán qua con trỏ

8.7.4. Phép cộng địa chỉ

8.7.5. Con trỏ và mảng

8.8. Cấu trúc tự trỏ và danh sách liên kết

 

 

doc141 trang | Chia sẻ: lethuong715 | Lượt xem: 680 | Lượt tải: 0download
Bạn đang xem trước 20 trang mẫu tài liệu Giáo trình Ngôn ngữ lập trình C, để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên
);
	}
Ví dụ 2 :	
Chương trình đọc vào một số rồi in nó ra dưới dạng các ký tự liên tiếp.
# include "stdio.h"
# include "conio.h"
void prind(int n);
main()
{ 
 int a;
 clrscr();
 printf("n=");
 scanf("%d",&a);
 prind(a);
 getch();
}
void prind(int n)
 { 
 int i;
 if (n<0)
 { putchar('-');
 n=-n;
 }
 if ((i=n/10)!=0)
 prind(i);
 putchar(n%10+'0');
 }
6.4. Bộ tiền sử lý C :
	C đưa ra một số cách mở rộng ngôn ngữ bằng các bộ tiền sử lý macro đơn giản. Có hai cách mở rộng chính là #define mà ta đã học và khả năng bao hàm nội dung của các file khác vào file đang được dịch.
Bao hàm file :	
	Để dễ dàng xử lý một tập các #define và khai báo ( trong các đối tượng khác ), C đưa ra cách bao hàm các file khác vào file đang dịch có dạng :
#include "tên file"
Dòng khai báo trên sẽ được thay thế bởi nội dung của file có tên là tên file. Thông thường có vài dòng như vậy xuất hiện tại đầu mỗi file gốc để gọi vào các câu lệnh #define chung và các khai báo cho các biến ngoài. Các #include được phép lồng nhau. Thường thì các #include được dùng nhiều trong các chương trình lớn, nó đảm bảo rằng mọi file gốc đều được cung cấp cùng các định nghĩa và khai báo biến, do vậy tránh được các lỗi khó chịu do việc thiếu các khai báo định nghĩa. Tất nhiên khi thay đổi file được bao hàm vào thì mọi file phụ thuộc vào nó đều phải dịch lại.
Phép thế MACRO :
	Định nghĩa có dạng :
#define biểu thức 1 [ biểu thức 2 ]
sẽ gọi tới một macro để thay thế biểu thức 2 (nếu có) cho biểu thức 1.
Ví dụ :
#define YES 1
	Macro thay biến YES bởi giá trị 1 có nghĩa là hễ có chỗ nào trong chương trình có xuất hiện biến YES thì nó sẽ được thay bởi giá trị 1.
	Phạm vi cho tên được định nghĩa bởi #define là từ điểm định nghĩa đến cuối file gốc. Có thể định nghĩa lại tên và một định nghĩa có thể sử dụng các định nghĩa khác trước đó. Phép thế không thực hiện cho các xâu dấu nháy, ví dụ như YES là tên được định nghĩa thì không có việc thay thế nào được thực hiện trong đoạn lệnh có "YES".
	Vì việc thiết lập #define là một bước chuẩn bị chứ không phải là một phần của chương trình biên dịch nên có rất ít hạn chế về văn phạm về việc phải định nghĩa cái gì. Chẳng hạn như những người lập trình ưa thích PASCAL có thể định nghĩa :
	#define then
	#define begin {
	#define end; }
sau đó viết đoạn chương trình :
	if (i>0)	 then
	begin
	a=i;
	......
	end;
Ta cũng có thể định nghĩa các macro có đối, do vậy văn bản thay thế sẽ phụ thuộc vào cách gọi tới macro.
Ví dụ :
	 Định nghĩa macro gọi max như sau :
	#define max(a,b) ((a)>(b) ?(a):(b))	
Việc sử dụng :
	x=max(p+q,r+s);
tương đương với :
	x=((p+q)>(r+s) ? (p+q):(r+s));
Như vậy ta có thể có hàm tính cực đại viết trên một dòng. Chừng nào các đối còn giữ được tính nhất quán thì macro này vẫn có giá trị với mọi kiểu dữ liệu, không cần phải có các loại hàm max khác cho các kiểu dữ liệu khác nhưng vẫn phải có đối cho các hàm.
Tất nhiên nếu ta kiểm tra lại việc mở rộng của hàm max trên, ta sẽ thấy rằng nó có thể gây ra số bẫy. Biểu thức đã được tính lại hai lần và điều này là không tốt nếu nó gây ra hiệu quả phụ kiểu như các lời gọi hàm và toán tử tăng. Cần phải thận trọng dùng thêm dấu ngoặc để đảm bảo trật tự tính toán. Tuy vậy, macro vẫn rất có giá trị.
Chú ý : 
	Không được viết dấu cách giữa tên macro với dấu mở ngoặc bao quanh danh sách đối.
Ví dụ :
	Xét chương trình sau :
main()
	{
	int x,y,z;
	x=5;
	y=10*5;
	z=x+y;
	z=x+y+6;
	z=5*x+y;
	z=5*(x+y);
	z=5*((x)+(y));
	printf("Z=%d",z);
	getch();
	return;
	}	
Chương trình sử dụng MACRO sẽ như sau :
#define BEGIN {
#define END }
#define INTEGER int
#define NB 10
#define LIMIT NB*5
#define SUMXY x+y
#define SUM1 (x+y)
#define SUM2 ((x)+(y))
main()
	BEGIN
	INTEGER x,y,z;
	x=5;
	y=LIMIT;
	z=SUMXY;
	z=5*SUMXY;
	z=5*SUM1;
	z=5*SUM2;
	printf("\n Z=%d",z);
	getch();
	return;
	END
Chương 7
Con trỏ
	Con trỏ là biến chứa địa chỉ của một biến khác. Con trỏ được sử dụng rất nhiều trong C, một phần là do chúng đôi khi là cách duy nhất để biểu diễn tính toán, và phần nữa do chúng thường làm cho chương trình ngắn gọn và có hiệu quả hơn các cách khác .
	Con trỏ đã từng bị coi như có hại chẳng kém gì lệnh goto do cách sử dụng chúng đã tạo ra các chương trình khó hiểu. Điều này chắc chắn là đúng khi người ta sử dụng chúng một cách lôn xộn và do đó tạo ra các con trỏ trỏ đến đâu đó không biết trước được.
7.1. Con trỏ và địa chỉ :
	Vì con trỏ chứa địa chỉ của đối tượng nên nó có thể xâm nhập vào đối tượng gián tiếp qua con trỏ. Giả sử x là một biến kiểu int, và giả sử px là con trỏ được tạo ra theo một cách nào đó. 
Phép toán một ngôi & sẽ cho địa chỉ của đối tượng, nên câu lệnh :
px=&x;
sẽ gán địa chỉ của biến x cho trỏ px, và px bây giờ được gọi là " trỏ tới biến x ". Phép toán & chỉ áp dụng được cho các biến và phần tử bảng, kết cấu kiểu &(x+1) và &3 là không hợp lệ. Lấy đại chỉ của biến register cũng là sai.
	Phép toán một ngôi * coi là toán hạng của nó là đại chỉ cần xét và thâm nhập tới địa chỉ đó để lấy ra nội dung. Nếu biến y có kiểu int thì thì lệnh :
y=*px;
sẽ gán giá trị của biến mà trỏ px trỏ tới. Vậy dãy lệnh :
	px=&x;
	y=*px;
sẽ gán giá trị của x cho y như trong lệnh : 
	y=x;
Các khai báo cho các biến con trỏ có dạng :
	tên kiểu *tên con trỏ
Ví dụ :
 Như trong ví dụ trên, ta khai báo con trỏ px kiểu int :	
	int *px;
Trong khai báo trên ta đã ngụ ý nói rằng đó là một cách tượng trưng, rằng tổ hợp *px có kiểu int, tức là nếu px xuất hiện trong ngữ cảnh *px thì nó cũng tương đương với biến có kiểu int.
	Con trỏ có thể xuất hiện trong các biểu thức. Chẳng hạn, nếu px trỏ tới số nguyên x thì *px có thể xuất hiện trong bất kỳ ngữ cảnh nào mà x có thể xuất hiện. 
Ví dụ :
Lệnh	y=*px+1; 
sẽ đặt y lớn hơn x một đơn vị.
Lệnh printf("%d",*px); 
sẽ in ra giá trị hiện tại của x
Lệnh :
 d=sqrt((double) *px); 
sẽ gán cho biến d căn bậc hai của x, giá trị này bị buộc phải chuyển sang double trước khi được chuyền cho sqrt ( cách dùng hàm sqrt ).
	Trong các biểu thức kiểu như :
	y=*px+1;
phép toán một ngôi * và & có mức ưu tiên cao hơn các phép toán số học, cho nên biểu thức này lấy bất ký giá trị nào mà px trỏ tới, cộng với 1 rồi gán cho y.
	Con trỏ cũng có thể xuất hiện bên vế trái của phép gán. Nếu px trỏ tới x thì sau lệnh :
	*px=0;
x sẽ có giá trị bằng 0. Cũng tương tự các lệnh:
	*px+=1;
	(*px)++;
sẽ tăng giá trị của x lên 1 dơn vị.
	Các dấu ngoặc đơn ở câu lệnh cuối là cần thiết , nếu không thì biểu thức sẽ tăng px thay cho tăng ở biến mà nó trỏ tới vì phép toán một ngôi như * và ++ được tính từ phải sang trái.
	Cuối cùng, vì con trỏ là biến nên ta có thao tác chúng như đối với các biến khác. Nếu py cũng là con trỏ int thì lệnh :
	py=px;
sẽ sao nội dung của px vào py, nghĩa là làm cho py trỏ tới nơi mà px trỏ.
7.2. Con trỏ và mảng một chiều :
	Trong C có mối quan hệ chặt chẽ giữa con trỏ và mảng : các phần tử của mảng có thể được xác định nhờ chỉ số hoặc thông qua con trỏ.
7.2.1.Phép toán lấy địa chỉ :
	Phép toán này chỉ áp dụng cho các phần tử của mảng một chiều. Giả sử ta có khai báo :
	double b[20];
Khi đó phép toán :
	&b[9]	
sẽ cho địa chỉ của phần tử b[9].
7.2.2. Tên mảng là một hằng địa chỉ :
	Khi khai báo :
	float a[10];
máy sẽ bố trí bố trí cho mảng a mười khoảng nhớ liên tiếp, mỗi khoảng nhớ là 4 byte. Như vậy, nếu biết địa chỉ của một phần tử nào đó của mảng a, thì ta có thể dễ dàng suy ra địa chỉ của các phần tử khác của mảng.
	Với C ta có :
	a tương đương với &a[0]
	a+i tương đương với &a[i]
	*(a+i) tương đương với a[i]
7.2.3. Con trỏ trỏ tới các phần tử của mảng một chiều :
	Khi con trỏ pa trỏ tới phần tử a[k] thì :
	pa+i trỏ tới phần tử thứ i sau a[k], có nghĩa là nó trỏ tới a[k+i].
	pa-i trỏ tới phần tử thứ i trước a[k], có nghĩa là nó trỏ tới a[k-i].
	*(pa+i) tương đương với pa[i].
Như vậy, sau hai câu lệnh :
	float a[20],*p;
	p=a;
thì bốn cách viết sau có tác dụng như nhau :
	a[i] *(a+i) p[i] *(p+i)
Ví dụ :
	Vào số liệu của các phần tử của một mảng và tính tổng của chúng :
Cách 1:
#include "stdio.h"
main()
	{
	float a[4],tong;
	int i;
	for (i=0;i<4;++i)
	 {
 	printf("\n a[%d]=",i);
	scanf("%f",a+i);
	 }
	tong=0;
	for (i=0;i<4;++i)
	tong+=a[i];
	printf("\n Tong cac phan tu mang la :%8.2f ",tong);
	}
Cách 2 :
#include "stdio.h"
main()
	{
	float a[4],tong, *troa;
	int i;
	troa=a;
	for (i=0;i<4;++i)
	 {
 	printf("\n a[%d]=",i);
	scanf("%f",&troa[i]);
	 }
	tong=0;
	for (i=0;i<4;++i)
	tong+=troa[i];
	printf("\n Tong cac phan tu mang la :%8.2f ",tong);
	}
Cách 3 :
#include "stdio.h"
main()
	{
	float a[4],tong,*troa;
	int i;
	troa=a;
	for (i=0;i<4;++i)
	 {
 	printf("\n a[%d]=",i);
	scanf("%f",troa+i);
	 }
	tong=0;
	for (i=0;i<4;++i)
	tong+=*(troa+i);
	printf("\n Tong cac phan tu mang la :%8.2f ",tong);
	}
Chú ý :
	Mảng một chiều và con trỏ tương ứng phải cùng kiểu.
7.2.4. Mảng, con trỏ và xâu ký tự :
	Như ta đã biết trước đây, xâu ký tự là một dãy ký tự đặt trong hai dấu nháy kép, ví dụ như :
	"Viet nam"
	Khi gặp một xâu ký tự, máy sẽ cấp phát một khoảng nhớ cho một mảng kiểu char đủ lớn để chứa các ký tự của xâu và chứa thêm ký tự '\0' là ký tự dùng làm ký tự kết thúc của một xâu ký tự. Mỗi ký tự của xâu được chứa trong một phần tử của mảng.
	Cũng giống như tên mảng, xâu ký tự là một hàng địa chỉ biểu thị địa chỉ đầu của mảng chứa nó. Vì vậy nếu ta khai báo biến xau như một con trỏ kiểu char :
	char *xau;
thì phép gán :
	xau="Ha noi"
là hoàn toàn có nghĩa. Sau khi thực hiện câu lệnh này trong con trỏ xau sẽ có địa chỉ đầu của mảng (kiểu char) đang chứa xâu ký tự bên phải. Khi đó các câu lệnh :
	puts("Ha noi");
	puts(xau);
sẽ có cùng một tác dụng là cho hiện lên màn hình dòng chữ Ha noi.
	Mảng kiểu char thường dùng để chứa một dãy ký tự đọc vào bộ nhớ. Ví dụ, để nạp từ bàn phím tên của một người ta dùng một mảng kiểu char với độ dài 25, ta sử dụng các câu lệnh sau :
	char ten[25];
	printf("\n Ho ten :");
	gets(ten);
	Bây giờ ta xem

File đính kèm:

  • docgiao trinh ngon ngu lap trinh C.doc