Berbagi teknologi

[Bahasa C] - Penjelasan detail tentang prapemrosesan (Bagian 1)

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

1. Simbol yang telah ditentukan sebelumnya

Ada beberapa pengaturan dalam bahasa CSimbol yang telah ditentukan sebelumnya, dapat digunakan secara langsung, simbol yang telah ditentukan sebelumnya adalah在预处理期间处理的

__FILE__    //进行编译的源文件
__LINE__	//文件当前的行号
__DATE__	//文件被编译日期
__TIME__	//文件被编译那一瞬的时间
__STDC__	//如果编译器遵循ANSI C(标准C),其值为1,否则未定义(报错)
  • 1
  • 2
  • 3
  • 4
  • 5

  
Contoh:

#include<stdio.h>

int main()
{
	printf("%sn", __FILE__);
	printf("%dn", __LINE__);
	printf("%sn", __DATE__);
	printf("%sn", __TIME__);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  
hasil operasi:

Masukkan deskripsi gambar di sini

Saya ingin menyebutkan di sini bahwa VS tidak sepenuhnya mendukung ANSI C (standar C); gcc gccGcc Mendukung ANSI C (Standar C)
  

dua,# Definisikan definisikanDBahasa Inggris:Fdi dalamBahasa Inggris: Definisikan konstanta (simbol)

Sintaks dasar:

# define name stuff
  • 1

Misalnya:

#define MAX 100
#define reg register  // 为 register 这个关键字创建一个简短的名字
#define do_forever for(;;)  //用更形象的符号来替换一种实现
#define CASE break;case		//在写 case 语句的时候自动吧 break 写上

//如果定义的 stuff 过长,可以分成几行写,除了最后一行外,每行的后面都加一个反斜杠(续行符)
#define DEBUG_PRINT printf("file:%stline:%dt 
							date:%sttime:%sn" ,
							__FILE__,__LINE__ , 
							__DATE__,__TIME__ )
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • Kalimat kedua adalah kemalasan, kenapa?
  • Kalimat ketiga untuk untukFatau Bundar初始化、判断、调整都可以省略, tetapi jika penilaiannya dihilangkan, berarti kondisi penilaiannya selalu benar, yaituputaran tak terbatas
  • Sebaiknya hal ini tidak dilakukan pada game keempat, karena dapat dengan mudah menimbulkan masalah.
  • Kalimat kelimakarakter kelanjutan garisIni untuk mencegah masalah setelah percabangan转义Kembali回车符 , sehingga pengangkutan kembali bukan lagi pengangkutan kembali.Tidak boleh ada apa pun setelah karakter kelanjutan baris, tekan“” Tekan saja Enter, jika tidak maka kelanjutannya bukan baris kode berikut.

  
Sekarang masalahnya: gunakan # Definisikan definisikanDBahasa Inggris:Fdi dalamBahasa Inggris: Saat mendefinisikan pengenal, haruskah Anda menambahkannya di akhir?

Misalnya:

#define MAX 1000
#define MAX 1000;

