TIN HỌC ĐẠI CƯƠNG TIN HỌC ĐẠI CƯƠNG
PHẦN 2: LẬP TRÌNH BẰNG NGÔN NGỮ C PHẦN 2: LẬP TRÌNH BẰNG NGÔN NGỮ C
BÀI 5: HÀM (FUNCTION) BÀI 5: HÀM (FUNCTION)
Nguyễn HữuHữu Nam Nam Dương Nguyễn Dương fit@mail.hut.edu.vn Email: duongnnhn--fit@mail.hut.edu.vn Email: duongnnhn
11
HÀM (FUNTION) BÀI 5: HÀM (FUNTION) BÀI 5:
5.1. Giới thiệu về hàm (function) 5.2. Khai báo và sử dụng hàm 5.3. Phạm vi biến 5.4. Nguyên mẫu hàm
22
1
5.1. Giới thiệu về hàm 5.1. Giới thiệu về hàm
để khi cần chỉ cần hàm (function) để khi cần chỉ cần
Viết thành
Một trong những tư tưởng chính của lập Một trong những tư tưởng chính của lập trình cấu trúc: trình cấu trúc: Chia nhỏ một chương trình thành các chương Chia nhỏ một chương trình thành các chương trình con đảm nhận những công việc nhỏ. trình con đảm nhận những công việc nhỏ. Viết thành hàm (function) gọi ra chứ không phải viết lại toàn bộ. gọi ra chứ không phải viết lại toàn bộ. Ví dụ: Ví dụ:
sin(x), pow(x,y), sqrt(x),......
33
Các hàm toán học: Các hàm toán học: sin(x), pow(x,y), sqrt(x), Các hàm do người dùng viết ra. Các hàm do người dùng viết ra.
Giới thiệu về hàm(tiếp) Giới thiệu về hàm (tiếp)
Đầu vào của hàm: Đầu vào của hàm: Các tham số Các tham số Hàm có thể có nhận vào các tham số đầu Hàm có thể có nhận vào các tham số đầu vào hoặc không. vào hoặc không. Đầu ra của hàm: Đầu ra của hàm: Giá trị của hàm Giá trị của hàm Sự thay đổi giá trị của các tham số. Sự thay đổi giá trị của các tham số. Hàm có thể trả về giá trị hoặc không trả về Hàm có thể trả về giá trị hoặc không trả về giá trị giá trị
44
2
BÀI 5: HÀMHÀM BÀI 5:
5.1. Giới thiệu về hàm (function) 5.2. Khai báo và sử dụng hàm 5.3. Phạm vi biến 5.4. Nguyên mẫu hàm
55
5.2.1. Khai báo hàm (tiếp)
Tên hàm Tên hàm Có thể là bất kì một định danh hợp Có thể là bất kì một định danh hợp lệ nào. lệ nào. Thường mang nghĩa gợi ý chức Thường mang nghĩa gợi ý chức năng công việc mà hàm thực hiện. năng công việc mà hàm thực hiện. Các hàm không được đặt tên trùng Các hàm không được đặt tên trùng nhau. nhau.
66
3
5.2.1. Khai báo hàm (tiếp)
Kiểu dữ liệu trả về của hàm: Kiểu dữ liệu trả về của hàm: Kiểu dữ liệu của giá trị tính toán mà Kiểu dữ liệu của giá trị tính toán mà hàm trả về sau khi thực hiện. hàm trả về sau khi thực hiện. Hàm có thể có giá trị trả về hoặc không Hàm có thể có giá trị trả về hoặc không có giá trị trả về. có giá trị trả về.
Nếu có giá trị trả về, trong thân hàm có ít Nếu có giá trị trả về, trong thân hàm có ít return.. nhất một lệnh return nhất một lệnh
Nếu không có giá trị trả về cần khai báo Nếu không có giá trị trả về cần khai báo void.. cho hàm đó có kiểu trả về là void cho hàm đó có kiểu trả về là
77
5.2.1. Khai báo hàm (tiếp)
ThamTham sốsố củacủa hàmhàm:: ThamTham sốsố chứa
chứa dữdữ liệuliệu vàovào cung
cung cấpcấp
chocho hàmhàm. (. (đầuđầu vàovào))
tínhtính toán
không cócó thamtham sốsố, ,
chứa dữdữ liệuliệu rara màmà hàmhàm ThamTham sốsố chứa được.. toán được MộtMột hàmhàm cócó thểthể không nhiều thamtham sốsố..
hoặc cócó nhiều hoặc
88
4
5.2.1. Khai báo hàm Cách khai báo và sử dụng Cách khai báo và sử dụng
Cách 1: Cách 1: Khai báo hàm Khai báo hàm Viết mã lệnh hoàn chỉnh cho hàm. Viết mã lệnh hoàn chỉnh cho hàm. Sử dụng hàm. Sử dụng hàm.
99
5.2.1. Khai báo hàm
Cách 2: Cách 2: Khai báo hàm nguyên mẫu Khai báo hàm nguyên mẫu Sử dụng các hàm đã khai báo Sử dụng các hàm đã khai báo Viết mã lệnh hoàn chỉnh cho các Viết mã lệnh hoàn chỉnh cho các hàm nguyên mẫu đã khai báo. hàm nguyên mẫu đã khai báo.
1010
5
5.2.1. Khai báo hàm
Cách 1: Cách 1:
Cú pháp: Cú pháp:
kieu_gia_tri_tra_ve kieu_gia_tri_tra_ve
ten_ham(danh_sach_tham_so) ten_ham(danh_sach_tham_so)
{{
//Than ham` bao gồm: //Than ham` bao gồm: //Các khai bao biến //Các khai bao biến ...... //Các câu lệnh //Các câu lệnh
}}
1111
5.2.1. Khai báo hàm (tiếp)
Ví dụ: Ví dụ:
Dong dau ham
int giai_thua(int a) {
Cac khai bao
int ket_qua; int i;
ket_qua = 1;
for(i = 1;i
ket_qua = ket_qua * i;
Cac cau lenh
if(a < 0) ket_qua = -1;
if(a == 0) ket_qua = 1;
return ket_qua;
}
1212
6
Ví dụ
#include
#include
#include
#include
int max(int x, int y, int z)
int max(int x, int y, int z)
{{
int max1;
int max1;
max1 = x>y?x:y;
max1 = x>y?x:y;
max1 = max1>z?max1:z;
max1 = max1>z?max1:z;
return max1;// max(x,y,z) = max1;
return max1;// max(x,y,z) = max1;
}}
1313
void main()
void main()
{{
a,b,c; ; clrscr
clrscr();();
Nhap giagia tri tri chocho 3 so 3 so nguyen nguyen a, a,
("%d %d %d",&a,&b,&c d",&a,&b,&c););
: ");
nhap: ");
intint a,b,c
printf("("\\n n Nhap
printf
b, c: ");
b, c: ");
scanf
scanf("%d %d %
printf("("\\n n GiaGia tri
printf
printf(" a = %
printf (" a = %--5d b = % tri caccac so so vuavua nhap
5d b = %--5d c = % 5d");
5d c = %--5d");
tri lonlon nhat nhat trong 3 so la
trong 3 so la d",max((a,b,c a,b,c));));
printf("("\\n n GiaGia tri
printf
%%d",max
getch();();
getch
1414
}}
7
5.2.1. Khai báo hàm (tiếp)
Lệnh return
Lệnh return
Cú pháp:
Cú pháp:
return bieu_thuc;
return bieu_thuc;
bieu_thuc Kết thúc việc thực
Kết thúc việc thực
Khi gặp lệnh này, chương trình sẽ tính toán
Khi gặp lệnh này, chương trình sẽ tính toán
bieu_thuc, lấy kết quả tính toán
giá trị của bieu_thuc
giá trị của
, lấy kết quả tính toán
được làm giá trị trả về cho lời gọi hàm rồi kết
được làm giá trị trả về cho lời gọi hàm rồi kết
thúc việc thực hiện hàm, trở về chương trình
thúc việc thực hiện hàm, trở về chương trình
đã gọi nó.
đã gọi nó.
Nếu thiếu bieu_thuc
Nếu thiếu
hiện hàm mà không trả về giá trị nào cả.
hiện hàm mà không trả về giá trị nào cả.
1515
5.2.2. Sử dụng hàm
5.2.2. Sử dụng hàm
Sử dụng hàm hay gọi hàm Cú pháp:
Sử dụng hàm hay gọi hàm
Cú pháp:
Ten_ham([danh_sach_tham_so])
Ten_ham([danh_sach_tham_so])
Tham số được cung cấp cho hàm trong
Tham số được cung cấp cho hàm trong
quá trình thực hiện được gọi là tham số
quá trình thực hiện được gọi là tham số
thực.
thực.
Kiểu dữ liệu của tham số hình thức và
Kiểu dữ liệu của tham số hình thức và
tham số thực phải giống nhau.
tham số thực phải giống nhau.
1616
8
Quá trình thực hiện một hàm
Quá trình thực hiện một hàm
Khi gặp lệnh
Khi hàm được gọi, quá trình diễn ra như sau:
Khi hàm được gọi, quá trình diễn ra như sau:
Nếu hàm có tham số, trước tiên các tham số sẽ được
Nếu hàm có tham số, trước tiên các tham số sẽ được
gán giá trị thực tương ứng
gán giá trị thực tương ứng. .
Chương trình sẽ thực hiện tiếp các câu lệnh trong
Chương trình sẽ thực hiện tiếp các câu lệnh trong
thân hàm bắt đầu từ lệnh đầu tiên đến câu lệnh cuối
thân hàm bắt đầu từ lệnh đầu tiên đến câu lệnh cuối
cùng.
cùng.
Khi gặp lệnh return
thân hàm, chương trình sẽ thoát khỏi hàm để trở về
thân hàm, chương trình sẽ thoát khỏi hàm để trở về
chương trình gọi nó và thực hiện tiếp tục những câu
chương trình gọi nó và thực hiện tiếp tục những câu
lệnh của chương trình này.
lệnh của chương trình này.
1717
hoặc dấu } } cuối cùng trong
cuối cùng trong return hoặc dấu
BÀI 5: HÀMHÀM
BÀI 5:
5.1. Khái niệm hàm
5.2. Khai báo và sử dụng hàm
5.3. Phạm vi biến
5.4. Nguyên mẫu hàm
1818
9
5.3. Phạm vi biến
5.3. Phạm vi biến
Biến địa phương (Local Variable):
Biến địa phương (Local Variable):
Là các biến được khai báo trong lệnh khối
Là các biến được khai báo trong lệnh khối
hoặc trong thân chương trình con.
hoặc trong thân chương trình con.
Biến toàn cục (Global Variable):
Biến toàn cục (Global Variable):
Vị trí khai báo của biến toàn cục là sau phần
Vị trí khai báo của biến toàn cục là sau phần
khai báo tệp tiêu đề và khai báo hàm nguyên
khai báo tệp tiêu đề và khai báo hàm nguyên
mẫu mẫu
1919
5.3. Phạm vi biến (tiếp)
5.3. Phạm vi biến (tiếp)
stdio.h>>
VíVí dụdụ 1:1:
#include <
#include
{{
d”,a););
intint a = 1;
a = 1;
printf(“(“\\n a1 = %
printf
n a1 = %d”,a
{ { intint a = 2;
a = 2;
printf(“(“\\n a2 = %
printf
n a2 = %d”,a
d”,a););
}}
printf(“(“\\n a3 = %
printf
n a3 = %d”,a
d”,a););
}}
{ {
a = 3;
intint a = 3;
printf(“(“\\n a4 = %
printf
n a4 = %d”,a
d”,a););
}}
}}
2020
10
Ví dụ 2
#include
#include
#include
#include
int a, b, c;
int a, b, c;
int tich()
int tich()
{{
5d b = %--5d c = %
5d c = %--
printf("
n Gia tri cac bien tong the
printf("\\n Gia tri cac bien tong the
a, b, c: ");
a, b, c: ");
printf(" a = %--5d b = %
printf(" a = %
5d“,a,b,c);
5d“,a,b,c);
return a*b*c;
return a*b*c;
}}
2121
void main()
void main()
{{
clrscr();
clrscr();
printf("
n Nhap gia tri cho 3
printf("\\n Nhap gia tri cho 3
so nguyen a, b, c: ");
so nguyen a, b, c: ");
scanf("%d %d %d",&a,&b,&c);
scanf("%d %d %d",&a,&b,&c);
n Tich cua 3 so la
printf("\\n Tich cua 3 so la
printf("
%d",tich());
%d",tich());
getch();
getch();
}}
2222
11
2323
Biến register
Biến register
register trước khai
trước khai
register int a;
register int a;
2424
Thanh ghi có tốc độ truy nhập nhanh hơn so với các loại
Thanh ghi có tốc độ truy nhập nhanh hơn so với các loại
bộ nhớ khác (RAM, bộ nhớ ngoài).
bộ nhớ khác (RAM, bộ nhớ ngoài).
NNếu một biến thường xuyên sử dụng được lưu vào trong
ếu một biến thường xuyên sử dụng được lưu vào trong
thanh ghi thì tốc độ thực hiện của chương trình sẽ được
thanh ghi thì tốc độ thực hiện của chương trình sẽ được
tăng lên.
tăng lên.
Để làm điều này ta đặt từ khóa register
Để làm điều này ta đặt từ khóa
báo của biến đó.
báo của biến đó.
Ví dụ:
Ví dụ:
Số lượng và kích thước các thanh ghi có hạn Số Số
Số lượng và kích thước các thanh ghi có hạn
register sẽ không nhiều và
lượng biến khai báo register
sẽ không nhiều và
lượng biến khai báo
thường chỉ áp dụng với những biến có kích thước nhỏ
thường chỉ áp dụng với những biến có kích thước nhỏ
như kiểu char
như kiểu char, , intint. .
12
Biến static
Biến static
static int a;
Ví dụ: static int a;
static là biến tĩnh, nghĩa là nó sẽ được
là biến tĩnh, nghĩa là nó sẽ được
Một biến cục bộ khi ra khỏi phạm vi của biến đó
Một biến cục bộ khi ra khỏi phạm vi của biến đó
thì bộ nhớ dành để lưu trữ biến đó sẽ được giải
thì bộ nhớ dành để lưu trữ biến đó sẽ được giải
phóng.
phóng.
Nếu cần lưu giá trị của các biến cục bộ này, cần
Nếu cần lưu giá trị của các biến cục bộ này, cần
static..
khai báo biến với từ khóa static
khai báo biến với từ khóa
Ví dụ:
Biến Biến static
cấp phát một vùng nhớ thường xuyên từ lúc khai
cấp phát một vùng nhớ thường xuyên từ lúc khai
báo và chỉ giải phóng khi chương trình chính kết
báo và chỉ giải phóng khi chương trình chính kết
thúc.
thúc.
2525
VíVí dụdụ
stdio.h>>
conio.h>>
# include <
# include
n Day la lanlan goigoi ham ham fctfct lanlan thuthu static
count = 1;
static intint count = 1;
printf("("\\n Day la
printf
%2d", count++);
%2d", count++);
}}
void main()
void main()
{{
2626
int i;
int i;
for(i = 0; i < 10; i++)
for(i = 0; i < 10; i++) fct();
fct(); getch();
getch(); }}
13
BÀI 5: HÀMHÀM
BÀI 5:
5.1. Khái niệm hàm
5.2. Khai báo và sử dụng hàm
5.3. Phạm vi biến
5.4. Nguyên mẫu hàm
2727
Nguyên mẫu hàm –– Ví dụVí dụ
Nguyên mẫu hàm
#include
#include
#include
#include
int max(int, int, int);
int max(int, int, int);
// khai bao nguyen mau ham
// khai bao nguyen mau ham
void main()
void main()
{{
5d");
5d c = %--5d");
int a,b,c;
int a,b,c;
clrscr();
clrscr();
printf("
n Nhap gia tri cho 3 so nguyen a, b, c: ");
printf("\\n Nhap gia tri cho 3 so nguyen a, b, c: ");
scanf("%d %d %d",&a,&b,&c);
scanf("%d %d %d",&a,&b,&c);
n Gia tri cac so vua nhap: ");
printf("
printf("\\n Gia tri cac so vua nhap: ");
printf(" a = %
5d b = %--5d c = %
printf(" a = %--5d b = %
printf("
n Gia tri lon nhat trong 3 so la
printf("\\n Gia tri lon nhat trong 3 so la
%d",max(a,b,c));
%d",max(a,b,c));
getch();
getch();
}}
2828
14
int max(int x, int y, int z)
int max(int x, int y, int z)
{{
int max;
int max;
max = x>y?x:y;
max = x>y?x:y;
max = max>z?max:z;
max = max>z?max:z;
return max;
return max;
} }
2929
Nguyên mẫu hàm
Nguyên mẫu hàm
Nếu muốn đặt phần khai báo hàm nằm sau hàm
Nếu muốn đặt phần khai báo hàm nằm sau hàm
main() CầnCần khai báo nguyên mẫu của hàm
main()
khai báo nguyên mẫu của hàm
Để báo cho chương trình dịch biết có một hàm có
Để báo cho chương trình dịch biết có một hàm có
dòng đầu hàm giống như trong phần nguyên mẫu này.
dòng đầu hàm giống như trong phần nguyên mẫu này.
Chương trình dịch có thể kiểm tra được là các lời gọi
Chương trình dịch có thể kiểm tra được là các lời gọi
hàm trong chương trình chính có đúng hay không
hàm trong chương trình chính có đúng hay không
Có phù hợp về kiểu dữ liệu trả về hay không
Có phù hợp về kiểu dữ liệu trả về hay không
Các tham số thực có kiểu dữ liệu có phù hợp với kiểu dữ liệu
Các tham số thực có kiểu dữ liệu có phù hợp với kiểu dữ liệu
đã khai báo hay không.
đã khai báo hay không.
Trong hàm nguyên mẫu có thể không cần nêu
Trong hàm nguyên mẫu có thể không cần nêu
tên các tham số hình thức, nhưng trong phần
tên các tham số hình thức, nhưng trong phần
khai báo hàm ta cần phải có các tham số hình
khai báo hàm ta cần phải có các tham số hình
thức.
thức.
3030
15
Câu hỏi?
Câu hỏi?
3131
16
{{
d”,a););
intint a = 1; a = 1; printf(“(“\\n a1 = % printf n a1 = %d”,a { { intint a = 2; a = 2; printf(“(“\\n a2 = % printf
n a2 = %d”,a
d”,a););
}} printf(“(“\\n a3 = % printf
n a3 = %d”,a
d”,a););
}} { {
a = 3; intint a = 3; printf(“(“\\n a4 = % printf
n a4 = %d”,a
d”,a););
}}
}}
2020
10
Ví dụ 2
#include
#include
#include
#include
int a, b, c;
int a, b, c;
int tich()
int tich()
{{
5d b = %--5d c = %
5d c = %--
printf(" n Gia tri cac bien tong the printf("\\n Gia tri cac bien tong the a, b, c: "); a, b, c: "); printf(" a = %--5d b = % printf(" a = % 5d“,a,b,c); 5d“,a,b,c); return a*b*c; return a*b*c;
}}
2121
void main() void main() {{
clrscr(); clrscr(); printf(" n Nhap gia tri cho 3 printf("\\n Nhap gia tri cho 3 so nguyen a, b, c: "); so nguyen a, b, c: "); scanf("%d %d %d",&a,&b,&c); scanf("%d %d %d",&a,&b,&c); n Tich cua 3 so la printf("\\n Tich cua 3 so la printf(" %d",tich()); %d",tich()); getch(); getch();
}}
2222
11
2323
Biến register Biến register
register trước khai trước khai
register int a; register int a;
2424
Thanh ghi có tốc độ truy nhập nhanh hơn so với các loại Thanh ghi có tốc độ truy nhập nhanh hơn so với các loại bộ nhớ khác (RAM, bộ nhớ ngoài). bộ nhớ khác (RAM, bộ nhớ ngoài). NNếu một biến thường xuyên sử dụng được lưu vào trong ếu một biến thường xuyên sử dụng được lưu vào trong thanh ghi thì tốc độ thực hiện của chương trình sẽ được thanh ghi thì tốc độ thực hiện của chương trình sẽ được tăng lên. tăng lên. Để làm điều này ta đặt từ khóa register Để làm điều này ta đặt từ khóa báo của biến đó. báo của biến đó. Ví dụ: Ví dụ: Số lượng và kích thước các thanh ghi có hạn Số Số Số lượng và kích thước các thanh ghi có hạn register sẽ không nhiều và lượng biến khai báo register sẽ không nhiều và lượng biến khai báo thường chỉ áp dụng với những biến có kích thước nhỏ thường chỉ áp dụng với những biến có kích thước nhỏ như kiểu char như kiểu char, , intint. .
12
Biến static Biến static
static int a; Ví dụ: static int a;
static là biến tĩnh, nghĩa là nó sẽ được là biến tĩnh, nghĩa là nó sẽ được
Một biến cục bộ khi ra khỏi phạm vi của biến đó Một biến cục bộ khi ra khỏi phạm vi của biến đó thì bộ nhớ dành để lưu trữ biến đó sẽ được giải thì bộ nhớ dành để lưu trữ biến đó sẽ được giải phóng. phóng. Nếu cần lưu giá trị của các biến cục bộ này, cần Nếu cần lưu giá trị của các biến cục bộ này, cần static.. khai báo biến với từ khóa static khai báo biến với từ khóa Ví dụ: Biến Biến static cấp phát một vùng nhớ thường xuyên từ lúc khai cấp phát một vùng nhớ thường xuyên từ lúc khai báo và chỉ giải phóng khi chương trình chính kết báo và chỉ giải phóng khi chương trình chính kết thúc. thúc.
2525
VíVí dụdụ
stdio.h>> conio.h>>
# include <
# include n Day la lanlan goigoi ham ham fctfct lanlan thuthu static
count = 1;
static intint count = 1;
printf("("\\n Day la
printf
%2d", count++);
%2d", count++); }}
void main()
void main()
{{ 2626 int i;
int i;
for(i = 0; i < 10; i++)
for(i = 0; i < 10; i++) fct();
fct(); getch();
getch(); }} 13 5.1. Khái niệm hàm
5.2. Khai báo và sử dụng hàm
5.3. Phạm vi biến
5.4. Nguyên mẫu hàm 2727 Nguyên mẫu hàm –– Ví dụVí dụ
Nguyên mẫu hàm 2828 14 2929 Có phù hợp về kiểu dữ liệu trả về hay không
Có phù hợp về kiểu dữ liệu trả về hay không
Các tham số thực có kiểu dữ liệu có phù hợp với kiểu dữ liệu
Các tham số thực có kiểu dữ liệu có phù hợp với kiểu dữ liệu
đã khai báo hay không.
đã khai báo hay không. Trong hàm nguyên mẫu có thể không cần nêu
Trong hàm nguyên mẫu có thể không cần nêu
tên các tham số hình thức, nhưng trong phần
tên các tham số hình thức, nhưng trong phần
khai báo hàm ta cần phải có các tham số hình
khai báo hàm ta cần phải có các tham số hình
thức.
thức. 3030 15 3131 16BÀI 5: HÀMHÀM
BÀI 5:
#include
5d");
5d c = %--5d");
int a,b,c;
int a,b,c;
clrscr();
clrscr();
printf("
n Nhap gia tri cho 3 so nguyen a, b, c: ");
printf("\\n Nhap gia tri cho 3 so nguyen a, b, c: ");
scanf("%d %d %d",&a,&b,&c);
scanf("%d %d %d",&a,&b,&c);
n Gia tri cac so vua nhap: ");
printf("
printf("\\n Gia tri cac so vua nhap: ");
printf(" a = %
5d b = %--5d c = %
printf(" a = %--5d b = %
printf("
n Gia tri lon nhat trong 3 so la
printf("\\n Gia tri lon nhat trong 3 so la
%d",max(a,b,c));
%d",max(a,b,c));
getch();
getch();
}}
int max(int x, int y, int z)
int max(int x, int y, int z)
{{
int max;
int max;
max = x>y?x:y;
max = x>y?x:y;
max = max>z?max:z;
max = max>z?max:z;
return max;
return max;
} }
Nguyên mẫu hàm
Nguyên mẫu hàm
Nếu muốn đặt phần khai báo hàm nằm sau hàm
Nếu muốn đặt phần khai báo hàm nằm sau hàm
main() CầnCần khai báo nguyên mẫu của hàm
main()
khai báo nguyên mẫu của hàm
Để báo cho chương trình dịch biết có một hàm có
Để báo cho chương trình dịch biết có một hàm có
dòng đầu hàm giống như trong phần nguyên mẫu này.
dòng đầu hàm giống như trong phần nguyên mẫu này.
Chương trình dịch có thể kiểm tra được là các lời gọi
Chương trình dịch có thể kiểm tra được là các lời gọi
hàm trong chương trình chính có đúng hay không
hàm trong chương trình chính có đúng hay không
Câu hỏi?
Câu hỏi?