Hàm nội tuyến so với macro Bộ xử lý


Câu trả lời:


127

Macro tiền xử lý chỉ là các mẫu thay thế được áp dụng cho mã của bạn. Chúng có thể được sử dụng ở hầu hết mọi nơi trong mã của bạn vì chúng được thay thế bằng các bản mở rộng của chúng trước khi bất kỳ quá trình biên dịch nào bắt đầu.

Các hàm nội tuyến là các hàm thực mà phần thân của nó được đưa trực tiếp vào trang web cuộc gọi của chúng. Chúng chỉ có thể được sử dụng khi một lệnh gọi hàm thích hợp.

Bây giờ, khi sử dụng macro so với các hàm nội tuyến trong ngữ cảnh giống như hàm, hãy lưu ý rằng:

  • Macro không phải là loại an toàn và có thể được mở rộng bất kể chúng có đúng về mặt cú pháp hay không - giai đoạn biên dịch sẽ báo cáo lỗi do sự cố mở rộng macro.
  • Macro có thể được sử dụng trong ngữ cảnh mà bạn không mong đợi, dẫn đến sự cố
  • Macro linh hoạt hơn, ở chỗ chúng có thể mở rộng các macro khác - trong khi các hàm nội tuyến không nhất thiết phải làm điều này.
  • Macro có thể dẫn đến các hiệu ứng phụ do sự mở rộng của chúng, vì các biểu thức đầu vào được sao chép ở bất cứ nơi nào chúng xuất hiện trong mẫu.
  • Hàm nội tuyến không phải lúc nào cũng được đảm bảo là nội tuyến - một số trình biên dịch chỉ thực hiện điều này trong các bản dựng phát hành hoặc khi chúng được định cấu hình cụ thể để làm như vậy. Ngoài ra, trong một số trường hợp có thể không thực hiện được nội tuyến.
  • Các hàm nội tuyến có thể cung cấp phạm vi cho các biến (đặc biệt là các biến tĩnh), các macro tiền xử lý chỉ có thể thực hiện điều này trong các khối mã {...} và các biến tĩnh sẽ không hoạt động theo cùng một cách.

39
Chức năng nội tuyến không phải lúc nào cũng được đảm bảo nội tuyến: Bởi vì trình biên dịch sẽ không nội tuyến nếu làm như vậy sẽ tạo ra mã chậm hơn, v.v. Trình biên dịch thực hiện rất nhiều phân tích mà Kỹ sư không thể và thực hiện đúng.
Martin York

14
Tôi tin rằng các hàm đệ quy là một ví dụ khác mà hầu hết các trình biên dịch bỏ qua nội tuyến.
LBushkin

Có sự khác biệt quan trọng nào trong C so với C ++ trong trường hợp này không?
rzetterberg

7
Một điểm không được đề cập là nội tuyến có thể bị ảnh hưởng bởi cờ biên dịch. Ví dụ: khi bạn xây dựng cho tốc độ tối đa (như GCC -O2 / -O3), trình biên dịch sẽ chọn nội tuyến nhiều hàm, nhưng khi bạn xây dựng cho kích thước tối thiểu (-O) nếu các hàm nội tuyến thường chỉ được gọi một lần (hoặc các hàm rất nhỏ ). Với macro không có sự lựa chọn như vậy.
dbrank0

Macro không thể che phủ bằng chỉ định truy cập (như, riêng tư hoặc được bảo vệ) trong khi các chức năng nội tuyến vẫn có thể thực hiện được.
Lượt truy cập

78

Đầu tiên, các macro của bộ xử lý trước chỉ là "sao chép dán" trong mã trước khi biên dịch. Vì vậy, không có kiểm tra loại và một số tác dụng phụ có thể xuất hiện

Ví dụ: nếu bạn muốn so sánh 2 giá trị:

#define max(a,b) ((a<b)?b:a)

Các tác dụng phụ xuất hiện nếu bạn sử dụng max(a++,b++)chẳng hạn ( ahoặc bsẽ được tăng lên hai lần). Thay vào đó, hãy sử dụng (ví dụ)

inline int max( int a, int b) { return ((a<b)?b:a); }

