Κοινή χρήση τεχνολογίας

[Γλώσσα C] - Λεπτομερής επεξήγηση της προεπεξεργασίας (Μέρος 2)

2024-07-12

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

Πρόλογος

στο τελευταίο τεύχος[Γλώσσα C] - Λεπτομερής επεξήγηση της προεπεξεργασίας (Μέρος 2) Κατά τη διάρκεια της μελέτης, εισαγάγαμε τις σχετικές γνώσεις σχετικά με τις μακροεντολές στην προεπεξεργασία με λεπτομέρεια, πιστεύω ότι όλοι θα κερδίσουν πολλά. Μην ανησυχείτε, ας συνεχίσουμε να μαθαίνουμε άλλες γνώσεις σχετικά με την προεπεξεργασία σε αυτό το τεύχος.

7. # και ##

7.1 # Χειριστής

  • # 运算符 Μετατρέπει ένα όρισμα μιας μακροεντολής σε μια κυριολεκτική συμβολοσειρά.Επιτρέπεται να εμφανίζεται στη λίστα αντικατάστασης μακροεντολών με παραμέτρους.
  • # 运算符Η επέμβαση που εκτελείται μπορεί να γίνει κατανοητή ως "stringification"

Τι σημαίνει αυτό;
Ας κάνουμε πρώτα μια πρόβλεψη:

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

Ποια είναι η διαφορά μεταξύ των παραπάνω δύο γραμμών κώδικα; Ας ρίξουμε μια ματιά μαζί:

Εισαγάγετε την περιγραφή της εικόνας εδώ

Όπως μπορείτε να δείτε, το αποτέλεσμα δύο χορδών και μιας χορδής είναι το ίδιο.C语言会把两个字符串天然连成一个字符串, η προσθήκη ενός κενού στη μέση είναι άχρηστη.

Τώρα υπάρχει μια σκηνή όπως αυτή:

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

Βρήκαμε ότι η λογική των τριών γραμμών κώδικα είναι πολύ相像Ναι, αλλά υπάρχουν些许不同
  
Σκεφτήκαμε λοιπόν, αφού μοιάζουν τόσο, θα μπορούσαμε να τα βάλουμε封装成一个函数, για ευκολία στη χρήση;
Αλλά οι συναρτήσεις δεν μπορούν να κάνουν αυτή τη λειτουργία.
τότε τι πρέπει να κάνουμε;
Μπορούμε να προσπαθήσουμε να χρησιμοποιήσουμε μακροεντολές για να το λύσουμε

#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

  
αποτέλεσμα λειτουργίας:

Εισαγάγετε την περιγραφή της εικόνας εδώ

ανακαλύπτουμε nnn Δεν έχει αλλάξει, οπότε πώς πρέπει να το τροποποιήσω;
Εδώ πρέπει να χρησιμοποιείται ο # τελεστής μας:# Μετατρέπει ένα όρισμα μιας μακροεντολής σεχορδή κυριολεκτικά,Τώρα αμέσως nnn γίνομαι “n” “n”n

Αυτή τη στιγμή, μπορούμε να χρησιμοποιήσουμε τη μέθοδο ματίσματος για να γίνει

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

  
δεν ξέρω;Θα καταλάβετε αφού διαβάσετε την παρακάτω εξήγηση.
Εισαγάγετε την περιγραφή της εικόνας εδώ

  

7.2 ## χειριστής

  ## μπορεί να εντοπιστεί σε αυτόνΤα σύμβολα και στις δύο πλευρές συνδυάζονται σε ένα σύμβολο, το οποίο επιτρέπει τον ορισμό μακροεντολών从分离的文本片段创建标识符## λέγεταικόλληση σημαδιού .Μια τέτοια σύνδεση πρέπει να παράγει α合法ένδειξη, διαφορετικά το αποτέλεσμα είναι απροσδιόριστο.
  
Έχουμε ξαναπεί ότι για να γράψετε μια συνάρτηση που βρίσκει τη μεγαλύτερη τιμή δύο αριθμών, πρέπει να γράψετε διαφορετικές συναρτήσεις για διαφορετικούς τύπους δεδομένων.

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

  

Εισαγάγετε την περιγραφή της εικόνας εδώ

Αυτό είναι αναπόφευκτα πολύ δυσκίνητο και υπάρχουν πολλές ομοιότητες μεταξύ των δύο λειτουργιών.
Υπάρχει κάποιος τρόπος να δημιουργηθεί γρήγορα μια τέτοια λειτουργία;σαν συνάρτησηΜούχλαΤο ίδιο, απλά εφαρμόστε μια συνάρτηση και θα βγει
  
Μπορούμε να γράψουμε ένα τέτοιο

#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

  
αποτέλεσμα λειτουργίας:

Εισαγάγετε την περιγραφή της εικόνας εδώ

  
Μπορούμε επίσης gcc gccσολcc Παρατηρήστε τα προεπεξεργασμένα αποτελέσματα στο περιβάλλον.i αρχείο, έχουν μια πιο διαισθητική κατανόηση του

