Fungsi sebaris vs makro praprosesor

Jawaban:

127

Makro preprocessor hanyalah pola substitusi yang diterapkan ke kode Anda. Mereka dapat digunakan hampir di mana saja dalam kode Anda karena diganti dengan perluasannya sebelum kompilasi dimulai.

Fungsi sebaris adalah fungsi aktual yang tubuhnya disuntikkan langsung ke situs panggilannya. Mereka hanya dapat digunakan jika pemanggilan fungsi sesuai.

Sekarang, sejauh menggunakan makro vs. fungsi sebaris dalam konteks mirip fungsi, perhatikan bahwa:

  • Makro bukanlah jenis yang aman, dan dapat diperluas terlepas dari apakah mereka benar secara sintatis - fase kompilasi akan melaporkan kesalahan yang disebabkan oleh masalah perluasan makro.
  • Makro dapat digunakan dalam konteks yang tidak Anda harapkan, sehingga menimbulkan masalah
  • Makro lebih fleksibel, karena dapat memperluas makro lain - sedangkan fungsi sebaris tidak selalu melakukan ini.
  • Makro dapat menyebabkan efek samping karena perluasannya, karena ekspresi input disalin di mana pun muncul dalam pola.
  • Fungsi sebaris tidak selalu dijamin untuk menjadi sebaris - beberapa kompiler hanya melakukan ini dalam versi rilis, atau ketika mereka secara khusus dikonfigurasi untuk melakukannya. Selain itu, dalam beberapa kasus, penyebarisan mungkin tidak dapat dilakukan.
  • Fungsi sebaris dapat memberikan ruang lingkup untuk variabel (terutama yang statis), makro preprocessor hanya dapat melakukan ini dalam blok kode {...}, dan variabel statis tidak akan berperilaku persis sama.
LBushkin
sumber
39
Fungsi inline tidak selalu dijamin untuk menjadi inline: Karena compiler tidak akan inline jika melakukannya akan menghasilkan kode yang lebih lambat, dll. Compiler melakukan banyak analisis yang tidak dapat dilakukan oleh Engineer dan melakukan hal yang benar.
Martin York
14
Saya percaya bahwa fungsi rekursif adalah contoh lain di mana sebagian besar kompiler mengabaikan sebaris.
LBushkin
Apakah ada perbedaan penting dalam C dibandingkan dengan C ++ dalam kasus ini?
rzetterberg
7
Satu hal yang tidak disebutkan adalah bahwa penyebarisan dapat dipengaruhi oleh tanda kompilasi. Misalnya, ketika Anda membangun untuk kecepatan maksimum (seperti GCC -O2 / -O3), compiler akan memilih untuk menyebariskan banyak fungsi, tetapi ketika Anda membuat untuk ukuran minimum (-O) jika biasanya fungsi sebaris dipanggil hanya sekali (atau fungsi yang sangat kecil) ). Dengan makro, tidak ada pilihan seperti itu.
dbrank0
Makro tidak dapat ditutup dengan penentu akses (seperti, pribadi atau dilindungi) sementara fungsi sebaris dimungkinkan.
Hit
78

Pertama, makro preprocessor hanya "copy paste" di kode sebelum kompilasi. Jadi tidak ada pemeriksaan tipe , dan beberapa efek samping bisa muncul

Misalnya, jika Anda ingin membandingkan 2 nilai:

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

Efek samping muncul jika Anda menggunakan max(a++,b++)misalnya ( aatau bakan bertambah dua kali). Sebagai gantinya, gunakan (misalnya)