3
Chỉ muốn thêm vào ví dụ của bạn rằng ngoài tác dụng phụ, vĩ mô cũng có thể giới thiệu thêm khối lượng công việc, hãy xem xét max(fibonacci(100), factorial(10000))lớn hơn một sẽ được tính gấp đôi :(
watashiSHUN

Mọi người đều nói về Kiểm tra kiểu nhưng chỉ cần bạn cung cấp một ví dụ trong thế giới thực, đó là lý do tại sao tôi ủng hộ câu trả lời này.
Ivanzinho

16

Hàm Inline được mở rộng bởi trình biên dịch khi các macro được mở rộng bởi Bộ tiền xử lý, nó chỉ là sự thay thế văn bản.

  • Không có kiểm tra kiểu trong khi gọi macro trong khi kiểm tra kiểu được thực hiện trong khi gọi hàm.

  • Kết quả không mong muốn và không hiệu quả có thể xảy ra trong quá trình mở rộng vĩ mô do đánh giá lại các đối số và thứ tự hoạt động. Ví dụ

    #define MAX(a,b) ((a)>(b) ? (a) : (b))
    int i = 5, j = MAX(i++, 0);

    sẽ dẫn đến

    int i = 5, j = ((i++)>(0) ? (i++) : (0));
  • Các đối số macro không được đánh giá trước khi mở rộng macro

    #define MUL(a, b) a*b
    int main()
    {
      // The macro is expended as 2 + 3 * 3 + 5, not as 5*8
      printf("%d", MUL(2+3, 3+5));
     return 0;
    }
    // Output: 16`
  • Từ khóa return không thể được sử dụng trong macro để trả về giá trị như trong trường hợp của hàm.

  • Các hàm nội tuyến có thể bị quá tải

  • Các mã thông báo được chuyển cho macro có thể được nối bằng cách sử dụng toán tử ## được gọi là toán tử Dán mã.

  • Macro thường được sử dụng để tái sử dụng mã khi các hàm nội tuyến được sử dụng để loại bỏ chi phí thời gian (thời gian dư thừa) trong khi gọi hàm (tránh nhảy đến một chương trình con).


13

Sự khác biệt chính là kiểm tra kiểu. Trình biên dịch sẽ kiểm tra xem những gì bạn chuyển vào làm giá trị đầu vào có thuộc loại có thể được truyền vào hàm hay không. Điều đó không đúng với macro của bộ xử lý trước - chúng được mở rộng trước khi kiểm tra kiểu bất kỳ và điều đó có thể gây ra lỗi nghiêm trọng và khó phát hiện.

Dưới đây là một số điểm ít rõ ràng hơn được nêu ra.


11

Để thêm một sự khác biệt khác cho những thứ đã được cung cấp: bạn không thể bước qua một #definetrong trình gỡ lỗi, nhưng bạn có thể bước qua một hàm nội tuyến.



3

Các hàm nội tuyến tương tự như macro (vì mã hàm được mở rộng tại điểm gọi tại thời điểm biên dịch), các hàm nội tuyến được trình biên dịch phân tích cú pháp, trong khi macro được mở rộng bởi bộ tiền xử lý. Kết quả là, có một số khác biệt quan trọng:

  • Các hàm nội tuyến tuân theo tất cả các giao thức an toàn kiểu được thực thi trên các hàm bình thường.
  • Các hàm nội tuyến được chỉ định bằng cú pháp giống như bất kỳ hàm nào khác ngoại trừ việc chúng bao gồm từ khóa nội tuyến trong khai báo hàm.
  • Các biểu thức được truyền làm đối số cho các hàm nội tuyến được đánh giá một lần.
  • Trong một số trường hợp, các biểu thức được truyền làm đối số cho macro có thể được đánh giá nhiều lần. http://msdn.microsoft.com/en-us/library/bf6bf4cf.aspx

  • macro được mở rộng tại thời điểm trước khi biên dịch, bạn không thể sử dụng chúng để gỡ lỗi, nhưng bạn có thể sử dụng các hàm nội tuyến.

- bài viết hay : http://www.codeguru.com/forum/showpost.php?p=1093923&postcount=1

;


2

Một hàm nội tuyến sẽ duy trì ngữ nghĩa giá trị, trong khi macro bộ tiền xử lý chỉ sao chép cú pháp. Bạn có thể gặp các lỗi rất nhỏ với macro bộ xử lý trước nếu bạn sử dụng đối số nhiều lần - ví dụ: nếu đối số chứa đột biến như "i ++" có thực thi hai lần là một điều khá bất ngờ. Một hàm nội tuyến sẽ không có vấn đề này.


1

Một hàm nội tuyến hoạt động theo cú pháp giống như một hàm thông thường, cung cấp sự an toàn về kiểu và phạm vi cho các biến cục bộ của hàm và quyền truy cập vào các thành viên lớp nếu nó là một phương thức. Ngoài ra khi gọi các phương thức nội tuyến, bạn phải tuân thủ các hạn chế riêng tư / được bảo vệ.


1

Để biết sự khác biệt giữa macro và hàm nội tuyến , trước hết chúng ta nên biết chính xác chúng là gì và khi nào chúng ta nên sử dụng chúng.

CHỨC NĂNG :

int Square(int x){
return(x*X);
}
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
  • Các lệnh gọi hàm có chi phí liên quan đến nó, vì sau khi hàm kết thúc thực thi, nó phải biết nơi nó phải trả về và cũng cần lưu giá trị trong bộ nhớ ngăn xếp.

  • Đối với các ứng dụng nhỏ thì sẽ không có vấn đề gì, nhưng hãy lấy ví dụ về các ứng dụng tài chính mà hàng nghìn giao dịch đang diễn ra mỗi giây, chúng ta không thể thực hiện với các lệnh gọi hàm.

MACROS:

# define Square(x) x*x;
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
  • Macro hoạt động ở giai đoạn tiền xử lý, tức là ở giai đoạn này, các câu lệnh được viết bằng từ khóa # sẽ được thay thế bằng nội dung tức là

int result = Square (x * x)

Nhưng macro có các lỗi liên quan đến nó.

#define Square(x) x*x
int main() {
    int val = 5;
    int result = Square(val + 1);
    cout << result << endl;
    return 0;
}

Ở đây đầu ra là 11 không phải 36 .

CÁC CHỨC NĂNG TRỰC TUYẾN :

inline int Square(int x) {
    return x * x;
}

int main() {
    using namespace std;
    int val = 5;
    int result = Square(val + 1);
    cout << result << endl;
    return 0;
}

Đầu ra 36

Từ khóa nội tuyến yêu cầu trình biên dịch thay thế lời gọi hàm bằng phần thân của hàm, ở đây kết quả đầu ra là chính xác vì trước tiên nó đánh giá biểu thức và sau đó được truyền vào. không cần bộ nhớ cho các đối số của hàm.

So sánh giữa Macro và Hàm nội tuyến:

  1. Macro hoạt động thông qua thay thế, trong khi trong Hàm nội tuyến, lệnh gọi hàm được thay thế bằng phần thân.
  2. Macro dễ bị lỗi do thay thế trong khi các hàm nội tuyến được sử dụng an toàn.
  3. Macro không có địa chỉ trong khi các hàm nội tuyến có địa chỉ.
  4. Macro khó sử dụng với nhiều dòng mã, trong khi các hàm nội tuyến thì không.
  5. Trong C ++, macro không thể được sử dụng với các hàm thành viên trong khi có thể sử dụng hàm nội tuyến.

PHẦN KẾT LUẬN:

Các hàm nội tuyến đôi khi hữu ích hơn macro, vì nó cải thiện hiệu suất và an toàn khi sử dụng cũng như giảm chi phí cuộc gọi hàm. Nó chỉ là một yêu cầu đối với trình biên dịch, một số hàm nhất định sẽ không được đưa vào như:

  • chức năng lớn
  • các hàm có quá nhiều đối số điều kiện
  • mã đệ quy và mã có vòng lặp, v.v.

đó là một điều tốt, bởi vì đó là bất cứ khi nào trình biên dịch nghĩ rằng tốt nhất là làm mọi thứ theo cách khác.


Cũng giống như một nhận xét: Có thể sửa macro để đánh giá theo cùng một số với dấu ngoặc. Tuy nhiên, nó vẫn dễ xảy ra lỗi, vì bạn cần phải suy nghĩ về sự phụ thuộc tuyệt đối và tất cả các trường hợp trong quá trình thực hiện.
mike

0

Trong GCC (tôi không chắc về những người khác), việc khai báo một hàm nội tuyến, chỉ là một gợi ý cho trình biên dịch. Vào cuối ngày, trình biên dịch vẫn quyết định xem nó có bao gồm phần thân của hàm hay không bất cứ khi nào nó được gọi.

Sự khác biệt giữa các hàm trong dòng và macro tiền xử lý là tương đối lớn. Macro tiền xử lý chỉ là sự thay thế văn bản vào cuối ngày. Bạn mất rất nhiều khả năng để trình biên dịch thực hiện kiểm tra kiểu kiểm tra trên các đối số và kiểu trả về. Đánh giá các đối số là khác nhau nhiều (nếu các biểu thức bạn chuyển vào các hàm có tác dụng phụ, bạn sẽ có một thời gian gỡ lỗi rất thú vị). Có sự khác biệt nhỏ về nơi có thể sử dụng các hàm và macro. Ví dụ nếu tôi có:

#define MACRO_FUNC(X) ...

Trong đó MACRO_FUNC rõ ràng xác định phần thân của hàm. Cần chú ý đặc biệt để nó chạy chính xác trong mọi trường hợp một hàm có thể được sử dụng, ví dụ MACRO_FUNC viết kém sẽ gây ra lỗi trong

if(MACRO_FUNC(y)) {
 ...body
}

Một chức năng bình thường có thể được sử dụng mà không có vấn đề gì ở đó.


0

Từ quan điểm của mã hóa, một hàm nội tuyến giống như một hàm. Do đó, sự khác biệt giữa hàm nội tuyến và macro cũng giống như sự khác biệt giữa hàm và macro.

Từ góc độ biên dịch, một hàm nội tuyến tương tự như một macro. Nó được tiêm trực tiếp vào mã, không được gọi.

Nói chung, bạn nên coi các hàm nội tuyến là các hàm thông thường với một số tối ưu hóa nhỏ xen vào. Và giống như hầu hết các tối ưu hóa, trình biên dịch quyết định xem nó có thực sự muốn áp dụng nó hay không. Thường thì trình biên dịch sẽ vui vẻ bỏ qua bất kỳ nỗ lực nào của lập trình viên để nội dòng một hàm, vì nhiều lý do khác nhau.


0

Các hàm nội tuyến sẽ hoạt động như một lời gọi hàm nếu tồn tại bất kỳ câu lệnh lặp hoặc đệ quy nào trong đó, để ngăn việc thực thi lặp lại các lệnh. Nó khá hữu ích để lưu bộ nhớ tổng thể của chương trình của bạn.


-1
#include<iostream>
using namespace std;
#define NUMBER 10 //macros are preprocessed while functions are not.
int number()
{ 
    return 10;
}
/*In macros, no type checking(incompatible operand, etc.) is done and thus use of micros can lead to errors/side-effects in some cases. 
However, this is not the case with functions.
Also, macros do not check for compilation error (if any). Consider:- */
#define CUBE(b) b*b*b
int cube(int a)
{
 return a*a*a;
}
int main()
{
 cout<<NUMBER<<endl<<number()<<endl;
 cout<<CUBE(1+3); //Unexpected output 10
 cout<<endl<<cube(1+3);// As expected 64
 return 0;
}

Macro thường nhanh hơn các hàm vì chúng không liên quan đến chi phí cuộc gọi hàm thực tế.

Một số Nhược điểm của macro: Không có kiểm tra kiểu. Khó gỡ lỗi vì chúng gây ra sự thay thế đơn giản.Macro không có không gian tên, vì vậy macro trong một phần mã có thể ảnh hưởng đến phần khác. Macro có thể gây ra các tác dụng phụ như trong ví dụ CUBE () ở trên.

Macro thường là một lớp lót. Tuy nhiên, chúng có thể bao gồm nhiều hơn một dòng, không có ràng buộc nào như vậy trong các hàm.


Bạn nhận được bao nhiêu niềm vui #define TWO_N(n) 2 << nsau đó cout << CUBE(TWO_N(3 + 1)) << endl;? (Đó là tốt hơn để kết thúc dòng đầu ra với endlhơn để bắt đầu họ với nó.)
Jonathan Leffler
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.