Εισαγάγετε την περιγραφή της εικόνας εδώ

Φυσικά, η συνάρτηση που δημιουργείται με αυτόν τον τρόπο είναι επίσης άβολη για τον εντοπισμό σφαλμάτων.

Τότε εδώ ## Τι ρόλο παίζει;

Προσθήκη ##, ο μεταγλωττιστής θα νομίζει ότι είναι σύμβολο
Ας ρίξουμε μια ματιά στο Kanga## Αποτέλεσμα:

Εισαγάγετε την περιγραφή της εικόνας εδώ

  

8. Σύμβαση Ονομασίας

Σε γενικές γραμμές, η σύνταξη για τη χρήση συναρτήσεων και μακροεντολών είναι πολύ παρόμοια, επομένως语言本身没法帮我们区分二者
  
Μία από τις συνήθεις συνήθειές μας είναι:

  • Ονομάστε τη μακροεντολήόλα κεφαλαία
  • Όνομα συνάρτησηςΜη χρησιμοποιείτε όλα τα καπάκια

Φυσικά, αυτοί οι κανόνες ονομασίας δεν είναι απόλυτοι
για παράδειγμα όφσετ μετατόπισηοffβλt Αυτή η μακροεντολή είναι γραμμένη με πεζά
  
Σημείωση όφσετ μετατόπισηοffβλt Χρησιμοποιείται για τον υπολογισμό της μετατόπισης των μελών της δομής σε σχέση με την αρχική θέση της δομής.
  

Εννέα,# undef undefunρεμιφά

  # undef undefunρεμιφά Οδηγίες χρησιμοποιούνται για ναΚαταργήστε έναν ορισμό μακροεντολής

Εισαγάγετε την περιγραφή της εικόνας εδώ
Ο παραπάνω κωδικός χρησιμοποιείται στη γραμμή 169# undef undefunρεμιφά Καταργήθηκε η μακροεντολή MAX.Δεν υπάρχει πρόβλημα κατά την κλήση των γραμμών 168 πριν από την αφαίρεση, αλλά θα αναφερθεί σφάλμα κατά την κλήση των γραμμών 170 μετά την αφαίρεση.

  

10. Ορισμός γραμμής εντολών

Πολλοί μεταγλωττιστές C (χωρίς το VS) παρέχουν τη δυνατότητα ορισμού συμβόλων στη γραμμή εντολών.Χρησιμοποιείται για την έναρξη της διαδικασίας μεταγλώττισης
  
  Για παράδειγμα : Αυτή η δυνατότητα είναι χρήσιμη όταν θέλουμε να μεταγλωττίσουμε διαφορετικές εκδόσεις ενός προγράμματος που βασίζονται στο ίδιο αρχείο προέλευσης. (Ας υποθέσουμε ότι ένας πίνακας συγκεκριμένου μήκους δηλώνεται σε ένα πρόγραμμα. Εάν η μνήμη του μηχανήματος είναι περιορισμένη, χρειαζόμαστε έναν πολύ μικρό πίνακα, αλλά εάν η μνήμη ενός άλλου μηχανήματος είναι μεγαλύτερη, ο πίνακας που χρειαζόμαστε μπορεί να είναι μεγαλύτερος)

Εισαγάγετε την περιγραφή της εικόνας εδώ

Εισαγάγετε την περιγραφή της εικόνας εδώ

Ο ορισμός της γραμμής εντολών είναι μέσαστάδιο προεπεξεργασίαςΕπεξεργάζεται, στο στάδιο της προεπεξεργασίας, στον παραπάνω κώδικα sz szsz Η τιμή του έχει καθοριστεί

  

11. Σύνταξη υπό όρους

Κατά τη μεταγλώττιση ενός προγράμματος, είναι πολύ δύσκολο να μεταγλωττίσετε ή να εγκαταλείψετε ένα (μια ομάδα δηλώσεων).方便 του.γιατί μπορούμε να χρησιμοποιήσουμεοδηγία για την κατάρτιση υπό όρους
  
Η υπό όρους εντολή μεταγλώττισης είναι αυτός ο κώδικας我想让你编译就编译,不想让你编译你就不要编译了 . Μπορούμε να ορίσουμε μια συνθήκη για αυτόν Εάν η συνθήκη είναι αληθής, αυτός ο κωδικός θα μεταγλωττιστεί.
  
Για παράδειγμα:
Θα ήταν κρίμα να διαγράψουμε κάποιο κώδικα εντοπισμού σφαλμάτων, αλλά θα ήταν εμπόδιο να τον διατηρήσουμε, ώστε να μπορούμε να τον μεταγλωττίσουμε επιλεκτικά.
  

Εισαγάγετε την περιγραφή της εικόνας εδώ

  
Οδηγίες σύνταξης υπό όρους που χρησιμοποιούνται συνήθως:

11.1 Μονοκλαδική υπό όρους σύνταξη

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

Εισαγάγετε την περιγραφή της εικόνας εδώ

  

11.2 Σύνταξη πολλαπλών κλάδων υπό όρους

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

Ποια πρόταση είναι αληθής, αυτή η πρόταση εκτελείται