inline int max( int a, int b) { return ((a<b)?b:a); }
ThibThib
sumber
3
Hanya ingin menambahkan contoh Anda bahwa selain efek samping, makro juga dapat memperkenalkan beban kerja ekstra, pertimbangkan max(fibonacci(100), factorial(10000))yang lebih besar akan dihitung dua kali :(
watashiSHUN
Semua orang berbicara tentang Pemeriksaan Jenis tetapi hanya Anda yang memberikan contoh dunia nyata, itulah mengapa saya memberi suara positif pada jawaban ini.
Ivanzinho
16

Fungsi Inline diperluas oleh compiler dimana makro diperluas oleh Preprocessor, yang merupakan substitusi tekstual belaka.

  • Tidak ada pemeriksaan jenis selama pemanggilan makro sementara pemeriksaan jenis dilakukan selama pemanggilan fungsi.

  • Hasil dan inefisiensi yang tidak diinginkan dapat terjadi selama ekspansi makro karena evaluasi ulang argumen dan urutan operasi. Sebagai contoh

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

    akan menghasilkan

    int i = 5, j = ((i++)>(0) ? (i++) : (0));
  • Argumen makro tidak dievaluasi sebelum perluasan makro

    #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`
  • Kata kunci return tidak dapat digunakan dalam makro untuk mengembalikan nilai seperti pada kasus fungsi.

  • Fungsi sebaris dapat kelebihan beban

  • Token yang diteruskan ke makro bisa digabungkan menggunakan operator ## yang disebut operator Token-Pasting.

  • Makro umumnya digunakan untuk penggunaan kembali kode di mana fungsi inline digunakan untuk menghilangkan overhead waktu (waktu berlebih) selama pemanggilan fungsi (menghindari lompatan ke subrutin).

DataCruncher
sumber
13

Perbedaan utamanya adalah pemeriksaan tipe. Kompilator akan memeriksa apakah yang Anda berikan sebagai nilai masukan adalah jenis yang dapat diteruskan ke fungsi. Itu tidak benar dengan makro praprosesor - mereka diperluas sebelum pemeriksaan jenis apa pun dan itu dapat menyebabkan bug yang parah dan sulit dideteksi.

Berikut adalah beberapa poin lain yang kurang jelas diuraikan.

gigi tajam
sumber
11

Untuk menambahkan perbedaan lain ke yang sudah diberikan: Anda tidak bisa melangkah melalui #definedi debugger, tapi Anda bisa melangkah melalui fungsi sebaris.

RichieHindle
sumber
8

Makro mengabaikan ruang nama. Dan itu membuat mereka jahat.

Kirill V. Lyadvinsky
sumber
3

fungsi sebaris mirip dengan makro (karena kode fungsi diperluas pada titik panggilan pada waktu kompilasi), fungsi sebaris diurai oleh kompilator, sedangkan makro diperluas oleh preprocessor. Akibatnya, ada beberapa perbedaan penting:

  • Fungsi inline mengikuti semua protokol jenis keamanan yang diterapkan pada fungsi normal.
  • Fungsi sebaris ditentukan menggunakan sintaks yang sama seperti fungsi lainnya kecuali fungsi tersebut menyertakan kata kunci sebaris dalam deklarasi fungsi.
  • Ekspresi yang diteruskan sebagai argumen ke fungsi sebaris dievaluasi satu kali.
  • Dalam beberapa kasus, ekspresi yang diteruskan sebagai argumen ke makro bisa dievaluasi lebih dari sekali. http://msdn.microsoft.com/en-us/library/bf6bf4cf.aspx

  • makro diperluas pada waktu pra-kompilasi, Anda tidak dapat menggunakannya untuk debugging, tetapi Anda dapat menggunakan fungsi sebaris.

- artikel bagus : http://www.codeguru.com/forum/showpost.php?p=1093923&postcount=1

;

Ahmad
sumber
2

Fungsi inline akan mempertahankan semantik nilai, sedangkan makro preprocessor hanya menyalin sintaksnya. Anda bisa mendapatkan bug yang sangat halus dengan makro preprocessor jika Anda menggunakan argumen beberapa kali - misalnya jika argumen berisi mutasi seperti "i ++" yang mengeksekusi dua kali cukup mengejutkan. Fungsi sebaris tidak akan mengalami masalah ini.

Michael Donohue
sumber
1

Fungsi inline berperilaku secara sintaksis seperti fungsi normal, memberikan keamanan tipe dan ruang lingkup untuk variabel lokal fungsi dan akses ke anggota kelas jika itu adalah metode. Juga saat memanggil metode inline, Anda harus mematuhi pembatasan pribadi / dilindungi.

heeen
sumber
1

Untuk mengetahui perbedaan antara fungsi makro dan sebaris , pertama-tama kita harus mengetahui apa itu sebenarnya dan kapan kita harus menggunakannya.

FUNGSI :

int Square(int x){
return(x*X);
}
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
  • Pemanggilan fungsi memiliki overhead yang terkait dengannya, karena setelah fungsi selesai dieksekusi, ia harus tahu ke mana ia harus kembali dan juga perlu menyimpan nilai dalam memori tumpukan.

  • Untuk aplikasi kecil tidak akan menjadi masalah, tetapi mari kita ambil contoh aplikasi keuangan di mana ribuan transaksi terjadi setiap detik, kita tidak bisa menggunakan pemanggilan fungsi.

MACROS:

# define Square(x) x*x;
int main()
{
int value = 5;
int result = Square(value);
cout << result << endl;
}
  • Macro bekerja pada tahap preprocessing yaitu pada tahap ini pernyataan yang ditulis dengan # kata kunci akan diganti dengan content ie

hasil int = Persegi (x * x)

Tetapi makro memiliki bug yang terkait dengannya.

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

Di sini outputnya 11 bukan 36 .

FUNGSI INLINE :

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;
}

Hasil 36

Kata kunci inline meminta kompilator untuk mengganti pemanggilan fungsi dengan tubuh fungsi, di sini keluarannya benar karena ia mengevaluasi ekspresi terlebih dahulu dan kemudian diteruskan. Ini mengurangi overhead panggilan fungsi karena tidak perlu menyimpan alamat yang dikembalikan dan tumpukan memori tidak diperlukan untuk argumen fungsi.

