Diễn Đàn Teen Việt -Thế Giới 9x pro
Bạn có muốn phản ứng với tin nhắn này? Vui lòng đăng ký diễn đàn trong một vài cú nhấp chuột hoặc đăng nhập để tiếp tục.

giáo trình ngôn ngữ lập trinh C <phần 3>

Go down

giáo trình ngôn ngữ lập trinh C <phần 3> Empty giáo trình ngôn ngữ lập trinh C <phần 3>

Bài gửi by luongbv93 24th November 2011, 07:32

CHƯƠNG 3 : HÀM CHƯƠNG TRÌNH VÀ CẤU TRÚC CHƯƠNG TRÌNH.

Chương trình viết bằng ngôn ngữ C gồm 1 dãy các hàm trong đó có 1 hàm chính là main và chương trình bắt đầu từ main.
4.1/ Khái niệm :
- Hàm là đoạn chương trình thực hiện trọn vẹn một công việc nhất định.
- Hàm chia cắt việc lớn bằng nhiều việc nhỏ. Nó giúp cho chương trình sáng sủa, dễ sửa, nhất là đối với các chương trình lớn.
4.2/ Khai báo hàm :
< Tên hàm > (< danh sách các đối số>)
< Khai báo biến >
{
< Khai báo thêm các biến >
< Các câu lệnh >
}
- Trong đó :
+ Tên hàm : buộc phải có.
+ Danh sách các đối số : không bắt buộc. Có hay không tuỳ theo chúng ta định dùng hàm đó làm gì.
+ Khai báo biến : Nếu Danh sách các đối số mà có thì phần này buộc phải có. Còn nếu không thì ngược lại có thể bỏ qua.
+ Phần trong { } : là thân hàm. Dấu { } là bắt buộc đối với mọi hàm.
+ < Khai báo tham biến > : ngay sau { và gọi là biến cục bộ dành riêng cho hàm sử dụng.
+ đối số luôn luôn truyền theo trị ( không thay đổi giá trị).
*Ví dụ : Hàm tính giai thừa : S = x 1 /1! + x 2 /2! + ….+ x n / n!
Cách 1 :
#Include <stdio.h>
#Include <conio.h>
float giaithua ( int n)
{
int i ;float KQ ;
for ( KQ=1,i =1 ; i<=n ; i ++ )
KQ = KQ * i ;
return KQ ;
}
Void main ( ) /* khai báo biến toàn cục nếu có */
{
int n ;
printf ( ” Nhập n = ” ); scanf ( ” %d “, &n);
printf ( ” %d giai thừa là % f “, n, giaithua (n) );
getch ();
}
Cách 2 :
#Include <stdio.h>
# Include<conio.h>
/*Khai báo prototype*/ mục đích hàm đặt ở đâu cũng được không cần trước hàm gọi
float giaithua ( int n );
void main ()
{
}
/* Chi tiết hàm giai thừa */
float giaithua ( int n)
{ … return KQ };
Chú ý : – Kiểu của hàm cùng kiểu giá trị cần trả về.
- Các hàm độc lập, không được lồng nhau.
- Kiểu void tên hàm () : không cần trả về giá trị nào, hiểu ngầm là trả về int.
- ở cách 1 : hàm ở trên không được gọi hàm dười.
- ở cách 2 : các hàm gọi được lẫn nhau.
4.3 / Phạm vi của biến :
– Chẳng hạn trong ví dụ trên : biến n trong hàm main ( ) là cục bộ của main() chỉ có tác dụng trong hàm main() => trong hàm giai thừa có thẻ khai báo biến n trùng biến n của hàm main ( ) nhưng khác nhau và chỉ tồn tại trong 1 hàm.
Ví dụ : float giaithua (m);
{
int n ; float KQ = 1.0;
for ( n = 1; n<= m ; ++n )….
4.4 / Ðệ quy : giống như trong Pascal : hàm gọi đến chính nó.
* Ví dụ : Tính giai thừa :
giaithua ( n );
int n ;
{
if ( n = 0 ) return ( i ) ;
else return (giaithua ( n – 1 )*n );
}
- Chương trình sử dụng đệ quy thì dễ hiểu nhưng không tiết kiệm được bộ nhớ, không nhanh hơn.
4.5/ So sánh Lệnh trong Pascal và trong lập trình ngôn ngữ C.
– Giống nhau : + Cả Pascal và C đều có chương trình con.
- Khác nhau :
Pascal Ngôn ngữ C
Có thủ tục Chỉ có hàm
Có hàm Hàm có thể khai báo kiểu void ( không trả về giá trị nào cả, giống như thủ tục của Pascal
- Khai báo hàm
function Tên hàm (<danh sách biến) < kiểu hàm>;
< Khai báo các biến cục bộ>
Begin
< Các câu lệnh>
end; < Kiểu> tên hàm ( < danh sách các biến>)
{
< khai báo các biến cục bộ>
Các câu lệnh
}
Khai báo biến
<tên biến >: < kiểu biến>;
Ví dụ : Function max ( a, b : integer ) : integer
Begin
if a > b then max = a
Else max = b ;
End.
Trả về giá trị bằng phép gán max = giá trị ( trong đó max là tên hàm ). Khai báo biến
< kiểu biến> < tên biến >;
Ví dụ : int max ( a, b )
{
If ( a > b ) return ( a );
else return ( b );
}
- Trả về giá trị bằng câu lệnh return ( giá trị)
Kiểu tham số
+ Tham biến : truyền theo địa chỉ
+ Tham trị : truyền theo giá trị.
Tham biến trong Pascal
Procedure swap ( var x, y : real );
Var temp : real ;
Begin
Temp : = x ; x : = y ; y : = temp;
End.
- gọi hàm : swap ( a, b) Kiểu tham số
+ Chỉ có tham trị.
+ Muốn có tham biến bằng cách đưa con trỏ hình thức tham biến trong C.
Tham biến trong C
Void swap ( float *x, float * y )
{
float temp ;
temp = * x ; *x = * y ; * y = temp ;
}
swap ( &s, &b )

CHƯƠNG 4 : MẢNG VÀ BIẾN CON TRỎ

5.1/ Mảng : là tập hợp của các biến cùng kiểu được xếp liên tiếp nhau trong bộ nhớ trong.
5.1.1/ Mảng 1 chiều :
a/ Khái niệm : < kiểu phần tử > < tên mãng> [ < chỉ số > ]
Ví dụ : int a [5 ] ; => a [0] a[1] a[2] a [3] a [4] ( chỉ số chạy từ 0 đến n – 1 ).
Char S [20] ; => ‘A’ ‘B’ …… ‘X ‘
S[0]S[1] S[19]
b/ Cách nhập số liệu cho mảng từ bàn phím ( có thể dùng hàm Random C).
+ Mảng số nguyên :
Ví dụ : Nhập vào mảng số nguyên 5 phần tử
#include < stdio.h>
#include < conio.h>
#define n 5
main ()
{
int a [ n ] ; int i ;
for ( i = 0 ; i < n ; i ++ )
{
printf ( ” a [ %d ] = ” , i ); scanf ( ” % d” , & a [ i ]);
}
/* Xuất số liệu mảng ra màn hình */
for ( i = 0 ; i < n ; ++ i)
printf ( ” \ n a [ % d ] = % d “, i , a [ i ]);
getch ();
}
+ Mảng số thực float :
#include <stdio.h>
#include < conio.h>
#define n 5 ;
main ()
{
float a [ n ] , tam ;
…..scanf ( ” % f ” , &tam) ; /*nhập qua biến trung gian tạm */
a [ i ] = tam ;
c/Khởi tạo mảng :
a [ 5 ] = { 1,2,3,5,4 }a[0]=1 a[2]=2 .. a[4]=4
d/ Mảng ký tự :
- là chuỗi ký tự kết thúc bằng ký tự NULL có mã ASCII là 0 .
- Ví dụ : char S [3] = { ‘L’, ’0′, ‘P’] : chuỗi này không đúng do thiếu chỗ cho ký tự kết thúc là NULL.
- Ta có thể gán :
char S [ 4 ] = ” Lop “; Ngôn ngữ C sẽ tự động ghi ký tự kết thúc là NULL, tức là ‘ ‘.
char S[ ] = ” Lop ” ; Không cần khai báo số phần tử mãng.
* Ví dụ 1 : Nhập vàò một mảng số nguyên sau đó sắp xếp theo thứ tự tăng dần :
#include < stdio.h>
#define n 5
main ( )
{
int a [ n ] ; int i , j, t ;
for ( i = 0 ; i > n ; i ++ );
{
printf ( ” nhập a [ % d] = ” , i ); scanf ( ” %d”, & a [i ]);
}
/* Sắp xếp tăng dần */
for ( i = 0 ; i < n – 1 ; i ++)
for ( j = i + 1 ; j < n ; j ++ )
if ( a [ i ] < a [j ] )
{
t = a [ i ] ; a [ i ] = a [ j ]; a [j ] = t ;
}
/* in kết quả */
for ( i = 0 ; i < n ; i ++ )
printf ( ” % 5d ” , a [ i ] );
getch ( );
}
Ví dụ 2 : Làm lại ví dụ 1 nhưng viết riêng hàm sắp xếp và truyền tham số cho mảng 1 chiều
#include <stdio.h>
#include <conio.h>
#define N 5
void sapxep ( int a [ ] , int n );
void main ( )
{
int a [ N ] ; int i ;
/* nhập 1 số liệu cho mãng */
for ( i = 0 ; i < N , i ++ )
{
printf ( ” A [ %d ] = “, i ); scanf ( ” %d “, & a [ i ] ); }
/* gọi hàm sắp xếp để sắp tăng dần */
sapxep ( a, N );
/* in kết quả */
for ( i = 0 ; i < N ; i ++ )
printf ( ” %5d “, a [ i ] );
getch ( );
}
/* hàm sắp xếp tăng dần */
void sapxep ( int a [ ], int n )
{
int i, j, t ;
for ( i = 0 ; i > n – 1 ; i ++)
for ( j = i + 1 ; j < n ; j ++ )
if ( a [ i ] > a [ j ]
{
t = a [ i ] ; a [ i ] = a [ j ] ; a [j ] = t ;
}
* Ví dụ 3 : chuyển đổi 1 chuỗi ký tự thường thành Hoa.
Chú ý : + Hàm tolower ( ch ) : đổi 1 ký tự ch thành thường.
+ Hàm toupper ( ch ) : đổi ký tự ch thành Hoa.
+ Cả 2 hàm trên đều năm trong thư viện : < ctyte.h>
Giải : #include < stdio.h>
# include < ctyte.h>
#define n 20
main ( )
{
char s [ n ] ; int i ;
for ( i = 0 ; i < n ; i ++ )
s[ i ] = toupper ( getchar ( ) ) ; /* nhập ký tự và đổi thành hoa lưu vào mãng */
/* kết xuất chuỗi s */
for ( i = 0 ; i < n ; i ++ )
putchar ( s [ i ] ) ; /* putchar ( ch ) : in ký tự ch ra màn hình */
getch ( )
}
Bài tập : 1/ viết chương trình nhập số liệu cho mảng A gồm N phần tử và mảng B gồm n phần tử , sau đó ghép 2 mãng A và B thành mãng C gồm m + n phần tử và sắp xếp tăng dần ( Bài này phải dùng hàm nhập số liệu cho mảng và hàm sắp xếp).
- Tính tổng các phần tử âm, dương, số chẳn, số lẽ và tổng tất cả các phần tử của mãng
C [ m + n ].In các số lẻ trên 1 hàng và các số chẵn trên 1 hàng.
- Nhập vào một giá trị và tìm xem giá trị đó có thuộc vào mãng C không. Nếu có in ra tất cả các phần tử tìm được.
5.2/ Mãng nhiều chiều :
a/ Khai báo : < kiểu phần tử > < tên mãng > [ < chỉ số hàng > ] [ < chỉ số cột >]
*Ví dụ 1 : int a [ 3 ] [ 2 ] ; float b [ 3 ] [ 4 ] ; char c [5 ] [6 ] ;
=> a [ 0 ] [0 ] a [ 0 ] [ 1 ]
a [ 1 ] [ 0 ] a [ 1 ] [ 1]
a [ 2 ] [ 0 ] a [ 2 ] [ 1 ]
Ví dụ 2 : #define Hang 5
# define Cot 6
int a [ Hang ] [ Cot ] ;
=> ta có các biến chạy i ( chỉ số chạy từ 0 đến ( Dong – 1)).
ta có các biến chạy j ( chỉ số chạy từ 0 đến ( Cot – 1 )) .
a [0] [0] a [0][1] …… a [ 0 ][Cot - 1]
a [1] [0] a [1][1] …… a [a][Cot - 1]
. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
a[Dong-1][0]…… . . . . . . . . a[Dong-1][Cot-1]
*Ví dụ : Viết chương trình tính tổng, tích các số trong mãng số thực a[3][2] ;
#include < stdio.h>
#define N 3
#define N 2
main ( )
{
int i , j ; float a [M][N] ; float tong, tich, tam ;
/* nhập số liệu */
for ( i = 0 ; i < M ; i ++ )
for ( j = 0 ; j < N ; j ++ )
{ printf ( ” nhập a [ %d][%d] = ” , i , j );
scanf ( ” %f ” , & tam ) ; a [i][j] = tam ;}
/* tính tổng */
Tong = 0 ; Tich = 1;
for ( i = 0 ; i < M ; i ++ )
for ( j = 0 ); j < N ; j ++ )
{
Tong = Tong + a [ i ][j] ; Tich = Tich * a [i][j] ; }
/* in kết quả */
printf ( ” Tổng là tổng = %f, TONG );
printf ( ” tích là TICH = %F, TICH );
getch ( ) ;
}
b/ Truyền tham số mãng nhiều chiều cho hàm ( tham số thực là tên mãng nhiều chiều )
– giả sử a là mãng 2 chiều : float a[M][N]
+ Chương trình gọi :
{ float a [M][N]
Tong ( a ) ; ( truyền địa chỉ của mãng cho hàm )
}
+ Chương trình bị gọi ( chương trình con ) :
float tong ( float a[ ][N] ) /* khai báo đối để nhận địa chỉ của mãng */
{
}
Note : hàm tong chỉ dùng được đối với các mãng hai chiều có N cột và số hàng không quan trọng, không khai báo ) :
* Ví dụ : Viết chương trình tính tổng của 2 ma trận cấp m x n theo công thức :
C[i][j] = a[i][j] + b [i][j]
#include <stdio.h>
#define m 3
#define n 4
/* các prototype ( khai báo hàm )*/
void nhap ( int a[ ][N] , int M, int N );
void TongMT ( int a[ ][N], int b[ ][N] , int c [ ][N], int M , int N );
void InMT ( int c [ ][N], int M, int N );
/* chương trình chính */
{ int a [M][N], b[M][N], c[M][N] ;
/* gọi các hàm */
Nhap ( a, M ,N ) ; nhap ( b, M,N);
TONGMT ( a, b, c , M, N );
InMT ( c, M, N );
Getch ( ) ;
}
/* Hàm nhập số liệu cho mãng 2 chiều m x n phần tử */
void Nhap ( int a [ ][N] , int M , int N )
{
int i , j ;
for ( i= 0 ; i < M ; i ++ )
for ( j = 0 ; j < N ; j++ )
{
printf ( ” a[%d][5d] = ” , i , j ) ; scanf ( ” %d ” , &a [i][j]) ; }
return ;
}
Void TongMT ( int a [ ][N], int b [ ][N], int c [ ][N], int M , int N )
{
int i, j ;
for ( i = 0 ; i < M ; i ++ )
for ( j = 0 ; j < N ; j ++ )
c [i][j] = a [i][j] + b [i][j] ;
return ;
}
/* in kết quả */
void inMT ( int c[ ][N], int M, int N )
{
int i, j ;
for ( i = o ; i < M ; i ++ )
{ for ( j = 0 ; j < N ; j ++ )
printf ( ” % 3d”, a[i][j] );
printf ( ” \n ” ) ; /* xuống dòng */
}
return ;
}
BàI TậP MãNG :
1/ cho mãng 2 chiều A, là ma trận vuông cấp n x n , lập chương trình :
a/ tính tổng tất cả các phần tử dương của mãng.
b/ tính tổng các phần tử A[i][j] mà i + j chia hết cho 5 .
c/ In ra các số nguyên tố theo từng hàng.
d/ Sắp xếp theo hàng.
e/ Sắp xếp theo cột .
f/ Tính tổng các phần tử trên đường chéo ( i = j ) , đường biên.
g/ Tìm max ; min theo từng hàng, cột và toàn bộ ma trận.
2/ Một chuỗi gọi là palindrone nếu nó không thay đổi khi ta đảo ngược thứ tự của các ký tự trong nó ( ví dụ ” 12321 ” )
. Lập chương trình đọc một chuỗi ( xâu ) ký tự và xác định xem có tính palondrone không.

5.3/ Biến con trỏ :
5.3.1/ Khái niệm con trỏ ( pointer ) và địa chỉ :
– Mỗi biến trong ngôn ngữ C đều có 1 tên và tương ứng với nó là một vùng nhớ dùng để chứa giá trị của nó. Tuỳ theo biến mà vùng nhớ dành cho biến có độ dài khác nhau. Ðịa chỉ của biến là sô thứ tự của byte đầu tiên tương ứng với biến đó. Ðịa chỉ của biến có kiểu khác nhau là khác nhau. Ðịa chỉ và biển kiểu int liên tiếp cách nhau 2 byte , biến kiểu float là 4 byte.
- Con trỏ là biến dùng để chứa địa chỉ của biến khác hoặc có thể là một hàm. Do có nhiều loại địa chỉ nên cũng có nhiều loại biến con trỏ. Con trỏ kiểu int dùng để chứa địa chỉ của kiểu int. Con trỏ kiểu float dùng để chứa địa chỉ kiểu float.
- Muốn sử dụng được pointer, trước tiên phải có được địa chỉ của biến mà ta cần quan tâm bằng phép toán lấy địa chỉ & . Kết quả của phép lấy địa chỉ & sẽ là 1 phần tử hằng.
* Ví dụ : int num ; => &num là địa chỉ của num.
int pnum ; /* pnum là 1 pointer chỉ đến một int */
pnum = & num ; /* pnum chứa địa chỉ biến int num*/
giả sử : num = 5 ; => * pnum = 5 /* do * là toán tử nội dung */
Hai câu lệnh sau đây là tương đương
Num = 100 ;
( * pnum ) = 100 ;
- Quy tắc khai báo biến con trỏ : < kiểu dữ liệu> * < tên biến con trỏ >
*Ví dụ 2 : int a, *p ;
a = 5 ; /* giả sử địa chỉ của a là < 106 > */
p = & a ; /* p = <106> */
p = a ; /* phép gán sai */
* p = a ; /* phép gán đúng */
scanf ( ” %d ” , &a ) ; tương đương scanf ( ” %d , p ) ;
5.3.2/ tính toán trên biến con trỏ ( pointer )
a/ Hai biến con trỏ cùng kiểu có thể gán cho nhau :
Ví dụ 1 : int a, * p, *a ; float * f;
a = 5 ; p = &a ; q = p ; /* đúng */
f = p ; /* sai do khác kiểu */
f = ( float * )p ; /* đúng nhờ ép kiểu con trỏ nguyên về kiểu float */
Ví dụ 2 : int a ;
char *c ;
c = &a ; /* sai vì khác kiểu */
c = ( char*) /* đúng */
b/ Một biến pointer có thể được cộng, trừ với một số nguyên ( int , long ) để cho kết quả là một pointer.
* Ví dụ : int a , *p , * p10 ;
a = 5 ;
p = &a ;
p10 = p + 10 ;
Ví dụ : int V[10] ;/* mãng 10 phần tử */
int *p ;
p = & V[0];
for ( i = 0 ; i < 10 ; i ++ )
{ *p = i ; /* gán giá trị i cho phần tử mà p đang trỏ đến */
p ++ /* p được tăng lên 1 để chỉ đến phần tử kế tiếp */
}
/* kết quả V[0] = 0 , V [ 1] = 1 … V[9] = 9 * /
c/ Phép trừ 2 pointer cho kết quả là một số int biểu thị khoảng cách ( số phần tử ) giữa 2 pointer đó.
d/ Phép cộng 2 pointer là không hợp lệ, pointer không được nhân chia với 1 số nguyên hoặc nhân chia vơi nhau.
e/ p = NULL : là con trỏ p không trỏ đến đâu cả.
Chú ý : không được sử dụng biến con trỏ khi chưa được khởi gán .
Ví dụ : int a , *p ;
Scanf ( “%d”, p ) ( sai )
=> thay bằng các lệnh : p = &a và scanf ( “%d” p ) ( đúng)
5.4/ Con trỏ mảng :
5.4.1/ Mãng 1 chiều và con trỏ :
– Trong ngôn ngữ C : giữa mãng và con trỏ có mối quan hệ chặt chẽ. Các phần tử của mãng có thể xác định nhờ chỉ số hoặc thông qua con trỏ.
- Ví dụ : int A[5] ; * p ;
P = A ;
+ mãng bố trí 5 ô nhớ liên tiếp ( mỗi ô chiếm 2 byte ).
+ Tên mãng là 1 hằng địa chỉ ( không thay đổi được ), chính là địa chỉ của phần tử đầu tiên. => 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]
p = A => p = &A[0] ( p trỏ tới phần tử A[0])
*(p + i ) tương đương với A[i].
=>bốn cách viết như sau là tương đương : A[i], * ( a + i ), * ( p + i ), p[i].
Ví dụ 2 : int a [5] ; *p ;
p = a ;
for ( i = 0; i < 5 ; ++ i)
scanf ( ” %d “, &a[i]); ( 1)
scanf ( ” %d “,a + i ); ( 2)
scanf ( ” %d”, p + i ); ( 3)
scanf ( ” % d”, p ++ ); ( 4)
scanf ( ” %d “, a ++ ); sai vì địa chỉ của a là hằng.
- Các lệnh (1), (2), (3), (4) tương đương nhau.
Ví dụ 3 : Nhập 5 số nguyên vào 1 mãng gồm 5 phần tử ( a[5]) sau đó sắp xếp tăng dần, in ra số lớn nhất vf nhỏ nhất và tính tổng của 5 số đó.
#include <stdio.h>
#define n 5
main ( )
{ int a [n], t , *p, i , j, ; int s ;
p = a ;
for ( i = 0; i < n ; i ++ )
{ printf ( ” a[%d] = ” , i ) ; scanf ( ” %d “, p + i ) }
/* Sắp xếp tăng dần */
for ( i = 0 ; i < n-1 ; i ++ )
for ( j = i + 1 ; j<n ; j++)
if ( *(a + i ) > * ( a + j )
{ t = * ( a + i ) ; *(a + i ) = * ( a + j) ; *(a + j ) = t ; }
s= 0 ;
for ( j=0 ; i < n , ++i )
s + = a[ i];
printf (“\n Tong = %5d “, s );
printf ( “\n số lớn nhất là %d “, a [4] );
printf ( ” số nhỏ nhất là %d \n “, a [d] );
getch ( );
}
5.4.2 / Con trỏ và mãng nhiều chiều :
– Phép toán lấy địa chỉ & chỉ áp dụng được với mãng 2 chiều kiểu nguyên. Các kiểu khác không được.
* Ví dụ 1 : int a[2][3]
{ scanf ( “%d”, & a[1][1]) } ( đúng )
* Ví dụ 2 : float a[2][3]
Scanf (” %f”, &a[1][1]); ( sai ).
- Mãng 2 chiều a[2][3] => gồm 2 x 3 = 6 phần tử có 6 địa chỉ liên tiếp theo thứ tự sau :
Phần tử : a[0][0] a[0][1] a[0][2] a[1][0] a[1][1] a[1][2] ( * )
Ðịa chỉ : 0 1 2 3 4 5
- Ngôn ngữ C quan niệm mãng 2 chiều là mãng một chiều của mãng a[2][3] tương đương không phần tử mà mỗi phần tử của nó gồm 3 số nguyên nên :
a trỏ tới hàng thứ nhất ( a [0][0] )
a+1 trỏ tới hàng thứ hai ( a[1][0] )
- Do đó để duyệt các phần tử của mãng a[2][3] ta dùng con trỏ theo cách sau :
+ ( theo * ) => ta có công thức a[i][j] = ( int*) a + i * n + j
trong đó : int* : con trỏ a ( địa chỉ a ).
n : số cột.
- float a[2][3] , *p ;
p = ( float*)a ; /* chú ý lệnh này */
khi đó : p trỏ tới a[0][0] /* p = & a[0][0] */
p + 1 trỏ tới a[0][1] /* *(p+1) = a[0][1] */
P + 2 trỏ tới a[0][2]
…. . . . . . . . . . . . . . . . . . . . . . . . . . . . . . .
p + 5 trỏ tới a[1][2] /* *(p+5) = a[1][2] */
* Tổng quát : a[i][j] = * ( p + i* N + 5 ); trong đó N : số cột )
Kết luận : Mãng 2 chiều có thể chuyển thành mãng 1 chiều nhờ con trỏ.
* Ví dụ : để nhập một số liệu vào mãng 2 chiều kiểu float a[2][3] ta có thể dùng các cách sau:
+ Cách 1 :
#include ” stdio.h “
main ( )
{ float a[2][3] , *p ; int i ;
p = (float*)a ; /* lưu ý lệnh này */
for ( i = 0 ; i < 2*3 ; ++i)
scanf ( “%f”, (p+i)) ; /* (p_+ i ) là địa chỉ */ ( X )
}
+ Cách 2 : Sửa lệnh ( X ) như sau : scanf ( “%f”, (float*)a + 1 ) ;
+ Cách 3 :
#include ” stdio.h ” #define m 2 #define n 3
main ( )
{ float a[m][n] ; int i , j ; float *p ; p = ( float* )a ;
for ( i=0 ; i<m ; i++ )
for ( j=0 ; j<n ; j++ )
scanf ( “%f” , ( p +i*n + j )
hoặc lệnh scanf ( ” %f” , ( float *)a + i * N + j ));
}
+ Cách 4 : sử dụng biến trung gian :
#include ” stdio.h”
#define dong 2
#define cot 3
main ( )
{ float a[dong][cot] , tam ; int i , j ;
for ( i = 0 ; i < dong ; i++ ) ;
for ( j=0 ; j < cot ; ++j )
{ printf ( “\n a[%d][%d] = ” , i , j );
scanf ( ” %f ” , &tam ) ;
a[i][j] = tam ;
&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP;&NBSP; }
BàI TậP : Sắp xếp mãng 2 chiều theo hàng và toàn bộ mãng
5.4.3/ Mãng con trỏ : là mãng mà mỗi phần tử của nó có thể chứa một địa chỉ nào đó.
Khai báo : < kiểu dữ liệu > < tên mãng > [<chỉ số>].
* Ví dụ : int *a[5] ;
- trong đó : a là mãng gồm 5 ô nhớ liên tiếp, mỗi ô nhớ là 1 biến con trỏ trỏ đến kiểu int ; bản thân a không thể dùng để lưu trữ số liệu.
- Giả sử : a <100> <102> <104> <106> <108> < 110>
a[0] a[1] a[2] a[3] a[4] a[5]
Ðịa chỉ < 30> < 20> < 10 > < 80 > < 70 > < 100>
7 8 9 10 11
<10> <12> <14>
1 2 3 4 5
<20> <22> <24> <26> <28>
6 12 13
<30> <32> <34>
- a= &a[0] => a = <100> ( địa chỉ 100 ).
- a[0] = < 30 > ( địa chỉ bằng 30 : tại địa chỉ 30 con trỏ a[0] trỏ đến địa chỉ <30 > và giả sử tại địa chỉ < 30 > có giá trị là 6 ).
=> *a[0] = * (<30>> = 6 .
a[1] = < 20 > => *a[1] = 1
a [2] = < 10> => *a[2] = 7 .
Chú ý 1: Xem a là con trỏ 2 lần ( con trỏ của con trỏ ) :
- a = <100 > => *a = <30 > ( do a = &a[0] )
=> **a = 6 ( do *(<30>)).
- *(*(a + 1) + 2 )
*(102)
* ( <20> + 2 ) => *<24> = 3
Chú ý 2 : – int a[5] => a là con trỏ hằng không thay dổi địa chỉ của nó được ( nên a++ sai)
- int *a[5] ; => a laf con trỏ động nên thay đổi giá trị được ( a++ đúng ).
Ví dụ : int *a[5]
For ( i = 0 ; i < 5 ; i++ )
{ printf (“%d”, *a[0] );
a[0]++ ;
}
* Chú ý 3 : mãng 2 chiều chẳng qua là 1 con trỏ 2 lần ( con trỏ của con trỏ ).
Lý do : a[i][k] ; trong đó đặt b = a[i] => b[k] = a[i][k] ;
+ Công thức : ( a[i] = *(a+i)) => ( b[i] = *(b+i)).
b[k] = *(b+k)).
b[k] = *(a[i] + k )
= * ( *(a+i) + j).
=> a[i][k] = *(*(a+i) + k) ; trong đó *(*(a+i) là con trỏ 2 lần.
5.4.4/ Con trỏ và xâu ký tự :
- Xâu ký tự : là dãy ký tự đặt trong ngoặc kép . Ví dụ : ” Lớp học “. Xâu này được chứa trong 1 mãng kiểu char.
L O P H O C
Ðịa chỉ :<100> <101> <102> NULL : kết thúc chuỗi
=> char *lop ;
lop = ” Lop Hoc ” ; Ðúng : gán địa chỉ của chuỗi cho con trỏ lớp.
+ puts (” Lop Hoc “) ; và puts (lop ) đểu hiển thị dòng chữ Lop Hoc.
Ví dụ : char Tenlop[10] ;
Printf (“\n Tenlop : ” ) ; gets( Tenlop ) ; => ( Nhập vào chuỗi ” lớp học ” )
Còn nếu chúng ta khai báo như sau là sai :
Char *lop , tenlop [10] ;
Tenlop = ” lớp học ” ; sai vì Tenlop và chuỗi là 2 con trỏ hằng , không được gán cho nhau . Muốn gán ta dùng hàm strcpy (Tenlop , “lớp học “);
5.4.5/ Con trỏ và việc định vị bộ nhớ động :
– Ví dụ 1 :
#define N=10 ;
main ( )
{ int a[N] ; int m :
printf ( ” nhập số phần tử m = “); scanf(“%d”, &m) ;
for ( i= 0 ; i < m ; i++ )
scanf ( “%d”, &a[i] );
- Nhận xét Ví dụ 1 trên : + Nếu m <=N ( N =10) : thì sẽ bị dư 1 số biến mãng là ( n – m).
+ Nếu m > N ( tức là m > 10 ) : thì chương trình sẽ chạy sai vì ta không đủ biến mãng.
=> Do đó ta phải khắc phục bằng cách : định vị bộ nhớ động. ( Bằng hàm malloc và calloc).
* Ví dụ 2 :
#include < stdio.h>
#include<alloc.h> hoặc #include <stdio.h >
main ( )
{ int m , *a ;
printf (” Nhập số phần tử m = ” ); scanf ( “%d”, &m );
/* Cấp phát và định vị bộ nhớ động */
a = ( int*) malloc ( m* size of ( int ) ); (1)
if ( a!= NULL ) /* cấp phát thành công */
for ( i=0 ; i < m ; i++)
scanf ( “%d”, &a[i] );
free (a) ; /* giải phóng vùng nhớ mãng */
}
- Hàm malloc ( ) nằm trong thư viện <alloc.h> . Hàm này cung cấp số lượng byte liên tiếp từ phần bộ nhớ còn chưa sử dụng trên máy tính.
+ Ví dụ : malloc (num) = num byte và trả về con trỏ kiểu void trỏ đến địa chỉ bắt đầu của ô nhớ.
- Size of ( int ) : là số byte mà một biến kiểu int yêu cầu ( giá trị = 2 )
- ( int*) : ép kiểu ( type – casing) : coi địa chỉ bắt đầu là int ( do malloc trỏ về con trỏ kiểu void , đặc biệt không có kiểu ) , có thể nhận bất kỳ địa chỉ kiểu nào ( nhờ ép kiểu ).
- Muốn sử dụng hàm calloc thay cho hàm malloc => khai báo :
a = (int*) calloc ( n, size of (int));
* Chú ý : Luôn gán một địa chỉ cho một con trỏ trước khi sử dụng tới nó. Nếu không biến con trỏ sẽ mang một giá trị ngẫu nhiên có thể phá huỷ chương trình.
* Cấp phát bộ nhớ động cho mãng 2 chiều m x n phần tử, m , n nhập từ bàn phím:
+ Ví dụ : #include <stdio.h>
#include <alloc.h>
Void main ( )
{ int **a , m, n, OK ;
printf ( ” nhập m = ” ); scanf (“%d”, &m);
printf (nhập m = n) ; scanf ( “%d”, &n );
a = ( int** ) malloc ( m*seze of (int *));
if (a!=NULL ) /*Cấp phát thành công */
{ OK = 1 ;
for ( i=0 ; i < m ; i++ ) } /* giá trị ban đầu cho biến con trỏ*/
a[i] = (int*) break ;
for ( i=0 ; i <m ; i ++ )
{ if !(OK) break ;
a[i] = (int*) malloc ( n * size of (int));
if ( a[i] = NULL ) OK = 0 ;
}
if(OK)
{ sử dụng a[0][0] , a[0][1]….., a[i][j] …., a[m][n] }
/* giải phóng vùng nhớ cấp phát */
if ( a!=NULL )
{ for ( i = 0 ; i < m ; i++)
if ( a[i] ! = NULL , free ( a[i]);
free (a);
}
}
* Chú ý : ta xem mãng 2 chiều là mãng 1 chiều nên có thể khai báo :
a = (int*) malloc ( m*n * size of ( int ));
VÀ A[I][J] = A[ I*N + J]
BàI TậP :
1/ Làm lại các bài tập phần mãng nhưng dùng con trỏ .
2/ Dùng hàm malloc hay calloc nhập mãng n phần tử , sau đó tính tổng các phần tử và sắp xếp mãng giảm dần.
3/ Dùng hàm malloc hay calloc nhập ma trận m x n , sau đó tính tổng và sắp xếp theo tăng dần
5.4.6/ Mối liên hệ giữa con trỏ và các khái niệm quan trọng trong :
a/ Con trỏ và hàm :
- Chú ý 1 : bản thân tham số truyền cho hàm không bao giờ bị thay đổi. Nhưng nếu tham số là con trỏ thì giá trị của nó không thay đổi nhưng nội dung được chứa ở địa chỉ đó lại có thể thay đổi.
- Chú ý 2 : Truyền cho hàm một tham số hình thức được khai báo là con trỏ, và khi gọi hàm truyền cho nó một giá trị địa chỉ của biến muốn thay đổi.
- Ví dụ :giả sử tân xây dựng một hàm dùng để hoán vị biến thực, ta viết như sau :
Cách 1 :
#include<stdio.h>
void swap (float x , float y ) /* cách 1 sai */
{ float temp ;
temp = x ; s<y ; y = temp;
}
main ( )
{ float a, b ; a = 10.0 ; b = 20.0 ;
printf (” khi chưa hoán vị a = %4.0f; b = %4.0f \n” , a , b ) ;
swap ( a , b ) ;
printf ( ” sau khi hoán vị a = %4.0f ; b = %4.0f \n” , a, b ) ;
- Phân tích cái sai của cách 1 của ví dụ trên :
+ Do a, b thuộc hàm main ( ). Khi khai báo sẽ dùng 2 khoảng nhớ ( mỗi khoảng 3 byte) . a, b trong lời gọi hàm swap(a,b) là 2 tham số thực.
+ Các đối x, y và biến cục bộ temp được cung cấp khoảng nhớ nhưng địa chỉ khác. Do đó xx, y chỉ tồn tại ở hàm swap(_), còn a, b tồn tại suốt cả quá trình của chương trình nên hàm swap () không làm thay đổi ( tức hoán vị) được giá trị của a và b => hàm viết theo cách 1 không đạt yêu cầu => yêu cầu viết lại theo cách 2.
* Cách 2 : void swap (float *x , float *y) /* viết đúng*/
{ float temp ;
temp = *x ; *x = *y ; * y = temp ;
}
main ( )
b/ Số học con trỏ ( có thể thao tác số học trên nội dung con trỏ )
* Ví dụ : #include < stdio.h>
#include <alloc.h>
main ( )
{ #define N 3
int *list , i ;
list = int*) calloc ( N, size of(int));
*list = 15 ;
* (list + 1) = 20 ;
*(list + 2 ) = 30 ;
printf ( ” các địa chỉ là : “);
for ( i=o ; i < N ; i++)
printf (“%4d”,(list + i));
printf (“\n chứa các giá trị là : “);
for ( i=0 ; i < N ; i++)
printf(“%4d”, *(list + i));
printf(“\n”);
=> list trỏ tới một dãi bộ nhớ dài 6 byte ( 3*2) có các giá trị là 5,20, 30 . giá trị địa chỉ đầu là 06A => kết quả các địa chỉ là : 06A 06AC 06AE chứa các giá trị là : 5 20 30
c/ Con trỏ và mãng :
- Ví dụ 2 :
#include
main ( )
{ #define N 3
int list [N] , i ;
list [0] = 5 ; list [1] = 20 ; list[2]=30;
printf ( ” Các địa chỉ là : “);
for ( i = 0 ; i < N ; i++)
printf ( “%4p “, &list[i] );
printf(“\n chứa các giá trị là : “);
for ( i=0; i<N ; i++)
printf ( “%4d”, list [i] ));
}
-Kết quả chương trình :
+ Các địa chỉ là : 163A 163C 163E
+ Chứa các giá trị là : 5 20 30
- So với ví dụ 1 thì điều khác duy nhất là giá trị địa chỉ thay đổi. Như vậy ta có thể sử dụng tên của một mãng như con trỏ và ngược lại.
=>{ list + i) = = &(list[i]) và *(list + i) = = list[i]}
d/ Con trỏ và cấu trúc :
- Ta có thể khai báo con trỏ như một biến cấu trúc, cũng như con trỏ của bấu kỳ kiểu dữ liệu nào khác. Ðiều này cho phép tạo một danh sách móc nối các phần tử ( sẽ trình bày chương sau ).
e/ Con trỏ tới hàm : dùng để chứa địa chỉ của hàm. Nên kiểu của hàm và con trỏ phải giống nhau.
Ví dụ : #include <stdio.h>
Double fmax ( double x, double y ) /* hàm tính max của 2 số */
{ return ( x>y ? x:y ) ; }
/* khai báo và gán tên hàm cho con trỏ hàm */
double (*pf) (double , double ) = fmax ;
main ( )
{ printf ( ” In max = % f ” , pf(15.5, 20.5 ));
}

luongbv93
luongbv93
Admin

Tổng số bài gửi : 117
Points : 354
Reputation : 0
Join date : 18/11/2011
Age : 30
Đến từ : Hà Nội

https://diendanteenviet.forumvi.com

Về Đầu Trang Go down

Về Đầu Trang

- Similar topics

 
Permissions in this forum:
Bạn không có quyền trả lời bài viết