Thứ Năm, 22 tháng 3, 2012

Bài 11.4 Sửa sai cho 11.1 - XEM LẠI ĐỂ SO SÁNH


#include<stdio.h>
#include<conio.h>
#include<math.h>

  bool check_prime (const int n ) {
    // 1 is not a prime
    if (n == 1)
        return false;
    // if we’ll find any divisor of n, then n is a combine
    for (int i = 2; i <= sqrtl(n); i ++)
        if (n % i == 0)
            return false;
    // in the end, n is a prime
    return true;
}

main ( )
{
     int n ;
     char ktra ;
   do{
     printf ( " n = ") ;
     scanf("%d", &n ) ;
  if (check_prime ( n )== true )
  printf ("%d nguyen to .\n", n ) ;
  else
  printf ("%d khong nguyen to.\n", n ) ;
     printf ("Tiep ? (an chu c neu khong an chu khac ):") ;
     ktra = getche();
     printf("\n\n");                
  }while ( ktra == 'c' ) ;
     getch();
     }

Bài 11.3 KIỂM TRA SỐ NGUYÊN TỐ VER 3


Xem ra có vẻ thuật toán của chúng ta đã tạm ổn, và chương trình đã chạy tương đối nhanh! Tuy nhiên, như
đã phân tích từ đầu, số nguyên tố là các số lẻ, trừ số 2. Vì thế chúng ta chỉ cần kiểm tra các ước số của N là các
số lẻ, nếu chúng không phải là ước số của N thì N chính là nguyên tố, ngược lại, là hợp số. Thuật toán 3 được cải
tiến thành thuật toán 4, như sau:

#include<stdio.h>
#include<conio.h>
#include<math.h>

     bool check_prime (const int n) {
    // 2 is prime
    if (n == 2)
        return true;
    // 1 is not a prime
    // each even number is not a prime
    if (n == 1 || n % 2 == 0)
        return false;
    // if we’ll find any divisor of n, then n is a combine
    // you should save the value of sqrtl(n) in a temporary
    // variable, otherwise program will calculate sqrt and
    // call function sqrtl, it will take a long time ...
    int j = sqrt (  n ) ;
    for (int i = 3; i <= j; i +=  2)
        if (n % i == 0)
            return false;
    // in the end, n is a prime
    return true;
}

main ( )
{
     int n ;
     char ktra ;
   do{
     printf ( " n = ") ;
     scanf("%d", &n ) ;
  if (check_prime ( n )== true )
  printf ("%d nguyen to .\n", n ) ;
  else
  printf ("%d khong nguyen to.\n", n ) ;
     printf ("Tiep ? (an chu c neu khong an chu khac ):") ;
     ktra = getche();
     printf("\n\n");                
  }while ( ktra == 'c' ) ;
     getch();
     }

Bài 11.2/ KIỂM TRA SỐ NGUYÊN TỐ.


#include<stdio.h>
#include<conio.h>
#include<math.h>
void isPrimes ( int a ) 
{
    int d =0 ; 
    int i ;
    int m ;
    m = int ( sqrt ( a) );
    printf ("can hai la %d .\n", m ) ;


    for ( i = 2 ; i <=m ; i++ ) 
    {
      //  d = 0 ; // nếu d để đây sẽ sai 
      if ( a% i == 0 )
      d++ ;
     }


    if (d ) 
    printf ("%d ko ng to.\n\n", a ) ;
    else 
    printf ("%d ng to.\n\n" , a ) ;
      
      }
main ( )
{
     int n ;
     char ktra ;
   do{
     printf ( " n = ") ;
     scanf("%d", &n ) ;
     isPrimes (  n ) ;                 
     printf ("Tiep ? (an chu c neu khong an chu khac ):") ;
     ktra = getche();
     printf("\n\n");                  
  }while ( ktra == 'c' ) ;
     getch();
     }

Bài 11.1/ KIỂM TRA SỐ NGUYÊN TỐ VER 2 - CHAY SAI 1 SO TRUONG HOP


#include<stdio.h>
#include<conio.h>
#include<math.h>
bool isPrimes ( int a )
{
    int d =0 ;
    int i ;
    int m ;
    m = int ( sqrt ( a) );
    printf ("can hai la %d .\n", m ) ;
    for ( i = 2 ; i <=m ; i++ )
    {
      //  d = 0 ;
      if ( a% i == 0)
   return false ;
  return true ;
   }
      }
main ( )
{
     int n ;
     char ktra ;
   do{
     printf ( " n = ") ;
     scanf("%d", &n ) ;
  if (isPrimes (  n )== true)
  printf ("%d nguyen to .\n", n ) ;
  else
  printf ("%d khong nguyen to.\n", n ) ;
     printf ("Tiep ? (an chu c neu khong an chu khac ):") ;
     ktra = getche();
     printf("\n\n");                
  }while ( ktra == 'c' ) ;
     getch();
     }