Perbandingan Antara Makro dan Fungsi Inline:

  1. Makro bekerja melalui substitusi, sedangkan dalam Fungsi inline, pemanggilan fungsi diganti dengan badan.
  2. Makro rawan kesalahan karena substitusi sementara fungsi sebaris aman digunakan.
  3. Makro tidak memiliki alamat sedangkan fungsi sebaris memiliki alamat.
  4. Makro sulit digunakan dengan beberapa baris kode, sedangkan fungsi sebaris tidak.
  5. Dalam C ++, makro tidak dapat digunakan dengan fungsi anggota sedangkan fungsi sebaris bisa.

KESIMPULAN:

Fungsi sebaris terkadang lebih berguna daripada makro, karena meningkatkan kinerja dan aman digunakan serta mengurangi overhead panggilan fungsi juga. Ini hanya permintaan ke kompiler, fungsi tertentu tidak akan disebariskan seperti:

  • fungsi besar
  • fungsi yang memiliki terlalu banyak argumen bersyarat
  • kode dan kode rekursif dengan loop dll.

yang merupakan hal yang baik, karena itu adalah kapanpun kompilator berpikir lebih baik untuk melakukan sesuatu dengan cara lain.

Sheetal
sumber
Hanya sebagai komentar: Makro dapat diperbaiki untuk mengevaluasi ke angka yang sama dengan tanda kurung. Namun, ini masih rawan kesalahan, karena Anda perlu memikirkan tentang subsitusi bodoh mutlak dan semua kasus selama implementasi.
mike
0

Di GCC (saya tidak yakin tentang yang lain), mendeklarasikan fungsi sebaris, hanyalah petunjuk bagi kompiler. Terserah pada kompilator di penghujung hari untuk memutuskan apakah ia menyertakan tubuh fungsi kapan pun ia dipanggil.

Perbedaan antara fungsi in-line dan makro preprocessor relatif besar. Makro preprocessor hanyalah pengganti teks di penghujung hari. Anda menyerahkan banyak kemampuan bagi kompilator untuk melakukan pemeriksaan jenis pemeriksaan pada argumen dan jenis kembalian. Evaluasi argumen jauh berbeda (jika ekspresi yang Anda berikan ke fungsi memiliki efek samping, Anda akan bersenang-senang saat debugging). Ada perbedaan halus tentang di mana fungsi dan makro dapat digunakan. Misalnya jika saya memiliki:

#define MACRO_FUNC(X) ...

Di mana MACRO_FUNC dengan jelas mendefinisikan tubuh fungsi. Perhatian khusus perlu diberikan agar berfungsi dengan benar di semua kasus, suatu fungsi dapat digunakan, misalnya MACRO_FUNC yang ditulis dengan buruk akan menyebabkan kesalahan pada

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

Fungsi normal dapat digunakan tanpa masalah.

Falaina
sumber
0

Dari perspektif pengkodean, fungsi sebaris seperti fungsi. Jadi, perbedaan antara fungsi sebaris dan makro sama dengan perbedaan antara fungsi dan makro.

Dari perspektif kompilasi, fungsi sebaris mirip dengan makro. Itu disuntikkan langsung ke dalam kode, tidak dipanggil.

Secara umum, Anda harus mempertimbangkan fungsi inline sebagai fungsi biasa dengan beberapa pengoptimalan kecil yang tercampur. Dan seperti kebanyakan pengoptimalan, terserah pada kompilator untuk memutuskan apakah benar-benar peduli untuk menerapkannya. Seringkali kompilator akan dengan senang hati mengabaikan setiap upaya programmer untuk menyebariskan suatu fungsi, karena berbagai alasan.

Brian
sumber
0

Fungsi inline akan berperilaku sebagai pemanggilan fungsi jika terdapat pernyataan berulang atau rekursif di dalamnya, untuk mencegah eksekusi instruksi yang berulang. Ini cukup membantu untuk menyimpan keseluruhan memori program Anda.

Arashdeep singh
sumber
-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;
}

Makro biasanya lebih cepat daripada fungsi karena tidak melibatkan overhead panggilan fungsi yang sebenarnya.

Beberapa Kekurangan dari makro: Tidak ada pemeriksaan tipe. Sulit untuk melakukan debug karena menyebabkan penggantian yang sederhana. Makro tidak memiliki namespace, jadi makro di satu bagian kode dapat mempengaruhi bagian lain. Makro dapat menyebabkan efek samping seperti yang ditunjukkan pada contoh CUBE () di atas.

Makro biasanya satu baris. Namun, mereka dapat terdiri dari lebih dari satu baris. Tidak ada batasan seperti itu dalam fungsi.

rahul_107
sumber
Seberapa banyak kesenangan yang Anda dapatkan dari #define TWO_N(n) 2 << ndan kemudian cout << CUBE(TWO_N(3 + 1)) << endl;? (Lebih baik mengakhiri baris keluaran endldaripada memulainya.)
Jonathan Leffler