01/10/2018, 10:18

Bản chất thực sự của Return và cách thức nó hoạt động?

Em biết mọi người sẽ kêu em google hoặc đọc sách tìm hiểu, nhưng em vẫn không thể nào hiểu cặn kẽ về return được.
Mọi người nói Return dùng để trả về một giá trị nào đó khi thực hiện xong một hàm.
Vậy nó trả về như thế nào ? Nó trả về chỗ nào ? Giá trị của nó lưu ở đâu ?
Bình thường em code C có thể không dùng return cũng vẫn được mà nhưng hoc đệ quy thì nhất thiết phải dùng đến return.
Ví dụ đi ạ:

  1. long giaiThua(int n)
  2. {
  3. if(n==0) return 1;
  4. else return n*giaiThua(n-1);
  5. }

Như đoạn code trên, thì sau khi return thì cái gì lưu giá trị được return ? Giá trị đó lưu ở đâu ? Tại sao n=0 thì lại return 1 ?
Rất mong mọi người giúp đỡ ạ.

明玉 viết 12:18 ngày 01/10/2018

Học assembly phát là hiểu ngay à

Henry viết 12:30 ngày 01/10/2018

Ví dụ mình sẽ chạy đoạn code này với số n = 3

int rec(int n)
{
    if(n==0) return 1;
    else return n * rec(n-1);
}

Vẽ paint hơi xấu

Thân Hoàng viết 12:23 ngày 01/10/2018

vấn đề chính là em muốn hiểu cách thức hoạt động của return. Giá trị sau khi return là cái gì? nằm ở đâu? Vì em không thấy có biến nào lưu giá trị đấy cả

Thân Hoàng viết 12:32 ngày 01/10/2018

return trả về như thế nào ? Nó trả về chỗ nào ? Giá trị của nó lưu ở đâu ? Tại sao không có biến để lưu giá trị đó mà nó vẫn có thể thực hiện được ạ ?

Thanh Duy viết 12:24 ngày 01/10/2018

Nó trả về giá trị, đơn giản z thôi. Nếu là các kiểu primative thì nó trả về 1 giá trị gán cho biến gọi nó(giá trị trong biến return sẽ được coppy gán cho biến gọi…). Nếu là tham chiếu trả về địa chỉ cho biến gọi nó
còn cái hàm giai thuật đó, thì khi tính giai thừa thì 0! = 1 => nó sẽ return 1, còn không phải nó sẽ gọi đệ qui, cái hàm này muốn đầy đủ thì phải kiểm tra điều kiện vd: n >=0 chẳng hạn
(cách trả về giá trị của hàm đệ qui thì tự search, không nên serch tiếng việt)
còn muốn hiểu sau thì hơi khó, như hàm trên giá trị trả về là 1 kiểu primative, nó sẽ lưu ở stack, giống như bạn gán : int x =10 vậy

Vô Thin viết 12:34 ngày 01/10/2018

À, chủ topic không học ngôn ngữ Pascal mà nhảy vô học C ngay nên mới có sự rối loạn như vậy.

Cần biết qua chút về “hàm”: Trong Pascal thì người ta phân biệt giữa hai loại “hàm”: 1. Hàm có nhận giá trị trả về, tên gọi nó là function; 2. Thủ tục: không nhận giá trị trả về, tên gọi nó là procedure.

Còn trong C, người ta gọi tất là function , tức gồm cả hàm (function) và thủ tục (procedure) bên Pascal.

Vậy, câu hỏi đặt ra là làm sao nhận biết lúc nào hàm thực sự là function, lúc nào hàm là có bản chất procedure bên C?

Cứ thấy cái nào mà có khai báo:

void tên-hàm() {
}

thì cái này đích thị là procedure bên Pascal.

Hiểu một cách rất đơn giản: đây là nơi gom các khai báo, gọi hàm được cung cấp sẵn bởi C, hoặc read/ write/ làm cái gì đó - ngoài việc tính toán biểu thức. Khi có từ void đứng đầu, thì function này không nhận giá trị trả về, và không lưu vào đâu hết, thực hiện xong không có lỗi là im re. Đây đích thị có bản chất là procedure.

Còn nếu thấy khai báo dạng:

int tên-của-hàm(đối số nếu có) {
    return value;
}

hoặc:

double tên-của-hàm(đối số nếu có) {
    return value;
}

thì lúc này ta thấy người tra khai báo có kiểu dữ liệu đứng trước hàm, ở 2 ví dụ trên là int, double , lúc này, từ hàm nó giống y như giải toán, có dữ liệu đầu vào, sau khi tính toán đã đời thì cần phải trả về giá trị (kết quả) để sử dụng trong các biểu thức tính toán khác và/ hoặc xuất ra. Vậy, nếu nó trả về mà cứ hiện lên màn hình thì quá bất tiện, cho nên, lúc này tên-của-hàm đóng vai trò gần như một biến, và giá trị nó nằm trong đó, ta cần thì write/ print nó ra hoặc ta gán cho một biến nào đó để dùng.

Ví dụ về hàm không có giá trị trả về:

void congHaiSo() { // ví dụ đầu (*)
   viết ra dòng nhắc nhập giá trị a;
   đọc giá trị a;
   viết ra dòng nhắc nhập giá trị b;
   đọc giá trị b;
   làm toán cộng a + b;
   viết ra kết quả ở màn hình.
   (ở đây không có return gì hết)
}

Còn hàm có giá trị trả về:

int congHaiSo(int a, int b) { // ví dụ sau (**)
   lấy hai giá trị ở tham số a, b trên thực hiện việc cộng, giả sử có biến trung gian tmp = a + b;
   return tmp; # tức là gán biến tmp trả về cho hàm, vì bên ngoài hàm không thể "sờ" được tmp.
}

