Tham số kiểu con trỏ

Một phần của tài liệu Giáo trình tin học đại cương (Trang 73 - 79)

CHƯƠNG 4 HÀM VÀ TỔ CHỨC CHƯƠNG TRÌNH

4.2 Tham số kiểu con trỏ

Trong phần này, chúng tôi giới thiệu sơ lược về con trỏ trong C và ứng dụng trong truyền tham số cho hàm.

4.2.1 ðịa ch

Liờn quan ủến một biến ta ủó cú khỏi niệm:

- Tên biến - Kiểu biến - Giá trị của biến Ví dụ câu lệnh

float alpha = 30.5;

xỏc ủịnh một biến cú tờn là alpha cú kiểu float và cú giỏ trị 30.5. Ta cũng ủó biết, theo khai báo trên, máy sẽ cấp phát cho biến alpha một khoảng nhớ gồm 4 byte liên tiếp.

ðịa chỉ của biến là số thứ tự của byte ủầu tiờn của vựng nhớ của biến trong bộ nhớ.

Một ủiều cần lưu ý là mặc dự ủịa chỉ của biến là một số nguyờn nhưng khụng ủược ủỏnh ủồng nú với cỏc số nguyờn thụng thường dựng trong cỏc phộp tớnh.

ðể lấy ủịa chỉ của một biến, dựng phộp toỏn &. Vớ dụ ủể lấy ủịa chỉ của biến alpha.

&alpha

4.2.2 Con tr

Con trỏ là biến dựng ủể chứa ủịa chỉ, cú nghĩa là giỏ trị của con trỏ là ủịa chỉ của một biến nào ủú.

Vỡ cú nhiều loại ủịa chỉ nờn cũng cú nhiều kiểu con trỏ tương ứng. Con trỏ kiểu int dựng ủể chứa ủịa chỉ cỏc biến kiểu int. Tương tự, ta cú con trỏ kiểu float, kiểu double,... . Cũng như ủối với bất kỳ một biến nào khỏc, một con trỏ cần khai bỏo trước khi sử dụng.

Việc khai bỏo biến con trỏ ủược thực hiện theo mẫu sau:

<kiểu> *<tên con trỏ>;

Ví dụ câu lệnh sau khai báo hai biến kiểu int là x, y và hai con trỏ kiểu int là px và py.

int x, y, *px, *py;

Tương tự câu lệnh sau khai báo các con trỏ kiểu float.

float f, *pf;

Khi ủó cú cỏc khai bỏo trờn thỡ cỏc cõu lệnh sau hoàn toàn xỏc ủịnh.

px = &x;

py = &y;

Cõu lệnh thứ nhất sẽ gỏn ủịa chỉ của x cho con trỏ px và cõu lệnh thứ hai sẽ gỏn ủịa chỉ của biến y cho con trỏ py.

Khi con trỏ px chứa ủịa chỉ của biến x thỡ ta núi px trỏ tới x.

Chỳng ta khụng ủược gỏn ủịa chỉ của một biến nguyờn cho một con trỏ kiểu thực. Vớ dụ câu lệnh sau là không hợp lệ.

pf = &y;

http://www.ebook.edu.vn 74

4.2.3 Qui tc s dng con tr trong các biu thc

Với một biến, cú hai ủại lượng liờn quan, ủú là giỏ trị của biến và ủịa chỉ của biến. Khi một con trỏ trỏ tới một biến, khi ủú cũng cú hai ủại lượng liờn quan, ủú là giỏ trị con trỏ (chớnh là ủịa chỉ của biến) và giỏ trị của biến mà con trỏ ủang trỏ tới.

Trong biểu thức, tờn con trỏ ủại diện cho giỏ trị của con trỏ và dạng khai bỏo ủại diện cho giỏ trị biến mà con trỏ ủang trỏ tới.

S dng tên con tr

Con trỏ cũng là một biến nên khi tên của nó xuất hiện trọng một biểu thức thì giá trị của nú sẽ ủược sử dụng trong biểu thức này. Khi tờn con trỏ ủứng ở bờn trỏi của một toỏn tử gỏn thỡ giỏ trị của biểu thức bờn phải (giỏ trị này phải là ủịa chỉ) ủược gỏn cho con trỏ.

Ta hãy xem các câu lệnh sau làm gì ?

float a, *p, *q;

p = &a;

q = p;

