CHƯƠng 1 : Các khái niệm cơ BẢn của ngôn ngữ C


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



tải về 230.67 Kb.
trang2/3
Chuyển đổi dữ liệu07.01.2018
Kích230.67 Kb.
#35812
1   2   3
CHƯƠNG 4 : 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
#Include
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
# Include
/*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 (;
< 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
: < 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 5 : 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
#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à ' \0 '.
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
#include
#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
#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
#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         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      for ( j=0 ; 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 > [].
* 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 \0
Ðị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 hoặc #include
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 . 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
#include
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                   { 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
void swap (float x , float y ) /* cách 1 sai */
{ float temp ;
  temp = x ; s}
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
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   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
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 ));
}

CHƯƠNG 6 : MỘT SỐ HÀM TRÊN CHUỖI KÝ TỰ



6.1/ Ký tự ( character ) :
- Ví dụ : char ch , ch1 ;
ch = 'a' ; /* Ðúng : ký tự chữ */
ch1 = '1' /* đúng : ký tự số */
- Ví dụ 2 : scanf ( "%c", &ch ) ; /* gõ A và Enter */
printf ("%c", ch) ; /* In ra chữ A */
printf("%d", ch) ; /* In ra 65 là mã ASCII của A */
* Hàm dùng cho kiểu ký tự :
char ch ;
ch = getchar ( ) ; ( Nhập 1 ký tự từ bàn phímm sau khi ấn Enter và ký tự nhập vào không hiện lên màn hinh ).
putchar (ch) ; in ký tự nằm trong biến ch ra màn hình.
putch ("\n") ; đưa dấu nháy về đầu dòng.ch = getche ( ) ; Nhập 1 ksy tự từ bàn phím và ký tự nhập vào sẽ hiển thị trên màn hình.
6.2/ Chuỗi ký tự : Ngôn ngữ C quan niệm 1 chuỗi ký tự là một mãng ký tự kết thúc bằng ký tự NULL ('\0') mã ASCII là 0.
- Ví dụ : char s[10] L E V A N A '\0'
s[0] s[1 ] s[3] s[4] s[5] s[7] s[8]
- Muốn nhập chuỗi ta thường dùng hàm gets(s)
- Muốn in chuỗi ta thường dùng hàm puts(s) : in xong xuống dòng.
6.3/ Một số hàm trên chuỗi : các hàm cơ bản trong thư viện string.h
a/ gets(s1) : nhập dữ liệu vào chuỗi s1.
b/ n = strlen(s1) : cho biết độ dài của chuỗi s1.
c/ n= strcmp (s1,s2) : so sánh 2 chuỗi s1,s2 ( so theo mã ASCII từng ký tự ).
+ nếu n>0 : s1> s2
n = 0 : s1=s2
n < 0 : s1d/ strcpy ( đích , nguồn ) ; chép chuỗi nguồn vào chuỗi đích, gán chuỗi.
- Ví dụ : char [30] ;
Ten = "Nguyễn Văn Ðông "; ( sai ).
strcpy ( ten , "Nguyễn Văn Ðông ");
gets (ten ) : Nhập vào từ bàn phím.
e/ strcat (s1,s2) : nối s1 và s2 .
- Ví dụ : giá trị cảu s1 : " ABC" ; s2 : " ABE" => strcat(s1,s2 ) ; => " ABCABE";
f/ m = strncmp (s1, s2, n ) ; so sánh n ký tự đầu tiên của chuỗi s1 với s2.
- Ví dụ : m = strncmp ( s1, s2, 2 ) ; thì m = 0 do 2 ký tự đầu của chuỗi là :
+ s1 : "ABC" và s2 : " ABE" là giống nhau.
g/ strnpy ( s1, s2, n ) ; chép n phần tử đầu tiên của chuỗi s2 vào chuỗi s1.
- Ví dụ : strnpy ( s1, "xyz", 2 ) ;
Puts (s1); -ă " xyC".
h/ strncat ( s1,s2, n) ; nối n phần tử đầu tiên của s2 vào đuôi s1.
- Ví dụ : strncat ( s1 , "xyz", 2);
Puts(s1) ; => "ABCxy".
* Chú ý : + char s1[10], s2[4]
+ strcpy (s1,"ABCDE");
+ strcpy(s2,"ABCDE"); => "ABCD" ( do s[4] = "\0").
i/ Hàm strstr :
- char *p ;
p = strstr (s1,s2);
- Tìm xem chuỗi s2 có trong s1 hay không. Nếu có thì in ra cuỗi s1 tại vị trí đầu tiên mà nó thấy. Nếu không có thì in ra giá trị NULL.
- Ví dụ : s1: "abc abc ac"
s2 : "bc", s3 = "cd"
p= strstr (s1,s2);
puts (p) ; => " bc abc ac "
p = strstr ( s1, s3)
Ðoán thử puts(p) ; => p[(NULL)] .
k/ d= atoi ( chuỗi số ) ; chuyển chuỗi số thành int.
f = atof ( chuỗi số ) ; chuyển chuỗi số thành số thực( float ).
l = atol(chuỗi số ); chuyển chuỗi số thành long ( nguyên 4 byte).
- Ví dụ : char s[20] ;
Gets (s) ; nhập vào s từ bàn phím chuỗi " 123.45"
d=atoi(s) ; thì d = 123.
F = atof(s); thì f = 123.45
l/ toupper (ch) ; làm thay đổi ký tự ch thành chữ Hoa.
tolower(ch); làm thay đổi ký tự ch thành chữ thường.
* Chú ý :Muốn dùng các hàm về chuỗi phải khai báo đầu chương TRÌNH #INCLUDE <STRING.H>
BàI TậP :
1/ Nhập vào chuỗi sau đó xoá các khoảng trắng xong in ra màn hình.
2/ Nhập chuỗi và xoá các khoảng trắng thừa phía trước, sau và giữa 2 từ gút lại 1 khoảng trắng.
3/ Viết hàm nhập vào một chuỗi sau đó đổi ký tự đầu mỗi từ (chữ) thành Hoa, các ký tự còn lại của 1 từ là chữ thường.
4/ Nhập chuỗi password nếu kiểm tra đúng mới cho chạy chương trình đếm số từ trong 1 chuỗi số nguyên âm, phụ âm.
5/ Ðảo thức tự các từ của chuỗi. Ví dụ : s1="con mèo con cắn con chó con" đổi thành s2=" con chó con cắn con mèo con".