Ở ví dụ đầu (*), ta không thể:

printf("%d", congHaiSo()); được, vì lúc này hàm congHaiSo thực chất là 1 procedure. Nhưng ở ví dụ sau (**) thì ta có thể:

printf("%d", congHaiSo(4,5)); và nó cho giá trị là 9.

Thêm một dấu hiệu nhận biết nữa là function có trả về thường hay có đối số, tức là int tên-hàm(đối số, đối số, đối số) { làm gì đó; return }, còn nếu không có giá trị trả về, nó ít khi có đối số mà void tên-hàm(void nốt) { làm gì đó }

Nếu chủ topic chưa hiểu nữa thì vui lòng kiếm một cuốn sách tốt để đọc, ở đó họ giải thích rất kỹ. Nếu vẫn bó tay, kiếm cuốn lập trình Pascal đọc trước để hiểu sơ qua về lập trình là gì, vì bắt đầu với C là quá khó với một số người.

Henry viết 12:26 ngày 01/10/2018

Mình không học sâu về cách mà máy tính hoạt động nhưng theo mình hiểu gì đệ quy giống như việc bạn gọi một hàm nhiều lần. Khi bạn thực hiện đệ quy thì hàm đầu tiên sẽ đợi giá trị từ hàm tiếp theo, rồi hàm tiếp theo lại đợi kết quả từ hàm kế tiếp nữa, cứ thế cho tới khi nào một hàm thỏa giá trị if n = 0 như trên ví dụ thì đệ quy coi như kết thúc bằng việc return 1. Ở đây không phải đợi một hàm nào khác nữa.
Mình sẽ coi như đệ quy gọi hàm n lần. Lần thứ nreturn 1. Vậy lần thứ n - 1 đang chờ lần thứ n sẽ có được kết quả 1. Sau khi trả về nó sẽ tính toán và trả về giá trị cho lần thứ n - 2. Và tiếp theo hàm thứ n - 2 cũng đang đợi kết quả từ hàm thứ n - 1, và cứ thế thằng sau có giá trị, nó sẽ vứt cho thằng trước, thằng trước có giá trị nó lại vứt về cho thằng trước nó nữa. Cho tới mà kết quả được vứt tới thằng đầu tiên.

rec(3)
3 * rec(2)
3 * 2 * rec(1)
3 * 2 * 1 * rec(0)
3 * 2 * 1 * 1 [return rec(0)]
3 * 2 * 1     [return rec(1)]
3 * 2         [return rec(2)]
6             [return rec(3)]

EDIT: Tưởng tượng kiểu thực tế như việc bạn hỏi thông tin vậy. Bạn hỏi thằng A, A kêu là đợi tao đi hỏi B, B lại nói với A đợi tao đi hỏi C, C lại tiếp tục kêu B đợi tao hỏi D. Sau khi D trả lời thì C biết câu trả lời về nói cho B, B lại về nói cho A. Khi A có đáp án về mách cho bạn

Thanh Duy viết 12:26 ngày 01/10/2018

Thằng cha này hoạt động nhiệt tình nhỉ

Henry viết 12:21 ngày 01/10/2018

Thằng cha này hoạt động nhiệt tình nhỉ

Ý anh là sao ạ.

Thanh Duy viết 12:18 ngày 01/10/2018

Tại ở đâu cũng thấy răng

Henry viết 12:26 ngày 01/10/2018

Biết không nhiều, nhưng nghĩ có thể giúp được người khác ở những kiến thức căn bản nên cũng muốn gõ góp vào vài chữ thôi ạ

Thanh Duy viết 12:23 ngày 01/10/2018

Ukm, thay mặt ae cám ơn bạn. Thấy bạn cũng hiểu khá sâu về cấu trúc dữ liệu

Thanh Duy viết 12:20 ngày 01/10/2018

Cái này chắc phải đề nghị a Đạt cho lên làm Mod

Trần Hoàn viết 12:32 ngày 01/10/2018

Hàm có return gần như chẳng khác gì một phép toán cả.
int a = b + c; tức là phép gán a = b + c;
Giả sử bạn có hàm Add như sau:

int Add(int param1, int param2)
{
    return param1 + param2;
}

Vậy thì int a = Add(b, c); hoàn toàn giống với int a = b + c;

return là trả về kết quả của 1 phép toán. Và kết quả của phép toán thì lưu trong thanh ghi của CPU (chém gió vui mồm. Mình cũng chẳng biết kết quả phép toán lưu ở đâu đâu.).

Thân Hoàng viết 12:21 ngày 01/10/2018

em cảm ơn anh rất nhiều. Em hiểu 90% rồi ^^
Trước đây em có có học qua Pascal trên trường, ở quê nên là thầy cô dạy qua quýt cho xong.

明玉 viết 12:28 ngày 01/10/2018

Kết quả của hàm số có thể được lưu trong thanh ghi của CPU, cũng có thể được lưu ngay trên thread stack

Thân Hoàng viết 12:31 ngày 01/10/2018

và tên của hàm sẽ lưu giá trị được trả về phải không bác ?

Thanh Duy viết 12:28 ngày 01/10/2018

Nói thế ông nội em nó cũng không hiểu
thread(luồng) stack: bộ nhớ chính(Ram)
register(thanh ghi), bộ nhớ đệm CPU, nằm bên trong CPU

Thanh Duy viết 12:23 ngày 01/10/2018

Mẹ tức đéo cám ơn mn, cám ơn có 1 người. Ghét không rep nữa

Thân Hoàng viết 12:19 ngày 01/10/2018

em đang đọc nốt mấy cmt bên trên anh ạ -_- nó hơi lằng nhằng nên em phải đọc đi đọc lại…mà em đã thả tim rồi còn gì :’(

Bài liên quan
0