Câu lệnh thứ nhất khai báo một biến kiểu float (biến a) và hai con trỏ p và q kiểu float.

Cõu lệnh thứ hai sẽ gỏn ủịa chỉ của biến a cho con trỏ p và cõu lệnh thứ ba sẽ gỏn giỏ trị của p cho q. Kột quả là con trỏ q chứa ủịa chỉ của biến a.

S dng dng khai báo ca con tr Xột ủoạn lệnh sau:

float x, y, *px, *py;

x = 100.0;

y = 215.3;

px = &x;

py = &y;

Sau cỏc lệnh gỏn thỡ px trỏ tới x, py trỏ tới y. Khi ủú biến mà px trỏ tới (biến x) ủược biểu diễn dưới dạng *px.

Nói một cách khác, nếu con trỏ px trỏ tới biến x thì các cách viết x và *px

là tương ủương trong mọi ngữ cảnh.

Theo nguyờn lý này thỡ ba cõu lệnh sau ủều cú hiệu lực như nhau

y = 3*x + 1;

*py = 3*x + 1;

*py = 3*(*px) + 1;

Từ ủõy cú thể rỳt ra một kết luận quan trong là: Khi biết ủược ủịa chỉ của một biến thỡ chẳng những chúng ta có thể sử dụng giá trị của nó mà còn có thể gán cho nó một giá trị mới (làm thay ủổi nội dung của nú). ðiều này sẽ ủược ỏp dụng như một phương phỏp chủ yếu ủể nhận kết quả của hàm thụng qua ủối.

4.2.4 Mng và con tr

Khi khai bỏo một mảng, mỏy cấp phỏt một vựng nhớ dựng ủể lưu giỏ trị cỏc phần tử của mảng.

Ví dụ khai báo một mảng số nguyên:

int a[100];

http://www.ebook.edu.vn 75

Khi ủú mỏy sẽ cấp phỏt 100 ụ nhớ, mỗi ụ nhớ cú kớch thước của một số nguyờn tương ứng với một phần tử, và cỏc phần tử sẽ sắp xếp liờn tiếp nhau trong bộ nhớ, từ phần tử ủầu tiên.

Trong C, tờn mảng chớnh là ủịa chỉ của phần tử ủầu tiờn của mảng, ủiều cú nghĩa là:

a bằng &a[0]

Vỡ tờn mảng là một ủịa chỉ, nờn ta cú thể gỏn cho một con trỏ. Vớ dụ:

int *p;

p = a;

Như vậy giữa mảng con trỏ cú sự tương ủồng. Do ủú, trong C cho phộp sử dụng thay thế giữa mảng và con trỏ trong một số trường hợp.

Vớ dụ, sau khi ủó gỏn p bằng a thỡ:

a[4] và p[4] ủều biểu diễn phần tử thứ 5 của mảng.

a[0], *a và *p cựng biểu diễn phần tử ủầu tiờn của mảng.

Tuy nhiên giữa mảng và con trỏ có sự khác nhau. Con trỏ là một biến, vì thế giá trị của nú cú thể thay ủổi ủược và ta cú thể gỏn giỏ trị cho một con trỏ. Trong khi ủú tờn mảng là một hằng ủịa chỉ, vỡ khi mảng ủược cấp phỏt thỡ ủịa chỉ của phần tử ủầu tiờn là xỏc ủịnh. Vỡ vậy khụng thể thay ủổi ủược giỏ trị của tờn mảng.

4.2.5 Hàm cú ủối con tr

ðối con trỏ thường ủược sử dụng trong cỏc trường hợp sau ủõy:

- Sử dụng hàm ủể thay ủổi giỏ trị của một biến.

- Truyền một mảng vào cho hàm.

- Trả về nhiều kết quả.

S dng hàm ủể thay ủổi giỏ tr ca mt biến

Như chỳng ta ủó biết, khi truyền tham số thực cho hàm, thỡ chỉ cú giỏ trị của cỏc biểu thức là ủược sử dụng. Nếu chỳng ta thay ủổi giỏ trị của ủối số bờn trong hàm, thỡ sự thay ủổi ủú khụng ảnh hưởng ủến giỏ trị cỏc biến truyền vào cho hàm.

Vỡ vậy, nếu muốn dựng hàm ủể thay ủổi giỏ trị của một biến, thỡ phải truyền ủịa chỉ của biến thay vì giá trị của biến.