CHƯƠNG 7 : KIỂU CẤU TRÚC



- Khái niệm : Cấu trúc là một kiểu dữ liệu kiểu bản ghi(record) , cho phép nhiều loại dữ liệu được nhóm lại với nhau. ( Khái niệm cấu trúc trong C tương tự như pascal hay Foxpro).
7.1/ Khai báo kiểu cấu trúc :
a/ struct tên _ kiểu cấu trúc
{
khai báo các thành phần của nó ( các field và kiểu dữ liệu của field)
} < danh sách biến>;
- Ví dụ 1 : struct kieu HV ò-> tên kiểu cấu trúc.
{ char Ten[30] ;
int namsinh ;float diemTB ;
} HV ; ( biến HV)
- Ví dụ 2 : struct kieu HV
{
các thành phần
}
struct kieu HV HV ; /* khai báo biến theo cách 2 */
b/ Dùng toán tử typedef để khai báo kiểu cấu trúc ( định nghĩa kiểu mới) ;
- Ví dụ 3 : typedef struct
{ char Ten[30]
   int namsinh ;
   float diemTB ;
} kieu HV ;
kieu HV Hoc vien ;
kieu HV DSLop[20];
kieu HV Lop[ ] = { { "nguyễn văn Ðông", 1980, 10.0},
{ " Trần văn Tây", 1982, 5.5},
{ " Phạm văn Nam ", 1979, 9.5}
};
- Ví dụ 4 : struct ngay{
                                      int ngay ;
                                   char Thang[10];
                                      int nam ;
                                   } ;
