Câu trả lời:
Đầu tiên, inline
đặc tả về một chức năng chỉ là một gợi ý. Trình biên dịch có thể (và thường không) hoàn toàn bỏ qua sự hiện diện hay vắng mặt của inline
vòng loại. Như đã nói, một trình biên dịch có thể nội tuyến một hàm đệ quy, vì nó có thể hủy bỏ một vòng lặp vô hạn. Nó chỉ đơn giản là phải đặt một giới hạn ở mức độ mà nó sẽ "hủy đăng ký" chức năng.
Trình biên dịch tối ưu hóa có thể biến mã này:
inline int factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * factorial(n - 1);
}
}
int f(int x)
{
return factorial(x);
}
vào mã này:
int factorial(int n)
{
if (n <= 1)
{
return 1;
}
else
{
return n * factorial(n - 1);
}
}
int f(int x)
{
if (x <= 1)
{
return 1;
}
else
{
int x2 = x - 1;
if (x2 <= 1)
{
return x * 1;
}
else
{
int x3 = x2 - 1;
if (x3 <= 1)
{
return x * x2 * 1;
}
else
{
return x * x2 * x3 * factorial(x3 - 1);
}
}
}
}
Trong trường hợp này, về cơ bản chúng tôi đã nội dung hàm 3 lần. Một số trình biên dịch làm thực hiện tối ưu hóa này. Tôi nhớ rằng MSVC ++ có cài đặt để điều chỉnh mức độ nội tuyến sẽ được thực hiện trên các hàm đệ quy (tối đa 20, tôi tin).
Thật vậy, nếu trình biên dịch của bạn không hoạt động thông minh, nó có thể thử chèn các bản sao của inline
hàm d của bạn một cách đệ quy, tạo mã lớn vô hạn. Hầu hết các trình biên dịch hiện đại sẽ nhận ra điều này, tuy nhiên. Họ có thể:
Đối với trường hợp 2, nhiều trình biên dịch có #pragma
s bạn có thể đặt để chỉ định độ sâu tối đa mà việc này sẽ được thực hiện. Trong gcc , bạn cũng có thể chuyển cái này từ dòng lệnh với --max-inline-insns-recursive
(xem thêm thông tin ở đây ).
Xem các câu trả lời đã được đưa ra để biết tại sao điều này thường không hoạt động.
Là một "chú thích", bạn có thể đạt được hiệu quả mà bạn đang tìm kiếm (ít nhất là cho giai thừa mà bạn đang sử dụng làm ví dụ) bằng cách sử dụng siêu lập trình mẫu . Dán từ Wikipedia:
template <int N>
struct Factorial
{
enum { value = N * Factorial<N - 1>::value };
};
template <>
struct Factorial<0>
{
enum { value = 1 };
};
Trình biên dịch sẽ tạo một biểu đồ cuộc gọi để phát hiện các loại điều này và ngăn chặn chúng. Vì vậy, nó sẽ thấy rằng hàm gọi chính nó và không nội tuyến.
Nhưng chủ yếu nó được điều khiển bởi các khóa chuyển đổi từ khóa và trình biên dịch nội tuyến (Ví dụ: bạn có thể tự động thực hiện các chức năng nhỏ nội tuyến ngay cả khi không có từ khóa.) Điều quan trọng cần lưu ý là các phần biên dịch Debug không bao giờ được đặt nội tuyến vì lệnh gọi sẽ không được bảo toàn để phản chiếu các cuộc gọi bạn đã tạo trong mã.
"Làm thế nào để trình biên dịch quyết định có nên nội tuyến một hàm hay không?"
Điều đó phụ thuộc vào trình biên dịch, các tùy chọn đã được chỉ định, số phiên bản của trình biên dịch, có thể là bao nhiêu bộ nhớ khả dụng, v.v.
Mã nguồn của chương trình vẫn phải tuân theo các quy tắc cho các hàm được nội tuyến. Cho dù chức năng có được nội tuyến hay không, bạn phải chuẩn bị cho khả năng nó sẽ được nội tuyến (một số lần không xác định).
Tuyên bố Wikipedia rằng các macro đệ quy thường có vẻ bất hợp pháp khá kém thông tin. C và C ++ ngăn chặn các yêu cầu đệ quy nhưng một đơn vị dịch thuật không trở thành bất hợp pháp bằng cách chứa mã macro trông giống như nó sẽ được đệ quy. Trong trình biên dịch, các macro đệ quy thường hợp pháp.