Thứ Bảy, 10 tháng 3, 2012

Bài 10.3. / CẤP PHÁT ĐỘNG NEW , DELETE , MẢNG XÂU KÝ TỰ .


1. Cấp phát động, toán tử cấp phát, thu hồi new, delete 
Khi tiến hành chạy chương trình, chương trình dịch sẽ bố trí các ô nhớ cụ thể cho 
các biến được khai báo trong chương trình. Vị trí cũng như số lượng các ô nhớ này tồn 
tại và cố định trong suốt thời gian chạy chương trình, chúng xem như đã bị chiếm dụng 
và sẽ không được sử dụng vào mục đích khác và chỉ được giải phóng sau khi chấm dứt 
chương trình. Việc phân bổ bộ nhớ như vậy được gọi là cấp phát tĩnh (vì được cấp sẵn 
trước khi chạy chương trình và không thể thay đổi tăng, giảm kích thước hoặc vị trí 
trong suốt quá trình chạy chương trình). Ví dụ nếu ta khai báo một mảng nguyên chứa 
1000 số thì trong bộ nhớ sẽ có một vùng nhớ liên tục 2000 bytes để chứa dữ liệu của 
mảng này. Khi đó dù trong chương trình ta chỉ nhập vào mảng và làm việc với một vài 
số thì phần mảng rỗi còn lại vẫn không được sử dụng vào việc khác. Đây là hạn chế 
thứ nhất của kiểu mảng. Ở một hướng khác, một lần nào đó chạy chương trình ta lại 



cần làm việc với hơn 1000 số nguyên. Khi đó vùng nhớ mà chương trình dịch đã dành 
cho mảng là không đủ để sử dụng. Đây chính là hạn chế thứ hai của mảng được khai 
báo trước. 
Khắc phục các hạn chế trên của kiểu mảng, bây giờ chúng ta sẽ không khai báo 
(bố trí) trước mảng dữ liệu với kích thước cố định như vậy. Kích thước cụ thể sẽ được 
cấp phát trong quá trình chạy chương trình theo  đúng yêu cầu của NSD. Nhờ vậy 
chúng ta có đủ số ô nhớ để làm việc mà vẫn tiết kiệm được bộ nhớ, và khi không dùng 
nữa ta có thể thu hồi (còn gọi là giải phóng) số ô nhớ này để chương trình sử dụng vào 
việc khác. Hai công việc cấp phát và thu hồi này được thực hiện thông qua các toán tử 
new, delete và con trỏ p. Thông qua p ta có thể làm việc với bất kỳ địa chỉ nào của 
vùng được cấp phát. Cách thức bố trí bộ nhớ như thế này được gọi là cấp phát động. 
Sau đây là cú pháp của câu lệnh new. 



p = new <kiểu> ;    // cấp phát 1 phần tử 
p = new <kiểu>[n] ;     // cấp phát n phần tử 
Ví dụ:   
int *p ; 
p = new int ;  // cấp phát vùng nhớ chứa được 1 số nguyên  
p = float int[100] ;  // cấp phát vùng nhớ chứa được 100 số thực 
Khi gặp toán tử new, chương trình sẽ tìm trong bộ nhớ một lượng ô nhớ còn rỗi 
và liên tục với số lượng đủ theo yêu cầu và cho p trỏ đến địa chỉ (byte đầu tiên) của 
vùng nhớ này. Nếu không có vùng nhớ với số lượng như vậy thì việc cấp phát là thất 
bại và p = NULL (NULL là một địa chỉ rỗng, không xác định). Do vậy ta có thể kiểm 
tra việc cấp phát có thành công hay không thông qua kiểm tra con trỏ p bằng hay khác 
NULL. Ví dụ: 
float *p ; 



int n ; 
cout << "Số lượng cần cấp phát = "; cin >> n; 
p = new double[n]; 
if (p == NULL) {  
cout << "Không đủ bộ nhớ" ;  
exit(0) ;      
}  
Ghi chú: lệnh exit(0) cho phép thoát khỏi chương trình, để sử dụng lệnh này cần 
khai báo file tiêu đề <process.h>. 