type struct
{ char Ten[30] ;
     ngay namsinh ; /* thành phần cấu trúc có kiểu cấu trúc*/
     float diemTB;
} kieu HV ; kieu HV HV;
* Chú ý :
- Khai báo struct phải nằm ở vị trí toàn cục của chương trình, thường sau các #include.
- Cấu trúc thường dùng để xây dựng một bảng các cấu trúc.
+ Ví dụ : kieu HV DSLop[30] ; struct kieu HV person[50];
- Có thể truyền cấu trúc như một tham số hình thức, nhưng với những cấu trúc kích thước lớn sẽ không tối ưu về thời gian lẫn độ nhớ. Khi không nên sử dụng con trỏ cấu trúc.
+ Ví dụ : struc kieu HV *HV ;
7.2/ Truy cập đến các thành phần của kiểu cấu trúc :
Tên cấu trúc. Tên thành phần
Hoặc Tên cấu trúc. Tên cấu trúc con. Tên thành phần.
- Ví dụ : + nhập vào tên, năm sinh, điểm cho biến cấu trúc học viên ( ví dụ 3).
gets(hoc vien.ten) /* nhập " Phạm thị Bắc" và Enter */
scanf("%d ", & hoc vien.namsinh );
scanf("%f", &tam); hoc vien.diem = tam; (*)
+ Nhập năm sinh cho biến học viên ở ví dụ 4 :
scanf("%d",&hv.ngay.namsinh);
* Chú ý : Nếu các thành phần không phải là nguyên(int) => nhập qua trung gian như (*).
puts(hoc vien.ten); => " Phạm thị Bắc"
printf("%d%f", hoc vien.namsinh, hoc vien.diemTB);
* Lệnh gán : + Ta có thể gán 2 biến cấu trúc có cùng kiểu cho nhau :
Ví dụ : hv2=hv1;
+ Gán giá trị đầu cho biến cấu trúc và khai báo một mãng cấu TRÚC( XEM VÍ DỤ 3)
BàI TậP : viết chương trình nhập danh sách học viên gồm các trường họ tên, tuổi, điểm, và tìm kiếm trong dánhách có ai tên " Phạm Tèo " không.
Tên Tuổi điểm
HV [ 0] Nguyễn A 20 5.5
HV [1] Trần B 22 6.5
HV [2] Phạm Tèo 25 8.5
HV [3] Lê C 21 7.5
#include
#define n 10
typedef struct
{ char Ten[30];
   int tuoi ;
   float diem ;
} kieu HV ;
kieu HV HV[11]
void main( )
{ int i ; float tam ; kieu HV HV;
/* nhập dữ liệu cách 1*/
for ( i = 0 ; i < n ; i++)
{ printf ("\n Nhập số liệu cho học viên thứ %d", i ) ;
   printf (" Họ và tên = " ) ; gets ( hv[i].ten);
   printf ("tuổi = "); scanf ( "%d" , &hv[i].tuoi);
   printf("điểm = "); scanf ("%f*c", &tam ); hv[i].diem = tam ;
}
/* cách 2 nhập vào biến cấu trúc và gán hv[i] = h */
for ( i = 0 ; i{ printf("Họ và tên = "); gets(h.ten);
} hv[i] = h ;
/* tìm kiếm Phạm Tèo */
thay = 0 ; i = 0 ; /* thay = 0 : không thấy, thấy = 1 : tìm thấy */
while ((!thay)&&(i if ( strcmp(hv[i].Ten , " Phạm Tèo ") = = 0 )
{ thay = 1 ;
printf ("%s%d%f ", hv[i].ten , hv[i].tuổi, hv[i].điểm );
}
else i++ ;
if (!thay ) puts ("\n không tìm thấy Phạm Tèo !");
getch( );
}
BàI TậP : Viết chương trình nhập danh sách gồm na học viên gồm các thông tin như : Họ , tên, điểm pascal , điểm c, sau đó tính điểm trung bình (điemTB) = (diemC*2 + diempascal)/3 .
- Và xét kết quả đậu hay rớt theo qui ước sau :
+ nếu điểm trung bình >= 5 thì kết quả đậu.
+ Nếu điểm trung bình <5 thì kết qua rớt.
+ Nếu điểm trung bình = 4 mà phái = "Nữ" thì kết quả là đậu.
1/ in danh sách vừa nhập gồm họ tên, phái , điểm c, điểm pascal, điểm TB , kết quả .
2/ Sắp xếp giảm dần theo điểm trung bình và in ra.
3/ Nhập vào tên cần tìm và tìm trong danh sách học viên nếu không tìm thấy thì in ra học viên có tên không tìm thấy. Nếu có nhiều học viên có cùng tên cần tìm thì hãy in ra người cuối cùng được tìm thấy.
4/ Giống câu 3 nhưng in ra 2 người tìm thấy đầu tiên ( nếu có nhiều người ).
5/ Giống câu 3 nhưng in ra người đầu tiên và người cuối cùng ( nếu có nhiều ngưòi). Nên viết theo từng hàm.
7.3/ Con trỏ trỏ đến cấu trúc và địa chỉ cấu trúc :
a/ Con trỏ và địa chỉ :
- Ví dụ : typedef struct
{ char Ten[30] ;
int tuoi ;
float diem ;
} kieu HV ;
kieu HV *p , HV , lop[50] ; HS [50] ( trong đó : HV là biến cấu trúc, *p : con trỏ cấu trúc dùng để lưu trữ địa chỉ cấu trúc và mãng cấu trúc ) ( *).
main ( )
/* ta có thể gán */
p = &HV ; /* Ðúng do (*)*/
p = &lop[i]/*đúng do (*) */
p = lơp ; /* đúng : p = địa chỉ Lop[0] , p = &lop[0] ) do Lop = &Lop[0])
b/ truy cập thông qua con trỏ :
- Cách 1 : tên con trỏ -ă tên thành phần.
- Cách 2 : (*tên con trỏ).tên thành phần.
- Ví dụ : p = &HV ; p = &Lop[2] '
=> HV.Ten ĩ p --ă tên;
Lop[2].tuổi ĩ (p*).tuoi ĩ p -ă tuổi ;
*p = HV ;
*P = Lop[2]
- Giả sử cần nhập số liệu ch vùng trên thì 3 cách viết sau là tương đương :
+ (1) : gets(HV.ten)
+ (2) gets ( pă ten) ĩ gets( (*p).ten).
+ (3) scanf("%d",&HV.tuoi) ; ĩ scanf("%d", p -ă tuổi );
scanf ("%d", (*p).tuoi);
- Giả sử cần nhập dữ liệu cho mãng cấu trúc thì các cách viết sau đây tương đương :
+ Ví dụ : p = lop ;
for ( i = 0 ; i < n ; i++)
{ gets (lop[i].tên); tương đương với :
. gets((*(lop* i ) ).ten);
.gets(*(p + i ).ten);
.gets ( p[i].ten);
.gets (p ă ten); p++ ;
.gets (*p).ten) ; p++;
- Ví dụ : làm lại bài tập mẫu nhưng sử dụng biến con trỏ :
#include
#define n 10
typedef struct
{ char ten[30] ;
int tuoi ;
float diem ;
} kieu HV ;
main ( )
{ kieu HV hv [n], *p , h;
int i ; int thay ; float tam ; int tuổi ; p = hv;
for ( i = 0 ; i < n ; i++)
{ printf (" nhập học viên thứ %d ", i );
   printf("Họ và tên"); gets ( p ă ten);
   printf("tuổi : ") ; scanf ("%d", &tuổi); p ă tuoi = tuoi;
   printf ("diem : ") ; scanf ("%f%*c ", &tam ); p ă diem = tam;
   p++ ; printf ("%c", getchar();
}
/* nhập theo cách 2 qua biến h xong gán *p = h */
/* tìm Phạm Tèo */
thấy = 0 ; i = 0 ; p = hv ; /* để di chuyển con trỏ về đầu danh sách */
for ( i = 0 ; i < n ; i++ )
if ( strcmp(p ă ten, " Phạm Tèo " ) = = 0 )
{ thấy = 1
    printf ("%s %d%f" , p ă ten, pă tuoi, pă điểm );
    break ;
 else p++ ;
     if (!thay) puts (" không có Phạm Tèo trong danh sách ");
         getch( );
}
BàI TậP : làm lại bài tập trước nhưng sử dụng con trỏ.
7.4/ Cấp phát bộ nhớ động cho kiểu dữ liệu cấu trúc :
- giả sử ta cần quản lý danh sách học viên nên dùng mãng cấu trúc ( cấp phát bộ nhớ tĩnh - danh sách đặc ) ta phải sử dụng số học viên tối đa => thừa vùng nhớ. Ðể cấp phát vừa đủ sĩ số học viên như ta muốn => ta dùng phương pháp cấp phát bộ nhớ động hàm malloc hoặc calloc(.)
- Ví dụ : Nhậ danh sách n học viên gồm họ tên, điểm và sắp xếp giảm dần theo điểm.
#include #include #include
#include< string.h>
typedef struct
{ char ten[30] ; int diem ; char kq[5] ; } kieu HV;
kieu HV *lop , *p , tam ;
/* Hàm nhập dan sách */
void nhapDS ( int n , kieu HV lop[ ])
{ int i , diem ;
p = lop ;
for ( i = 0 ; i < n ; i++)
{ printf("nhập Họ tên người thứ %d : " , i +1 ) ; gets ( p ăten);
   printf ( " điểm = " ) ; scanf ( "%d" , &diem ) ; p ă diem = diem ;
   printf ("%c", getchar()); /* khử stdin */
   p++ ;
}
/* Hàm sắp xếp*/
void sapxep ( int n, kieu HV lop[ ])
{ int i , j ; kieu HV tam ;
    for ( i = 0 ; i < n-1 ; i++)
    for ( j=i + 1 ; j< n ; j++)
    if ( lop[i].diem < lop[j].diem )
        { tam = lop[i] ; lop[j] = lop [j] ; lop [j] = tam ; }
/* hàm in danh sách */
void inds( intn, kieu HV lop[ ] )
{ int i ;
    for ( i = 0 ; i < n ; i++ )
   { printf ("%20s%5d ", lop[i].ten,lop[i].diem );
      printf ("\n" ; /* xuống hàng */
/* chương trình chính */
void main ( )
{ int i , j, n , t, diem ;
   printf ("\n Nhập sĩ số : ") ; scanf ( "%d", &n);
  lop = (kieu HV*)malloc ( n * size of ( kieu HV) ) ; printf ("%c", getchar ());
   nhapds (n, lop ) ; sapxep ( n, lop ) ; inds ( in lop );
   getch ( );
}
KIểU FILE ( TậP TIN/ TệP TIN )

- Trong ngôn ngữ C , một tập tin là một khái niệm logic, được áp dụng không những đối với các tập tin trên đĩa mà cả với các terminal ( bàn phím, màn hình, máy in...).
- File có 2 loại : + Text file ( file văn bản ).
+ Binary ( nhị phân : dbf, doc, bitmap,...).
- File văn bản chỉ khác binary khi xử lý ký tự chuyển dòng (LF) ( mã 10 ) được chuyển thành 2 ký tự CR (mã 13) và LF ( mã 10) và khi đọc 2 ký tự liên tiếp CR và LF trên file cho ta một ký tự LF.
- Các thao tác trên file thực hiện thông qua con trỏ kiểu FILE. Mỗi biến FILE có 1 con trỏ lúc đầu sẽ trỏ vào phần tử đầu tiên của file. Sau mỗi thao tác đọc hay ghi dữ liệu con trỏ tự động dời xuống mẫu tin kế tiếp. Làm việc trên kiểu File thường có 3 công đoạn : mở file, nhập xuất thông trên file và đóng file.
* Một số hàm thông dụng thao tác trên file ( tập tin/tệp tin ) :
+ Mở file : FILE *fopen ( char *filename, char *mode);
. Nếu có lỗi fp sẽ trỏ đến NULL.
+ Các mode chế độ mở file :
" r" " rt " / " rb " : mở file để đọc theo kiểu văn bản / nhị phân - file phải tồn tại trước nếu không sẽ có lỗi.
"w" "wt" / " wb " : mở ( tạo ) file mới để ghi theo kiểu văn bản/nhị phân - nếu file đã có nó sẽ bị xóa(ghi đè )( luôn luôn tạo mới ).
"a" "at"/ "ab" : mở file để ghi bổ sung (append) thêm theo kiểu văn bản hoặc nhị phân( chưa có thì tạo mới ).
+ Ðóng file : int fclose ( file + biến file ) ;
Ví dụ : Void main ( )
 { FILE *fp ;
    fp = fopen ("c:\\THUCTAP\\Data.txt", "wt" );
     if (fp = NULL )
        printf ( " không mở được file c/Thuctap\data.txt");
     else {< xử lý file > }
     fclose (fp) ; /* đóng file */
}
+ Làm đóng tất cả các tập đang mở : int fclose all(void) ; nếu thành công trả về số nguyên bằng tổng số các file đóng được, ngược lại trả về EOF.
+ Hàm xóa tập : remove (const + char*ten tập ) ; nếu thành công cho giá trị 0, ngược lại EOF.
+ Hàm kiểm tra cuối tập : int feof(FILE*fp) : !=0 : nếu cuối tập= 0 : chưa cuối tập.
+ Hàm int putc ( int ch, FILE*fp);
Hàm int fputc( int ch, FILE*fp);
Công dụng của hai hàm này :ghi một ký tự lên tập fp theo khuôn dạng được xác định trong chuỗi điều khiển dk. Chuỗi dk và danh sách đối tương tự hàm printf( ).
+ Hàm int fscanf ( FILE *fp, const char *dk, ...);
Công dụng : đọc dữ liệu từ tập tin fp theo khuôn dạng ( đặc tả) làm việc giống scanf( ).
*Ví dụ : giả sử có file c/data.txt lưu 10 số nguyên 1 5 7 9 8 0 4 3 15 20 . Hãy đọc các số nguyên thêm vào một mãng sau đó sắp xếp tăng dần rồi ghi vào file datasx.txt
Giải :
#include
#include
#include
#define n 10
void main ( )
   { FILE *fp ; int i, j, t, a[n]
      clrscr ( ) ;
     fp = fopen (" c :\\data.txt ", "rt" ); /* mở file để đọc vào mãng */
     if (fp = NULL)
        { printf ("không mở được file ");
          exit (1);
        }
/* Sắp xếp mãng */
      for ( i=0 ; i      for (j=i+1; j          if (a[i]              { t = a[i] ; a[i]=a[j] ; a[j] = t ; }
      fclose (fp);
/* mở file datasx.txt để ghi sau khi sắp xếp */
     fp = fopen ("c:\\datasx.txt ", "wt");
     for ( i=0 ; i     printf (fp,"%2d", a[i] );
      fclose (fp);
/* đọc dữ liệu từ file cách 2 ( tổng quát hơn ) không phụ thuộc vào n */
    i = 0 ;
while (1)
{ fscanf (fp,"%d",&a[i] ;
   i++;
   if (foef(fp) ) break ;
}
- Hàm int fputs ( const char *s, file *fp );
Công dụng : ghi chuỗi s lên tập tin fp ( dấu "\0" ghi lên tập) nếu có lỗi hàm cho eof.
- Hàm char fgets ( char *s, int n , FILE *fp);
Công dụng : đọc 1 chuỗi ký tự từ tập tin fp chứa vào vùng nhớ s. Việc đọc kết thúc khi : hoặc đã đọc n-1 ký tự hoặc gặp dấu xuống DÒNG( CẮPMÃ 13 10). KHI ÐÓ MÃ 10 ÐƯỢC ÐƯA VÀO CHUỖI KẾT QUẢ.
 CáC HàM ÐọC GHI FILE KIểU CấU TRúC
- Hàm int fwrite (void *p, int size , int n , FILE*fp);
Ðối : p : là con trỏ trỏ tới vùng nhớ chứa dữ liệu cần ghi.
size : là kích thước của mẫu tin theo byte.
n số mẫu tin cần ghi.
fp là con trỏ tập.
- Ví dụ : fwrite(&tam) size of(tam),1,fv); /* tam là 1 mẫu tin(record) nào đó*/
Công dụng : ghi một mẫu tin (record) kích thước sizebyte ( size of (tam)) từ vùng nhớ p(&tam) lên tập fp. Hàm sẽ trả về một giá trị = số mẫu tin thực sự ghi được.
+ Hàm int fread (void*p), int size , int n, FILE *fp);
Ðối : p : là con trỏ trỏ tới vùng nhớ chứa dữ liệu đọc được.
size là kích thước của mẫu tin theo byte
n : là số mẫu tin cần đọc, fp là con trỏ tập tin.
Ví dụ : fread (&tam, size of(KIEUHS) , 1, 4 )>0)
Công dụng : đọc n(1) mẫu tin kích thước sizebyte (size of(tam)) từ tập tin fp chứa vào vùng nhớ p(&tam). Hàm trả về một giá trị bằng số mẫu tin thực sự đọc được.
* Ví dụ áp dụng : Nhập vào danh sách lớp gồm n học viên ("nhập vào). Thông tin về mỗi học viên gồm Họ tên, phái , điểm, kết quả. Xét kết quả theo điều kiện sau : nếu Ðiểm>= 5 ( đậu ), điểm <5 : rớt. Sau đó sắp xếp theo điểm và ghi vào tập tin c:\lop.txt. Ðọc lại tập tin c:\lop.txt và xét lại kết quả nếu điểm =4 và phái là nữ sẽ đậu vf chép sang tập tin c:\ketqua.txt.
Giải : #include
#include
#include
#include
typedef struct
{ char ten[20] ; char phai[4] ; int diem ; char kq[4] ; } KieuHV;
KieuHV *lop ,*p, tam;
/* Hàm nhập danh sách n học viên */
void nhapds ( int n, KieuHV lop[ ] )
{ int i , diem ; p = lop ;
for ( i=0; i{ printf (" nhập họ và tên người thứ %d : " , i + 1) ; gets ( pă ten);
printf ("phái (nam/nữ ) : ") ; gets (pă phai );
printf ("nhập điểm = ") ; scanf ("%d%c*c", &diem); pă diem=diem;
if (diem>5)strcpy (p--> kq,"Ðậu");
else strcpy (pă kq, "rớt " ) ; p++;
}
/* Hàm sắp xếp */
void sapxep ( int n , KieuHV lop[ ] )
{ int i , j ;
for ( i=0 ; ifor ( j=i+1 ; j<0; j++)
if (lop[i].diem< lop[j]diem )
{ tam = lop[i] ; lop[i] = lop[j] ; lop [i] = tam ;}
}
/* Hàm in danh sách */
void inds ( int n, KieuHS lop[ ] )
{ int i ;
for ( i=0 ; iprintf ("\n %20s %5s%5d%5s, lop[i].ten, lop[i].phai, lop[i].diem, LOP[I].KQ );
/* CHƯƠNG TRìNH CHíNH */

void main ( )
{ int i , j, n, t, diem ; FILE *fp, *fr ;
   printf ("\n nhập sĩ số : ") ; scanf("%d%*c",&n);
   lop = (KieuHV*) malloc (n*size of (KieuHV));
   nhapds(n, lop) ; sapxep ( n, lop ); inds( n, lop); getch( );
   fp = fopen ( "c :\\lop.txt ", "wb");
   for ( i = 0; i   fwrite (&lop[ i], size of (KieuHV), 1, fp);
   fclose(fp);
    printf ("\n ghi dữ liệu xong ");
    printf("\n in file sau khi sắp xếp và xét kết quả lại ");
    fr = fopen ("c:\\ketqua.txt", "wb");
    while ( fread (&tam, size of ( KieuHV), 1, fp ) > 0)
         { printf ("\n %s %s%d%s", tam.ten, tam.phai, tam.diem, tam.kq);
             if (tam.diem = = 4 &&strcmp(tam.phai,"nữ")= =0 )
                  strcmp(&tam.kq, "đậu");
                  fwrite(&tam,size of(tam),1, fr);
        }
     fclose (fp); fclose(fr);
    printf ("\n in file ketqua.txt sau khi xét lại kết qủa ");
    fp = fopen ("c:\\ketqua.txt", "rb");
     while (fread(&tam, size of (KieuHV) , 1, fp) > 0)
         printf("\n %s%s%d%s",tam.ten,tam.phai, tam.diem,tam.kq);
          fclose (fp); getch( );
&NBSP; }
CáC HàM XUấT NHậP NGẫU NHIVà DI CHUYểN CON TRỏ CHỉ Vị (File position locator )
- Khi mở tệp tin để đọc hay ghi, con trỏ chỉ vị luôn luôn ở đầu tập tin (byte 0) nếu mở mode "a" (append) => con trỏ chỉ vị ở cuối tập tin.
+ Hàm void rewind (FILE*fp) : chuyển con trỏ chỉ vị của tập fp về đầu tập tin.
+ Hàm int fseek (FILE*fp, long số byte, int xp)
Ðối : fp : là con trỏ tập tin; số byte : là số byte cần di chuyển.
xp " cho biết vị trí xuất phát mà việc dịch chuyển được bắt đầu từ đó.
xp = SEEK - SET hay 0 xuất phát từ đầu tập.
xp = SEEK - CUR hay 1 : xuất phát từ vị trí hiện tại của con trỏ.
xp= SEEK - END HAY 2 : xuất phát từ vị trí cuối tập của con trỏ.
+ Công dụng : hàm di chuyển con trỏ chỉ vị của tập fp từ vị trí xác định bởi xp qua một số byte bằng giá trị tuyệt đối của số byte. Nếu số byte > 0 : chuyển về hướng cuối tập ngược lại chuyển về hướng đầu tập. Nếu thành công trả về trị 0. Nếu có lỗi trả khác 0.
+ Chú ý : không nên dùng fseep trên kiểu văn bản, vì sự chuyển đổi ký tự( mã 10) sẽ làm cho việc định vị thiếu chính xác.
+ Hàm long ftell(FILE*fp) ; : cho biết vị trí hiện tại của con trỏ chỉ vị (byte thứ mấy trên tập fp) nếu không thành công trả về trị -1L.
+ Ví dụ 1: giả sử tập fp có 3 ký tự .
fseek (fp,0,SEEK-END) => ftell(fp) = 3
fseek(fp,0,2) => ftell(fp) = 3
fseek (fp,-2, SEEK-END) => ftell(fp) = 1
fseek(fp,0,SEEK -SET) => ftell(fp) = 0
fseek(fp,0, 0) =>ftell(fp) = 0
+ Ví dụ 2 : giả sử ta có tập tin c:\lop.txt chứa danh sách các học viên. Hãy đọc danh sách và sắp xếp giảm dần theo điểm sau đó ghi lại file c:\lop.txt ( nối điểm)
#include
#include
#include
#define N 100
typedef struct
{ char ten[20] ; int tuoi; float diem ; } KieuHV ;
void main( )
{ KieuHV hv[N] ; t;
FILE*fp ; int i, , n ;
fp = fopen ("c:\\lop.txt ", "rat");
if (fp = =NULL)
   { printf ("không mở được file "); exit(1); }
       n = 0 ; i = 0 ;
     while (!feof (fp))
         { fread (&hv[i], size of (KieuHV), 1,fp);
             i++; n++ ;
  /* sắp xếp giảm dần theo điểm */
       for (i=0, i        for (j=i+1; j            if (hv[i].diem

Каталог: 2007
2007 -> Mẫu 01/hc-sn-dn (Ban hành kèm theo Thông tư số 83/2007/tt-btc ngày 16/7/2007 của Bộ Tài chính) TỜ khai hiện trạng sử DỤng nhà, ĐẤt thuộc sở HỮu nhà NƯỚc và ĐỀ xuất phưƠng án xử LÝ
2007 -> BỘ NÔng nghiệP & phát triển nông thôn cục trồng trọt giới Thiệu
2007 -> 10tcn tiêu chuẩn ngành 10tcn 1011 : 2006 giống cà RỐt-quy phạm khảo nghiệm tính khác biệT, TÍnh đỒng nhấT
2007 -> TIÊu chuẩn ngành 10tcn 683 : 2006 giống dưa chuột-quy phạm khảo nghiệM
2007 -> PHÁt triển nông thôN
2007 -> ĐOÀn tncs hồ chí minh
2007 -> List of the countries of the world sorted by total area
2007 -> Số: 962/QĐ-ubnd vĩnh Long, ngày 16 tháng 5 năm 2007
2007 -> Hồ sơ ngành hàng rau quả
2007 -> BẢn cáo bạch domesco vcbs

tải về 230.67 Kb.

Chia sẻ với bạn bè của bạn:
1   2   3




Cơ sở dữ liệu được bảo vệ bởi bản quyền ©hocday.com 2024
được sử dụng cho việc quản lý

    Quê hương