Answers:
භාවිතා extern
කිරීම අදාළ වන්නේ ඔබ ගොඩනඟන වැඩසටහන එකට සම්බන්ධ වී ඇති බහු මූලාශ්ර ලිපිගොනු වලින් වන අතර, එහිදී සමහර විචල්යයන් අර්ථ දක්වා ඇත, නිදසුනක් ලෙස, ප්රභව ගොනුවේ file1.c
වෙනත් මූලාශ්ර ලිපිගොනු වල සඳහන් කිරීම අවශ්ය වේ file2.c
.
එය වැදගත් වේ අතර ඇති වෙනස තේරුම් නිර්වචනය විචල්ය හා ප්රකාශ විචල්ය :
විචල්යයක් පවතින බව සම්පාදකයාට දන්වන විට විචල්යයක් ප්රකාශයට පත් කෙරේ (මෙය එහි වර්ගයයි); එය එම අවස්ථාවේදී විචල්යය සඳහා ආචයනය වෙන් නොකරයි.
සම්පාදකයා විචල්යය සඳහා ආචයනය වෙන් කරන විට විචල්යයක් අර්ථ දැක්වේ .
ඔබට විචල්යය කිහිප වතාවක් ප්රකාශ කළ හැකිය (වරක් ප්රමාණවත් වුවද); ඔබට එය අර්ථ දැක්විය හැක්කේ යම් විෂය පථයක් තුළ එක් වරක් පමණි. විචල්ය අර්ථ දැක්වීමක් ද ප්රකාශයකි, නමුත් සියලු විචල්ය ප්රකාශයන් අර්ථ දැක්වීම් නොවේ.
ගෝලීය විචල්යයන් ප්රකාශ කිරීමට හා අර්ථ දැක්වීමට පිරිසිදු, විශ්වසනීය ක්රමය වන්නේ විචල්යයේ extern
ප්රකාශයක් අඩංගු කිරීම සඳහා ශීර්ෂ ගොනුවක් භාවිතා කිරීමයි .
විචල්යය නිර්වචනය කරන එක් ප්රභව ගොනුවක් සහ විචල්යය සඳහන් කරන සියලුම ප්රභව ගොනු මගින් ශීර්ෂකය ඇතුළත් වේ. සෑම වැඩසටහනක් සඳහාම, එක් ප්රභව ගොනුවක් (සහ එක් ප්රභව ගොනුවක් පමණක්) විචල්යය අර්ථ දක්වයි. ඒ හා සමානව, එක් ශීර්ෂ ගොනුවක් (සහ එක් ශීර්ෂ ගොනුවක් පමණක්) විචල්යය ප්රකාශ කළ යුතුය. ශීර්ෂ ගොනුව ඉතා වැදගත් ය; එය ස්වාධීන TUs (පරිවර්තන ඒකක - මූලාශ්ර ලිපිගොනු සිතන්න) අතර හරස් පරීක්ෂා කිරීමට ඉඩ සලසයි.
එය කිරීමට වෙනත් ක්රම තිබුණද, මෙම ක්රමය සරල හා විශ්වාසදායකය. එය නිරූපණය කරන්නේ file3.h
, file1.c
සහ file2.c
:
extern int global_variable; /* Declaration of the variable */
#include "file3.h" /* Declaration made available here */
#include "prog1.h" /* Function declarations */
/* Variable defined here */
int global_variable = 37; /* Definition checked against declaration */
int increment(void) { return global_variable++; }
#include "file3.h"
#include "prog1.h"
#include <stdio.h>
void use_it(void)
{
printf("Global variable: %d\n", global_variable++);
}
ගෝලීය විචල්යයන් ප්රකාශ කිරීමට හා අර්ථ දැක්වීමට හොඳම ක්රමය එයයි.
ඊළඟ ලිපිගොනු දෙක සඳහා මූලාශ්රය සම්පූර්ණ කරයි prog1
:
සම්පූර්ණ වැඩසටහන් භාවිත ශ්රිත භාවිතා කරයි, එබැවින් ශ්රිත ප්රකාශන ක්රියාත්මක වී ඇත. C99 සහ C11 යන දෙකටම ඒවා භාවිතා කිරීමට පෙර ප්රකාශ කිරීම හෝ නිර්වචනය කිරීම අවශ්ය වේ (C90 එසේ නොකළ නමුත් හොඳ හේතු නිසා). extern
ශීර්ෂයන්හි extern
විචල්ය ප්රකාශන ඉදිරියේ ගැලපීම සඳහා මම ශීර්ෂයන්හි ක්රියාකාරී ප්රකාශන ඉදිරිපිට යතුරු පදය භාවිතා කරමි . බොහෝ අය extern
ක්රියාකාරී ප්රකාශ ඉදිරියේ භාවිතා නොකිරීමට කැමැත්තක් දක්වයි ; සම්පාදකයා එය ගණන් ගන්නේ නැත - අවසාන වශයෙන්, ඔබ ස්ථාවර වන තාක් කල්, අවම වශයෙන් ප්රභව ගොනුවක් තුළවත් මම නොසිටිමි.
extern void use_it(void);
extern int increment(void);
#include "file3.h"
#include "prog1.h"
#include <stdio.h>
int main(void)
{
use_it();
global_variable += 19;
use_it();
printf("Increment: %d\n", increment());
return 0;
}
prog1
භාවිතා prog1.c
, file1.c
, file2.c
, file3.h
හා prog1.h
.ගොනුව පමණක් prog1.mk
makefile වේ prog1
. make
සහස්රයේ ආරම්භයේ සිට නිපදවන ලද බොහෝ සංස්කරණ සමඟ එය ක්රියා කරනු ඇත . එය විශේෂයෙන් GNU Make සමඟ බැඳී නොමැත.
# Minimal makefile for prog1
PROGRAM = prog1
FILES.c = prog1.c file1.c file2.c
FILES.h = prog1.h file3.h
FILES.o = ${FILES.c:.c=.o}
CC = gcc
SFLAGS = -std=c11
GFLAGS = -g
OFLAGS = -O3
WFLAG1 = -Wall
WFLAG2 = -Wextra
WFLAG3 = -Werror
WFLAG4 = -Wstrict-prototypes
WFLAG5 = -Wmissing-prototypes
WFLAGS = ${WFLAG1} ${WFLAG2} ${WFLAG3} ${WFLAG4} ${WFLAG5}
UFLAGS = # Set on command line only
CFLAGS = ${SFLAGS} ${GFLAGS} ${OFLAGS} ${WFLAGS} ${UFLAGS}
LDFLAGS =
LDLIBS =
all: ${PROGRAM}
${PROGRAM}: ${FILES.o}
${CC} -o $@ ${CFLAGS} ${FILES.o} ${LDFLAGS} ${LDLIBS}
prog1.o: ${FILES.h}
file1.o: ${FILES.h}
file2.o: ${FILES.h}
# If it exists, prog1.dSYM is a directory on macOS
DEBRIS = a.out core *~ *.dSYM
RM_FR = rm -fr
clean:
${RM_FR} ${FILES.o} ${PROGRAM} ${DEBRIS}
විශේෂ experts යින් විසින් පමණක් බිඳ දැමිය යුතු නීති, සහ හොඳ හේතු සහිතව පමණි:
ශීර්ෂ ගොනුවක අඩංගු extern
වන්නේ විචල්ය ප්රකාශයන් පමණි - කිසි විටෙක
static
හෝ සුදුසුකම් නොලත් විචල්ය අර්ථ දැක්වීම්.
ඕනෑම විචල්යයක් සඳහා, එය ප්රකාශ කරන්නේ එක් ශීර්ෂ ගොනුවක් පමණි (SPOT - Single Point of Truth).
ප්රභව ගොනුවක කිසි විටෙක extern
විචල්ය ප්රකාශ අඩංගු නොවේ - ප්රභව ලිපිගොනු සෑම විටම ඒවා ප්රකාශ කරන (එකම) ශීර්ෂය ඇතුළත් වේ.
ඕනෑම විචල්යයක් සඳහා, හරියටම එක් ප්රභව ගොනුවක් විචල්යය නිර්වචනය කරයි. (පැහැදිලිවම ශුන්යයට ආරම්භ කිරීමේ අවශ්යතාවයක් නොතිබුණද, එයින් කිසිදු හානියක් සිදු නොවන අතර හොඳක් කළ හැකිය. මන්දයත් වැඩසටහනක නිශ්චිත ගෝලීය විචල්යයක් සඳහා ආරම්භක අර්ථ දැක්වීමක් පමණක් තිබිය හැකි බැවිනි).
විචල්යය නිර්වචනය කරන ප්රභව ගොනුවට අර්ථ දැක්වීම සහ ප්රකාශනය අනුකූල බව සහතික කිරීම සඳහා ශීර්ෂකය ද ඇතුළත් වේ.
ශ්රිතයක් කිසි විටෙක විචල්යයක් භාවිතා කරමින් ප්රකාශ කිරීමට අවශ්ය නොවිය යුතුය extern
.
හැකි සෑම විටම ගෝලීය විචල්යයන්ගෙන් වළකින්න - ඒ වෙනුවට කාර්යයන් භාවිතා කරන්න.
මෙම පිළිතුරේ ප්රභව කේතය සහ පෙළ src / so-0143-3204 උප බහලුමේ ඇති GitHub හි මගේ SOQ (Stack Overflow Questions) ගබඩාවේ ඇත .
ඔබ පළපුරුදු සී ක්රමලේඛකයෙකු නොවේ නම්, ඔබට මෙහි කියවීම නැවැත්විය හැකිය (සමහර විට).
සමහර (ඇත්ත වශයෙන්ම, බොහෝ) සී සම්පාදකයන් සමඟ, විචල්යයක 'පොදු' අර්ථ දැක්වීමෙන් ද ඔබට ගැලවිය හැකිය. 'පොදු', මෙහි, ෆෝට්රාන් හි මූලාශ්ර ලිපිගොනු අතර විචල්යයන් හුවමාරු කර ගැනීම සඳහා භාවිතා කරන තාක්ෂණයකි. මෙහි සිදුවන්නේ එක් එක් ලිපිගොනු ගණන විචල්යය පිළිබඳ තාවකාලික අර්ථ දැක්වීමකි. එක් ගොනුවකට වඩා ආරම්භක අර්ථ දැක්වීමක් ලබා නොදෙන තාක් කල්, විවිධ ලිපිගොනු විචල්යයේ පොදු තනි අර්ථ දැක්වීමක් බෙදා ගනී:
#include "prog2.h"
long l; /* Do not do this in portable code */
void inc(void) { l++; }
#include "prog2.h"
long l; /* Do not do this in portable code */
void dec(void) { l--; }
#include "prog2.h"
#include <stdio.h>
long l = 9; /* Do not do this in portable code */
void put(void) { printf("l = %ld\n", l); }
මෙම තාක්ෂණය සී ප්රමිතියේ අකුරට හා 'එක් අර්ථ දැක්වීමේ රීතියට' අනුකූල නොවේ - එය නිල වශයෙන් නිර්වචනය නොකළ හැසිරීමකි:
බාහිර සම්බන්ධතා සහිත අනන්යතාවයක් භාවිතා කරයි, නමුත් වැඩසටහනේ අනන්යතාවය සඳහා හරියටම එක් බාහිර අර්ථ දැක්වීමක් නොපවතී, නැතහොත් හඳුනාගැනීමේ යන්ත්රය භාවිතා නොකෙරේ.
ක බාහිර අර්ථ දැක්වීම ද කාර්යය (ක පේළිගත අර්ථ දැක්වීම හැර) හෝ වස්තුවක් පිළිබඳ අර්ථ නිරූපනයක් බව බාහිර ප්රකාශ කිරීමකි. බාහිර සම්බන්ධතාවයකින් ප්රකාශිත හඳුනාගැනීමක් ප්රකාශනයක භාවිතා කරන්නේ නම් (ප්රති result ල පූර්ණ සංඛ්යා නියතයක් වන ක්රියාකරුගේ
sizeof
හෝ_Alignof
ක්රියාකරුගේ ක්රියාකාරිත්වයේ කොටසක් හැර ), සමස්ත වැඩසටහනේ කොතැනක හෝ හඳුනාගැනීමේ යන්ත්රයට හරියටම එක් බාහිර අර්ථ දැක්වීමක් තිබිය යුතුය; එසේ නොමැති නම්, එකකට වඩා තිබිය යුතු නොවේ. 161)
161) මේ අනුව, ප්රකාශනයක බාහිර සම්බන්ධතාවයකින් ප්රකාශිත හඳුනාගැනීමක් භාවිතා නොකරන්නේ නම්, ඒ සඳහා බාහිර අර්ථ දැක්වීමක් අවශ්ය නොවේ.
කෙසේ වෙතත්, සී ප්රමිතිය එය තොරතුරු දිග ඇනෙක්ස් ජේ හි පොදු දිගුවක් ලෙස ලැයිස්තුගත කරයි .
වස්තුවක් හදුනා ගැනීම සඳහා බාහිර අර්ථ දැක්වීම් එකකට වඩා තිබිය හැකිය, බාහිර වචන පදය පැහැදිලිව භාවිතා කිරීම හෝ රහිතව; අර්ථ දැක්වීම් එකඟ නොවන්නේ නම් හෝ එකකට වඩා ආරම්භ කර ඇත්නම්, හැසිරීම නිර්වචනය නොකෙරේ (6.9.2).
මෙම තාක්ෂණය සැමවිටම සහාය නොදක්වන බැවින්, එය භාවිතා කිරීමෙන් වැළකී සිටීම වඩාත් සුදුසුය, විශේෂයෙන් ඔබේ කේතය අතේ ගෙන යා හැකි නම් . මෙම තාක්ෂණය භාවිතා කිරීමෙන් ඔබට නොදැනුවත්වම ද pun ුවම් පැමිණවිය හැකිය.
ඉහත ලිපිගොනු වලින් එකක් ඒ වෙනුවට
ආදේශකයක් l
ලෙස ප්රකාශයට පත් කළ හොත් , සී වර්ගයේ අනාරක්ෂිත සම්බන්ධකයන් නොගැලපීම හඳුනා නොගනී. ඔබ 64-bit සමග පරිගණකයේ නම් හා , ඔබ පවා අනතුරු ඇඟවීමක් ලබා ගැනීමට නොහැකි කැමතියි; 32-බිට් සහ 64-බිට් සහිත යන්ත්රයක , ඔබට විවිධ ප්රමාණ ගැන අනතුරු ඇඟවීමක් ලැබෙනු ඇත - සම්බන්ධකය විශාලතම ප්රමාණය භාවිතා කරනු ඇත, හරියටම ෆෝට්රාන් වැඩසටහනක් ඕනෑම පොදු කුට්ටි වල විශාලතම ප්රමාණය ගනී.double
long
long
double
long
double
2020-05-07 දින නිකුත් කරන ලද GCC 10.1.0, භාවිතා කිරීමට පෙරනිමි සම්පාදන විකල්පයන් වෙනස් කරන බව සලකන්න,
-fno-common
එයින් අදහස් වන්නේ පෙරනිමියෙන් ඔබ පෙරනිමිය ඉක්මවා නොයන්නේ නම් -fcommon
(හෝ ගුණාංග භාවිතා කිරීම යනාදිය) - සබැඳිය බලන්න).
ඊළඟ ලිපිගොනු දෙක සඳහා මූලාශ්රය සම්පූර්ණ කරයි prog2
:
extern void dec(void);
extern void put(void);
extern void inc(void);
#include "prog2.h"
#include <stdio.h>
int main(void)
{
inc();
put();
dec();
put();
dec();
put();
}
prog2
භාවිතා prog2.c
, file10.c
, file11.c
, file12.c
, prog2.h
.මෙහි අදහස් දැක්වීම්වල සඳහන් කර ඇති පරිදි, ඒ හා සමාන ප්රශ්නයකට මගේ පිළිතුරෙහි සඳහන් කර ඇති පරිදි , ගෝලීය විචල්යයක් සඳහා බහුවිධ නිර්වචන භාවිතා කිරීම නිර්වචනය නොකළ හැසිරීමට තුඩු දෙයි (J.2; .6.9), එය “ඕනෑම දෙයක් සිදුවිය හැකිය” යනුවෙන් සම්මතයේ ක්රමයයි. සිදුවිය හැකි එක් දෙයක් නම්, වැඩසටහන ඔබ අපේක්ෂා කළ පරිදි ක්රියා කිරීමයි; සහ J.5.11, දළ වශයෙන්, "ඔබ ලැබීමට වඩා බොහෝ විට ඔබ වාසනාවන්ත විය හැකිය". නමුත් බාහිර විචල්යයක බහුවිධ නිර්වචන මත රඳා පවතින වැඩසටහනක් - පැහැදිලි 'බාහිර' මූල පදය සමඟ හෝ නැතිව - දැඩි ලෙස අනුකූල වන වැඩසටහනක් නොවන අතර සෑම තැනකම වැඩ කිරීමට සහතික නොවේ. සමානව: එය පෙන්විය හැකි හෝ නොපෙන්වන දෝෂයක් අඩංගු වේ.
ඇත්ත වශයෙන්ම, මෙම මාර්ගෝපදේශ බිඳ දැමිය හැකි බොහෝ ක්රම තිබේ. ඇතැම් විට, මාර්ගෝපදේශ කඩ කිරීමට හොඳ හේතුවක් තිබිය හැකි නමුත් එවැනි අවස්ථාවන් අතිශයින්ම අසාමාන්ය ය.
int some_var; /* Do not do this in a header!!! */
සටහන 1: ශීර්ෂකය යතුරුපදය නොමැතිව විචල්යය නිර්වචනය කරන්නේ නම්, ශීර්ෂය extern
ඇතුළත් සෑම ගොනුවක්ම විචල්යයේ තාවකාලික අර්ථ දැක්වීමක් නිර්මාණය කරයි. කලින් සඳහන් කළ පරිදි, මෙය බොහෝ විට ක්රියාත්මක වනු ඇත, නමුත් සී ප්රමිතිය එය ක්රියාත්මක වන බවට සහතික නොවේ.
int some_var = 13; /* Only one source file in a program can use this */
සටහන 2: ශීර්ෂකය විචල්යය නිර්වචනය කර ආරම්භ කරන්නේ නම්, ලබා දී ඇති වැඩසටහනක එක් ප්රභව ගොනුවකට පමණක් ශීර්ෂකය භාවිතා කළ හැකිය. ශීර්ෂයන් මූලික වශයෙන් තොරතුරු හුවමාරු කර ගැනීම සඳහා වන බැවින්, එක් වරක් පමණක් භාවිතා කළ හැකි එකක් නිර්මාණය කිරීම තරමක් මෝඩය.
static int hidden_global = 3; /* Each source file gets its own copy */
සටහන 3: ශීර්ෂකය ස්ථිතික විචල්යයක් නිර්වචනය කරන්නේ නම් (ආරම්භය සමඟ හෝ රහිතව), එවිට සෑම ප්රභව ගොනුවක්ම 'ගෝලීය' විචල්යයේ පුද්ගලික අනුවාදය සමඟ අවසන් වේ.
විචල්යය සැබවින්ම සංකීර්ණ අරාවක් නම්, උදාහරණයක් ලෙස, මෙය කේතයේ අන්ත අනුපිටපතකට තුඩු දිය හැකිය. එය, ඉඳහිට, යම් බලපෑමක් ලබා ගැනීම සඳහා සංවේදී ක්රමයක් විය හැකි නමුත් එය ඉතා අසාමාන්යය.
මා මුලින් පෙන්වූ ශීර්ෂ තාක්ෂණය භාවිතා කරන්න. එය විශ්වසනීයව හා සෑම තැනකම ක්රියා කරයි. විශේෂයෙන් ශීර්ෂකය ප්රකාශයට පත් කරන බව සලකන්නglobal_variable
එය භාවිතා කරන සෑම ගොනුවකම ඇතුළත් කර ඇති . සෑම දෙයක්ම ස්වයං-අනුකූල බව මෙයින් සහතික කෙරේ.
කාර්යයන් ප්රකාශ කිරීම හා නිර්වචනය කිරීම හා සමාන උත්සුකයන් පැන නගී - ප්රතිසම නීති අදාළ වේ. නමුත් ප්රශ්නය විශේෂයෙන් විචල්යයන් ගැන විය, එබැවින් මම විචල්යයන්ට පමණක් පිළිතුර තබා ඇත.
ඔබ පළපුරුදු සී ක්රමලේඛකයෙකු නොවේ නම්, ඔබ බොහෝ විට මෙහි කියවීම නැවැත්විය යුතුය.
ප්රධාන ප්රධාන එකතු කිරීම
මෙහි විස්තර කර ඇති 'ශීර්ෂයන්හි ප්රකාශයන්, ප්රභවයේ අර්ථ දැක්වීම්' යාන්ත්රණය පිළිබඳව සමහර විට (සහ නීත්යානුකූලව) මතු වන එක් කරුණක් නම්, සමමුහුර්තව තබා ගත යුතු ලිපිගොනු දෙකක් තිබීමයි - ශීර්ෂකය සහ ප්රභවය. මෙය සාමාන්යයෙන් අනුගමනය කරනුයේ සාර්ව භාවිතා කළ හැකි වන අතර එමඟින් ශීර්ෂකය ද්විත්ව රාජකාරියක් ඉටු කරයි - සාමාන්යයෙන් විචල්යයන් ප්රකාශ කරයි, නමුත් ශීර්ෂකය ඇතුළත් කිරීමට පෙර නිශ්චිත සාර්වයක් සකසා ඇති විට එය විචල්යයන් අර්ථ දක්වයි.
තවත් කරුණක් නම්, එක් එක් 'ප්රධාන වැඩසටහන්' ගණනක විචල්යයන් නිර්වචනය කළ යුතු බවයි. මෙය සාමාන්යයෙන් ව්යාජ කරුණකි; විචල්යයන් නිර්වචනය කිරීමට සහ එක් එක් වැඩසටහන් සමඟ නිපදවන වස්තු ගොනුව සම්බන්ධ කිරීමට ඔබට සී ප්රභව ගොනුවක් හඳුන්වා දිය හැකිය.
මෙහි දක්වා ඇති මුල් ගෝලීය විචල්යය භාවිතා කරමින් සාමාන්ය යෝජනා ක්රමයක් මේ ආකාරයට ක්රියා කරයි file3.h
:
#ifdef DEFINE_VARIABLES
#define EXTERN /* nothing */
#else
#define EXTERN extern
#endif /* DEFINE_VARIABLES */
EXTERN int global_variable;
#define DEFINE_VARIABLES
#include "file3a.h" /* Variable defined - but not initialized */
#include "prog3.h"
int increment(void) { return global_variable++; }
#include "file3a.h"
#include "prog3.h"
#include <stdio.h>
void use_it(void)
{
printf("Global variable: %d\n", global_variable++);
}
ඊළඟ ලිපිගොනු දෙක සඳහා මූලාශ්රය සම්පූර්ණ කරයි prog3
:
extern void use_it(void);
extern int increment(void);
#include "file3a.h"
#include "prog3.h"
#include <stdio.h>
int main(void)
{
use_it();
global_variable += 19;
use_it();
printf("Increment: %d\n", increment());
return 0;
}
prog3
භාවිතා prog3.c
, file1a.c
, file2a.c
, file3a.h
, prog3.h
.පෙන්වා ඇති පරිදි මෙම යෝජනා ක්රමයේ ඇති ගැටළුව නම් එය ගෝලීය විචල්යය ආරම්භ කිරීම සඳහා ලබා නොදීමයි. C99 හෝ C11 සහ මැක්රෝස් සඳහා විචල්ය තර්ක ලැයිස්තු සමඟ, ඔබට ආරම්භයට සහය දැක්වීම සඳහා සාර්ව අර්ථ දැක්විය හැකිය. (C89 සහ මැක්රෝස් වල විචල්ය තර්ක ලැයිස්තු සඳහා සහය නොදක්වන හෙයින්, අත්තනෝමතික ලෙස දිගු ආරම්භකයින් හැසිරවීමට පහසු ක්රමයක් නොමැත.)
#ifdef DEFINE_VARIABLES
#define EXTERN /* nothing */
#define INITIALIZER(...) = __VA_ARGS__
#else
#define EXTERN extern
#define INITIALIZER(...) /* nothing */
#endif /* DEFINE_VARIABLES */
EXTERN int global_variable INITIALIZER(37);
EXTERN struct { int a; int b; } oddball_struct INITIALIZER({ 41, 43 });
ඩෙනිස් ක්නියාෂෙව් විසින් හඳුනාගෙන ඇති දෝෂ නිවැරදි
කිරීම #if
සහ අන්තර්ගතය ආපසු හරවන්න#else
#define DEFINE_VARIABLES
#include "file3b.h" /* Variables now defined and initialized */
#include "prog4.h"
int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }
#include "file3b.h"
#include "prog4.h"
#include <stdio.h>
void use_them(void)
{
printf("Global variable: %d\n", global_variable++);
oddball_struct.a += global_variable;
oddball_struct.b -= global_variable / 2;
}
ඔඩ්බෝල් ව්යුහය සඳහා වන කේතය ඔබ සාමාන්යයෙන් ලිවීමට කැමති දෙයක් නොවන බව පැහැදිලිය. දෙවන පිහිට පැතීමක් කළ පළමු තර්කය INITIALIZER
වන්නේ { 41
අතර ඉතිරි තර්කය (මෙම උදාහරණය තුල ඒක වචන) යනු 43 }
. C99 හෝ මැක්රෝස් සඳහා විචල්ය තර්ක ලැයිස්තු සඳහා සමාන සහය නොමැතිව, කොමාව අඩංගු කළ යුතු ආරම්භකයින් ඉතා ගැටළු සහගතය.
ඩෙනිස් ක්නියාෂෙව්ට නිවැරදි ශීර්ෂකය file3b.h
ඇතුළත් කර ඇත (වෙනුවට fileba.h
)
ඊළඟ ලිපිගොනු දෙක සඳහා මූලාශ්රය සම්පූර්ණ කරයි prog4
:
extern int increment(void);
extern int oddball_value(void);
extern void use_them(void);
#include "file3b.h"
#include "prog4.h"
#include <stdio.h>
int main(void)
{
use_them();
global_variable += 19;
use_them();
printf("Increment: %d\n", increment());
printf("Oddball: %d\n", oddball_value());
return 0;
}
prog4
භාවිතා prog4.c
, file1b.c
, file2b.c
, prog4.h
, file3b.h
.ඕනෑම ශීර්ෂයක් නැවත ඇතුළත් කිරීමට එරෙහිව ආරක්ෂා කළ යුතුය, එවිට වර්ග අර්ථ දැක්වීම් (enum, struct හෝ Union වර්ග, හෝ සාමාන්යයෙන් typedefs) ගැටළු ඇති නොකරයි. සම්මත තාක්ෂණය නම් ශීර්ෂකයේ සිරුර ශීර්ෂ ආරක්ෂකයා තුළ ඔතා තැබීමයි:
#ifndef FILE3B_H_INCLUDED
#define FILE3B_H_INCLUDED
...contents of header...
#endif /* FILE3B_H_INCLUDED */
ශීර්ෂය දෙවරක් වක්රව ඇතුළත් කළ හැකිය. නිදසුනක් ලෙස, පෙන්වා නැති ආකාරයේ අර්ථ දැක්වීමක් සඳහා file4b.h
ඇතුළත් වන්නේ නම්
සහ ශීර්ෂකය යන දෙකම භාවිතා කිරීමට අවශ්ය file3b.h
නම් සහ file1b.c
විසඳීමට ඔබට තවත් උපක්රමශීලී ගැටළු කිහිපයක් තිබේ. පැහැදිලිවම, ඔබට ශීර්ෂ ලැයිස්තුව සංශෝධනය කළ හැකිය . කෙසේ වෙතත්, ඔබ අභ්යන්තර පරායත්තතාවයන් ගැන නොදැන සිටිය හැකිය - සහ කේතය ඉතා මැනවින් අඛණ්ඩව ක්රියාත්මක විය යුතුය.file4b.h
file3b.h
file4b.h
තව දුරටත්, එය අර්ථ දැක්වීමට ඇතුළත් file4b.h
කිරීමට පෙර ඔබ ඇතුළත් කළ හැකි නිසා එය ව්යාකූල වීමට පටන් ගනී file3b.h
, නමුත් සාමාන්ය ශීර්ෂක ආරක්ෂකයින් file3b.h
විසින් ශීර්ෂකය නැවත ඇතුළත් කිරීම වලක්වනු ඇත.
එබැවින්, ඔබට file3b.h
එකවරම ශරීරය ප්රකාශන සඳහාද, එක් වරක් නිර්වචන සඳහාද ඇතුළත් කළ යුතුය, නමුත් ඔබට තනි පරිවර්තන ඒකකයක (TU - ප්රභව ගොනුවක එකතුවක් සහ එය භාවිතා කරන ශීර්ෂයන්) අවශ්ය විය හැකිය.
කෙසේ වෙතත්, එය අසාධාරණ නොවන සීමාවකට යටත්ව කළ හැකිය. අපි නව ගොනු නාම මාලාවක් හඳුන්වා දෙමු:
external.h
බාහිර සාර්ව අර්ථ දැක්වීම් සඳහා.
file1c.h
වර්ග නිර්වචනය කිරීමට (විශේෂයෙන්, struct oddball
වර්ගය oddball_struct
).
file2c.h
ගෝලීය විචල්යයන් නිර්වචනය කිරීමට හෝ ප්රකාශ කිරීමට.
file3c.c
එය ගෝලීය විචල්යයන් අර්ථ දක්වයි.
file4c.c
එය සරලවම ගෝලීය විචල්යයන් භාවිතා කරයි.
file5c.c
එයින් පෙන්නුම් කරන්නේ ඔබට ගෝලීය විචල්යයන් ප්රකාශ කිරීමට හා නිර්වචනය කිරීමට හැකි බවයි.
file6c.c
එයින් පෙන්නුම් කරන්නේ ඔබට ගෝලීය විචල්යයන් නිර්වචනය කර (ප්රකාශ කිරීමට උත්සාහ කළ හැකි) බවයි.
මෙම උදාහරණ වලදී, file5c.c
සහ file6c.c
ශීර්ෂය file2c.h
කිහිප වතාවක් සෘජුවම ඇතුළත් කරන්න
, නමුත් යාන්ත්රණය ක්රියාත්මක වන බව පෙන්වීමට ඇති සරලම ක්රමය එයයි. එහි අර්ථය වන්නේ ශීර්ෂය දෙවරක් වක්රව ඇතුළත් කර ඇත්නම් එයද ආරක්ෂිත බවයි.
මෙය ක්රියාත්මක වීමට ඇති සීමාවන්:
ගෝලීය විචල්යයන් නිර්වචනය කිරීම හෝ ප්රකාශ කිරීම සඳහා වන ශීර්ෂකය කිසිදු වර්ගයක් නිර්වචනය නොකරයි.
විචල්යයන් නිර්වචනය කළ යුතු ශීර්ෂයක් ඇතුළත් කිරීමට පෙර, ඔබ සාර්ව DEFINE_VARIABLES අර්ථ දක්වයි.
විචල්යයන් නිර්වචනය කිරීම හෝ ප්රකාශ කිරීම ශීර්ෂයෙහි ශෛලීගත අන්තර්ගතයන් ඇත.
/*
** This header must not contain header guards (like <assert.h> must not).
** Each time it is invoked, it redefines the macros EXTERN, INITIALIZE
** based on whether macro DEFINE_VARIABLES is currently defined.
*/
#undef EXTERN
#undef INITIALIZE
#ifdef DEFINE_VARIABLES
#define EXTERN /* nothing */
#define INITIALIZE(...) = __VA_ARGS__
#else
#define EXTERN extern
#define INITIALIZE(...) /* nothing */
#endif /* DEFINE_VARIABLES */
#ifndef FILE1C_H_INCLUDED
#define FILE1C_H_INCLUDED
struct oddball
{
int a;
int b;
};
extern void use_them(void);
extern int increment(void);
extern int oddball_value(void);
#endif /* FILE1C_H_INCLUDED */
/* Standard prologue */
#if defined(DEFINE_VARIABLES) && !defined(FILE2C_H_DEFINITIONS)
#undef FILE2C_H_INCLUDED
#endif
#ifndef FILE2C_H_INCLUDED
#define FILE2C_H_INCLUDED
#include "external.h" /* Support macros EXTERN, INITIALIZE */
#include "file1c.h" /* Type definition for struct oddball */
#if !defined(DEFINE_VARIABLES) || !defined(FILE2C_H_DEFINITIONS)
/* Global variable declarations / definitions */
EXTERN int global_variable INITIALIZE(37);
EXTERN struct oddball oddball_struct INITIALIZE({ 41, 43 });
#endif /* !DEFINE_VARIABLES || !FILE2C_H_DEFINITIONS */
/* Standard epilogue */
#ifdef DEFINE_VARIABLES
#define FILE2C_H_DEFINITIONS
#endif /* DEFINE_VARIABLES */
#endif /* FILE2C_H_INCLUDED */
#define DEFINE_VARIABLES
#include "file2c.h" /* Variables now defined and initialized */
int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }
#include "file2c.h"
#include <stdio.h>
void use_them(void)
{
printf("Global variable: %d\n", global_variable++);
oddball_struct.a += global_variable;
oddball_struct.b -= global_variable / 2;
}
#include "file2c.h" /* Declare variables */
#define DEFINE_VARIABLES
#include "file2c.h" /* Variables now defined and initialized */
int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }
#define DEFINE_VARIABLES
#include "file2c.h" /* Variables now defined and initialized */
#include "file2c.h" /* Declare variables */
int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }
ඊළඟ ප්රභව ගොනුව ප්රභවය සම්පූර්ණ කරයි (ප්රධාන වැඩසටහනක් සපයයි) prog5
, prog6
සහ prog7
:
#include "file2c.h"
#include <stdio.h>
int main(void)
{
use_them();
global_variable += 19;
use_them();
printf("Increment: %d\n", increment());
printf("Oddball: %d\n", oddball_value());
return 0;
}
prog5
භාවිතා prog5.c
, file3c.c
, file4c.c
, file1c.h
, file2c.h
, external.h
.
prog6
භාවිතා prog5.c
, file5c.c
, file4c.c
, file1c.h
, file2c.h
, external.h
.
prog7
භාවිතා prog5.c
, file6c.c
, file4c.c
, file1c.h
, file2c.h
, external.h
.
මෙම යෝජනා ක්රමය බොහෝ ගැටළු මඟහරවා ගනී. ඔබ ගැටළුවකට මුහුණ දෙන්නේ විචල්යයන් නිර්වචනය කරන ශීර්ෂයක් (වැනි file2c.h
) විචල්යයන් අර්ථ දක්වන වෙනත් ශීර්ෂයක් (කියන්න file7c.h
) ඇතුළත් කළහොත් පමණි. "එය නොකරන්න" හැර වෙනත් පහසු ක්රමයක් නොමැත.
සංශෝධනය file2c.h
කිරීමෙන්
ඔබට අර්ධ වශයෙන් ගැටලුව විසඳා ගත හැකිය file2d.h
:
/* Standard prologue */
#if defined(DEFINE_VARIABLES) && !defined(FILE2D_H_DEFINITIONS)
#undef FILE2D_H_INCLUDED
#endif
#ifndef FILE2D_H_INCLUDED
#define FILE2D_H_INCLUDED
#include "external.h" /* Support macros EXTERN, INITIALIZE */
#include "file1c.h" /* Type definition for struct oddball */
#if !defined(DEFINE_VARIABLES) || !defined(FILE2D_H_DEFINITIONS)
/* Global variable declarations / definitions */
EXTERN int global_variable INITIALIZE(37);
EXTERN struct oddball oddball_struct INITIALIZE({ 41, 43 });
#endif /* !DEFINE_VARIABLES || !FILE2D_H_DEFINITIONS */
/* Standard epilogue */
#ifdef DEFINE_VARIABLES
#define FILE2D_H_DEFINITIONS
#undef DEFINE_VARIABLES
#endif /* DEFINE_VARIABLES */
#endif /* FILE2D_H_INCLUDED */
ගැටළුව 'ශීර්ෂයට ඇතුළත් විය #undef DEFINE_VARIABLES
යුතුද?' ඔබ හෙඩිම සිට පළමුෙවන් හා සමග මොන යම් හෝ නිර්වචනය පිහිට පැතීමක් ආවරණය කරනවා නම්
#define
සහ #undef
:
#define DEFINE_VARIABLES
#include "file2c.h"
#undef DEFINE_VARIABLES
ප්රභව කේතයේ (එබැවින් ශීර්ෂයන් කිසි විටෙකත් එහි අගය වෙනස් නොකරයි
DEFINE_VARIABLES
), එවිට ඔබ පිරිසිදු විය යුතුය. අමතර පේළිය ලිවීමට මතක තබා ගැනීම කරදරයක් පමණි. විකල්පයක් විය හැක්කේ:
#define HEADER_DEFINING_VARIABLES "file2c.h"
#include "externdef.h"
/*
** This header must not contain header guards (like <assert.h> must not).
** Each time it is included, the macro HEADER_DEFINING_VARIABLES should
** be defined with the name (in quotes - or possibly angle brackets) of
** the header to be included that defines variables when the macro
** DEFINE_VARIABLES is defined. See also: external.h (which uses
** DEFINE_VARIABLES and defines macros EXTERN and INITIALIZE
** appropriately).
**
** #define HEADER_DEFINING_VARIABLES "file2c.h"
** #include "externdef.h"
*/
#if defined(HEADER_DEFINING_VARIABLES)
#define DEFINE_VARIABLES
#include HEADER_DEFINING_VARIABLES
#undef DEFINE_VARIABLES
#undef HEADER_DEFINING_VARIABLES
#endif /* HEADER_DEFINING_VARIABLES */
මෙම ඇති සංකීර්ණ, පොඩ්ඩක් ලබා ඇත, නමුත් ආරක්ෂිත බව පෙනේ (භාවිතා
file2d.h
නොමැති, #undef DEFINE_VARIABLES
තුළ file2d.h
).
/* Declare variables */
#include "file2d.h"
/* Define variables */
#define HEADER_DEFINING_VARIABLES "file2d.h"
#include "externdef.h"
/* Declare variables - again */
#include "file2d.h"
/* Define variables - again */
#define HEADER_DEFINING_VARIABLES "file2d.h"
#include "externdef.h"
int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }
/* Standard prologue */
#if defined(DEFINE_VARIABLES) && !defined(FILE8C_H_DEFINITIONS)
#undef FILE8C_H_INCLUDED
#endif
#ifndef FILE8C_H_INCLUDED
#define FILE8C_H_INCLUDED
#include "external.h" /* Support macros EXTERN, INITIALIZE */
#include "file2d.h" /* struct oddball */
#if !defined(DEFINE_VARIABLES) || !defined(FILE8C_H_DEFINITIONS)
/* Global variable declarations / definitions */
EXTERN struct oddball another INITIALIZE({ 14, 34 });
#endif /* !DEFINE_VARIABLES || !FILE8C_H_DEFINITIONS */
/* Standard epilogue */
#ifdef DEFINE_VARIABLES
#define FILE8C_H_DEFINITIONS
#endif /* DEFINE_VARIABLES */
#endif /* FILE8C_H_INCLUDED */
/* Define variables */
#define HEADER_DEFINING_VARIABLES "file2d.h"
#include "externdef.h"
/* Define variables */
#define HEADER_DEFINING_VARIABLES "file8c.h"
#include "externdef.h"
int increment(void) { return global_variable++; }
int oddball_value(void) { return oddball_struct.a + oddball_struct.b; }
ඊළඟ ලිපිගොනු දෙක සඳහා මූලාශ්රය සම්පූර්ණ කරන්නේ prog8
සහ prog9
:
#include "file2d.h"
#include <stdio.h>
int main(void)
{
use_them();
global_variable += 19;
use_them();
printf("Increment: %d\n", increment());
printf("Oddball: %d\n", oddball_value());
return 0;
}
#include "file2d.h"
#include <stdio.h>
void use_them(void)
{
printf("Global variable: %d\n", global_variable++);
oddball_struct.a += global_variable;
oddball_struct.b -= global_variable / 2;
}
prog8
භාවිතා prog8.c
, file7c.c
, file9c.c
.
prog9
භාවිතා prog8.c
, file8c.c
, file9c.c
.
කෙසේ වෙතත්, ගැටළු ප්රායෝගිකව සිදුවීමට ඉඩක් නැත, විශේෂයෙන් ඔබ සම්මත උපදෙස් ලබා ගන්නේ නම්
මෙම ප්රදර්ශනයෙන් කිසිවක් මග හැරී තිබේද?
_Confession_: මෙහි දක්වා ඇති 'අනුපිටපත් මග හැරීමේ' යෝජනා ක්රමය සංවර්ධනය කර ඇත්තේ මෙම ගැටළුව මා වැඩ කරන (නමුත් අයිති නැති) සමහර කේත වලට බලපාන හෙයිනි, සහ පිළිතුරේ පළමු කොටසේ දක්වා ඇති යෝජනා ක්රමය පිළිබඳ නොසැලකිලිමත් සැලකිල්ලකි. කෙසේ වෙතත්, මුල් යෝජනා ක්රමය මඟින් විචල්ය අර්ථ දැක්වීම් සහ ප්රකාශන සමමුහුර්තව තබා ගැනීම සඳහා වෙනස් කිරීමට ස්ථාන දෙකක් පමණක් ඉතිරිව ඇති අතර, එය කේත පදනම පුරා විසිරී ඇති බාහිර විචල්ය ප්රකාශයන් තිබීමට වඩා විශාල ඉදිරි පියවරකි (මුළු ලිපිගොනු දහස් ගණනක් ඇති විට එය සැබවින්ම වැදගත් වේ) . කෙසේ වෙතත්, ලිපිගොනු වල `fileNc. [Ch]` (ඊට අමතරව `external.h` සහ` externdef.h`) නම් සහිත කේතය පෙන්නුම් කරන්නේ එය ක්රියාත්මක කළ හැකි බවයි. පැහැදිලිවම, විචල්ය නිර්වචනය හා ප්රකාශ කරන ශීර්ෂ ගොනුවක් සඳහා ප්රමිතිගත අච්චුවක් ඔබට ලබා දීම සඳහා ශීර්ෂ උත්පාදක ස්ක්රිප්ටයක් සෑදීම දුෂ්කර නොවනු ඇත.සැ.යු: මේවා සෙල්ලම් වැඩසටහන් වන අතර ඒවා සුළු වශයෙන් සිත්ගන්නා සුළු කේතයක් ඇත. ඉවත් කළ හැකි උදාහරණ තුළ පුනරාවර්තනයක් ඇත, නමුත් අධ්යාපනික පැහැදිලි කිරීම සරල කිරීම නොවේ. (නිදසුනක් ලෙස: ඇතුළත් කර ඇති එක් ශීර්ෂයක නම prog5.c
සහ prog8.c
එය අතර වෙනස වේ. main()
ශ්රිතය නැවත නොකිරීමට කේතය ප්රතිසංවිධානය කිරීමට හැකි වනු ඇත , නමුත් එය හෙළි කළ ප්රමාණයට වඩා එය සඟවනු ඇත.)
foo.h
) විසින් නිදර්ශනය කර ඇත : #define FOO_INITIALIZER { 1, 2, 3, 4, 5 }
අරාව සඳහා ආරම්භක අර්ථ දැක්වීම, අරාවේ enum { FOO_SIZE = sizeof((int [])FOO_INITIALIZER) / sizeof(((int [])FOO_INITIALIZER)[0]) };
ප්රමාණය ලබා ගැනීම සහ extern int foo[];
අරාව ප්රකාශ කිරීම . පැහැදිලිවම, අර්ථ දැක්වීම සාධාරණ විය යුතුය int foo[FOO_SIZE] = FOO_INITIALIZER;
, නමුත් ප්රමාණය සැබවින්ම අර්ථ දැක්වීමට ඇතුළත් කළ යුතු නැත. මෙය ඔබට පූර්ණ සංඛ්යාවක් නියතයක් ලබා දෙයි FOO_SIZE
.
ක extern
විචල්ය තවත් පරිවර්තන ඒකකය අර්ථ දක්වා ඇත වන විචල්ය ප්රකාශයක් (ස්තුතිය නිවැරදි කිරීම සඳහා රාජරත්නම් කිරීමට) වේ. ඒ කියන්නේ විචල්යය සඳහා ආචයනය වෙනත් ගොනුවක වෙන් කර ඇත.
ඔබ සතුව දෙකක් කියන්න .c
-files test1.c
හා test2.c
. ඔබ ගෝලීය විචල්ය නිර්වචනය නම් int test1_var;
දී test1.c
ඇති අතර එවිට ඔබට මෙම විචල්ය ප්රවේශ කැමතියි test2.c
ඔබ භාවිතා කිරීමට ඇති extern int test1_var;
දී test2.c
.
සම්පූර්ණ නියැදිය:
$ cat test1.c
int test1_var = 5;
$ cat test2.c
#include <stdio.h>
extern int test1_var;
int main(void) {
printf("test1_var = %d\n", test1_var);
return 0;
}
$ gcc test1.c test2.c -o test
$ ./test
test1_var = 5
extern int test1_var;
කිරීමට int test1_var;
, එම linker (gcc 5.4.0) තවමත් ගමන් කරයි. ඉතින්, extern
ඇත්ත වශයෙන්ම මෙම නඩුවේ අවශ්යද?
extern
බොහෝ විට ක්රියාත්මක වන පොදු දිගුවක් බව ඔබට පෙනී යනු ඇත - සහ විශේෂයෙන් GCC සමඟ ක්රියා කරයි (නමුත් GCC එයට සහය දක්වන එකම සම්පාදකයා වීමට වඩා බොහෝ සෙයින් වෙනස් ය; එය යුනික්ස් පද්ධතිවල බහුලව පවතී). - ඔබ "J.5.11" හෝ කොටසක් "නොවේ එසේ හොඳ ක්රමයක්" මගේ පිළිතුර දී (එය මම දන්නේ දැකිය හැකි වේ එය පැහැදිලි (හෝ එසේ කිරීමට උත්සාහ) ඒ අසල පෙළ දිගු).
විචල්යය වෙනත් පරිවර්තන ඒකකයක වාසය කරන බව ප්රකාශ කිරීමට ඔබ භාවිතා කරන ප්රධාන පදය බාහිර ය.
එබැවින් ඔබට පරිවර්තන ඒකකයක විචල්යයක් භාවිතා කිරීමට තීරණය කළ හැකි අතර පසුව එය වෙනත් එකකින් ප්රවේශ කළ හැකිය, දෙවනුව ඔබ එය බාහිර යැයි ප්රකාශ කරන අතර සංකේතය සම්බන්ධකය විසින් විසඳනු ඇත.
ඔබ එය බාහිර ලෙස ප්රකාශ නොකරන්නේ නම් ඔබට විචල්ය 2 ක් ලැබෙනු ඇත.
බාහිර විචල්යයක් ඔබ සම්පාදකයාට දෙන පොරොන්දුවක් ලෙස සිතීමට මම කැමතියි.
බාහිරව හමු වූ විට, සම්පාදකයාට සොයාගත හැක්කේ එහි වර්ගය මිස එය "ජීවත්වන" ස්ථානය නොවේ, එබැවින් එයට යොමු කිරීම විසඳිය නොහැක.
ඔබ එය කියන්නේ, "මාව විශ්වාස කරන්න. සම්බන්ධක වේලාවේදී මෙම සඳහන විසඳිය හැකිය."
මෙම විචල්යය සඳහා වන මතකය වෙනත් තැනක ප්රකාශයට පත් කර ඇති බව විශ්වාස කිරීමට බාහිර විසින් සම්පාදකයාට පවසයි, එබැවින් එය මතකය වෙන් කිරීමට / පරීක්ෂා කිරීමට උත්සාහ නොකරයි.
එමනිසා, ඔබට බාහිරට යොමු දැක්විය හැකි ගොනුවක් සම්පාදනය කළ හැකිය, නමුත් එම මතකය කොතැනක හෝ ප්රකාශ කර නොමැති නම් ඔබට සම්බන්ධ කළ නොහැක.
ගෝලීය විචල්යයන් සහ පුස්තකාල සඳහා ප්රයෝජනවත් නමුත් සම්බන්ධකය චෙක්පත ටයිප් නොකරන නිසා භයානක ය.
ක එකතු extern
වෙලා ඉන්නෙ විචල්ය නිර්වචනය විචල්ය බවට ප්රකාශ . ප්රකාශයක් සහ අර්ථ දැක්වීමක් අතර ඇති වෙනස කුමක්දැයි මෙම නූල බලන්න .
declare | define | initialize |
----------------------------------
extern int a; yes no no
-------------
int a = 2019; yes yes yes
-------------
int a; yes yes no
-------------
ප්රකාශනය මඟින් මතකය වෙන් නොකරනු ඇත (මතක විබෙදුම සඳහා විචල්යය අර්ථ දැක්විය යුතුය) නමුත් අර්ථ දැක්වීම එසේ වනු ඇත. බාහිර පිළිතුරු පදය පිළිබඳ තවත් සරල දර්ශනයක් මෙය බැවින් අනෙක් පිළිතුරු ඇත්තෙන්ම විශිෂ්ටයි.
බාහිර පිළිබඳ නිවැරදි අර්ථ නිරූපණය නම් ඔබ සම්පාදකයාට යමක් පැවසීමයි. ඔබ සම්පාදකයාට කියනවා, මේ මොහොතේ නොතිබුණද, ප්රකාශිත විචල්යය කෙසේ හෝ සම්බන්ධකය විසින් සොයා ගනු ඇත (සාමාන්යයෙන් වෙනත් වස්තුවක (ගොනුවක). එවිට ඔබට බාහිර ප්රකාශ කිහිපයක් තිබුනත් නැතත්, සියල්ල සොයා ගෙන එය එකට තැබීමේ වාසනාවන්තයා සම්බන්ධකය වනු ඇත.
C හි ගොනුවක් තුළ විචල්යයක් පවසන්නේ example.c ට දේශීය විෂය පථය ලබා දී ඇති බවයි. සම්පාදකයා අපේක්ෂා කරන්නේ විචල්යයට එහි අර්ථ දැක්වීම එකම ගොනුව example.c තුළ තිබිය යුතු අතර එය සොයාගත නොහැකි වූ විට එය දෝෂයක් ඇති කරයි. අනෙක් අතට ශ්රිතයක් පෙරනිමියෙන් ගෝලීය විෂය පථයට ඇත. මේ අනුව ඔබ සම්පාදකයාට නිශ්චිතවම සඳහන් කළ යුතු නැත "බලන්න මචං ... ඔබට මෙම ශ්රිතයේ අර්ථ දැක්වීම මෙහි සොයාගත හැකිය". ගොනුව ඇතුළු ප්රකාශයක් අඩංගු ශ්රිතයක් සඳහා එය ප්රමාණවත් වේ (ඔබ ඇත්ත වශයෙන්ම ශීර්ෂ ගොනුවක් ලෙස හඳුන්වන ගොනුව). උදාහරණයක් ලෙස පහත ලිපිගොනු 2 සලකා බලන්න:
example.c
#include<stdio.h>
extern int a;
main(){
printf("The value of a is <%d>\n",a);
}
example1.c
int a = 5;
දැන් ඔබ ගොනු දෙක එකට සම්පාදනය කරන විට, පහත දැක්වෙන විධානයන් භාවිතා කරන්න:
පියවර 1) cc -o ex example.c example1.c පියවර 2) ./ උදා
ඔබට පහත ප්රතිදානය ලැබේ: a හි අගය <5> වේ
GCC ELF ලිනක්ස් ක්රියාත්මක කිරීම
වෙනත් පිළිතුරු භාෂා භාවිතය පිළිබඳ දෘෂ්ටි කෝණයෙන් ආවරණය කර ඇත, එබැවින් දැන් එය ක්රියාත්මක කිරීමේදී එය ක්රියාත්මක කරන්නේ කෙසේදැයි සොයා බලමු.
main.c
#include <stdio.h>
int not_extern_int = 1;
extern int extern_int;
void main() {
printf("%d\n", not_extern_int);
printf("%d\n", extern_int);
}
සම්පාදනය කර විසංයෝජනය කරන්න:
gcc -c main.c
readelf -s main.o
ප්රතිදානයේ අඩංගු වන්නේ:
Num: Value Size Type Bind Vis Ndx Name
9: 0000000000000000 4 OBJECT GLOBAL DEFAULT 3 not_extern_int
12: 0000000000000000 0 NOTYPE GLOBAL DEFAULT UND extern_int
මෙම පද්ධතිය V ABI යාවත්කාලීන ELF පිරිවිතර "සංකේතය වගුව" පරිච්ඡේදය මඟින්:
SHN_UNDEF මෙම කොටසේ වගු දර්ශකයේ තේරුම සංකේතය නිර්වචනය කර නොමැති බවයි. සම්බන්ධක සංස්කාරකය මෙම වස්තු ගොනුව වෙනත් සංකේතයක් අර්ථ දක්වන සංකේතයක් සමඟ සංයුක්ත කරන විට, සංකේතයට මෙම ගොනුවේ යොමු කිරීම් සත්ය අර්ථ දැක්වීමට සම්බන්ධ වේ.
එය මූලික වශයෙන් සී ප්රමිතියට ලබා දෙන හැසිරීමයි extern
විචල්යයන්ට .
මෙතැන් සිට, අවසාන වැඩසටහන සකස් කිරීම සම්බන්ධකයේ කාර්යය වන නමුත් extern
තොරතුරු දැනටමත් ප්රභව කේතයෙන් වස්තු ගොනුවට උපුටා ගෙන ඇත.
GCC 4.8 හි පරීක්ෂා කර ඇත.
සී ++ 17 පේළි විචල්යයන්
C ++ 17 හි, ඔබට බාහිර ඒවා වෙනුවට පේළිගත විචල්යයන් භාවිතා කිරීමට අවශ්ය විය හැකිය, මන්ද ඒවා භාවිතා කිරීම පහසුය (ශීර්ෂයට එක් වරක් පමණක් අර්ථ දැක්විය හැකිය) සහ වඩා බලවත් (ආධාරක constexpr). බලන්න: C සහ C ++ වලින් 'const static' යන්නෙන් අදහස් කරන්නේ කුමක්ද?
readelf
හෝ nm
ප්රයෝජනවත් විය හැකි වුවද, ඔබ එය භාවිතා කරන්නේ කෙසේද යන්න පිළිබඳ මූලධර්ම පැහැදිලි කර extern
නැත, හෝ පළමු වැඩසටහන සත්ය අර්ථ දැක්වීම සමඟ සම්පූර්ණ කර නැත. ඔබේ කේතය පවා භාවිතා නොකරයි notExtern
. නාමකරණ ගැටළුවක් ද ඇත: notExtern
ප්රකාශයට පත් කරනවාට වඩා මෙහි අර්ථ දක්වා ඇතත් extern
, එය පරිවර්තන ඒකකවල සුදුසු ප්රකාශයක් (අවශ්ය වනු ඇත extern int notExtern;
!) තිබේ නම් එය වෙනත් ප්රභව ලිපිගොනු වලට ප්රවේශ විය හැකි බාහිර විචල්යයකි .
notExtern
නොකිරීම කැතයි, එය සවි කළා. නාමකරණය ගැන, ඔබට වඩා හොඳ නමක් තිබේ නම් මට දන්වන්න. ඇත්ත වශයෙන්ම එය සත්ය වැඩසටහනක් සඳහා හොඳ නමක් නොවනු ඇත, නමුත් මම සිතන්නේ එය මෙහි ක්රියාකාරී භූමිකාවට හොඳින් ගැලපේ.
global_def
මෙහි අර්ථ දක්වා ඇති විචල්යය සහ extern_ref
වෙනත් මොඩියුලයක අර්ථ දක්වා ඇති විචල්යය ගැන කුමක් කිව හැකිද? ඔවුන්ට පැහැදිලි සමමිතියක් තිබේද? ඔබ තවමත් අවසන් වී ඇත්තේ int extern_ref = 57;
හෝ එය අර්ථ දක්වා ඇති ගොනුවේ එවැනි දෙයක් නිසා නම එතරම් සුදුසු නැත, නමුත් තනි ප්රභව ගොනුවේ සන්දර්භය තුළ එය සාධාරණ තේරීමකි. ඇති extern int global_def;
වූ ශීර්ෂ දී මෙන් බොහෝ ගැටළු මතු නොවේ, එය මට පෙනේ. ඇත්ත වශයෙන්ම ඔබට සම්පූර්ණයෙන්ම.
extern
ඔබේ වැඩසටහනේ එක් මොඩියුලයකට ඔබේ වැඩසටහනේ තවත් මොඩියුලයක ප්රකාශිත ගෝලීය විචල්යයකට හෝ ශ්රිතයකට ප්රවේශ වීමට ඉඩ දෙයි. ඔබ සාමාන්යයෙන් ශීර්ෂ ලිපිගොනු වල ප්රකාශිත බාහිර විචල්යයන් ඇත.
ඔබේ විචල්යයන් හෝ කාර්යයන් වෙත ප්රවේශ වීමට ඔබට වැඩසටහනක් අවශ්ය නොවේ නම්, ඔබ භාවිතා static
කරන්නේ සම්පාදකයාට මෙම විචල්යය හෝ ක්රියාකාරිත්වය මෙම මොඩියුලයෙන් පිටත භාවිතා කළ නොහැකි බවයි.
පළමුවෙන්ම, extern
විචල්යයක් නිර්වචනය කිරීම සඳහා ප්රධාන පදය භාවිතා නොවේ; ඒ වෙනුවට එය විචල්යයක් ප්රකාශ කිරීම සඳහා යොදා ගනී. මට කිව හැක්කේ extern
ගබඩා පන්තියක් මිස දත්ත වර්ගයක් නොවේ.
extern
මෙම විචල්යය දැනටමත් කොතැනක හෝ අර්ථ දක්වා ඇති බව වෙනත් සී ගොනු හෝ බාහිර කොටස් වලට දැන ගැනීමට භාවිතා කරයි. උදාහරණය: ඔබ පුස්තකාලයක් ගොඩනඟන්නේ නම්, පුස්තකාලයේ කොතැනක හෝ අනිවාර්යයෙන්ම ගෝලීය විචල්යය නිර්වචනය කිරීම අවශ්ය නොවේ. පුස්තකාලය කෙලින්ම සම්පාදනය කරනු ඇත, නමුත් ගොනුව සම්බන්ධ කරන අතරතුර, එය අර්ථ දැක්වීම පරීක්ෂා කරයි.
extern
භාවිතා කරන බැවින් එක් first.c
ගොනුවකට තවත් second.c
ගොනුවක ගෝලීය පරාමිතියකට පූර්ණ ප්රවේශය ලබා ගත හැකිය .
මෙම extern
තුළ ප්රකාශයට පත් කළ හැකි first.c
ගොනු හෝ ශීර්ෂ ගොනු කිසිදු first.c
ඇතුළත් වේ.
extern
ප්රකාශය විය යුත්තේ ශීර්ෂයක මිස ඇතුළත නොවන බව සලකන්න first.c
, එවිට වර්ගය වෙනස් වුවහොත් ප්රකාශය ද වෙනස් වේ. එසේම, විචල්යය ප්රකාශ කරන ශීර්ෂකය ඇතුළත් කළ යුත්තේ second.c
අර්ථ දැක්වීම ප්රකාශයට අනුකූල බව සහතික කිරීම සඳහා ය. ශීර්ෂයේ ප්රකාශනය යනු සියල්ල එකට තබා ගන්නා මැලියම් ය; එමඟින් ලිපිගොනු වෙන වෙනම සම්පාදනය කිරීමට ඉඩ ලබා දෙන නමුත් ගෝලීය විචල්යයේ වර්ගය පිළිබඳ ස්ථාවර දැක්මක් ඇති බව සහතික කරයි.
Xc8 සමඟ එක් එක් ගොනුවේ විචල්යය එකම වර්ගයට සමාන ලෙස ප්රකාශ කිරීම ගැන ඔබ සැලකිලිමත් විය යුතුය, වැරදියට, int
එක් ගොනුවක යමක් ප්රකාශ කරන්න සහ achar
කියන්න. මෙය විචල්යයන්ගේ දූෂණයට හේතු විය හැක.
මීට වසර 15 කට පමණ පෙර මයික්රොචිප් සංසදයකදී මෙම ගැටළුව ඉතා අලංකාර ලෙස විසඳා ඇත / * "http: www.htsoft.com" / / බලන්න "සංසදය / සියල්ල / showflat.php / Cat / 0 / අංකය / 18766 / an / 0 / page / 0 # 18766 "
නමුත් මෙම සබැඳිය තවදුරටත් ක්රියාත්මක නොවන බව පෙනේ ...
එබැවින් මම එය ඉක්මනින් පැහැදිලි කිරීමට උත්සාහ කරමි; global.h නමින් ගොනුවක් සාදන්න.
එහි පහත සඳහන් දේ ප්රකාශ කරන්න
#ifdef MAIN_C
#define GLOBAL
/* #warning COMPILING MAIN.C */
#else
#define GLOBAL extern
#endif
GLOBAL unsigned char testing_mode; // example var used in several C files
දැන් main.c ගොනුවේ
#define MAIN_C 1
#include "global.h"
#undef MAIN_C
මෙයින් අදහස් වන්නේ main.c හි විචල්යය a ලෙස ප්රකාශයට පත් කරනු ඇති unsigned char
බවයි.
දැන් global.h ඇතුළු වෙනත් ලිපිගොනු වල එය එම ගොනුව සඳහා බාහිරයක් ලෙස ප්රකාශයට පත් කරනු ඇත .
extern unsigned char testing_mode;
නමුත් එය නිවැරදිව ප්රකාශයට පත් කරනු ඇත unsigned char
.
පැරණි සංසද පෝස්ට් මෙය තරමක් පැහැදිලිව පැහැදිලි කර ඇත. gotcha
එක් ගොනුවක විචල්යයක් ප්රකාශ කිරීමට ඉඩ දී එය වෙනත් ආකාරයකින් වෙනත් වර්ගයක් ලෙස ප්රකාශ කිරීමට ඉඩ සලසන සම්පාදකයක් භාවිතා කරන විට මෙය සැබෑ විභවයකි. ඒ හා සම්බන්ධ ගැටලු නම්, ඔබ වෙනත් ගොනුවක int_ ලෙස ප්රකාශිත ටෙස්ටිං_මෝඩ් යැයි පැවසුවහොත් එය එය බිට් 16 var යැයි සිතන අතර රැම්හි තවත් කොටසක් නැවත ලිවීමෙන් වෙනත් විචල්යයක් දූෂිත විය හැකිය. නිදොස් කිරීම දුෂ්කර!
ශීර්ෂ ගොනුවකට බාහිර යොමු කිරීම හෝ වස්තුවක සත්ය ලෙස ක්රියාත්මක කිරීම ඇතුළත් කිරීමට මම භාවිතා කරන්නේ ඉතා කෙටි විසඳුමකි. ඇත්ත වශයෙන්ම වස්තුව අඩංගු ගොනුව එසේ #define GLOBAL_FOO_IMPLEMENTATION
කරයි. මම මෙම ගොනුවට නව වස්තුවක් එකතු කළ විට එය අර්ථ දැක්වීම පිටපත් කර අලවා නොගෙන එම ගොනුවේ ද පෙන්වයි.
මම මෙම රටාව බහු ගොනු හරහා භාවිතා කරමි. එබැවින් හැකි තරම් ස්වයං අන්තර්ගතයන් තබා ගැනීම සඳහා, මම සෑම ශීර්ෂයකම තනි ගෝලීය සාර්ව නැවත භාවිතා කරමි. මගේ ශීර්ෂකය මේ වගේ ය:
//file foo_globals.h
#pragma once
#include "foo.h" //contains definition of foo
#ifdef GLOBAL
#undef GLOBAL
#endif
#ifdef GLOBAL_FOO_IMPLEMENTATION
#define GLOBAL
#else
#define GLOBAL extern
#endif
GLOBAL Foo foo1;
GLOBAL Foo foo2;
//file main.cpp
#define GLOBAL_FOO_IMPLEMENTATION
#include "foo_globals.h"
//file uses_extern_foo.cpp
#include "foo_globals.h