Để giải phóng bộ nhớ đã cấp phát cho một biến (khi không cần sử dụng nữa) ta sử 
dụng câu lệnh delete. 
delete p ;  // p là con trỏ được sử dụng trong new 
và để giải phóng toàn bộ mảng được cấp pháp thông qua con trỏ p ta dùng câu lệnh: 
delete[] p ;  // p là con trỏ trỏ đến mảng 
Dưới đây là ví dụ sử dụng tổng hợp các phép toán trên con trỏ. 
Ví dụ 1 : Nhập dãy số (không dùng mảng). Sắp xếp và in ra màn hình. 
Trong ví dụ này chương trình xin cấp phát bộ nhớ đủ chứa n số nguyên và được 
trỏ bởi con trỏ head. Khi đó địa chỉ của số nguyên đầu tiên và cuối cùng sẽ là head và 
head+n-1. p và q là 2 con trỏ chạy trên dãy số này, so sánh và đổi nội dung của các số 
này với nhau để sắp thành dãy tăng dần và cuối cùng in kết quả. 
main() 




 int *head, *p, *q, n, tam;  // head trỏ đến (đánh dấu) đầu dãy 
 cout << "Cho biết số số hạng của dãy: "); cin >> n ; 
 head = new int[n] ;   // cấp phát bộ nhớ chứa n số nguyên 
for (p=head; p<head+n; p++)  // nhập dãy 

cout << "So thu " << p-head+1 << ": " ; cin >> *p ;   
 } 
 for (p=head; p<head+n-1; p++)      // sắp xếp 
 for (q=p+1; q<head+n; q++)  
  if (*q < *p) { tam = *p; *p = *q; *q = tam; }  // đổi chỗ 
 for (p=head; p<head+n; p++) cout << *p ;   // in kết quả 




2. Con trỏ và mảng, xâu kí tự  
a. Con trỏ và mảng 1 chiều 
Việc cho con trỏ trỏ đến mảng cũng tương tự trỏ đến các biến khác, tức gán địa 
chỉ của mảng (chính là tên mảng) cho con trỏ. Chú ý rằng địa chỉ của mảng cũng là địa 
chỉ của thành phần thứ 0 nên a+i sẽ là địa chỉ thành phần thứ i của mảng. Tương tự, 
nếu p trỏ đến mảng a thì p+i là địa chỉ thành phần thứ i của mảng a và do đó *(p+i) = 
 a[i] = *(a+i). 
Chú ý khi viết *(p+1) = *(a+1) ta thấy vai trò của p và a trong biểu thức này là
như nhau, cùng truy cập đến giá trị của phần tử a[1]. Tuy nhiên khi viết *(p++) thì lại
khác với *(a++), cụ thể viết p++ là hợp lệ còn a++ là không được phép. Lý do là tuy p
và a cùng thể hiện địa chỉ của mảng a nhưng p thực sự là một biến, nó có thể thay đổi
được giá trị còn a là một hằng, giá trị không được phép thay đổi. Ví dụ viết x = 3 và
sau đó có thể tăng x bởi x++ nhưng không thể viết x = 3++. 
Ví dụ 1 : In toàn bộ mảng thông qua con trỏ. 
int a[5] = {1,2,3,4,5}, *p, i; 
1:  p = a; for (i=1; i<=5; i++) cout << *(p+i);   // p không thay đổi 
hoặc:  
2: for (p=a; p<=a+4; p++) cout << *p ;    // thay đổi p 



Trong phương án 1, con trỏ p không thay đổi trong suốt quá trình làm việc của 
lệnh for, để truy nhập đến phần tử thứ i của mảng a ta sử dụng cú pháp *(p+i). 
Đối với phương án 2 con trỏ sẽ dịch chuyển dọc theo mảng a bắt đầu từ địa chỉ a 
(phần tử đầu tiên) đến phần tử cuối cùng. Tại bước thứ i, p sẽ trỏ vào phần tử a[i], do 
đó ta chỉ cần in giá trị *p. Để kiểm tra khi nào p đạt đến phần tử cuối cùng, ta có thể so 
sánh p với địa chỉ cuối mảng chính là địa chỉ đầu mảng cộng thêm số phần tử trong a 
và trừ 1 (tức a+4 trong ví dụ trên).  
b. Con trỏ và xâu kí tự 
Một con trỏ kí tự có thể xem như một biến xâu kí tự, trong đó xâu chính là tất cả 
các kí tự kể từ byte con trỏ trỏ đến cho đến byte '\0' gặp đầu tiên. Vì vậy ta có thể khai 
báo các xâu dưới dạng con trỏ kí tự như sau. 
char *s ;  
char *s = "Hello" ; 



Các hàm trên xâu vẫn được sử dụng như khi ta khai báo nó dưới dạng mảng kí tự. 
Ngoài ra khác với mảng kí tự, ta được phép sử dụng phép gán cho 2 xâu dưới dạng con 
trỏ, ví dụ: 
char *s, *t = "Tin học" ; s = t;  // thay cho hàm strcpy(s, t) ; 
Thực chất phép gán trên chỉ là gán 2 con trỏ với nhau, nó cho phép s bây giờ cũng 
được trỏ đến nơi mà t trỏ (tức dãy kí tự "Tin học" đã bố trí sẵn trong bộ nhớ) 
Khi khai báo xâu dạng con trỏ nó vẫn chưa có bộ nhớ cụ thể, vì vậy thông thường 
kèm theo khai báo ta cần phải xin cấp phát bộ nhớ cho xâu với độ dài cần thiết. Ví dụ: 
char *s = new char[30], *t ; 
  

