VI. Các lệnh rẽ nhánh không điều kiện: break, continue, goto:
III.4 Con trỏ và mảng:
Tên mảng không có cặp ngoặc vuông là hằng con trỏ. Đây là con trỏ không thể thay đổi giá trị được, nó luôn trỏ đến phần tử đầu tiên của mảng.
Khi ta khai báo 1 mảng, các phần tử của mảng được sắp xếp tuần tự nhau, phần tử đầu tiên có địa chỉ thấp nhất.
Số học con trỏ: phép tăng / giảm con trỏ làm cho giá trị của nó tăng / giảm một lượng bằng số byte nhớ mà biến được nó trỏ tới chiếm trong bộ nhớ. Từ đó, nếu ta khai báo mảng a có 10 phần tử thì : a=&a[0]; a+1 = &a[1]; ... a+i = &a[i] với i từ 0 đến 9.
Ví dụ : p=a; khi đó để truy cập a[i] ta có thể dùng *(p+i) hoặc p[i] hoặc a[i].
Khi tăng / giảm con trỏ p thì p sẽ trỏ đến phần tử sau / trước so với phần tử ban đầu. Nếu ptr1 và ptr2 là 2 con trỏ trỏ đến 2 phần tử của cùng 1 mảng thì hiệu ptr1 - ptr2 cho ta biết khoảng cách giữa 2 phần tử đó trong mảng. Phép trừ giữa 2 con trỏ chỉ có ý nghĩa khi 2 con trỏ cùng trỏ đến 2 phần tử trong một mảng.
Ngoài việc truy cập phần tử mảng bằng chỉ số, việc truy cập gián tiếp qua con trỏ thì chương trình chạy nhanh hơn.
Ví dụ sai : Xuất các phần tử của một biến mảng int a[10],i;
for(i=0; i<10; i++)
{ printf(“%d \n”, *a); a++;
}
Ví dụ: Xuất các phần tử của một biến mảng int a[10],i,*p;
p=a; //p=&a[0] for(i=0; i<10; i++)
{ printf(“%d \n”, *p); p++;
Khi dùng biến con trỏ p để duyệt các phần tử của mảng, chúng ta có thể tìm chỉ số i thông qua biểu thức i = p - a ;
Truyền mảng một chiều cho hàm :
Đối số có kiểu dữ liệu bất kì nhưng phải là giá trị đơn, vì vậy để truyền cả 1 mảng cho hàm ta phải sử dụng con trỏ. Khi đó ta có thể thay đổi giá trị của mảng ở bên trong hàm và khi thoát ra khỏi hàm, giá trị của mảng có thể thay đổi so với lúc gọi hàm (khác với truyền biến đơn cho hàm). #include <stdio.h> int mang[10],i; int lonnhat(int x[]); main() { for(i=0;i<10;i++) mang[i]=i;
prìnt(“\n So lon nhat = %d “,lonnhat(mang)); return 0;
}
int lonnhat(int x[]) // int lonnhat(int *x) { int j,ln; ln=*x; // ln=x[0]; for(j=0;j<10;j++) if( *(x+j) > ln ) // if ( x[j] > ln ) ln=*(x+j); // ln=x[j]; return ln; }
Khi ta truyền mảng cho hàm nhưng không muốn thay đổi giá trị của mảng thì ta khai báo đầu mảng với từ khoá “const”.
Ví dụ : Nhập một mảng các số nguyên và sắp xếp mảng theo thứ tự tăng dần. #include <stdio.h>
#include <conio.h> #define N 20;
viod nhap(int *), sapxep(int *), xuat(const int *); void main()
{
int a[N];
nhap(a); xuat(a); sapxep(a); xuat(a); }
void nhap(int *x) {
clrscr();
printf(“x[%d]=”,i); scanf(“%d”,&x[i]); } } void sapxep(int *x) { char i, j; int tam;
for(i=0; i<N-1; i++) for(j=0; j<N; j++) if (x[i]>x[j]) { tam=x[i]; x[i]=a[j]; x[j]=tam; } }
void xuat(const int *x) { clrscr();
for(int i=0; i<N; i++)
printf(“\n x[%d]=%d”, i, x[i]); getch();
}
III.5. Con trỏ và xâu kí tự :
Ví dụ :
char *pst,st[50]; // pst là con trỏ kiểu char, st[50] là mảng kiểu char pst=”thu”;
gets(st);
printf(“%s %s”,pst,st); là đúng.
Ở đây con trỏ pst sẽ có giá trị là địa chỉ đầu của mảng đang chứa xâu kí tự “thu”. Vì vậy 2 câu lệnh sau : puts(“thu”) và puts(pst) là tương đương.
Nếu ta viết st=”thu”;
gets(pst); thì sai.
st là hằng con trỏ của mảng st (cụ thể là st[0]) nên ta không thể gán hằng địa chỉ cho hằng con trỏ được. pst là con trỏ, khi nó chưa được khởi tạo thì không nên gán giá trị cho nó.
Nếu trước đó ta gán pst=st;
Thì gets(pst); là đúng, lệnh này tương đương gets(st).
Các hàm thao tác trên xâu kí tự : các hàm này nằm trong tập tin string.h Ví dụ : char s1[80]=”Công nghệ”;
char s2[80]=”Thông tin”;
• Hàm xác định độ dài xâu : int strlen(char *str);
Độ dài xâu bằng số kí tự có nghĩa trong xâu tính từ đầu đến kí tự \0. Ví dụ : strlen(s1)=9.
• Hàm ghép xâu kí tự : char *strcat(char *str1,char *str2);
Copy nội dung xâu str2 vào đuôi xâu str1, kết quả được lưu trở lại trong str1. Ví dụ : strcat(s1,s2); s1 sẽ là “Cong ngheThong tin”, s2 không đổi.
Ta cần chú ý xem xâu str1 có đủ chỗ chứa cả 2 xâu không.
• Hàm copy xâu kí tự : char *strcpy(char *str1,char *str2); Copy nội dung xâu str2 vào xâu str1.
Ví dụ : strcpy(s1,s2); s1 sẽ là “Thong tin”, s2 không đổi.
Ta không thể viết s1=s2 vì tên mảng là hằng địa chỉ, không thể gán được. Trước khi dùng hàm này cần cấp phát bộ nhớ cho chuỗi đích.
• Hàm copy xâu kí tự : char *strdup(const char *s);
Như hàm strcpy() nhung nó tự động cấp phát bộ nhớ cho chuỗi đích
• Hàm tìm kiếm kí tự c trong xâu kí tự str : strchr(str,c)
• Hàm tìm kiếm xâu kí tự str2 trong xâu kí tự str1 : strstr(str1,str2)
• Hàm xác định kí tự alphabet : isalpha(c)
• Hàm xác định kí tự thường và kí tự in : islower(c) và isupper(c)
• Hàm xác định kí tự chữ số : isdigit()
• Hàm xác định kí tự điều khiển : iscntrl()
• Hàm xác định kí tự \n , \t, blank : isspace()
• Hàm so sánh xâu kí tự : int strcmp(char *str1,char *str2); Hàm trả về giá trị :
0 nếu str1=str2 >0 nếu str1>str2 <0 nếu str1<str2 Hàm so sánh như sau :
- So sánh từng cặp kí tự của 2 xâu theo giá trị của chúng trong bảng mã ASCII - Hai xâu có độ dài khác nhau, giống nhau đến kí tự cuối cùng của xâu ngắn thì xâu dài lớn hơn.
- Hai xâu trùng nhau cả nội dung lẫn độ dài thì bằng nhau.
• Một số hàm so sánh khác :
- strcmpi(str1,str2) : so sánh không phân biệt chữ hoa, chữ thường. - strncmp(str1,str2,n) : so sánh n kí tự đầu.
- strnicmp(str1,str2,n) : so sánh n kí tự đầu không phân biệt chữ hoa, chữ thường.
• Các hàm chuyển đổi :
- int atoi(str): chuyển đổi xâu str thành số nguyên int. - long atol(str): chuyển đổi xâu str thành số nguyên long. - float atof(str): chuyển đổi xâu str thành số thực float.
- itoa(int value,char *string,int radix): chuyển đổi số nguyên cơ số radix thành xâu - ltoa(long value, char *string, int radix): chuyển đổi số nguyên value cơ số radix thành xâu
- ultoa(unsigned long value,char *string,int radix): chuyển đổi số nguyên value không dấu cơ số radix thành xâu
- char *ecvt(double value, int ndig, int *dec, int *sign) : chuyển số thực thành xâu - char *fcvt(double value, int ndig, int *dec, int *sign) : chuyển số thực thành xâu