ðể khai bỏo một tham số là ủịa chỉ của một biến, ta khai bỏo tham số ủú là một con trỏ cú kiểu tương ứng. Vớ dụ sau ủịnh nghĩa một hàm cho phộp ủảo giỏ trị của hai biến.

#include <stdio.h>

void hoan_vi(float *px, float *py) {

float z;

z = *px;

*px = *py;

*py = z;

}

void main() {

float a = 7.6, b = 13.5;

hoan_vi(&a, &b);

printf("\na = %0.2f b = %0.2f", a, b);

}

http://www.ebook.edu.vn 76

Kết quả thực hiện chương trình

a = 13.50 b = 7.60

Ta hóy xem hàm hoỏn_vị làm việc thế nào. Như ủó biết, chương trỡnh bắt ủầu từ cõu lệnh ủầu tiờn trong hàm main(). Kết qủa là biến a nhận giỏ trị 7.6 và biến b nhận giỏ trị 13.5. Tiếp ủú là lời gọi hàm hoan_vi. Mỏy sẽ gỏn giỏ trị của cỏc tham số thực cho ủối tương ứng. Như vậy ủịa chỉ của a ủược gỏn cho con trỏ px , ủịa chỉ của b ủược gỏn cho con trỏ py. Sau ủú mỏy lần lượt xột ủến cỏc cõu lệnh trong thõn hàm. Cõu lệnh thứ nhất sẽ cấp phỏt cho biến cục bộ z một khoảng nhớ 4 byte. Theo qui tắc về sử dụng con trỏ nêu trong mục 4.2.3 thì ba câu lệnh tiếp theo tương ủương với cỏc cõu lệnh:

z = a;

a = b;

b = z;

Như vậy a sẽ nhận giỏ trị của b và ngược lại. Tiếp ủú, mỏy trở về hàm main() và in ra những dũng kết qủa như ủó chỉ ra ở trờn.

Truyn mt mng mt chiu vào cho hàm

Khi muốn truyền một mảng vào cho một hàm, ví dụ như hàm in các số hạng của một dãy số, khi ủú ta dựng ủối con trỏ.

Do mảng là ủịa chỉ của phần tử ủầu nờn kiểu của ủối là kiểu con trỏ của kiểu phần tử mảng. Vớ dụ, với mảng cỏc phần tử int thỡ kiểu ủối là con trỏ kiểu int.

Khi truyền mảng vào cho hàm, bên trong hàm không rõ số lượng phần tử của mảng, do ủú cần thờm ủối chỉ ra số lượng phần tử của mảng.

Vớ dụ sau ủịnh nghĩa hai hàm, một hàm nhập dữ liệu cho một dóy số và một hàm in dóy số ủú ra màn hỡnh.

#include <stdio.h>

// Nguyen mau ham

void NhapDaySo(int *a, int n);

void InDaySo(int *a, int n);

void main() { int a[100];

int n;

printf("Nhap so phan tu: ");

scanf("%d", &n);

// Nhap day so NhapDaySo(a, n);

printf("Day so vua nhap:\n");

// In day so InDaySo(a, n);

}

void NhapDaySo(int *a, int n) {

int i;

for (i = 0; i < n; i++) {

printf("a[%d] = ", i + 1);

scanf("%d", &a[i]);

} }

http://www.ebook.edu.vn 77

void InDaySo(int *a, int n) {

int i;

for (i = 0; i < n; i++) printf("%8d", a[i]);

}

Trong vớ dụ trờn, cỏc tham số của hai hàm NhapDaySo và InDaySo ủược khai bỏo giống nhau, bao gồm một tham số kiểu con trỏ nguyên và một tham số nguyên.

Tuy nhiên, trong hai trường hợp ý nghĩa tham số là khác nhau.

- Tham số a trong hàm NhapDaySo là kết quả ủưa ra.

- Tham số a trong hàm InDaySo là dữ liệu ủầu vào.

Về mặt cỳ phỏp, chỳng ta khụng phõn biệt ủược sự khỏc nhau ở trờn, mà chỉ phõn biệt ủược theo mục ủớch sử dụng.

Vỡ vậy, khi truyền một mảng vào cho hàm mà trong hàm khụng cú nhu cầu thay ủổi giỏ trị của các phần tử của mảng, thì có thể thêm từ khoá const khi khai báo tham số. Ví dụ, hàm InDaySo cú thể ủược ủịnh nghĩa như sau:

void InDaySo(const int *a, int n) {

int i;

for (i = 0; i < n; i++) printf("%8d", a[i]);

}

Khi ủú thỡ cú thể xỏc ủịnh ngay tham số a là dữ liệu vào cho hàm.

Truyn mt mng nhiu chiu vào cho hàm

Trong một số trường hợp, chúng ta phải truyền một mảng nhiều chiều vào cho hàm, ví dụ như truyền một ma trận vào hàm in ma trận.

Khi khai báo một tham số là một mảng nhiều chiều, cần phải chỉ ra số phần tử tương ứng của cỏc chiều, trừ chiều ủầu tiờn là khụng cần thiết phải chỉ ra.

Ví dụ sau nhập in một ma trận thực theo dạng bảng, sử dụng cả hai cú pháp khai báo là mảng và con trỏ.

#include <stdio.h>

// Nguyen mau ham

void NhapMaTran(float a[20][20], int, int);

// Su dung dang khai bao mang

void InMaTran(float a[20][20], int, int);

void main() {

float a[20][20];

int m, n;

printf("Nhap so hang, so cot: ");

scanf("%d%d", &m, &n);

// Nhap ma tran NhapMaTran(a, m, n);

// In ma tran InMaTran(a, 3, 3);

}

http://www.ebook.edu.vn 78

void NhapMaTran(float a[20][20], int m, int n) {

int i, j;

float f;

for (i = 0; i < m; i++) for (j = 0; j < n; j++) {

printf("a[%d][%d] = ", i + 1, j + 1);

scanf("%f", &f);

a[i][j] = f;

} }

void InMaTran(float a[20][20], int m, int n) {

int i, j;

for (i = 0; i < m; i++) {

for (j = 0; j < n; j++) printf("%8.2f", a[i][j]);

printf("\n");

} }

Chỳ ý: một số trỡnh dịch khụng cho phộp lấy ủịa chỉ của một phần tử mảng nhiều chiều, do ủú chỳng ta phải dựng một biến trung gian khi nhập giỏ trị cỏc phần tử mảng, như trong hàm NhapMaTran ở trên.

Tr v nhiu kết qu

Một trường hợp mà ủối là con trỏ thường ủược sử dụng ủú là khi chỳng ta muốn hàm trả về nhiều giỏ trị. Như chỳng ta ủó biết, hàm chỉ cú thể trả về một giỏ trị dưới dạng tờn hàm, do ủú ủể trả về nhiều giỏ trị chỳng ta phải sử dụng tham số là con trỏ.

Vớ dụ: khi viết một hàm giải phương trỡnh bậc hai, thỡ chỳng ta cần phải trả về cỏc ủại lượng:

- Hàm ủú cú bao nhiờu nghiệm (0, 1, 2).

- Nếu có nghiệm, thì giá trị của nghiệm là bao nhiêu.

Như vậy ở ủõy chỳng ta phải trả về ba ủại lượng: số nghiệm, một hoặc hai nghiệm (trong trường hợp có nghiệm).

Ví dụ sau thể hiện cách chúng ta trả về nhiều kết quả.

/* Chuong trinh giai phuong trinh bac hai */

#include <stdio.h>

#include <math.h>

// Nguyen mau ham

int GiaiPTBac2(float, float, float, float*, float*);

void main() {

float a, b, c, x1, x2;

int songhiem;

printf("Nhap a, b, c: ");

scanf("%f%f%f", &a, &b, &c);

// Giai phuong trinh

http://www.ebook.edu.vn 79

songhiem = GiaiPTBac2(a, b, c, &x1, &x2);

if (songhiem == 0)

printf("Phuong trinh vo nghiem.");

else if (songhiem == 1)

printf("Nghiem kep: %.3f", x1);

else

printf("x1: %.3f\nx2: %.3f", x1, x2);

}

int GiaiPTBac2(float a, float b, float c, float *px1, float *px2) {

float delta;

delta = b*b - 4*a*c;

if (delta < 0) return 0;

else if (delta == 0) {

*px1 = -b/(2*a);

return 1;

} else {

*px1 = (-b - sqrt(delta))/(2*a);

*px2 = (-b + sqrt(delta))/(2*a);

return 2;

} }

Một phần của tài liệu Giáo trình tin học đại cương (Trang 73 - 79)

Tải bản đầy đủ (PDF)

(96 trang)