strcpy(s, "Hello") ;  // trong trường hợp này không cần cấp phát bộ 
t = s ;    // nhớ cho t vì t và s cùng sử dụng chung vùng nhớ  
nhưng: 
char *s = new char[30], *t ; 
strcpy(s, "Hello") ;   
t = new char[30];  // trong trường hợp này phải cấp bộ nhớ cho t vì 
strcpy(t, s) ;   // có chỗ để strcpy sao chép sang nội dung của s.  
c. Con trỏ và mảng hai chiều 
Để dễ hiểu việc sử dụng con trỏ trỏ  đến mảng hai chiều, chúng ta nhắc lại về 
mảng 2 chiều thông qua ví dụ. Giả sử ta có khai báo: 
float a[2][3], *p;  
khi đó a được bố trí trong bộ nhớ như là một dãy 6 phần tử float như sau  




           
a  a+1 
tuy nhiên a không được xem là mảng 1 chiều với 6 phần tử mà được quan niệm 
như mảng một chiều gồm 2 phần tử, mỗi phần tử là 1 bộ 3 số thực. Do đó địa chỉ của 
mảng a chính là địa chỉ của phần tử đầu tiên a[0][0], và a+1 không phải là địa chỉ của 
phần tử tiếp theo a[0][1] mà là địa chỉ của phần tử a[1][0]. Nói cách khác a+1 cũng là 
tăng địa chỉ của a lên một thành phần, nhưng 1 thành phần ở đây được hiểu là toàn bộ 
một dòng của mảng.  
Mặt khác, việc lấy địa chỉ của từng phần tử (float) trong a thường là không chính 
xác. Ví dụ: viết &a[i][j] (địa chỉ của phần tử dòng i cột j) là được đối với mảng nguyên 
nhưng lại không đúng đối với mảng thực.  
Từ các thảo luận trên, phép gán p = a là dễ gây nhầm lẫn vì p là con trỏ float còn 

a là địa chỉ mảng (1 chiều). Do vậy trước khi gán ta cần ép kiểu của a về kiểu float. 
Tóm lại cách gán địa chỉ của a cho con trỏ p được thực hiện như sau:  
Cách sai: 
p = a ;   // sai vì khác kiểu 
Các cách đúng: 
p = (float*)a;   // ép kiểu của a về con trỏ float (cũng là kiểu của p) 
p = a[0];    // gán với địa chỉ của mảng a[0]  
p = &a[0][0];   // gán với địa chỉ số thực đầu tiên trong a  



trong đó cách dùng p = (float*)a; là trực quan và đúng trong mọi trường hợp nên 
được dùng thông dụng hơn cả. 
Sau khi gán a cho p (p là con trỏ thực), việc tăng giảm p chính là dịch chuyển con 
trỏ trên từng phần tử (thực) của a. Tức: 
p trỏ tới a[0][0] 
p+1 trỏ tới a[0][1] 
p+2 trỏ tới a[0][2] 
p+3 trỏ tới a[1][0] 
p+4 trỏ tới a[1][1] 
p+5 trỏ tới a[1][2] 
Tổng quát, đối với mảng m x n phần tử: 
p + i*n + j trỏ tới a[i][j]  hoặc  a[i][j] = *(p + i*n + j) 
Từ đó để truy nhập đến phần tử a[i][j] thông qua con trỏ p ta nên sử dụng cách 