int main()
{
	int n = MAX;
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Masukkan deskripsi gambar di sini

Kode di atas ditambah, memang terkesan sedikit mubazir, namun tidak berdampak pada jalannya program.
Tampaknya menambahkan Atau jangan tambahkan Apakah akan baik-baik saja?
apakah benar seperti ini?
  
Mari kita lihat contoh berikut:

//例一
int main()
{
	printf("%dn", MAX);
	return 0;
}

//例二
int mian()
{
	int max = 0;
	if (1)
		max = MAX;
	else
		max = 1;
	return 0
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

Setelah penggantian:

printf("%dn", 1000;);
  • 1

Mencetak1000;Apa artinya?

if (1)
	max = 1000;
	;
else
	max = 1;
  • 1
  • 2
  • 3
  • 4
  • 5

yang lain yang lainBahasa Inggris:akumelihat Siapa yang cocok?
  
Soalnya, ada yang salah dengan ini, jadimenggunakan # Definisikan definisikanDBahasa Inggris:Fdi dalamBahasa Inggris: Saat mendefinisikan suatu indikator, jangan menambahkannya setelahnya

  

tiga,# Definisikan definisikanDBahasa Inggris:Fdi dalamBahasa Inggris: Definisikan makro

  # Definisikan definisikanDBahasa Inggris:Fdi dalamBahasa Inggris: Mekanisme tersebut mencakup ketentuan,允许把参数替换到文本中, implementasi ini sering disebutMakro ( marco ) (marco)sayarkoatau Definisikan makro ( definisikanmakro) (definisikanmakro)DBahasa Inggris:Fdi dalamBahasa Inggris:sayamenyilang
Perbedaan makro dengan pengidentifikasi definisi makro di atas adalah:Makro memiliki parameter
  
Berikut cara makro dideklarasikan:

#define name(parament - list) stuff
  • 1

salah satu diantara mereka parament paramentPARsayaBahasa Inggris:NT - daftar daftarakuSayaST(daftar parameter) adalah daftar simbol yang dipisahkan koma yang mungkin muncul di barang barangSTkamkamutidak tengah
Catatan: parament paramentPARsayaBahasa Inggris:NT - daftar daftarakuSayaST(daftar parameter)Tanda kurung kiriHarus bersama nama namanamaBahasa Inggris: di sebelah, jika ada spasi di antara keduanya, daftar argumen ditafsirkan sebagai barang barangSTkamkamutidak bagian dari.
  

Contoh:

//实现一个宏,计算一个数的平方
#define SQUARE(x) x*x

int main()
{
	int a = 5;
	int ret = SQUARE(a);
	printf("%dn", ret);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  
hasil operasi:

Masukkan deskripsi gambar di sini

Seperti yang Anda lihat, kuadrat 5 dihitung dengan benar

Namun nyatanya ada masalah pada kode di atas. Silakan lihat cuplikan kode berikut:

int a = 5;
printf("%dn", SQUARE(a + 1));
  • 1
  • 2

  
hasil operasi:

Masukkan deskripsi gambar di sini

Mengapa demikian? Hasil dari 5+1 adalah 36, dan 6 ∗ * 6 seharusnya 36. Bagaimana kamu dapat 11?

Masalahnya ada pada makro, kita tahu宏是直接替换, maka hasil langsung mengganti kode diatas adalah:

printf("%dn", a+1*a+1);
  • 1

5 + 6 + 1, hasilnya tentu saja 11

Kita dapat dengan mudah mengatasi masalah ini dengan menambahkan tanda kurung di kedua sisi definisi makro.

#define SQUARE(x) (x)*(x)
  • 1

Jadi apakah tidak ada masalah dengan definisi ini?Mari kita lihat definisi makro berikut

#define DOUBLE(X) (X)+(X)
  • 1

Kami menggunakan tanda kurung dalam definisi untuk menghindari masalah sebelumnya, namun makro ini dapat menyebabkan kesalahan baru.

int a = 5;
printf("%dn", 10 * DOUBLE(a));
  • 1
  • 2

  
hasil operasi:

Masukkan deskripsi gambar di sini

Hasil keluarannya bukan 100 melainkan 55. Alasannya masih sama seperti di atas Pertanyaan prioritas
  
Larutan:

#define DOUBLE(X) ((X)+(X))
  • 1

Singkatnya, saat menggunakan makroJangan pernah berhemat pada tanda kurunguntuk menghindari interaksi yang tidak terduga antar operator dalam parameter atau operator yang berdekatan saat menggunakan makro.

  

4. Parameter makro dengan efek samping

Ketika parameter makro muncul lebih dari sekali dalam definisi makro, jika parameter tersebut memilikiefek samping, maka Anda mungkin berada dalam bahaya saat menggunakan makro ini, sehingga mengakibatkan不可预测 hasil dari. Efek samping adalah efek permanen ketika ekspresi dievaluasi.
  
Misalnya:

x + 1; //不带副作用
x++//带副作用
  • 1
  • 2

Makro MAX berikut menunjukkan masalah yang disebabkan oleh parameter dengan efek samping.

#define MAX(a,b) ((a) > (b) ? (a):(b))

int main()
{
	int x = 5;
	int y = 8;
	int z = MAX(x++, y++);
	printf("x=%d, y=%d, z=%dn", x, y, z);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

  
hasil operasi:

Masukkan deskripsi gambar di sini

Mengapa demikian?Mari kita analisa bersama

z = ((X++) > (y++) ? (x++) : (y++))
  • 1
  • Pertama buatlah penilaian: ukuran besarX++ dengan Y ykamu++ penilaian, karena merupakan postfix ++, saat menilai ukuran besarX adalah 5, Y ykamu adalah 8, 8 &gt; 5
  • Setelah menilai ukuran besarX adalah 6, Y ykamu untuk 9
  • Kemudian jalankan Y ykamu ++, karena di postfix ++ maka hasilnya 9
  • Kemudian lanjutkan Y ykamu Lakukan peningkatan otomatis, Y ykamu Hasil akhirnya adalah 10

kami akan ukuran besarX Dan Y ykamu Saat diteruskan ke makro, hasilnya terutama berubah Y ykamu, setelah dua perubahan, Anda bilang itu tidak buruk
Ketika parameter dengan efek samping diteruskan ke makro, dan parameter tersebut muncul lebih dari sekali di makro, efek samping parameter juga lebih dari satu kali.
  

5. Aturan Penggantian Makro

Perluas dalam program # Definisikan definisikanDBahasa Inggris:Fdi dalamBahasa Inggris: Ada beberapa langkah yang terlibat saat mendefinisikan simbol dan makro

  • Saat memanggil makro, terlebih dahulu参数进行检查, lihat apakah berisi # Definisikan definisikanDBahasa Inggris:Fdi dalamBahasa Inggris: Didefinisikan标识符 .Kalau iya, diganti dulu
  • Teks pengganti menyusul被插入ke lokasi asli dalam program, dengan nama parameter makro diganti dengan nilainya
#define MAX(a,b) ((a) > (b) ? (a):(b))
#define M 10

int main()
{
	int x = 5;
	int z = MAX(x, M);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • MAKS ( x , M ) MAKS( x , M )MAX(X,M) tengah MMM pertama kali diganti dengan 10, dan 10 dimasukkan ke dalam aslinya MMM lokasi
  • Terakhir, hasilnya dipindai lagi untuk melihat apakah ada yang disebabkan oleh # Definisikan definisikanDBahasa Inggris:Fdi dalamBahasa Inggris: simbol yang ditentukan, jika ya, ulangi proses di atas

MAX pada kode di atas juga diwakili oleh # Definisikan definisikanDBahasa Inggris:Fdi dalamBahasa Inggris: DidefinisikanMakro .Pada pemeriksaan terakhir, parameter M-nya telah diganti kali ini.
   MAKS ( x , 10 ) MAKS(x, 10)MAX(X,10) digantikan oleh ((x) &gt; (10) ((x) &gt; (10)((X)>(10) ? ? ? Bahasa Indonesia: (x): (10)) (x):(10))(X):(10))
  
tentu,宏里面嵌套宏itu juga baik-baik saja

MAX(x, MAX(2, 3))
  • 1

Saat ini, ganti makro terlebih dahulu di parameter, lalu ganti seluruh makro
Namun perlu diperhatikan bahwa ini不是递归, yang hanya merupakan makro yang berfungsi sebagai argumen terhadap makro lainnya.递归是宏内部又调用了宏本身
Juga, ketika praprosesor mencari # Definisikan definisikanDBahasa Inggris:Fdi dalamBahasa Inggris: Saat mendefinisikan suatu simbol,Isi konstanta string tidak dicari
Maksudnya itu apa?Berikan saja kastanye dan Anda akan mengerti.

#define M 10

int main()
{
	int x = 5;
	int z = MAX(x, MAX(2, 3));
	printf("M = %d", x);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

Dalam kode di atasprintf("M = %d", x);tengah MMM tidak akan digantikan oleh 10

  

6. Perbandingan antara makro dan fungsi

Makro di atas digunakan untuk mencari nilai lebih besar dari dua bilangan. Kita dapat menuliskannya sepenuhnya sebagai sebuah fungsi.

int Max(int x, int y)
{
	return x > y ? x : y;
}
  • 1
  • 2
  • 3
  • 4

Kami menemukan bahwa semuanya mempunyai fungsi yang sama.Namun untuk fungsi "mencari bilangan terbesar dari dua bilangan", penulisannya akan menggunakan makro更有优势Beberapa
  
Ada dua alasan:

  • digunakan untuk调用函数Dan函数返回darikodemungkin memerlukan waktu lebih lama daripada melakukan perhitungan kecil ini (saat memanggil fungsiBuat bingkai tumpukan ).Oleh karena itu, fungsi rasio makro ada di dalam program规模Dan速度Lebih baik dari yang pernah ada
  • Lebih penting函数的参数必须声明类型 , yang menyebabkan fungsi hanya digunakan pada ekspresi dengan tipe yang sesuai.Sebaliknya, makro ini dapat diterapkan ke banyak tipe: integer, integer panjang, floating point, dll
    > Untuk membandingkan.Parameter makronya adalahKetik mandiridari

Apakah kita akan selalu menggunakan makro di masa depan?Sebenarnya makro hanya digunakan untukPerhitungan sederhana, tidak cocok untuk operasi dan fungsi yang kompleks dan besar dibandingkan dengan makro Kekurangan

  1. Setiap kali makro digunakan, salinan kode definisi makro dimasukkan ke dalam program.Kecuali jika makronya relatif pendek, hal itu mungkin terjadi大幅度增加程序的长度
  2. makro adalah没法调试dari
  3. Makro tidak bergantung pada tipe, yaitu,不够严谨
  4. Makro dapat menyebabkan masalah prioritas operasi, menyebabkan program tersebut容易出错

Namun terkadang, makro dapat melakukan hal-hal yang tidak dapat dilakukan oleh fungsi
  
Misalnya:

int* p = (int*)malloc(10 * sizeof * (int));
  • 1

Kami tidak suka menulis seperti ini malok maloksayaIIok Fungsinya terlalu rumit, saya ingin大小Dan类型Berikan untuk membuka ruang

Malloc(10, int);
  • 1

Bisakah fungsi melakukan ini?Tidak karenaFungsi tidak dapat meneruskan tipe
Dan makro bisa melakukannya, karena makro tidakJangan periksaparameter Andajenisdari

#define Malloc(n, type) (type*)malloc(n * sizeof(type))

int main()
{
	int* p = Malloc(10, int);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

  
Perbandingan makro dan fungsi:

Atribut# Definisikan definisikanDBahasa Inggris:Fdi dalamBahasa Inggris:Definisikan makrofungsi
panjang kodeSetiap kali digunakan, kode makro akan dimasukkan ke dalam program. Kecuali makro yang sangat kecil, panjang program akan bertambah secara signifikan.Nilai kode fungsi muncul di satu tempat, dan setiap kali fungsi digunakan, kode yang sama di tempat itu dipanggil.
Kecepatan eksekusilebih cepatAda overhead tambahan dari pemanggilan dan pengembalian fungsi (membuka frame tumpukan), jadi mari kita mulai dengan yang lebih lambat.
prioritas operatorParameter makro dievaluasi dalam konteks semua ekspresi di sekitarnya. Kecuali jika tanda kurung disertakan, prioritas operator yang berdekatan mungkin memiliki konsekuensi yang tidak dapat diprediksi. Oleh karena itu, disarankan agar makro ditulis dengan lebih banyak tanda kurung.Parameter fungsi hanya dievaluasi satu kali saat fungsi dipanggil, dan nilai hasilnya diteruskan ke fungsi, sehingga hasil evaluasi ekspresi lebih dapat diprediksi.
Parameter dengan efek sampingParameter dapat diganti ke beberapa posisi di makro. Jika parameter makro dievaluasi beberapa kali, evaluasi parameter dengan efek samping dapat menghasilkan hasil yang tidak terduga.Parameter fungsi hanya dipanggil satu kali saat meneruskan parameter, dan hasilnya lebih mudah diprediksi.
Tipe ParameterParameter makro tidak ada hubungannya dengan tipe. Selama operasi pada parameter legal, dia dapat digunakan untuk tipe parameter apa pun.Parameter suatu fungsi berkaitan dengan tipe. Jika parameternya memiliki tipe yang berbeda, fungsi yang berbeda diperlukan, meskipun fungsi tersebut menjalankan tugas yang sama.
men-debugMakro tidak nyaman untuk di-debugFungsi dapat di-debug langkah demi langkah
pengulanganMakro tidak bisa bersifat rekursifFungsi bisa bersifat rekursif

Apakah ada cara untuk menggabungkan kelebihannya?
Diperkenalkan di C++fungsi sebaris sebaris sebarisdi dalamakudi dalamBahasa Inggris: ——Memiliki keunggulan makro dan fungsi
Ini dijalankan secepat makro tetapi memiliki efek yang sama dengan suatu fungsi