#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 Προσδιορίστε εάν ορίζεται

#if defined(symbol)
#ifdef symbol

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

Εισαγάγετε την περιγραφή της εικόνας εδώ

  

11.4 Ένθετες οδηγίες

#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. Συμπερίληψη αρχείων κεφαλίδας

12.1 Πώς περιλαμβάνονται τα αρχεία κεφαλίδας

(1) Πώς περιλαμβάνονται τα τοπικά αρχεία

# include "filename"
  • 1

Στρατηγική αναζήτησης: πρώτη μέσαΟ κατάλογος του έργου όπου βρίσκονται τα αρχεία προέλευσηςΕάν το αρχείο κεφαλίδας δεν βρεθεί, ο μεταγλωττιστής θα αναζητήσει το αρχείο κεφαλίδας της συνάρτησης βιβλιοθήκης με τον ίδιο τρόπο.Τυπική αναζήτηση τοποθεσίαςεπικεφαλής αρχείο
Εάν δεν μπορείτε να το βρείτε ξανάΣφάλμα μεταγλώττισης
  
L inux Linuxμεγάλοσεux Η τυπική διαδρομή αρχείου κεφαλίδας του περιβάλλοντος (όπου τοποθετείται το αρχείο κεφαλίδας):

 /usr/include
  • 1

Τυπική διαδρομή αρχείου κεφαλίδας για περιβάλλον VS:

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

  

(2) Το αρχείο της βιβλιοθήκης περιέχει

#include <filename.h>
  • 1

Βρείτε το αρχείο κεφαλίδας και μεταβείτε απευθείας στοτυπική διαδρομήΠηγαίνετε και αναζητήστε και αν δεν μπορείτε να το βρείτε, θα λάβετε ένα μήνυμα.Σφάλμα μεταγλώττισης
  
Αυτό σημαίνει ότι μπορεί να χρησιμοποιηθεί και για αρχεία βιβλιοθήκης;“ ” Η φόρμα περιέχει
η απάντηση είναιβεβαιώνωΝαι, αλλά η αναζήτηση γίνεται με αυτόν τον τρόπολιγότερο αποτελεσματική, φυσικά και αυτό ισχύειδεν είναι εύκολο να διακριθείΕίναι αρχείο βιβλιοθήκης ή τοπικό αρχείο;
  

12.2 Συμπερίληψη ένθετων αρχείων

Αφού μελετήσουμε το προηγούμενο (σύνδεση και σύνδεση), γνωρίζουμε ότι το αρχείο κεφαλίδας περιλαμβάνεται στο στάδιο της προεπεξεργασίας.直接将该文件的代码拷贝到包含头文件的地方
  
Εάν ένα αρχείο κεφαλίδας συμπεριληφθεί 10 φορές, στην πραγματικότητα μεταγλωττίζεται 10 φορές, εάν περιλαμβάνεται επανειλημμένα, η πίεση στη μεταγλώττιση θα είναι μεγαλύτερη.

δοκιμή . γ τεστ.γtest.ντο

#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

δοκιμή . h τεστ.ηtest.η

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

Αλλά σε ένα έργο, ένα αρχείο θα συμπεριληφθεί αναπόφευκτα πολλές φορές, οπότε πώς να λύσετε αυτό το πρόβλημα;
Απάντηση:υπό όρους σύνταξη

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

  
Πώς να το καταλάβετε;

  • Όταν συμπεριλαμβάνεται το αρχείο κεφαλίδας για πρώτη φορά, πρέπει να γίνει μεταγλώττιση;κρίνετε πρώτα
  • Το σύμβολο __TEST_H__ δεν είναιμη καθορισμένοθέλωσυντάσσω
  • Αμέσως μετάΟρίστε το σύμβολο __TEST_H__
  • Στη συνέχεια, συμπεριλάβετε ξανά το αρχείο κεφαλίδας και βρείτε __TEST_H__έχει οριστείόχι πιαΓια το αρχείο κεφαλίδας που περιλαμβάνεται αργότερα, εκτελέστεσυντάσσω

Ωστόσο, ο παραπάνω τρόπος γραφής είναι πιο ενοχλητικός.

#pragma once
  • 1

Το αποτέλεσμα είναι το ίδιο με την παραπάνω μέθοδο
Αυτό αποφεύγει την επαναλαμβανόμενη εισαγωγή αρχείων κεφαλίδας
  

13. Άλλες οδηγίες προεπεξεργασίας

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

Οι ενδιαφερόμενοι φίλοι μπορούν να διαβάσουν "Σε βάθος Ανατομία της Γλώσσας Γ"
  
  
  
  
  


   Εντάξει, αυτές είναι όλες οι γνώσεις σχετικά με την προεπεξεργασία σε αυτό το τεύχος, ελπίζω ότι αυτό το ιστολόγιο μπορεί να σας βοηθήσει. Ταυτόχρονα, παρακαλώ διορθώστε με αν υπάρχουν λάθη και ας προχωρήσουμε μαζί στο δρόμο της εκμάθησης της γλώσσας C!