viết sau: 
p = (float*)a; 
cin >> *(p+i*n+j) ;  // nhập cho a[i][j] 
cout << *(p+i*n+j);  // in a[i][j] 
Ví dụ sau đây cho phép nhập và in một mảng 2 chiều m*n (m dòng, n cột) thông 
qua con trỏ p. Nhập liên tiếp m*n số vào mảng và in thành ma trận m dòng, n cột. 
main()  
{  
clrscr();  
float a[m][n], *p;  
int i, j;  
p = (float*) a;  



for (i=0; i<m*n; i++) cin >> *(p+i);   // nhập như dãy mxn phần tử  
 *(p+2*n+3) = 100; *(p+4*n) = 100;  // gán a[2,3] = a[4][0] = 100 
for (i=0; i<m; i++)      // in lại dưới dạng ma trận 
{  
for (j=0; j<n; j++) cout << *(p+i*n+j);  
cout << endl;  
  

}  
getch();  

Chú ý: việc lấy địa chỉ phần tử a[i][j] của mảng thực a là không chính xác. Tức: viết p 
= &a[i][j] có thể dẫn đến kết quả sai. 



3. Mảng con trỏ  
a. Khái niệm chung 
Thực chất một con trỏ cũng là một biến thông thường có tên gọi (ví dụ p, q, …), 
do đó cũng giống như biến, nhiều biến cùng kiểu có thể tổ chức thành một mảng với 
tên gọi chung, ở đây cũng vậy nhiều con trỏ cùng kiểu cũng được tổ chức thành mảng. 
Như vậy mỗi phần tử của mảng con trỏ là một con trỏ trỏ đến một mảng nào đó. Nói 
cách khác một mảng con trỏ cho phép quản lý nhiều mảng dữ liệu cùng kiểu. Cách 
khai báo: 
<kiểu> *a[size]; 
Ví dụ:  
int *a[10]; 
khai báo một mảng chứa 10 con trỏ. Mỗi con trỏ a[i] chứa địa chỉ của một mảng 
nguyên nào đó.  



b. Mảng xâu kí tự 
Là trường hợp riêng của mảng con trỏ nói chung, trong đó kiểu cụ thể là char. 
Mỗi thành phần mảng là một con trỏ trỏ đến một xâu kí tự, có nghĩa các thao tác tiến 
hành trên *a[i] như đối với một xâu kí tự.  
Ví dụ 1 : Nhập vào và in ra một bài thơ.  
main()  
{  
clrscr();  
char *dong[100];    // khai báo 100 con trỏ kí tự (100 dòng) 
int i, n;  
cout << "so dong = "; cin >> n ;  // nhập số dòng thực sự 

cin.ignore();     // loại dấu ↵ trong lệnh cin ở trên 
for (i=0; i<n; i++)  
 {  
dong[i] = new char[80]; // cấp bộ nhớ cho dòng i 
cin.getline(dong[i],80);  // nhập dòng i 
}  
for (i=0; i<n; i++) cout << dong[i] << endl; // in kết quả 
getch(); 



















Bài 10 . 2 ./ NHẬP XÂU VÀ CÁC HÀM XỬ LÝ XÂU.


1. Phương thức nhập xâu (#include <iostream.h>) 
Do toán tử nhập >> có hạn chế đối với xâu kí tự nên C++ đưa ra hàm riêng (còn 
gọi là phương thức) cin.getline(s,n) để nhập xâu kí tự. Hàm có 2 đối với s là xâu cần 
nhập nội dung và n-1 là số kí tự tối  đa của xâu. Giống phương thức nhập kí tự 
cin.get(c), khi gặp hàm cin.getline(s,n) chương trình sẽ nhìn vào bộ đệm bàn phím lấy 
ra n-1 kí tự (nếu đủ hoặc lấy tất cả kí tự còn lại, trừ kí tự enter) và gán cho s. Nếu tại 
thời điểm đó bộ đệm đang rỗng, chương trình sẽ tạm dừng chờ NSD nhập dữ liệu (dãy 
kí tự) vào từ bàn phím. NSD có thể nhập vào dãy với độ dài bất kỳ cho đến khi nhấn 
Enter, chương trình sẽ lấy ra n-1 kí tự đầu tiên gán cho s, phần còn lại vẫn được lưu 
trong bộ đệm (kể cả kí tự Enter) để dùng cho lần nhập sau. Hiển nhiên, sau khi gán các 
kí tự cho s, chương trình sẽ tự động đặt kí tự kết thúc xâu vào ô tiếp theo của xâu s. 
Ví dụ 1 : Xét đoạn lệnh sau  
char s[10] ; 

cin.getline(s, 10) ; 
cout << s << endl ; 
cin.getline(s, 10) ; 
cout << s << endl ; 
giả sử ta nhập vào bàn phím dòng kí tự: 1234567890abcd  ↵. Khi  đó lệnh 
cin.getline(s,10) đầu tiên sẽ gán xâu "123456789" (9 kí tự) cho s, phần còn lại vẫn lưu 
trong bộ đệm bàn phím. Tiếp theo s được in ra màn hình. Đến lệnh cin.getline(s,10) thứ 
hai NSD không phải nhập thêm dữ liệu, chương trình tự động lấy nốt số dữ liệu còn lại 
(vì chưa đủ 9 kí tự) "0abcd" để gán cho s. Sau đó in ra màn hình. Như vậy trên màn 
hình sẽ xuất hiện hai dòng: 
123456789 
0abcd 



Ví dụ 2 : Nhập một ngày tháng dạng Mỹ (mm/dd/yy), đổi sang ngày tháng dạng Việt 
Nam rồi in ra màn hình. 
#include <iostream.h> 
main() 

 char US[9], VN[9] = " / / " ;   // khởi tạo trước hai dấu / 
 cin.getline(US, 9) ;   // nhập ngày tháng, ví dụ "05/01/99" 
 VN[0] = US[3]; VN[1] = US[4] ;  // ngày 
 VN[3] = US[0]; VN[4] = US[1] ;  // tháng 



 VN[6] = US[6]; VN[7] = US[7] ;  // năm  
 cout << VN << endl ; 

2. Một số hàm xử lí xâu (#include <string.h>) 
•2.1.  strcpy(s, t) ;  
Gán nội dung của xâu t cho xâu s (thay cho phép gán = không được dùng). Hàm 
sẽ sao chép toàn bộ nội dung của xâu t (kể cả kí tự kết thúc xâu) vào cho xâu s. Để sử 
dụng hàm này cần đảm bảo độ dài của mảng s ít nhất cũng bằng độ dài của mảng t. 
Trong trường hợp ngược lại kí tự kết thúc xâu sẽ không được ghi vào s và điều này có 
thể gây treo máy khi chạy chương trình. 
Ví dụ:  



char s[10], t[10] ; 
t = "Face" ;     // không được dùng 
s = t ;      // không được dùng 
strcpy(t, "Face") ;    // được, gán "Face" cho t 
strcpy(s, t) ;     // được, sao chép t sang s 
cout << s << " to " << t ;   // in ra: Face to Face 
• 2.2. strncpy(s, t, n) ; 
Sao chép n kí tự của t vào s. Hàm này chỉ làm nhiệm vụ sao chép, không tự động 
gắn kí tự kết thúc xâu cho s. Do vậy NSD phải thêm câu lệnh đặt kí tự '\0' vào cuối xâu 
s sau khi sao chép xong. 
Ví dụ: 



char s[10], t[10] = "Steven"; 
strncpy(s, t, 5) ;     // copy 5 kí tự "Steve" vào s 
s[5] = '\0' ;      // đặt dấu kết thúc xâu 
// in câu: Steve is young brother of Steven 
cout << s << " is young brother of " << t ; 
Một sử dụng có ích của hàm này là copy một xâu con bất kỳ của t và đặt vào s. Ví 
dụ cần copy xâu con dài 2 kí tự bắt đầu từ kí tự thứ 3 của xâu t và đặt vào s, ta viết 
strncpy(s, t+3, 2). Ngoài ra xâu con được copy có thể được đặt vào vị trí bất kỳ của s 
(không nhất thiết phải từ  đầu xâu s) chẳng hạn  đặt vào từ vị trí thứ 5, ta viết: 
strncpy(s+5, t+3, 2). Câu lệnh này có nghĩa: lấy 2 kí tự thứ 3 và thứ 4 của xâu t đặt 
vào 2 ô thứ 5 và thứ 6 của xâu s. Trên cơ sở này chúng ta có thể viết các đoạn chương 

trình ngắn để thay thế một đoạn con bất kỳ nào đó trong s bởi một đoạn con bất kỳ (có 
độ dài tương đương) trong t. Ví dụ các dòng lệnh chuyển đổi ngày tháng trong ví dụ 
trước có thể viết lại bằng cách dùng hàm strncpy như sau: 
strncpy(VN+0, US+3, 2) ;   // ngày 
strncpy(VN+3, US+0, 2) ;   // tháng 
strncpy(VN+6, US+6, 2);   // năm 
•2.3.  strcat(s, t); 
Nối một bản sao của t vào sau s (thay cho phép +). Hiển nhiên hàm sẽ loại bỏ kí 
tự kết thúc xâu s trước khi nối thêm t. Việc nối sẽ đảm bảo lấy cả kí tự kết thúc của xâu 
t vào cho s (nếu s đủ chỗ) vì vậy NSD không cần thêm kí tự này vào cuối xâu. Tuy 
nhiên, hàm không kiểm tra xem liệu độ dài của s có đủ chỗ để nối thêm nội dung, việc 
kiểm tra này phải do NSD đảm nhiệm. Ví dụ: 
char a[100] = "Mẫn", b[4] = "tôi"; 

strcat(a, “ và ”);  
strcat(a, b); 
cout << a       // Mẫn và tôi 

char s[100] , t[100] = "Steve" ; 
strncpy(s, t, 3); s[3] = '\0';    // s = "Ste" 
strcat(s, "p");      // s = "Step" 
cout << t << " goes "<< s << " by " <<s // Steve goes Step by Step 
•2.4.  strncat(s, t, n); 
Nối bản sao n kí tự đầu tiên của xâu t vào sau xâu s. Hàm tự động đặt thêm dấu 
kết thúc xâu vào s sau khi nối xong (tương phản với strncpy()). Cũng giống strcat hàm 
đòi hỏi độ dài của s phải đủ chứa kết quả. Tương tự, có thể sử dụng cách viết strncat(s, 
t+k, n) để nối n kí tự từ vị trí thứ k của xâu t cho s. 



Ví dụ: 
char s[20] = "Nhà " ;  
char t[] = "vua chúa" 
strncat(s, t, 3) ;     // s = "Nhà vua" 
hoặc:  
strncat(s, t+4, 4) ;     // s = "Nhà chúa" 
•2.5.  strcmp(s, t);  



Hàm so sánh 2 xâu s và t (thay cho các phép toán so sánh). Giá trị trả lại là hiệu 2 
kí tự khác nhau đầu tiên của s và t. Từ đó, nếu s1 < s2 thì hàm trả lại giá trị âm, bằng 0 
nếu s1==s2, và dương nếu s1 > s2. Trong trường hợp chỉ quan tâm đến so sánh bằng, 
nếu hàm trả lại giá trị 0 là 2 xâu bằng nhau và nếu giá trị trả lại khác 0 là 2 xâu khác 
nhau. 
Ví dụ: 
if (strcmp(s,t)) cout << "s khác t"; else cout << "s bằng t" ;  
•2.6.  strncmp(s, t) ; 
Giống hàm strcmp(s, t) nhưng chỉ so sánh tối đa n kí tự đầu tiên của hai xâu. 
Ví dụ: 
char s[] = "Hà Nội" , t[] = "Hà nội" ; 
cout << strcmp(s,t) ;    // -32 (vì 'N' = 78, 'n' = 110) 
cout << strncmp(s, t, 3) ;   // 0 (vì 3 kí tự đầu của s và t là như 

nhau) 
•2.7.  strcmpi(s, t) ;  
Như strcmp(s, t) nhưng không phân biệt chữ hoa, thường.  
Ví dụ: 
char s[] = "Hà Nội" , t[] = "hà nội" ; 
cout << strcmpi(s, t) ;    // 0 (vì s = t) 
•2.8.  strupr(s);  
Hàm đổi xâu s thành in hoa, và cũng trả lại xâu in hoa đó. 
Ví dụ: 
char s[10] = "Ha noi" ; 
cout << strupr(s) ;    // HA NOI 



cout << s ;      // HA NOI (s cũng thành in hoa)  
•2.9.  strlwr(s);  
Hàm đổi xâu s thành in thuờng, kết quả trả lại là xâu s. 
Ví dụ: 
char s[10] = "Ha Noi" ; 
cout << strlwr(s) ;    // ha noi 
cout << s ;      // ha noi (s cũng thành in thường)  



•2.10.  strlen(s) ;  
Hàm trả giá trị là độ dài của xâu s.  
Ví dụ: 
char s[10] = "Ha Noi" ; 
cout << strlen(s) ;     // 5 

Sau đây là một số ví dụ sử dụng tổng hợp các hàm trên
Ví dụ 1 : Thống kê số chữ 'a' xuất hiện trong xâu s.  
main() 

 const int MAX = 100; 
 char s[MAX+1];  



int sokitu = 0; 
cin.getline(s, MAX+1); 
 for (int i=0; i < strlen(s); i++)   if (s[i] = 'a ') sokitu++; 
 cout << "Số kí tự = " << sokitu << endl ; 

Ví dụ 2 : Tính độ dài xâu bằng cách đếm từng kí tự (tương đương với hàm strlen()) 
main() 

 char s[100];     // độ dài tối đa là 99 kí tự 
 cin.getline(s, 100);    // nhập xâu s 
 for (int i=0 ; s[i] != '\0' ; i++) ;  // chạy từ đầu đến cuối xâu 



 cout << "Độ dài xâu = " << i ; 

Ví dụ 3 : Sao chép xâu s sang xâu t (tương đương với hàm strcpy(t,s)) 
void main() 

 char s[100], t[100]; 
 cin.getline(s, 100);    // nhập xâu s 
int i=0;  



while ((t[i] = s[i]) != '\0') i++;  // copy cả dấu kết thúc xâu '\0' 
cout << t << endl ;          


Ví dụ 4 : Cắt dấu cách 2 đầu của xâu s. Chương trình sử dụng biến i chạy từ đầu xâu 
đến vị trí đầu tiên có kí tự khác dấu trắng. Từ vị trí này sao chép từng kí tự còn lại của 
xâu về đầu xâu bằng cách sử dụng thêm biến j để làm chỉ số cho xâu mới. Kết thúc sao 
chép j sẽ ở vị trí cuối xâu (mới). Cho j chạy ngược về đầu xâu cho đến khi gặp kí tự 
đầu tiên khác dấu trắng. Đặt dấu kết thúc xâu tại đây.  
main() 

 char s[100];  

 cin.getline(s, 100);    // nhập xâu s 
 int i, j ; 
 i = j = 0; 
 while (s[i++] == ' '); i-- ;   // bỏ qua các dấu cách đầu tiên 
 while (s[i] != '\0') s[j++] = s[i++] ;  // sao chép phần còn lại vào s 
 while (s[--j] == ' ') ;    // bỏ qua các dấu cách cuối 
 s[j+1] = '\0' ;     // đặt dấu kết thúc xâu 
 cout << s ; 



Ví dụ 5 : Chạy dòng chữ quảng cáo vòng tròn từ phải sang trái giữa màn hình.  
Giả sử hiện 30 kí tự của xâu quảng cáo. Ta sử dụng vòng lặp. Cắt 30 kí tự đầu 
tiên của xâu cho vào biến hien, hiện biến này ra màn hình. Bước lặp tiếp theo cắt ra 30 
kí tự của xâu nhưng dịch sang phải 1 kí tự cho vào biến hien và hiện ra màn hình. Quá 
trình tiếp tục, mỗi bước lặp ta dịch chuyển nội dung cần hiện ra màn hình 1 kí tự, do 
hiệu ứng của mắt ta thấy dòng chữ sẽ chạy từ biên phải về biên trái của màn hình. Để 
quá trình chạy theo vòng tròn (khi hiện đến kí tự cuối của xâu sẽ hiện quay lại từ kí tự 
đầu của xâu) chương trình sử dụng biến i đánh dấu điểm đầu của xâu con cần cắt cho 
vào hien, khi i bằng độ dài của xâu chương trình đặt lại i = 0 (cắt lại từ đầu xâu). Ngoài 
ra, để phần cuối xâu nối với phần đầu (tạo thành vòng tròn) ngay từ đầu chương trình, 
xâu quảng cáo sẽ được nối thành gấp đôi. 
Vòng lặp tiếp tục đến khi nào NSD ấn phím bất kỳ (chương trình nhận biết điều 
này nhờ vào hàm kbhit()  thuộc file nguyên mẫu conio.h) thì dừng. Để dòng chữ chạy 



không quá nhanh chương trình sử dụng hàm trễ delay(n) (thuộc dos.h, tạm dừng trong 
n phần nghìn giây) với n  được  điều chỉnh thích hợp theo tốc  độ của máy. Hàm 
gotoxy(x, y) (thuộc  conio.h) trong chương trình đặt con trỏ màn hình tại vị trí cột x 
dòng y để đảm bảo dòng chữ luôn luôn hiện ra tại đúng một vị trí trên màn hình. 
#include <iostream.h> 
#include <conio.h> 
#include <dos.h> 
main() 

char qc[100] = "Quảng cáo miễn phí: Không có tiền thì không có kem. "; 
 int dd = strlen(qc); 
 char tam[100] ; strcpy(tam, qc) ;   

strcat(qc, tam) ;     // nhân đôi dòng quảng cáo 
 clrscr();       // xoá màn hình 
char hien[31] ;     // chứa xâu dài 30 kí tự để hiện 
 i = 0; 
 while (!kbhit()) {     // trong khi chưa ấn phím bất kỳ 
  strncpy(hien, s+i, 30);   
hien[30] = '\0';    // copy 30 kí tự từ qc[i] sang hien
  gotoxy(20,10); cout << hien ; // in hien tại dòng 10 cot 20 
  delay(100);     // tạm dừng 1/10 giây 
i++; if (i==dd) i = 0;   // tăng i 
 }        

Ví dụ 6 : Nhập mật khẩu (không quá 10 kí tự). In ra "đúng" nếu là "HaNoi2000", "sai" 
nếu ngược lại. Chương trình cho phép nhập tối đa 3 lần. Nhập riêng rẽ từng kí tự (bằng 
hàm getch()) cho mật khẩu. Hàm getch() không hiện kí tự NSD gõ vào, thay vào đó 
chương trình chỉ hiện kí tự 'X' để che giấu mật khẩu. Sau khi NSD đã gõ xong (9 kí tự) 
hoặc đã Enter, chương trình so sánh xâu vừa nhập với "HaNoi2000", nếu đúng chương 
trình tiếp tuc, nếu sai tăng số lần nhập (cho phép không quá 3 lần). 
#include <iostream.h> 
#include <conio.h> 
  #include <string.h> 
void main() 

 char pw[11]; int solan = 0;   // Cho phep nhap 3 lan 
 do { 
clrscr(); gotoxy(30,12) ; 
int i = 0; 
  while ((pw[i]=getch()) != 13 && ++i < 10) cout << 'X' ; // 13 = Enter 
  pw[i] = '\0' ;  
  cout << endl ; 
if (!strcmp(pw, "HaNoi2000")) { cout << "Mời vào" ; break; } 
  else { cout << "Sai mật khẩu. Nhập lại") ; solan++ ; } 

} while (solan < 3);