Berbagi teknologi

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

2024-07-12

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

Kata pengantar

di edisi terakhir[Bahasa C] - Penjelasan detail tentang prapemrosesan (Bagian 2) Selama studi, kami memperkenalkan pengetahuan yang relevan tentang makro dalam pra-pemrosesan secara mendetail. Saya yakin semua orang akan memperoleh banyak manfaat. Tenang saja, yuk pelajari terus ilmu lainnya tentang preprocessing pada edisi kali ini.

7.# dan ##

7.1 # Operator

  • # 运算符 Mengonversi argumen makro menjadi string literal.Itu diperbolehkan untuk muncul di daftar substitusi makro dengan parameter.
  • # 运算符Operasi yang dilakukan dapat dipahami sebagai "stringifikasi"

Maksudnya itu apa?
Mari kita buat bayangannya terlebih dahulu:

int mian()
{
	printf("hello"   "worldn");
	printf("helloworldn");
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Apa perbedaan antara dua baris kode di atas? Mari kita lihat bersama:

Masukkan deskripsi gambar di sini

Seperti yang Anda lihat, efek dari dua senar dan satu senar adalah sama.C语言会把两个字符串天然连成一个字符串, menambahkan spasi di tengah tidak ada gunanya.

Sekarang ada pemandangan seperti ini:

int main()
{
	int a = 1;
	printf("The value of a is %dn", a);

	int b = 20;
	printf("The value of b is %dn", b);

	float f = 8.5f;
	printf("The value of f is %fn", f);

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

Kami menemukan bahwa logika dari tiga baris kode tersebut sangat相像Ya, tapi ada些许不同
  
Jadi kami pikir karena keduanya sangat mirip, bolehkah kami memasangnya封装成一个函数, untuk kemudahan penggunaan?
Namun fungsi tidak dapat melakukan fungsi ini.
lalu apa yang harus kita lakukan?
Kita bisa mencoba menggunakan makro untuk mengatasinya

#define Print(n, format) printf("The value of n is " format "n", n)

int main()
{
	int a = 1;
	Print(a, "%d");
	//printf("The value of a is %dn", a);

	int b = 20;
	Print(b, "%d");
	//printf("The value of b is %dn", b);

	float f = 8.5f;
	Print(f, "%f");
	//printf("The value of f is %fn", f);

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

  
hasil operasi:

Masukkan deskripsi gambar di sini

kami menemukan tidak adaN Itu tidak berubah, jadi bagaimana saya harus memodifikasinya?
Di sinilah # operator kami harus digunakan:# Mengonversi argumen makro menjadistring literal,Sekarang tidak adaN menjadi “n” “n”N

Saat ini, kita bisa menggunakan metode penyambungan menjadi

#define Print(n, format) printf("The value of " #n " is " format "n", n)
  • 1

  
tidak tahu?Anda akan memahaminya setelah membaca penjelasan di bawah ini.
Masukkan deskripsi gambar di sini

  

7.2 ## operator

  ## dapat ditemukan di dalam dirinyaSimbol di kedua sisi digabungkan menjadi satu simbol, yang memungkinkan definisi makro从分离的文本片段创建标识符## disebuttandai perekatan .Sambungan seperti itu harus menghasilkan a合法indikator, jika tidak, hasilnya tidak terdefinisi.
  
Kami telah mengatakan sebelumnya bahwa untuk menulis fungsi yang menemukan nilai lebih besar dari dua angka, Anda perlu menulis fungsi berbeda untuk tipe data berbeda.

int int_max(int x, int y)
{
	return x > y ? x : y;
}
float float_max(float x, float y)
{
	return x > y ? x : y;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

  

Masukkan deskripsi gambar di sini

Hal ini pasti terlalu rumit, dan ada banyak kesamaan antara kedua fungsi tersebut.
Apakah ada cara untuk membuat fungsi seperti itu dengan cepat?seperti sebuah fungsiCetakanSama, cukup terapkan suatu fungsi dan itu akan keluar
  
Kita bisa menulisnya seperti ini

#define GENERIC_MAX(type) 
		type type##_max(type x, type y)
		{
			return x > y ? x : y;
		}
  • 1
  • 2
  • 3
  • 4
  • 5

  

#define GENERIC_MAX(type) 
		type type##_max(type x, type y)
		{
			return x > y ? x : y;
		}

GENERIC_MAX(int);    //相当于定义了一个函数int_max
GENERIC_MAX(float);  //相当于定义了一个函数float_max

int main()
{
	int r1 = int_max(3, 5);
	printf("%dn", r1);
	float r2 = float_max(2.3f, 7.6f);
	printf("%fn", r2);
	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

  
hasil operasi:

Masukkan deskripsi gambar di sini

  
Kita juga bisa gcc gccGcc Amati hasil praproses di lingkungan.i file, memiliki pemahaman yang lebih intuitif

Masukkan deskripsi gambar di sini

Tentu saja, fungsi yang dihasilkan dengan cara ini juga tidak nyaman untuk di-debug.

Lalu di sini ## Peran apa yang dimainkannya?

menambahkan ##, kompiler akan menganggapnya sebagai simbol
Mari kita lihat Kanga## Memengaruhi:

Masukkan deskripsi gambar di sini

  

8. Konvensi Penamaan

Secara umum, sintaks untuk menggunakan fungsi dan makro sangat mirip语言本身没法帮我们区分二者
  
Salah satu kebiasaan kita yang biasa adalah:

  • Beri nama makrohuruf besar semua
  • Nama fungsiJangan gunakan huruf besar semua

Tentu saja aturan penamaan ini tidak bersifat mutlak
Misalnya mengimbangi mengimbangiHaitidakmelihatT Makro ini ditulis dengan huruf kecil semua
  
Catatan mengimbangi mengimbangiHaitidakmelihatT Ini digunakan untuk menghitung offset anggota struktur relatif terhadap posisi awal struktur.
  

Sembilan,# tidak terdefinisi tidak terdefinisikamkamkamkamuNDBahasa Inggris:F

  # tidak terdefinisi tidak terdefinisikamkamkamkamuNDBahasa Inggris:F Instruksi digunakan untukHapus definisi makro

Masukkan deskripsi gambar di sini
Kode di atas digunakan pada baris 169# tidak terdefinisi tidak terdefinisikamkamkamkamuNDBahasa Inggris:F Menghapus makro MAX.Tidak ada masalah saat memanggil 168 baris sebelum penghapusan, tetapi kesalahan akan dilaporkan saat memanggil 170 baris setelah penghapusan.

  

10. Definisi baris perintah

Banyak kompiler C (tidak termasuk VS) menyediakan kemampuan untuk mendefinisikan simbol pada baris perintah.Digunakan untuk memulai proses kompilasi
  
  Misalnya : Fitur ini berguna ketika kita ingin mengkompilasi versi program yang berbeda berdasarkan file sumber yang sama. (Misalkan sebuah array dengan panjang tertentu dideklarasikan dalam suatu program. Jika memori mesin terbatas, kita memerlukan array yang sangat kecil, tetapi jika memori mesin lain lebih besar, array yang kita perlukan bisa lebih besar)

Masukkan deskripsi gambar di sini

Masukkan deskripsi gambar di sini

Definisi baris perintah sudah masuktahap pra-pemrosesanDiproses, pada tahap preprocessing, pada kode di atas seger segerdia Nilai telah ditentukan

  

11. Kompilasi bersyarat

Saat mengkompilasi suatu program, sangat sulit untuk mengkompilasi atau meninggalkan satu (sekelompok pernyataan).方便 dari.karena kita bisa menggunakannyaarahan kompilasi bersyarat
  
Instruksi kompilasi bersyarat adalah kode ini我想让你编译就编译,不想让你编译你就不要编译了 . Kita dapat menetapkan kondisi untuknya. Jika kondisinya benar, kode ini akan dikompilasi. Jika kondisinya salah, kode ini tidak akan dikompilasi.
  
Misalnya:
Akan sangat disayangkan untuk menghapus beberapa kode debugging, tapi itu akan menjadi cara untuk menyimpannya, sehingga kita dapat mengkompilasinya secara selektif.
  

Masukkan deskripsi gambar di sini

  
Instruksi kompilasi bersyarat yang umum digunakan:

11.1. Kompilasi bersyarat satu cabang

#if 常量表达式
    //···
#endif
  • 1
  • 2
  • 3

Masukkan deskripsi gambar di sini

  

11.2. Kompilasi bersyarat multi-cabang

#if  常量表达式
	//···
#elif  常量表达式
	//···
#else
	//···
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

Pernyataan mana yang benar, pernyataan itu dijalankan

#define M 1
int main()
{
#if M == 0
	printf("hellon");
#elif M == 1
	printf("worldn");
#elif M == 2
	printf("csdnn");
#endif
	printf("886n");

	return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

  

11.3. Tentukan apakah itu didefinisikan

#if defined(symbol)
#ifdef symbol

//上面两个的反面
if !defined(symbol)
#ifndef symbol
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Masukkan deskripsi gambar di sini

  

11.4. Instruksi bersarang

#if defined(OS_UNIX)

	#ifdef OPTION1
		unix_version_option1();
	#endif
	
	#ifdef OPTION2
		unix_version_option2();
	#endif
	
#elif defined(OS_MSDOS)

	#ifdef OPTION2
		msdos_version_option2();
	#endif
	
#endif
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

  

12. Penyertaan file header

12.1 Bagaimana file header disertakan

(1) Bagaimana file lokal disertakan

# include "filename"
  • 1

Strategi pencarian: masuk pertamaDirektori proyek tempat file sumber beradaJika file header tidak ditemukan, kompiler akan mencari file header fungsi perpustakaan dengan cara yang sama.Pencarian lokasi standarberkas kepala
Jika Anda tidak dapat menemukannya lagiKesalahan Kompilasi
  
Linux dan LinuxSayadi dalamBahasa Inggris Jalur file header standar lingkungan (tempat file header ditempatkan):

 /usr/include
  • 1

Jalur file header standar untuk lingkungan VS:

C:Program Files (x86)Microsoft Visual Studio 12.0VCinclude
//这是VS2013的默认路径
  • 1
  • 2

  

(2) File perpustakaan berisi

#include <filename.h>
  • 1

Temukan file header dan langsung kejalur standarPergi dan cari, dan jika Anda tidak dapat menemukannya, Anda akan mendapat perintah.Kesalahan Kompilasi
  
Apakah ini berarti dapat juga digunakan untuk file perpustakaan?“ ” Formulir berisi
jawabannya adalahmenegaskanYa, tapi pencarian dilakukan dengan cara inikurang efisien, tentu saja demikian jugatidak mudah untuk membedakannyaApakah itu file perpustakaan atau file lokal?
  

12.2 Penyertaan file bersarang

Setelah mempelajari sebelumnya (kompilasi dan linking), kita mengetahui bahwa file header termasuk dalam tahap preprocessing.直接将该文件的代码拷贝到包含头文件的地方
  
Jika file header dimasukkan 10 kali, sebenarnya dikompilasi 10 kali. Jika dimasukkan berulang kali, tekanan pada kompilasi akan lebih besar.

uji . c uji.cTBahasa Inggris: aku sT.C

#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
#include "test.h"
int main()
{
return 0;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

uji .h uji.hTBahasa Inggris: aku sT.H

void test();
struct Stu
{
int id;
char name[20];
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Namun dalam sebuah proyek, sebuah file pasti akan dimasukkan berkali-kali, jadi bagaimana cara mengatasi masalah ini?
Menjawab:kompilasi bersyarat

#ifndef __TEST_H__
#define __TEST_H__
		//头文件的内容
#endif
  • 1
  • 2
  • 3
  • 4

  
Bagaimana cara memahaminya?

  • Saat memasukkan file header untuk pertama kalinya, haruskah file tersebut dikompilasi?menilai terlebih dahulu
  • Simbol __TEST_H__ tidaktak terdefinisikaninginmenyusun
  • Segera setelah ituTentukan simbol __TEST_H__
  • Kemudian sertakan kembali file header dan temukan __TEST_H__telah didefinisikantidak lagiUntuk file header yang disertakan nanti, lakukanmenyusun

Namun cara penulisan di atas lebih merepotkan. Ada cara penulisan lain:

#pragma once
  • 1

Efeknya sama dengan cara di atas
Hal ini untuk menghindari pengenalan berulang-ulang file header
  

13. Instruksi pra-pemrosesan lainnya

#error
#pragma
#line
···
#pragma pack()//在结构体部分介绍
  • 1
  • 2
  • 3
  • 4
  • 5

Teman yang berminat bisa membaca "Anatomi Mendalam Bahasa C"
  
  
  
  
  


   Oke, sekian ilmu tentang preprocessing pada edisi kali ini. Semoga blog ini dapat bermanfaat bagi anda. Pada saat yang sama, mohon koreksi saya jika ada kesalahan, dan mari kita membuat kemajuan bersama dalam pembelajaran bahasa C!