con tro
PHP Code:
#include <stdio.h>
#include <conio.h>
#include <ctype.h>
void main()
{
char xau[200];
printf("Nhap xau : ");
scanf("%[a-zA-Z ]",xau); //nếu bạn chưa hiểu dòng lệnh này hãy xem bài viết này để hiểu sâu thêm về scanf
//http://forums.congdongcviet.com/showthread.php?t=34612
//Viết hoa xâu (duyệt xuôi)
printf("Viet hoa : ");
for (char *p=xau;*p;p++) //p trỏ đến xâu; kí tự trỏ đến khác NULL;p=p+1
printf("%c",toupper(*p));
//viết đầy đủ sẽ là (char *p=xau;*p!=NULL;p++)
//viết ngắn gọn lại cho độc đáo
//Viết đảo ngược xâu (duyệt ngược)
printf("
Dao nguoc xau : ");
for(char *p=xau+strlen(xau)-1;p>=xau;p--) // cho p trỏ vào từ cuối cùng; p còn lớn hơn xau;p=p-1
printf("%c",*p);
getch();
}
ứng dụng đổi số thực thành số nhị phân
Cách 1 : C style
PHP Code:
#include <stdio.h>
#include <conio.h>
void nhiphan(float n)
{
for(int i=0,*temp=(int *)(void*)&n;i<sizeof(n)*8;i++,(*temp)<<=1)
printf("%d",*temp>=0);
}
void main()
{
nhiphan(3.9f);
getch();
}
Cách 2: C++ style
PHP Code:
#include <iostream>
using namespace std;
void nhiphan(unsigned n)
{
n>>1?nhiphan(n>>1):0;
printf("%d",n&1);
}
void nhiphan(float n)
{
nhiphan(*(unsigned *)(void*)&n);
}
void main()
{
nhiphan(3.9f);
getch();
}
Ứng dụng tìm (số float lớn hơn ko) nhỏ nhất
đấy chính là số 00000000 00000000 00000000 00000001
PHP Code:
#include <iostream>
int main()
{
float x = 0;
char *p = (char*)&x;
(*p) |= 1;
std::cout<<x<<std::endl;
return 0;
}
Chap VI : Con trỏ với mảng, xâu, cấp phát bộ nhớ động
I. Hằng con trỏ - const pointer ???? Con trỏ hằng , pointer to const???
a. Hằng là gì?
Ta đã biét hằng số (toán học) là những đại lương có giá trị không đổi,
trong lập trình là những đại lương có giá trị không đổi trong suốt trương trình.
Hằng trong C/C++/C++0x có định kiểu rõ ràng
Hằng trong C/C++/C++0x được định nghĩa bằng từ khóa const
Chú ý : Có 1 số người hiểu lầm rằng dùng từ khóa define định nghĩa hằng số, đây thật sự là 1 cái hiểu sai lầm hoàn toàn. Define định nghĩa nên macro và có rất nhiều sự khác nhau khi ta dùng define và const
Để viết về hằng và các vấn đề liên quan đến hằng trong C/C++/C++0x chắc cũng mất 1 bài khá dài, nên để không bị loãng vấn đề mình đang viết, mình xin tạm dừng vấn đề về hằng ở đây......... mình sẽ viết ở 1 topic khác vậy
b. Hằng con trỏ ?
Mình đã tìm hiểu về kiểu dữ liệu con trở ở :
+ nó lưu trữ cái gì, miền giá trị của nó
+ các phép toán trên con trỏ
bây giờ mình sẽ tìm hiểu thêm về kiểu dữ liệu con trở ở : hằng con trỏ . Vậy hằng con trỏ là gì ? Đối với hằng và con trỏ có 2 loại như sau
+ những con trỏ mà chỉ trỏ cố định vào 1 vùng nhớ , những con trỏ này ko có khả năng trỏ vào vùng nhớ khác, ko thay đổi được (1)
+ những con trỏ mà trỏ vào 1 vùng nhớ cố định, con trỏ này chỉ có tác dụng trỏ đến, chứ không có khả năng thay đổi giá trị của vùng nhớ này, con trỏ này được ứng dụng gần như là tác dụng của phương thức hằng trong OOP (2)
Để tiên phân biệt, mình gọi (1) là hằng con trỏ và (2) là con trỏ hằng, và chúng ta có thể gộp cả 2 kiểu này để thành 1 kiểu mới
ví dụ về loại (1) by clamvn
C++ Code:
Lựa chọn code | Ẩn/Hiện code
void main()
{
char buf[] = "bonjour";
char * const p = buf;
p++; <<<<<<<<<<<<<<<<<<<<<<< báo lỗi tại đây
p[4]++; <<<<<<<<<<<<<<<<<<<<<<<<< ko vấn đề, hoàn toàn có thể thay đổi giá trị vùng nhớ mà p trỏ đến
}
ví dụ về loại (2) by langman, tham khảo về những sai lầm thường gặp
C++ Code:
Lựa chọn code | Ẩn/Hiện code
void main()
{
char *p="Bui Tan Quang"
p++;
*p++; <<<<<<<<<<<<<<<<<<<<<<< báo lỗi tại đây
p[2]='b';<<<<<<<<<<<<<<<<<<<<<<< báo lỗi tại đây
}
ví dụ tiếp về loại (2) by clamvn
C++ Code:
Lựa chọn code | Ẩn/Hiện code
char buf[] = "bonjour";
char const * p = buf; /* hay const char * p = buf; */
p++; /* được */
p[4]++; /* ko được, sai */
ví dụ về kết hợp by clamvn
C++ Code:
Lựa chọn code | Ẩn/Hiện code
char buf[] = "bonjour";
char const * const p = buf;
p++; /* Sai */
p[4]++; /* Sai */
II. Mảng liên quan gì đến con trỏ và cho vào bài viết này chi ?
Khi ta khai báo mảng thì tương đương với : xin cấp phát 1 vùng nhớ có kick thước như bạn khai báo và khai báo ra 1 hằng con trỏ trỏ vào đầu vùng nhớ đó
int a[100];
+ a là 1 hằng con trỏ trỏ vào phần tử thứ 0 của mảng
+ các phép toán nhằm làm a trỏ tới vùng khác (thay đổi giá trị của a) là ko thể (++ -- = )
+ a tương đương với &a[0]
+ a+i tương đương với &a[i]
+ *a tương đương với a[0]
+ *(a+i) tương đương với a[i]
Chú ý : trình biên dịch luôn hiểu a[i] là *(a+i)
Biết điều này để làm gì ?
Mình demo 2 điều
1. nhập mảng
PHP Code:
#include <stdio.h>
#include <conio.h>
void main()
{
float a[100];
int n;
//nhập n
printf("Nhap n :");
scanf("%d",&n);
// nhập mảng
for(int i=0;i<n;i++)
{
printf("Nhap vao phan tu thu %d",i+1);
scanf("%f",a+i);
}
// xuất mảng
printf("mang vua nhap :
");
for(int i=0;i<n;i++)
printf("%f ",*(a+i));
getch();
}
2. bài toán vui
PHP Code:
#include <stdio.h>
#include <conio.h>
void main()
{
int a[100]={0,1,2,3,4,5,6};
printf("%d",2[a]); //in ra 2, tại sao vậy ?
getch();
}
chắc chắn lúc nhìn thấy 2[a] ko ít người sẽ thấy là lạ, nghĩ nó là lỗi,......
có người thì nghĩ là nó in ra 2, nhưng tại sao vậy, thì nhìu người
thật ra : 2[a] trình biên dịch sẽ hiểu là *(2+a)
*(2+a) hoàn toàn tương đương với *(a+2)
mà *(a+2) chính là a[2]
vậy 2[a] cũng đơn giản là a[2]
>>> cool phải hok nào
Ngoài 2 điều này ra còn nhiều cái thú vị lắm, bạn hãy thử khám phá xem sao
III. À, thế còn con trỏ hằng là cái gì thế ?
(đây là phần nâng cao)
con trỏ hằng là 1 optional ability trong lập trình, tác dụng của nó tựa như là (gần như thôi, ko thể bằng được) phương thức hằng trong C++;
ý nghĩa là 1 con trỏ, trỏ đến 1 ô nhớ, nhưng ko được quyền thay đổi giá trị của ô nhớ đó!!!!!!!!
PHP Code:
int a=3;
const int *p;
p=&a; <<<<<<<<<<<<< bản thân p thì có thể thay đổi, cho p gán vào chỗ khác được nhưng
(*p)++;<<<<<<<<<<< báo lỗi tại đây!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
đối với các bạn mới thì chắc ko hiểu hoặc có hiểu cũng nghĩ là úi giời ơi, biết làm quái gì!!! hì hì.....
Ví dụ điển hình nhất ở đây là hàm strlen của chúng ta
PHP Code:
int strlen(const char *Str)
Khi bạn code trong 1 project C lớn 1 tí hoặc lớn nhiều tí, hơ hơ, giả sử bạn có 1 hàm, thao tác với 1 mảng , hàm này chỉ đọc mảng thôi, ko làm thay đổi các giá trị trong mảng . Và quan trọng là, khi share code cho các bạn khác trong cùng project, làm sao để họ biết điều này ??????????
Vậy ta sẽ cài đặt hàm của mình như sau
demo ví dụ mẫu
PHP Code:
// đối với trường hợp hằng con trỏ là tham số hình thức thì
// void ham(const int *) và void ham(int const *)
// là như nhau, từ const khi đóng góp vào trong tham số hình thức là như nhau
void ham(const int *a,int n)
{
//xử lý gì đó
}
void main()
{
int a[100]={1,2},n=2;
ham(a,n); // khi sử dụng hàm này tôi hiểu là, à, nó ko thay đổi mảng a của tôi đâu
//yên tâm xài, nếu có lỗi gì đó thì ko phải sinh ra từ đây
}
IV. Thế còn xâu kí tự thì sao ?
+ Xâu kí tự là trường hợp đặc biệt của mảng 1 chiều khi mà cách thành phần của mảng là 1byte
+ Xâu kí tự kết thúc bằng NULL. NULL là 1 kí tự đặc biệt có mã là 0,
Có 3 cách viết NULL trong C như sau : NULL , '\0' , 0
A. Sai lầm thường gặp khi làm việc với xâu kí tự
đối với xâu kí tự thì các bạn phải nhớ được những trường hợp sau
1. Chưa có bộ nhớ đã sử dụng như đúng rồi >>>> sai lè ra
PHP Code:
char *xau;
gets(xau); // vẫn biên dịch được
//nhưng khi chạy sẽ sinh ra lỗi run-time
// ở 1 số trình biên dịch cùi bắp ngày xưa thì có thể ko bị lỗi đâu
// nhưng sai thì vẫn là sai, code này sai thuộc loại chưa cấp phát
2. Thay đổi giá trị của một hằng >>>>> sai lè ra tiếp
PHP Code:
char *xau="langman-congdongcviet";
xau[6]='A';// vẫn biên dịch được
//nhưng khi chạy sẽ sinh ra lỗi run-time
// lỗi này là lỗi cố tình thay đổi giá trị của 1 hằng
Nguyên nhân sâu xa của vấn đề như sau :
khi khai báo char *xau="langman-congdongcviet"; thì bản chất là
+ trong vùng nhớ data của chương trình sẽ có 1 hằng chuỗi "langman-congdongcviet" . <<<< là hằng chuỗi, đã là hằng thì ko thể bị thay đổi
+ cho con trỏ xau trỏ đến đầu của vùng nhớ đó.
Câu lệnh tiếp theo xau[6]='A'; cố tình thay đổi giá trị của hằng , rõ ràng là sinh ra lỗi rồi
3. Cố tình thay đổi giá trị của hằng con trỏ <<<<<<<< sai lè tiếp nữa
PHP Code:
char xau[100];
xau="bùi tấn quang"; // không biên dịch được
// vì phép toán trên có nghĩa là khai báo 1 chuỗi "bùi tấn quang" trong vùng nhớ code
// rồi sau đó cho hằng con trỏ xâu trỏ vào đó
// rất tiếc xau là hằng con trỏ nên ko thể trỏ đi đâu khác được
// ngoài vị trí đã được khởi tạo trong câu lệnh khai báo
chú ý char xau[100]="bùi tấn quang"; hoặc char xau[100]={0}; thì hoàn toàn hợp lệ
trích :
4. Dùng phép toán so sánh để so sánh nội dung 2 xâu <<<<<<<< sai lè tiếp nữa
C++ Code:
Lựa chọn code | Ẩn/Hiện code
void main()
{
char xau[100]="quangxeng";
if (xau=="quangxeng") ... // code này ko sai về ngữ pháp, ko sinh ra lỗi runtime
//nhưng mang lại kết quả ko như người dùng mong muốn
// vì theo mục b. ở trên ta có
//Phép so sánh ngang bằng dùng để kiểm tra 2 con trỏ có trỏ vào cùng 1 vùng nhớ hay không,
//hoặc kiểm tra 1 con trỏ có phải là đang trỏ vào NULL hay không
//(trong trường hợp cấp phát động, mở file, mở resource,........)
// chứ ko phải là phép so sánh nội dung của xâu
//để so sáng nội dung của xâu ta phải dùng những hàm strcmp (string compare) hoặc stricmp
// hoặc những hàm bạn tự định nghĩa
}
Phụ lục
B. Biết thêm 1 style duyệt xâu mới
Xem chap V, phần 3
mở rộng ứng dụng duyệt xâu để làm bài xâu sau : Nhập vào dạng "họ đệm tên", viết ra màn hình "Tên Đệm Họ"
PHP Code:
#include <stdio.h>
#include <conio.h>
void main()
{
char xau[100],*p=xau,*q,*i;
printf("Nhap : "),scanf("%[a-z ]",xau); // nhap vao "ho dem ten"
while(*p!=' ') p++;
q=xau+strlen(xau)-1;
while(*q!=' ') q--;
//viet hoa
*xau=toupper(*xau);
p[1]=toupper(1[p]);
q[1]=toupper(1[q]);
//viet
printf("Xuat :%s",q); //ten
for(i=p;i<=q;i++) printf("%c",*i); // dem
for(i=xau;i<p;i++) printf("%c",*i); // ho
getch();
}
V. Thế còn cái từ cấp phát động thì sao nhỉ? Nghe quen quá đi...
1. Bản chất của việc cấp phát động.
Đầu tiên để hiểu được cấp phát động, bạn hãy nghe lời tôi, tạm thời bỏ qua tất cả các lý thuyết, các câu lệnh, các code mà bạn biết, tạm thời chưa quan tâm đến nó vội, hãy đọc cho tôi bài viết này đã : http://forums.congdongcviet.com/showthread.php?t=36221 (rất cần thiết đấy)
(và làm ơn ko hỏi đáp, thắc mắc gì trên tất cả các topic mình hướng dẫn, cần đặt câu hỏi hãy qua box hỏi đáp lập, mình sẽ tận tình trả lời bạn bằng tất cả những gì mình biết)
2. cấp phát động như thế nào (cú pháp làm ơn xem sách giáo khoa nhé)
a. C
contro = (ép kiểu) malloc(...)
Trong C chúng ta cấp phát động chủ yếu sử dụng các hàm trong alloc.h
các bạn có thể tham khảo các hàm ở đây
http://forums.congdongcviet.com/showpost.php?p=30657
chú ý là :
+ malloc trả về 1 địa chỉ đến 1 vùng nhớ và coi vùng nhớ này là void *, nên trong câu lệnh malloc luôn đi kèm với việc ép kiểu
+cấp phát là luôn phải đi kèm với giải phóng, ở đâu cũng thế, malloc là phải free, ok ? Code mà để thoát chương trình rồi chưa giải phóng cho dù là có hệ thống có tự giải phóng đi nữa vẫn bị coi là bad!!!!
+Trong java chỉ cần cho reference = null là nó giải phóng nhưng trong C thì bắt buộc phải có thao tác giải phóng free()
b. C++
trong C++ chúng ta dùng new và delete để cấp phát động
new và delete về cú pháp tham khảo trong sách
Câu hỏi của quyết 1991 : sự khác nhau giữa malloc và new?
Trả lời :
new và malloc khác nhau cực cực kì nhiều đó các pạn à
sơ bộ như sau, chưa phân tích kĩ
malloc là hàm, cấp phát trả về kiểu void *, malloc thì ko gọi hàm tạo
free ko gọi hàm hủy
malloc trả về NULL nếu thất bại
new là toán tử, new gọi hàm tạo, new có thể được đa năng hóa (nạp chồng),
new ném ra exception nếu thất bại
toán tử new và toán tử new[] ko có khả năng realloc
VI. Mảng 2 chiều, bản chất như thế nào, khác gì mảng một chiều ?
cũng chả cần nói nhiều làm gì, chỉ cần bạn xem cái này là hiểu rồi
khi khai báo như trên ta có a trỏ vào a[0][0]
2. Sai lầm trong suy nghĩ
Có nhiều thật nhiều người nói rằng trong C, ta có thể sử dụng con trỏ trong tham số của hàm như là 1 tham biến, qua hàm ta có thể thay đổi được giá trị của tham số.
tôi xin khẳng định lại, điều này thật là 1 hiểu lầm, sai lầm trong suy nghĩ, 1 sự hiểu biết nông cạn, 1 câu phát biểu kiểu ù ù cạc cạc!!!
Nguyên nhân
+ Hàm trong C ko hề có tham biến, hàm trong C đều hoạt động theo nguyên tắc sau :
Khi gọi hàm, 1 bản sao của tham số được tạo ra (cấp phát vùng nhớ mới, copy giá trị sang. quá trình này theo giáo trình của đại học FPT gọi là shadow copy, là 1 yếu tố cần quan tầm, 1 C/C++ Developer đừng bao giờ quên điều này), và hàm sẽ làm việc với bản sao này
(trong C++ nó sẽ dùng hàm tạo sao chép để tiến hành quá trình shadow copy này)
+ Vậy khi làm việc với con trỏ thì hàm làm thế nào
vâng, hàm vẫn cứ làm theo nguyên tắc 1 và 1 bản sao của con trỏ được tạo ra, và hàm làm việc với bản sao hàm, và trước khi gọi hàm con trỏ trỏ vào đâu thì nó vẫn được trỏ vào đấy chứng minh :
C++ Code:
Lựa chọn code | Ẩn/Hiện code
#include <stdio.h>
#include <conio.h>
int ham(int *a)
{
*a=2;
a++;
}
void main()
{
int *a;
printf("Truoc : %x",a); //trước và sau khi gọi hàm
ham(a); //con trỏ a trỏ vào đâu
printf("Sau %x",a); // thì nó vẫn trỏ vào đó
getch();
}
+ Vậy tại sao lại có sự thay đổi và tại sao lại sử dụng con trỏ trong hàm? Con trỏ ko thay đổi thì cái gì thay đổi được ?
vâng, các bạn chú ý nhé, giá trị nằm trong vùng nhớ trỏ đến thay đổi. Vâng đúng thế đấy bạn à, do biến của ta nằm trong vùng nhớ được trỏ đến nên nó được thay đổi
C++ Code:
Lựa chọn code | Ẩn/Hiện code
#include <stdio.h>
#include <conio.h>
int ham(int *a)
{
*a=2; // làm việc với địa chỉ nhận được
}
void main()
{
int n;
ham(&n);// truyền địa chỉ của n vào đây
// do đó sau hàm này n =2
getch();
}
3. Sai lầm trong hành động
Một trong những sai lầm cơ bản nhưng lại hay gặp đó là ví dụ sau.
sai lầm vì trong hàm chúng ta cấp phát bộ nhớ rồi cho bản sao đang làm việc trỏ đến. ra khỏi hàm rồi thì x của ta vẫn chưa có trỏ vào bộ nhớ nào cả
C++ Code:
Lựa chọn code | Ẩn/Hiện code
#include <stdio.h>
#include <conio.h>
void nhap(int *a,int n)
{
//a=new int[n]; //sai lầm
a=(int*)malloc(n * sizeof(int)); //sai lầm
for(int i=0;i<n;i++)
cin>>a[i];
}
void main()
{
int *x;
int n=6;
nhap(x,n);
//xuat
delete[] x; // sản sinh ra lỗi run-time , tung là 1 exception, do x chưa trỏ vào đâu mà đòi giải phóng
}
VI. Vậy tôi phải làm thế nào để mà thay đổi giá trị của 1 con trỏ qua 1 hàm
Vâng, hôm nay có người bạn hỏi mình như vậy, hì hì, lại nhớ ra bài này mình chưa trả lời, vậy nên tôi đề xuất ra đây 2 cách để có thể thay đổi giá trị của 1 con trỏ qua 1 hàm
Cách 1 : dùng tham chiếu trong C++
C++ Code:
Lựa chọn code | Ẩn/Hiện code
void ham(int *&a)
{
a=new int[100];
}
void ham(int **&a)
{
a=new int*[100];
}
xin chú ý là * đứng trước &
Cách 2 : up level của * dùng con trỏ cấp cao hơn con trỏ hiện tại
Cách 2 này mình chỉ demo thôi, bạn cần phải đọc chi tiếp ở chap con trỏ đa cấp
C++ Code:
Lựa chọn code | Ẩn/Hiện code
#include <stdio.h>
#include <conio.h>
void ham(int **a)
{
*a=(int*)malloc(100*sizeof(int));
//a[0]=(int*)malloc(100*sizeof(int));
// 2 cach nay nhu nay
}
void main()
{
int *a;
ham(&a);
free(a);
}
VII. Nâng cao về con trỏ hàm,mảng con trỏ hàm và kĩ năng phân tích vấn đề
mới các bạn đọc 3 bài viết sau
Giải thích ý nghĩa của dòng lệnh khai báo int(*) : http://forums.congdongcviet.com/showthread.php?t=49779
Kĩ năng phân tích vấn đề : http://forums.congdongcviet.com/showthread.php?p=117404
So sánh (*ptr)[10] và *ptr[10] trong C! : http://forums.congdongcviet.com/showthread.php?t=34085
Hỏi đáp :
Trích dẫn:
Nguyên bản được gửi bởi ddatduong
a ơi, có thể giải thích dùm e cái lệnh này là như nào ko?
C Code:
Lựa chọn code | Ẩn/Hiện code
void (*p)(int);
p=(void (*)(int)) 0x873AB;//<< lệnh này ạ
C Code:
Lựa chọn code | Ẩn/Hiện code
void (*p)(int); // khai báo ra 1 con trỏ hàm
// hàm này có dạng [COLOR="Red"]void ham(int a)[/COLOR]
// vậy thì con trỏ để trỏ tới hàm này phải có dạng [COLOR="Red"]void (*)(int)[/COLOR]
// ở đây ta có thể hiểu cả cụm đấy là kiểu dữ liệu
p=(void (*)(int)) 0x873AB;
// 0x873AB là địa chỉ của 1 hàm nào đó mà ta qua quá trìng debug phát hiện ra nó nằm tại địa chỉ trên
// nó là 1 số nguyên nên ta ép kiểu để nó về đúng kiểu dữ liệu với p
//câu lệnh này có ý nghĩa là cho p trỏ vào đầu hàm đó,
//sau khi trỏ rồi, nếu ta p(2) chính là gọi hàm với tham số thực là 2
Bạn đang đọc truyện trên: AzTruyen.Top