উত্স ফাইলগুলির মধ্যে ভেরিয়েবলগুলি ভাগ করতে আমি কীভাবে বহিরা ব্যবহার করব?


987

আমি জানি যে সি তে গ্লোবাল ভেরিয়েবলগুলির মাঝে মাঝে externকীওয়ার্ড থাকে। একটি externপরিবর্তনশীল কি? ঘোষণা কীসের মতো? এর সুযোগ কী?

এটি উত্স ফাইলগুলি জুড়ে ভেরিয়েবলগুলি ভাগ করার সাথে সম্পর্কিত, তবে কীভাবে এটি সুনির্দিষ্টভাবে কাজ করে? আমি কোথায় ব্যবহার করব extern?

উত্তর:


1750

ব্যবহার externযখন প্রোগ্রাম তুমি ভবন একসঙ্গে লিঙ্ক একাধিক উৎস ফাইল, যেখানে ভেরিয়েবল কিছু সংজ্ঞায়িত, উদাহরণস্বরূপ, উৎস ফাইলের মধ্যে নিয়ে গঠিত শুধুমাত্র প্রাসঙ্গিকতা হয় file1.cযেমন প্রয়োজন অন্যান্য উৎস ফাইল উল্লেখ করতে হবে, file2.c

এটা তোলে গুরুত্বপূর্ণ মধ্যে পার্থক্য বুঝতে সংজ্ঞা একটি পরিবর্তনশীল এবং প্রকাশক একটি পরিবর্তনশীল :

  • একটি পরিবর্তনশীল ঘোষিত যখন কম্পাইলার অবহিত করা হয় যে একটি পরিবর্তনশীল বিদ্যমান (এবং এই তার প্রকার); এটি সেই সময়ে ভেরিয়েবলের জন্য সঞ্চয়স্থান বরাদ্দ করে না।

  • একটি পরিবর্তনশীল সংজ্ঞায়িত করা হয় যখন সংকলক ভেরিয়েবলের জন্য স্টোরেজ বরাদ্দ করে।

আপনি একাধিকবার ভেরিয়েবল ঘোষণা করতে পারেন (যদিও একবারে যথেষ্ট); আপনি কেবলমাত্র একটি নির্দিষ্ট সুযোগের মধ্যে এটি একবার সংজ্ঞায়িত করতে পারেন। একটি পরিবর্তনশীল সংজ্ঞা এছাড়াও একটি ঘোষণা, কিন্তু সমস্ত পরিবর্তনশীল ঘোষণা সংজ্ঞা হয় না।

গ্লোবাল ভেরিয়েবলগুলি ঘোষণা ও সংজ্ঞায়নের সর্বোত্তম উপায়

বিশ্বব্যাপী ভেরিয়েবলগুলি extern ঘোষিত ও সংজ্ঞায়িত করার পরিষ্কার, নির্ভরযোগ্য উপায় হ'ল ভেরিয়েবলের একটি ডিক্লেশন ধারণ করতে একটি শিরোনাম ফাইল ব্যবহার করা ।

শিরোনামটি একটি উত্স ফাইলের দ্বারা অন্তর্ভুক্ত করা হয় যা ভেরিয়েবলকে সংজ্ঞায়িত করে এবং সমস্ত উত্স ফাইল যা ভেরিয়েবলটি উল্লেখ করে। প্রতিটি প্রোগ্রামের জন্য একটি উত্স ফাইল (এবং কেবল একটি উত্স ফাইল) ভেরিয়েবলকে সংজ্ঞায়িত করে। একইভাবে, একটি শিরোনাম ফাইল (এবং শুধুমাত্র একটি শিরোনাম ফাইল) ভেরিয়েবল ঘোষণা করা উচিত। শিরোনাম ফাইলটি অত্যন্ত গুরুত্বপূর্ণ; এটি স্বাধীন টিউ (অনুবাদ ইউনিট - থিং সোর্স ফাইলগুলি) এর মধ্যে ক্রস চেকিং সক্ষম করে এবং ধারাবাহিকতা নিশ্চিত করে।

যদিও এটি করার অন্যান্য উপায় রয়েছে তবে এই পদ্ধতিটি সহজ এবং নির্ভরযোগ্য। এটি দ্বারা প্রদর্শিত হয় file3.h, file1.cএবং file2.c:

file3.h

extern int global_variable;  /* Declaration of the variable */

file1.c

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

file2.c

#include "file3.h"
#include "prog1.h"
#include <stdio.h>

void use_it(void)
{
    printf("Global variable: %d\n", global_variable++);
}

এটি বিশ্বব্যাপী ভেরিয়েবলগুলি ঘোষণা ও সংজ্ঞায়নের সেরা উপায় way


পরবর্তী দুটি ফাইল এর জন্য উত্সটি সম্পূর্ণ করে prog1:

সম্পূর্ণ প্রোগ্রামগুলি ব্যবহারের ফাংশনগুলি দেখানো হয়েছে, সুতরাং ফাংশন ঘোষণাগুলি শুরু হয়েছে C C99 এবং C11 উভয়ই ফাংশনগুলি তাদের ব্যবহারের আগে ঘোষিত বা সংজ্ঞায়িত করতে হবে (যেখানে সি 90 নেই, ভাল কারণে নয়)। আমি externধারাবাহিকতার জন্য শিরোনামগুলিতে ফাংশন ঘোষণার সামনে কীওয়ার্ডটি ব্যবহার করি - externশিরোনামগুলিতে পরিবর্তনশীল ঘোষণার সামনের দিকে মেলে । অনেক লোক externফাংশন ঘোষণার সামনে ব্যবহার না করা পছন্দ করেন ; সংকলকটির কোনও যত্ন নেই - এবং শেষ পর্যন্ত, আমি যতক্ষণ না আপনি সুসংগত হিসাবে যতক্ষণ না অন্তত কোনও উত্স ফাইলে থাকি।

prog1.h

extern void use_it(void);
extern int increment(void);

prog1.c

#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একটি মেকফাইল prog1। এটি makeসহস্রাব্দের পালা সম্পর্কে প্রায় উত্পাদিত বেশিরভাগ সংস্করণের সাথে কাজ করবে । এটি বিশেষত জিএনইউ মেকের সাথে আবদ্ধ নয়।

prog1.mk

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


নির্দেশিকা

বিধিগুলি কেবল বিশেষজ্ঞদের দ্বারা এবং কেবলমাত্র যুক্তিসঙ্গত কারণেই ভাঙতে হবে:

  • একটি হেডার ফাইলটিতে কেবল externভেরিয়েবলের ঘোষণা থাকে - কখনই staticবা অযোগ্য ভেরিয়েবল সংজ্ঞা।

  • প্রদত্ত যে কোনও ভেরিয়েবলের জন্য, কেবলমাত্র একটি হেডার ফাইলই এটি ঘোষণা করে (এসপট - সত্যের একক পয়েন্ট)।

  • উত্স ফাইলটিতে কখনও externভেরিয়েবলের ঘোষণা থাকে না - উত্স ফাইলগুলিতে সর্বদা (একমাত্র) শিরোনাম অন্তর্ভুক্ত থাকে যা সেগুলি ঘোষণা করে।

  • যে কোনও প্রদত্ত ভেরিয়েবলের জন্য, ঠিক একটি উত্স ফাইলটি ভেরিয়েবলকে সংজ্ঞায়িত করে, খুব সম্ভবত এটিকে আরম্ভ করে। (যদিও শূন্যে স্পষ্টভাবে আরম্ভ করার প্রয়োজন নেই, এটি কোনও ক্ষতি করে না এবং কিছুটা ভাল করতে পারে, কারণ একটি প্রোগ্রামে নির্দিষ্ট গ্লোবাল ভেরিয়েবলের কেবলমাত্র একটি প্রাথমিক সংজ্ঞা থাকতে পারে)।

  • উত্স ফাইল যা ভেরিয়েবলকে সংজ্ঞায়িত করে তাতে সংজ্ঞা এবং ঘোষণাপত্র সুসংগত হয় তা নিশ্চিত করার জন্য শিরোনামও অন্তর্ভুক্ত থাকে।

  • কোনও ক্রিয়াকলাপটি ব্যবহার করে কোনও চলক ঘোষণার দরকার নেই extern

  • যখনই সম্ভব বৈশ্বিক চলকগুলি এড়িয়ে চলুন - পরিবর্তে ফাংশন ব্যবহার করুন।

এই উত্তরের উত্স কোড এবং পাঠ্যটি আমার এসওকিউ (স্ট্যাক ওভারফ্লো প্রশ্নগুলি) src / so-0143-3204 উপ-ডিরেক্টরিতে GitHub এর সংগ্রহস্থলে পাওয়া যায়

আপনি যদি অভিজ্ঞ সি প্রোগ্রামার না হন তবে আপনি এখানে পড়া বন্ধ করতে পারেন (এবং সম্ভবত হওয়া উচিত)।

গ্লোবাল ভেরিয়েবলগুলি সংজ্ঞায়িত করার জন্য এত ভাল উপায় নয়

কিছু (প্রকৃতপক্ষে, অনেক) সি সংকলক সহ, আপনি একটি ভেরিয়েবলের একটি 'সাধারণ' সংজ্ঞা বলে যা পেয়ে যেতে পারেন। 'প্রচলিত', এখানে, ফোরট্রানে উত্স ফাইলগুলির মধ্যে ভেরিয়েবলগুলি ভাগ করে নেওয়ার জন্য (সম্ভবত নামযুক্ত) সিএমএমএন ব্লক ব্যবহার করে এমন একটি কৌশল বোঝায়। এখানে যা ঘটে তা হ'ল প্রতিটি ফাইলের প্রতিটি ভেরিয়েবলের একটি অস্থায়ী সংজ্ঞা প্রদান করে। যতক্ষণ না একের বেশি ফাইল কোনও প্রাথমিক সংজ্ঞা প্রদান করে না, ততক্ষণে বিভিন্ন ফাইলগুলি ভেরিয়েবলের একটি সাধারণ একক সংজ্ঞা ভাগ করে নেয়:

file10.c

#include "prog2.h"

long l; /* Do not do this in portable code */ 

void inc(void) { l++; }

file11.c

#include "prog2.h"

long l; /* Do not do this in portable code */ 

void dec(void) { l--; }

file12.c

#include "prog2.h"
#include <stdio.h>

long l = 9; /* Do not do this in portable code */ 

void put(void) { printf("l = %ld\n", l); }

এই কৌশলটি সি স্ট্যান্ডার্ডের বর্ণ এবং 'একটি সংজ্ঞা বিধি' অনুসারে মেনে চলে না - এটি আনুষ্ঠানিকভাবে অপরিজ্ঞাত আচরণ:

J.2 অপরিবর্তিত আচরণ

বাহ্যিক সংযোগ সহ একটি শনাক্তকারী ব্যবহৃত হয়, তবে প্রোগ্রামে সনাক্তকারীটির জন্য ঠিক একটি বাহ্যিক সংজ্ঞা উপস্থিত থাকে না বা সনাক্তকারী ব্যবহার করা হয় না এবং সনাক্তকারী (6.9) এর জন্য একাধিক বাহ্যিক সংজ্ঞা রয়েছে।

§6.9 বাহ্যিক সংজ্ঞা ¶5

একটি বহিরাগত সংজ্ঞা একটি বহিস্থিত ঘোষণা একটি ফাংশন (একটি ইনলাইন সংজ্ঞা ছাড়া) বা একটি বস্তুর একটি সংজ্ঞা হয় না। বাহ্যিক সংযোগের সাথে ঘোষিত কোনও শনাক্তকারী যদি কোনও অভিব্যক্তিতে ব্যবহার করা হয় (কোনও অপারেটরের অপারেন্ড sizeofবা অংশের অংশ হিসাবে _Alignofযার ফলস্বরূপ পূর্ণসংখ্যার ধ্রুবক হয়), পুরো প্রোগ্রামে কোথাও শনাক্তকারীর জন্য ঠিক একটি বাহ্যিক সংজ্ঞা থাকবে; অন্যথায়, একের বেশি আর কিছু থাকবে না। 161)

১1১) সুতরাং, বাহ্যিক সংযোগের সাথে ঘোষিত কোনও সনাক্তকারী যদি কোনও অভিব্যক্তিতে ব্যবহার না করা হয় তবে এর জন্য কোনও বাহ্যিক সংজ্ঞা থাকতে হবে না।

যাইহোক, সি স্ট্যান্ডার্ড এটিকে সাধারণ এক্সটেনশানগুলির একটি হিসাবে তথ্যযুক্ত অ্যাঙ্কেক্স জেতে তালিকাভুক্ত করে ।

J.5.11 একাধিক বাহ্যিক সংজ্ঞা

বহিরাগত কীওয়ার্ডের বহিঃপ্রকাশের স্পষ্ট ব্যবহার ছাড়া বা কোনও বস্তুর শনাক্তকারীর জন্য একাধিক বাহ্যিক সংজ্ঞা থাকতে পারে; সংজ্ঞাগুলি যদি একমত না হয় বা একাধিককে সূচনা করা হয় তবে আচরণটি অপরিজ্ঞাত হয় (6.9.2)।

কারণ এই কৌশলটি সর্বদা সমর্থিত নয়, এটি ব্যবহার করা এড়ানো ভাল, বিশেষত আপনার কোডটি বহনযোগ্য । এই কৌশলটি ব্যবহার করে, আপনি অনিচ্ছাকৃত ধরণের পাঙ্ক দিয়েও শেষ করতে পারেন।

উপরের যে lকোনও একটি ফাইলকে এ হিসাবে doubleপরিবর্তে ঘোষণা করা হলে longসি এর ধরণের-অনিরাপদ লিঙ্কারগুলি সম্ভবত মিল খুঁজে পাবে না। আপনি যদি 64-বিটযুক্ত কোনও মেশিনে থাকেন longএবং doubleআপনি কোনও সতর্কতাও পান না; ৩২-বিট longএবং 64৪-বিটযুক্ত একটি মেশিনে doubleআপনি সম্ভবত বিভিন্ন আকারের সম্পর্কে একটি সতর্কতা পেয়ে যাবেন - লিঙ্কারটি সবচেয়ে বড় আকারটি ব্যবহার করবে ঠিক যেমন ফোর্টরান প্রোগ্রামটি কোনও সাধারণ ব্লকের বৃহত্তম আকার নেয়।

নোট করুন যে 2020-05-07 এ প্রকাশিত GCC 10.1.0, ডিফল্ট সংকলন বিকল্পগুলি ব্যবহারের জন্য পরিবর্তন করে -fno-commonযার অর্থ ডিফল্টরূপে উপরের কোডটি আর লিঙ্ক করে না আপনি যদি না ডিফল্টকে ওভাররাইড করে -fcommon(বা বৈশিষ্ট্যগুলি ব্যবহার করে ইত্যাদি) - লিঙ্কটি দেখুন)।


পরবর্তী দুটি ফাইল এর জন্য উত্সটি সম্পূর্ণ করে prog2:

prog2.h

extern void dec(void);
extern void put(void);
extern void inc(void);

prog2.c

#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

সতর্কতা

যেমন এখানে মন্তব্যে উল্লিখিত হয়েছে, এবং একই অনুরূপ প্রশ্নের আমার উত্তরে বলা হয়েছে যে, বৈশ্বিক পরিবর্তনশীলের জন্য একাধিক সংজ্ঞা ব্যবহার অনির্ধারিত আচরণের দিকে পরিচালিত করে (জে .২; §.9.৯), যা "কিছু হতে পারে" বলার স্ট্যান্ডার্ড পদ্ধতি। ঘটতে পারে এমনগুলির মধ্যে একটি হ'ল প্রোগ্রামটি আপনার প্রত্যাশা অনুযায়ী আচরণ করে; এবং জে .৫.১১ প্রায় হিসাবে বলেছে, "আপনি আপনার প্রাপ্যের চেয়ে বেশি ভাগ্যবান হতে পারেন"। তবে একটি প্রোগ্রাম যা বাহ্যিক চলকের একাধিক সংজ্ঞা উপর নির্ভর করে - স্পষ্টভাবে 'বাহ্যিক' কীওয়ার্ড সহ বা ছাড়াই - কোনও কঠোরভাবে মেনে চলার প্রোগ্রাম নয় এবং সর্বত্র কাজ করার গ্যারান্টিযুক্ত নয়। সমতুল্য: এটিতে একটি বাগ রয়েছে যা এটি নিজেই প্রদর্শন করতে পারে বা নাও পারে।

নির্দেশিকা লঙ্ঘন

অবশ্যই অনেকগুলি উপায় রয়েছে যার মাধ্যমে এই নির্দেশিকাগুলি ভঙ্গ করা যেতে পারে। মাঝে মাঝে গাইডলাইনগুলি ভাঙার একটি ভাল কারণ থাকতে পারে তবে এই জাতীয় অনুষ্ঠানগুলি অত্যন্ত অস্বাভাবিক।

faulty_header.h

c int some_var; /* Do not do this in a header!!! */

নোট 1: যদি externশিরোনামটি কীওয়ার্ড ছাড়াই ভেরিয়েবলটি সংজ্ঞায়িত করে , তবে প্রতিটি ফাইল যার মধ্যে শিরোনাম অন্তর্ভুক্ত থাকে তারা ভেরিয়েবলের একটি অস্থায়ী সংজ্ঞা তৈরি করে। পূর্বে উল্লিখিত হিসাবে, এটি প্রায়শই কাজ করবে, তবে সি স্ট্যান্ডার্ড গ্যারান্টি দেয় না যে এটি কাজ করবে।

broken_header.h

c int some_var = 13; /* Only one source file in a program can use this */

নোট 2: যদি শিরোনামটি ভেরিয়েবলটি সংজ্ঞায়িত করে এবং আরম্ভ করে, তবে প্রদত্ত প্রোগ্রামে কেবলমাত্র একটি উত্স ফাইল হেডার ব্যবহার করতে পারে। যেহেতু শিরোনামগুলি প্রাথমিকভাবে তথ্য ভাগ করে নেওয়ার জন্য, তাই এটি তৈরি করা কিছুটা নির্বোধ যা কেবল একবার ব্যবহার করা যেতে পারে।

seldom_correct.h

c static int hidden_global = 3; /* Each source file gets its own copy */

নোট 3: যদি শিরোনাম একটি স্ট্যাটিক ভেরিয়েবল সংজ্ঞা দেয় (আরম্ভের সাথে বা না করে), তবে প্রতিটি উত্স ফাইলটি 'গ্লোবাল' ভেরিয়েবলের নিজস্ব ব্যক্তিগত সংস্করণ দিয়ে শেষ হয়।

যদি ভেরিয়েবলটি আসলে একটি জটিল অ্যারে হয়, উদাহরণস্বরূপ, এটি কোডের চূড়ান্ত সদৃশ হতে পারে। এটি খুব মাঝেমধ্যে কিছু প্রভাব অর্জনের জন্য একটি বুদ্ধিমান উপায় হতে পারে তবে এটি খুব অস্বাভাবিক।


সারসংক্ষেপ

প্রথম দেখানো শিরোনাম কৌশলটি ব্যবহার করুন। এটি নির্ভরযোগ্যভাবে এবং সর্বত্র কাজ করে। দ্রষ্টব্য, বিশেষত, যেটি ঘোষণাকারী শিরোনামটি এটি global_variableব্যবহার করে এমন প্রতিটি ফাইলের মধ্যে অন্তর্ভুক্ত রয়েছে - এটির সংজ্ঞায়িত করা ফাইলটিও। এটি নিশ্চিত করে যে সবকিছু স্ব-সামঞ্জস্যপূর্ণ।

অনুরূপ উদ্বেগগুলি কার্য সম্পাদন এবং সংজ্ঞায়নের সাথে উদ্ভূত হয় - অভিন্ন নিয়ম প্রযোজ্য। তবে প্রশ্নটি বিশেষত ভেরিয়েবল সম্পর্কে ছিল, সুতরাং আমি কেবল ভেরিয়েবলের উত্তর রেখেছি।

আসল উত্তরের সমাপ্তি

আপনি যদি অভিজ্ঞ সি প্রোগ্রামার না হন তবে আপনার সম্ভবত এখানে পড়া বন্ধ করা উচিত।


দেরিতে মেজর সংযোজন

কোড সদৃশ এড়ানো

এখানে বর্ণিত 'শিরোনামগুলিতে ঘোষণাপত্র, উত্সের সংজ্ঞা' সম্পর্কে উত্থাপিত একটি উদ্বেগটি হ'ল শিরোনাম এবং উত্স - দুটি ফাইল সিঙ্ক্রোনাইজ রাখতে হবে। এটি সাধারণত একটি পর্যবেক্ষণের সাথে অনুসরণ করা হয় যে একটি ম্যাক্রো ব্যবহার করা যেতে পারে যাতে হেডারটি ডাবল ডিউটি ​​পরিবেশন করে - সাধারণত ভেরিয়েবলগুলি ঘোষণা করে, তবে যখন শিরোনাম অন্তর্ভুক্ত করার আগে একটি নির্দিষ্ট ম্যাক্রো সেট করা হয়, পরিবর্তে এটি ভেরিয়েবলগুলি সংজ্ঞায়িত করে।

আর একটি উদ্বেগ হতে পারে যে ভেরিয়েবলগুলি প্রতিটি 'মূল প্রোগ্রাম' এর একটিতে সংজ্ঞায়িত করা দরকার। এটি সাধারণত উদ্দীপনাজনিত উদ্বেগ; আপনি ভেরিয়েবলগুলি সংজ্ঞায়িত করতে এবং প্রোগ্রামের প্রতিটিটির সাথে উত্পাদিত অবজেক্ট ফাইলটি লিঙ্ক করার জন্য কেবল একটি সি উত্স ফাইল চালু করতে পারেন।

একটি সাধারণ স্কিম এর মধ্যে চিত্রিত মূল গ্লোবাল ভেরিয়েবল ব্যবহার করে এর মতো কাজ করে file3.h:

file3a.h

#ifdef DEFINE_VARIABLES
#define EXTERN /* nothing */
#else
#define EXTERN extern
#endif /* DEFINE_VARIABLES */

EXTERN int global_variable;

file1a.c

#define DEFINE_VARIABLES
#include "file3a.h"  /* Variable defined - but not initialized */
#include "prog3.h"

int increment(void) { return global_variable++; }

file2a.c

#include "file3a.h"
#include "prog3.h"
#include <stdio.h>

void use_it(void)
{
    printf("Global variable: %d\n", global_variable++);
}

পরবর্তী দুটি ফাইল এর জন্য উত্সটি সম্পূর্ণ করে prog3:

prog3.h

extern void use_it(void);
extern int increment(void);

prog3.c

#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 এবং ম্যাক্রোগুলির জন্য পরিবর্তনশীল যুক্তি তালিকার সাহায্যে আপনি আরম্ভকে সমর্থন করার জন্য ম্যাক্রোও সংজ্ঞায়িত করতে পারেন। (সি 89 এবং ম্যাক্রোগুলিতে পরিবর্তনশীল আর্গুমেন্ট তালিকার কোনও সমর্থন না থাকলে, নির্বিচারে দীর্ঘ আরম্ভকারীদের পরিচালনা করার সহজ উপায় নেই))

file3b.h

#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ব্লকগুলি বিপরীত করুন

file1b.c

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

file2b.c

#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:

prog4.h

extern int increment(void);
extern int oddball_value(void);
extern void use_them(void);

prog4.c

#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

হেডার গার্ডস

যে কোনও শিরোনাম পুনরায় অন্তর্ভুক্তির বিরুদ্ধে সুরক্ষিত করা উচিত, যাতে প্রকারের সংজ্ঞাগুলি (এনাম, স্ট্রাক্ট বা ইউনিয়নের ধরণ বা টাইপডেফগুলি সাধারণত) সমস্যা তৈরি করে না। মানক কৌশলটি শিরোনামের রক্ষায় যেমন শিরোনামের শরীরে শিরোনামের দেহটি মোড়ানো হয়:

#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ঘোষণার জন্য এবং একবারে সংজ্ঞাগুলির জন্য একবারে বডি অন্তর্ভুক্ত করতে হবে তবে আপনার একক অনুবাদ ইউনিটে (টিইউ - কোনও উত্স ফাইল এবং এটি ব্যবহার করা শিরোনামের সংমিশ্রণ) উভয়ের প্রয়োজন হতে পারে।

পরিবর্তনশীল সংজ্ঞা সহ একাধিক অন্তর্ভুক্তি

তবে এটি একটি খুব অযৌক্তিক বাধা নয়। আসুন ফাইলের নামের একটি নতুন সেট প্রবর্তন করা যাক:

  • external.h এক্সটারন ম্যাক্রো সংজ্ঞা ইত্যাদির জন্য

  • file1c.hধরণের সংজ্ঞা দিতে (উল্লেখযোগ্যভাবে, struct oddballপ্রকারের oddball_struct)

  • file2c.h গ্লোবাল ভেরিয়েবলগুলি সংজ্ঞায়িত করতে বা ঘোষণা করতে।

  • file3c.c যা গ্লোবাল ভেরিয়েবলকে সংজ্ঞায়িত করে।

  • file4c.c যা কেবল গ্লোবাল ভেরিয়েবল ব্যবহার করে।

  • file5c.c যা দেখায় যে আপনি ঘোষণা করতে পারেন এবং তারপরে গ্লোবাল ভেরিয়েবলগুলি সংজ্ঞায়িত করতে পারেন।

  • file6c.c যা দেখায় যে আপনি সংজ্ঞায়িত করতে পারেন এবং তারপরে গ্লোবাল ভেরিয়েবলগুলি ঘোষণা করার চেষ্টা করতে পারেন।

এই উদাহরণগুলিতে file5c.cএবং file6c.cসরাসরি file2c.hবেশ কয়েকবার শিরোনাম অন্তর্ভুক্ত করেন তবে প্রক্রিয়াটি কাজ করে তা দেখানোর সহজতম উপায়। এর অর্থ হ'ল যদি শিরোলেখকে অপ্রত্যক্ষভাবে দু'বার অন্তর্ভুক্ত করা হয় তবে এটিও নিরাপদ হবে।

এটির কাজ করার জন্য বিধিনিষেধগুলি হ'ল:

  1. গ্লোবাল ভেরিয়েবলগুলি সংজ্ঞায়িত বা ঘোষণার শিরোনাম নিজেই কোনও প্রকারের সংজ্ঞা দিতে পারে না।

  2. ভেরিয়েবল সংজ্ঞায়িত করা উচিত এমন একটি শিরোনাম অন্তর্ভুক্ত করার সাথে সাথে আপনি ম্যাক্রো DEFINE_VARIABLES সংজ্ঞায়িত করুন।

  3. ভেরিয়েবলগুলি সংজ্ঞায়িত বা ঘোষণার শিরোনামে স্টাইলাইজড সামগ্রী রয়েছে।

external.h


#ifdef DEFINE_VARIABLES
#define EXTERN              /* nothing */
#define INITIALIZE(...)     = __VA_ARGS__
#else
#define EXTERN              extern
#define INITIALIZE(...)     /* nothing */
#endif /* DEFINE_VARIABLES */

file1c.h

#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 */

file2c.h


/* 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 */

file3c.c

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

file4c.c

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

file5c.c


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

file6c.c


#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:

prog5.c

#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:

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), তবে আপনার পরিষ্কার হওয়া উচিত। অতিরিক্ত লাইনটি লিখতে হবে তা মনে রাখা কেবল উপদ্রব is বিকল্প হতে পারে:

#define HEADER_DEFINING_VARIABLES "file2c.h"
#include "externdef.h"

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)।

file7c.c

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

file8c.h

/* 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 */

file8c.c

/* 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:

prog8.c

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

file9c.c

#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


তবে, সমস্যাগুলি অনুশীলনের ক্ষেত্রে অপেক্ষাকৃত কম হওয়ার সম্ভাবনা রয়েছে, বিশেষত যদি আপনি মানক পরামর্শ গ্রহণ করেন

গ্লোবাল ভেরিয়েবলগুলি এড়িয়ে চলুন


এই প্রকাশটি কি কিছু মিস করে?

স্বীকারোক্তি : এখানে বর্ণিত 'ডুপ্লিকেট কোড এড়ানো' প্রকল্পটি তৈরি করা হয়েছিল কারণ সমস্যাটি আমি কাজ করি এমন কিছু কোডকে প্রভাবিত করে (তবে তার মালিক নয়), এবং উত্তরের প্রথম অংশে বর্ণিত এই স্কিমটি নিয়ে উদ্বেগজনক উদ্বেগ। যাইহোক, আসল স্কিমটি আপনাকে পরিবর্তনশীল সংজ্ঞা এবং ঘোষণাকে সিঙ্ক্রোনাইজ করার জন্য পরিবর্তনের জন্য মাত্র দুটি জায়গা রেখে দেয়, যা কোড বেজ জুড়ে বহিরাগত ভেরিয়েবল ডিক্লেয়ারেশন ছড়িয়ে ছিটিয়ে থাকার চেয়ে বড় পদক্ষেপ (যা মোটামুটি কয়েক হাজার ফাইল রয়েছে তখনই গুরুত্বপূর্ণ) । তবে নামগুলির fileNc.[ch]( ফাইল external.hএবং externdef.h) সহ ফাইলগুলিতে কোড দেখায় যে এটি কাজ করতে পারে। স্পষ্টতই, হেডারের ফাইলটি সংজ্ঞায়িত ও ঘোষণার জন্য আপনাকে মানকযুক্ত টেম্পলেট দেওয়ার জন্য শিরোনাম জেনারেটর স্ক্রিপ্ট তৈরি করা কঠিন হবে না।

এনবি এগুলিকে কেবল সামান্য আকর্ষণীয় করে তুলতে কেবল পর্যাপ্ত কোড সহ খেলনা প্রোগ্রাম। উদাহরণগুলির মধ্যে পুনরাবৃত্তি রয়েছে যা মুছে ফেলা হতে পারে, তবে শিক্ষাগত ব্যাখ্যাটি সরল করার জন্য নয়। (উদাহরণস্বরূপ: অন্তর্ভুক্ত শিরোনামগুলির মধ্যে একটির মধ্যে পার্থক্য prog5.cএবং prog8.cনাম the কোডটি পুনর্গঠন করা সম্ভব হবে যাতে main()ফাংশনটি পুনরাবৃত্তি না হয় তবে এটি প্রকাশের চেয়ে আরও বেশি গোপন করে))


3
@ লিটব: সাধারণ সংজ্ঞার জন্য অ্যাঙ্কেক্স জে .5.11 দেখুন - এটি একটি সাধারণ বর্ধিতাংশ।
জোনাথন লেফলার

3
@ লিটব: এবং আমি সম্মত হই যে এড়ানো উচিত - এ কারণেই এটি 'গ্লোবাল ভেরিয়েবলগুলি সংজ্ঞায়নের পক্ষে এত ভাল উপায় নয়' বিভাগে রয়েছে।
জোনাথন লেফলার

3
প্রকৃতপক্ষে এটি একটি সাধারণ সম্প্রসারণ, তবে কোনও প্রোগ্রামের উপর নির্ভর করার জন্য এটি অনির্ধারিত আচরণ। আপনি কেবল বলছিলেন যে এটি সি এর নিজস্ব নিয়ম দ্বারা অনুমোদিত। এখন আমি দেখছি আপনি বলছেন এটি কেবল একটি সাধারণ এক্সটেনশান এবং আপনার কোডটি পোর্টেবল করার প্রয়োজন হলে এটি এড়াতে। সুতরাং আমি আপনাকে সন্দেহ ছাড়াই উন্নীত করতে পারি। সত্যিই দুর্দান্ত উত্তর আইএমএইচও :)
জোহানেস স্কাউব - 15

19
আপনি যদি শীর্ষে থামেন, এটি সাধারণ জিনিসগুলি সহজ রাখে। আপনি আরও পড়তে পড়তে, এটি আরও সংক্ষিপ্তসার, জটিলতা এবং বিশদ নিয়ে কাজ করে। আমি কম অভিজ্ঞ সি প্রোগ্রামার - বা সি প্রোগ্রামার যারা ইতিমধ্যে বিষয়টি জানে তাদের জন্য দুটি 'প্রাথমিক স্টপিং পয়েন্ট' যুক্ত করেছি। আপনি যদি ইতিমধ্যে উত্তরটি জানেন তবে এগুলি পড়ার দরকার নেই (তবে কোনও প্রযুক্তিগত ত্রুটি খুঁজে পাওয়া যায় তবে আমাকে জানান)।
জোনাথন লেফলার

4
@supercat: এটা আমার কাছে যেটা আপনি C99 অ্যারের লিটারেল ব্যবহার করতে পারেন অ্যারের আকার জন্য একটি শুমার মান, দ্বারা (exemplified পেতে 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,।
জোনাথন লেফলার

125

একটি externপরিবর্তনশীল হ'ল একটি ঘোষণা (সংশোধন করার জন্য এসবিআইকে ধন্যবাদ) যা অন্য অনুবাদ ইউনিটে সংজ্ঞায়িত করা হয়। তার মানে ভেরিয়েবলের স্টোরেজটি অন্য একটি ফাইলে বরাদ্দ দেওয়া হয়েছে।

বলুন আপনার কাছে দু- .cফাইল রয়েছে 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

21
কোনও "সিউডো-সংজ্ঞা" নেই। এটি একটি ঘোষণা।
এসবিআই

3
উপরের উদাহরণে, আমি যদি পরিবর্তন extern int test1_var;করতে int test1_var;, linker (জিসিসি 5.4.0) এখনও প্রেরণ করা হয়। সুতরাং, externএই ক্ষেত্রে সত্যিই প্রয়োজন?
রেডিওহেড

2
@ অ্যাডিয়োহেড: আমার জবাবে , আপনি তথ্য পাবেন যে externএটিকে বাদ দেওয়া একটি সাধারণ বর্ধন যা প্রায়শই কাজ করে - এবং বিশেষভাবে জিসিসির সাথে কাজ করে (তবে জিসিসি এটির সমর্থনকারী একমাত্র সংকলক হতে অনেক দূরে; এটি ইউনিক্স সিস্টেমে প্রচলিত)। আপনি "J.5.11" বা অধ্যায় "তাই ভাল না পথ" আমার উত্তর খুঁজতে পারেন (আমি জানি - এটা হল দীর্ঘ) এবং যে কাছাকাছি পাঠ্য এটা ব্যাখ্যা করে (অথবা চেষ্টা তা করার)।
জোনাথন লেফলার

একটি বাহ্যিক ঘোষণা অবশ্যই অন্য অনুবাদ ইউনিটে সংজ্ঞায়িত করতে হবে না (এবং সাধারণত এটি হয় না)। আসলে, ঘোষণা এবং সংজ্ঞা এক এবং এক হতে পারে।
মনিকা

40

বাহ্যিকটি আপনি যে কীওয়ার্ডটি ঘোষণা করতে ব্যবহার করেন তা ভেরিয়েবল নিজেই অন্য অনুবাদ ইউনিটে থাকে।

সুতরাং আপনি কোনও অনুবাদ ইউনিটে একটি ভেরিয়েবল ব্যবহার করার সিদ্ধান্ত নিতে পারেন এবং তারপরে অন্যটি থেকে এটি অ্যাক্সেস করতে পারেন, তারপরে দ্বিতীয়টিতে আপনি এটি বাহ্যিক হিসাবে ঘোষণা করেন এবং প্রতীকটি লিঙ্কার দ্বারা সমাধান করা হবে।

আপনি যদি এটিকে বহিরাগত হিসাবে ঘোষণা না করেন তবে আপনি 2 টি ভেরিয়েবল একই নামের পাবেন তবে একেবারেই সম্পর্কিত নয় এবং ভেরিয়েবলের একাধিক সংজ্ঞায়িত ত্রুটি।


5
অন্য কথায় অনুবাদ ইউনিট যেখানে বহিরাগত ব্যবহৃত হয় এটি এই পরিবর্তনশীল, এর প্রকার ইত্যাদি সম্পর্কে জানে এবং অতএব অন্তর্নিহিত লজিকের উত্স কোডটিকে এটি ব্যবহারের অনুমতি দেয় তবে এটি ভেরিয়েবল বরাদ্দ করে না , অন্য অনুবাদ ইউনিট এটি করবে। যদি উভয় অনুবাদ ইউনিট ভেরিয়েবলটিকে সাধারণভাবে ঘোষণা করে থাকে তবে সংশ্লেষিত কোডের মধ্যে সম্পর্কিত "ভুল" রেফারেন্স সহ এবং লিঙ্কারের জন্য ফলস্বরূপ অস্পষ্টতার সাথে ভেরিয়েবলের জন্য কার্যকরভাবে দুটি শারীরিক অবস্থান থাকবে।
এমজেভি

26

আপনি কম্পাইলারকে যে প্রতিশ্রুতি দিয়েছেন তা হিসাবে আমি বাহ্যিক পরিবর্তনশীল সম্পর্কে ভাবতে চাই।

কোনও বাহ্যিকের মুখোমুখি হওয়ার সময়, সংকলকটি কেবল এটির ধরণটি সন্ধান করতে পারে, যেখানে এটি "বাস করে" না, তাই এটি উল্লেখটি সমাধান করতে পারে না।

আপনি এটিকে বলছেন, "আমাকে বিশ্বাস করুন link সংযোগের সময় এই রেফারেন্সটি সমাধানযোগ্য হবে।"


আরও সাধারণভাবে, একটি ঘোষণা হল একটি প্রতিশ্রুতি যে লিঙ্ক সময়ে নামটি হুবহু এক সংজ্ঞাতে সমাধান করা যাবে। একটি বহিরাগত নির্ধারণ না করে একটি ভেরিয়েবল ঘোষণা করে।
মিথ্যা রায়ান

18

বহির্মুখী সংস্থাপককে আপনাকে বিশ্বাস করতে বলে যে এই ভেরিয়েবলের জন্য মেমরিটি অন্য কোথাও ঘোষিত হয়েছে, সুতরাং এটি মেমরির বরাদ্দ / পরীক্ষা করার চেষ্টা করে না।

অতএব, আপনি কোনও ফাইলের সংকলন করতে পারেন যাতে কোনও বাহ্যিকের রেফারেন্স রয়েছে তবে আপনি যদি সেই স্মৃতিটি কোথাও ঘোষিত না হয় তবে আপনি লিঙ্ক করতে পারবেন না।

গ্লোবাল ভেরিয়েবল এবং লাইব্রেরিগুলির জন্য দরকারী তবে বিপজ্জনক কারণ লিঙ্কারটি চেক টাইপ করে না।


স্মৃতি ঘোষণা করা হয় নি। আরও তথ্যের জন্য এই প্রশ্নের উত্তর দেখুন: stackoverflow.com/questions/1410563
এসবিআই

15

যোগ করার সময় একটি externএকটি পরিবর্তনশীল পালাক্রমে সংজ্ঞা একটি পরিবর্তনশীল মধ্যে ঘোষণা । একটি ঘোষণা এবং সংজ্ঞা মধ্যে পার্থক্য কি তা এই থ্রেড দেখুন ।


int fooএবং extern int foo(ফাইল স্কোপ) এর মধ্যে পার্থক্য কী ? দুটোই ঘোষণা, তাই না?

@ ব্যবহারকারী ১৪৪৪৪: এগুলি উভয়ই কেবলমাত্র এই অর্থে ঘোষণা যে প্রতিটি সংজ্ঞাও একটি ঘোষণা। তবে আমি এর একটি ব্যাখ্যার সাথে যুক্ত হয়েছি। ("একটি ঘোষণার এবং সংজ্ঞার মধ্যে পার্থক্য কী তা এই থ্রেডটি দেখুন" ") আপনি কেন সহজ লিঙ্কটি অনুসরণ করেন এবং পড়েন না?
sbi

14
                 declare | define   | initialize |
                ----------------------------------

extern int a;    yes          no           no
-------------
int a = 2019;    yes          yes          yes
-------------
int a;           yes          yes          no
-------------

ঘোষণা মেমরি বরাদ্দ করবে না (মেমরি বরাদ্দের জন্য পরিবর্তনশীল অবশ্যই সংজ্ঞায়িত করা হবে) তবে সংজ্ঞাটি হবে। বাহ্যিক কীওয়ার্ডে এটি অন্য একটি সাধারণ দৃষ্টিভঙ্গি যেহেতু অন্যান্য উত্তরগুলি দুর্দান্ত।


11

বাহ্যিকের সঠিক ব্যাখ্যাটি হ'ল আপনি কম্পাইলারকে কিছু বলছেন। আপনি সংকলককে বলছেন যে, এখনই উপস্থিত না থাকা সত্ত্বেও ঘোষিত ভেরিয়েবলটি কোনওভাবে লিঙ্কারের দ্বারা পাওয়া যাবে (সাধারণত অন্য কোনও অবজেক্টে (ফাইল))। লিঙ্কারটি তখন ভাগ্যবান লোক হবে যা আপনাকে খুঁজে বের করে একসাথে রাখবে, আপনার কিছু বাহ্যিক ঘোষণা ছিল বা না হোক।


8

সি-তে একটি ফাইলের অভ্যন্তরে একটি ভেরিয়েবল বলতে উদাহরণস.কে স্থানীয় সুযোগ দেওয়া হয়। সংকলকটি আশা করে যে ভেরিয়েবলটির একই সংখ্যার ফাইলের মধ্যে তার সংজ্ঞা থাকবে। উদাহরণস্বরূপ, এটি যখন একটির সন্ধান না করে, এটি একটি ত্রুটি ছুঁড়ে মারবে the অন্যদিকে ডিফল্টরূপে বিশ্বব্যাপী সুযোগ রয়েছে A এইভাবে আপনাকে সংকলকটি স্পষ্টভাবে উল্লেখ করতে হবে না "চেহারা দোস্ত ... আপনি এখানে এই ফাংশনটির সংজ্ঞা পেতে পারেন"। ফাইল সহ কোনও ফাংশনের জন্য যার ঘোষণাপত্র রয়েছে এটি যথেষ্ট ((যে ফাইলটি আপনি আসলে একটি শিরোনাম ফাইলকে কল করেন)। উদাহরণস্বরূপ বিবেচনা নিম্নলিখিত 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) সিসি -o উদাহরণস্বরূপ। উদাহরণ 1.c পদক্ষেপ 2) ./ প্রাক্তন

আপনি নিম্নলিখিত আউটপুট পাবেন: একটি এর মান <5>


8

জিসিসি ইএলএফ লিনাক্স বাস্তবায়ন

অন্যান্য উত্তরগুলি ভাষা ব্যবহারের দিকের দিকটি .েকে রেখেছে তাই এখন এই বাস্তবায়নে কীভাবে এটি প্রয়োগ করা হয় তা একবার দেখুন have

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

সিস্টেম ভী ABI- র আপডেট ELF বৈশিষ্ট "চিহ্ন ছক" অধ্যায় ব্যাখ্যা করেছেন:

SHN_UNDEF এই বিভাগের সারণী সূচীর অর্থ প্রতীকটি অপরিজ্ঞাত। লিঙ্ক এডিটর এই অবজেক্ট ফাইলটিকে অন্য একটির সাথে সংযুক্ত করে যা নির্দেশিত প্রতীকটিকে সংজ্ঞায়িত করে, প্রতীকটির সাথে এই ফাইলটির রেফারেন্সগুলি আসল সংজ্ঞাটির সাথে যুক্ত হবে।

যা মূলত সি স্ট্যান্ডার্ডটি externভেরিয়েবলগুলিকে দেয় ।

এখন থেকে, চূড়ান্ত প্রোগ্রামটি তৈরি করা লিঙ্কারের কাজ, তবে externইতিমধ্যে উত্স ফাইল থেকে তথ্য কোড থেকে তথ্যটি বের করা হয়েছে।

জিসিসি ৪.৮ এ পরীক্ষিত।

সি ++ 17 ইনলাইন ভেরিয়েবল

সি ++ 17 এ আপনি বহিরাগতগুলির পরিবর্তে ইনলাইন ভেরিয়েবলগুলি ব্যবহার করতে চাইতে পারেন, কারণ এগুলি ব্যবহার করা সহজ (কেবল একবার শিরোনামে সংজ্ঞায়িত করা যেতে পারে) এবং আরও শক্তিশালী (সমর্থন কনটেক্সটার)। দেখুন: সি এবং সি ++ এ 'কনস্ট্যান্ট স্ট্যাটিক' অর্থ কী?


3
এটি আমার ডাউন-ভোট নয়, তাই আমি জানি না। তবে আমি একটি মতামত জানাব। যদিও আউটপুটটি অনুসন্ধান করা readelfবা nmসহায়ক হতে পারে, আপনি কীভাবে ব্যবহার করবেন তার মৌলিক বিষয়গুলি ব্যাখ্যা externকরেননি বা আসল সংজ্ঞা সহ প্রথম প্রোগ্রামটি সম্পূর্ণ করেছেন না। আপনার কোড এমনকি ব্যবহার করে না notExtern। নামকরণের সমস্যাটিও রয়েছে: এটি notExternঘোষণার পরিবর্তে এখানে সংজ্ঞায়িত করা হলেও externএটি একটি বাহ্যিক ভেরিয়েবল যা অন্য উত্স ফাইলগুলি অ্যাক্সেস করতে পারে যদি সেই অনুবাদ ইউনিটগুলিতে একটি উপযুক্ত ঘোষণা থাকে (যা প্রয়োজন extern int notExtern;!)।
জোনাথন লেফলার

1
প্রতিক্রিয়াটির জন্য @ জোনাথনলফলারকে ধন্যবাদ! অন্যান্য আচরণে ইতিমধ্যে স্ট্যান্ডার্ড আচরণ এবং ব্যবহারের পরামর্শ দেওয়া হয়েছে, তাই আমি বাস্তবায়নটি কিছুটা দেখানোর সিদ্ধান্ত নিয়েছি যা সত্যিই আমাকে যা চলছে তা বুঝতে সাহায্য করেছে helped ব্যবহার না notExternকরা কুশ্রী ছিল, এটি স্থির করুন। নামকরণ সম্পর্কে, আপনার আরও ভাল নাম থাকলে আমাকে জানান। অবশ্যই এটি একটি আসল প্রোগ্রামের জন্য ভাল নাম হবে না তবে আমি মনে করি এটি এখানে ডিড্যাকটিক ভূমিকাটি ভালভাবে ফিট করে।
সিরো সান্তিলি :5 病毒 审查 六四 事件 法轮功

নাম হিসাবে, global_defএখানে সংজ্ঞায়িত ভেরিয়েবল সম্পর্কে এবং extern_refঅন্যান্য কিছু মডিউলে সংজ্ঞায়িত ভেরিয়েবল সম্পর্কে কী ? তাদের কি যথাযথভাবে সুস্পষ্ট প্রতিসাম্য থাকতে পারে? আপনি এখনও int extern_ref = 57;ফাইলের যেখানে এটি সংজ্ঞায়িত হয়েছে এমন কিছু বা এর সাথে শেষ করেছেন , তাই নামটি বেশ আদর্শ নয়, তবে একক উত্স ফাইলের প্রসঙ্গে এটি একটি যুক্তিসঙ্গত পছন্দ। রয়ে extern int global_def;একটি শিরোলেখ নেই যতটা কোন সমস্যা হওয়ার, আমার মনে হয়। অবশ্যই আপনার উপর নির্ভর করে।
জোনাথন লেফলার

7

বহিরাগত কীওয়ার্ডটি বৈশ্বীয় ভেরিয়েবল হিসাবে সনাক্তকরণের জন্য ভেরিয়েবলের সাথে ব্যবহৃত হয়।

এটি উপস্থাপন করে যে আপনি যে কোনও ফাইলের বহিরাগত কীওয়ার্ড ব্যবহার করে ঘোষিত ভেরিয়েবলটি অন্য ফাইলটিতে ঘোষিত / সংজ্ঞায়িত হিসাবে ব্যবহার করতে পারবেন।


5

extern আপনার প্রোগ্রামের একটি মডিউল আপনার প্রোগ্রামের অন্য মডিউলে ঘোষিত গ্লোবাল ভেরিয়েবল বা ফাংশন অ্যাক্সেস করার অনুমতি দেয়। আপনার সাধারণত বহিরাগত ভেরিয়েবলগুলি হেডার ফাইলগুলিতে ঘোষিত হয়।

আপনি যদি কোনও ভেরিয়েবল বা ফাংশন অ্যাক্সেস করার জন্য কোনও প্রোগ্রাম না চান তবে আপনি ব্যবহার করেন staticযা সংকলককে বলে যে এই পরিবর্তনশীল বা ফাংশনটি এই মডিউলটির বাইরে ব্যবহার করা যাবে না।


5

extern সহজভাবে একটি ভেরিয়েবল অন্য কোথাও সংজ্ঞায়িত করা হয় (যেমন, অন্য কোনও ফাইলে)।


4

প্রথমে, externকীওয়ার্ডটি ভেরিয়েবল সংজ্ঞায়িত করার জন্য ব্যবহৃত হয় না; বরং এটি ভেরিয়েবল ঘোষণার জন্য ব্যবহৃত হয়। আমি বলতে পারি externএকটি স্টোরেজ ক্লাস, কোনও ডেটা টাইপ নয়।

externঅন্যান্য সি ফাইল বা বহিরাগত উপাদানগুলি এই ভেরিয়েবলটি ইতিমধ্যে কোথাও সংজ্ঞায়িত হয়েছে তা জানাতে ব্যবহৃত হয়। উদাহরণ: আপনি যদি একটি লাইব্রেরি তৈরি করে থাকেন তবে গ্রন্থাগারে নিজেই কোথাও বৈশ্বিক পরিবর্তনশীল নির্ধারণ করার প্রয়োজন নেই। লাইব্রেরিটি সরাসরি সংকলিত হবে, তবে ফাইলটি সংযুক্ত করার সময় এটি সংজ্ঞাটি পরীক্ষা করে।


3

externব্যবহার করা হয় যাতে একটি first.cফাইলের অন্য একটি second.cফাইলের গ্লোবাল প্যারামিটারে সম্পূর্ণ অ্যাক্সেস থাকতে পারে ।

externঘোষণা করা যেতে পারে first.cফাইল বা হেডার ফাইল কোন first.cঅন্তর্ভুক্ত করা হয়েছে।


3
দ্রষ্টব্য যে externঘোষণাটি শিরোনামে থাকা উচিত, এতে নয় first.c, যাতে প্রকারটি পরিবর্তিত হয়, তবে ঘোষণাপত্রটিও পরিবর্তিত হবে। এছাড়াও, যে শিরোনামটি ভেরিয়েবল ঘোষণা করে second.cতা অবশ্যই ঘোষণার সাথে সংজ্ঞাটি সুসংগত হয় তা নিশ্চিত করে অন্তর্ভুক্ত করা উচিত । শিরোনামে ঘোষণাটি হ'ল আঠালো যা এগুলি সমস্তকে একত্রে ধারণ করে; এটি ফাইলগুলি পৃথকভাবে সংকলন করার অনুমতি দেয় তবে বৈশ্বিক ভেরিয়েবলের ধরণের বিষয়ে তাদের ধারাবাহিক দৃষ্টিভঙ্গি নিশ্চিত করে।
জোনাথন লেফলার

2

Xc8 এর সাহায্যে আপনাকে প্রতিটি ফাইলে একই ধরণের ভেরিয়েবল ঘোষণার বিষয়ে সতর্কতা অবলম্বন করতে হবে, ভ্রান্তভাবে, intএকটি ফাইলে কিছু charএবং অন্যটিতে একটি বলার মতো ঘোষণা করতে পারে । এর ফলে ভেরিয়েবলের দুর্নীতি হতে পারে।

এই সমস্যাটি প্রায় 15 বছর আগে একটি মাইক্রোচিপ ফোরামে মার্জিতভাবে সমাধান করা হয়েছিল / * "HTTP: www.htsoft.com" / / দেখুন "ফোরাম / সমস্ত / showflat.php / বিড়াল / 0 / সংখ্যা / 18766 / an / 0 / পৃষ্ঠা / 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

এখন ফাইল ফাইল

#define MAIN_C 1
#include "global.h"
#undef MAIN_C

এর অর্থ মেইন সি। এ চলকটি হিসাবে ঘোষিত হবে unsigned char

এখন অন্যান্য ফাইলগুলিতে কেবল গ্লোবাল এইচ সহ এটিকে সেই ফাইলের জন্য একটি বাহ্যিক হিসাবে ঘোষণা করা হবে

extern unsigned char testing_mode;

তবে এটি সঠিকভাবে হিসাবে ঘোষণা করা হবে unsigned char

পুরানো ফোরাম পোস্টটি সম্ভবত এটি আরও কিছুটা স্পষ্টভাবে ব্যাখ্যা করেছে। তবে এটি একটি বাস্তব সম্ভাবনা gotchaযখন একটি সংকলক ব্যবহার করে যা আপনাকে একটি ফাইলের মধ্যে একটি ভেরিয়েবল ঘোষণা করতে এবং তারপরে একে অন্যের মধ্যে অন্য ধরণের হিসাবে বহিরাগত ঘোষণা করতে দেয়। এর সাথে সম্পর্কিত সমস্যাগুলি হ'ল যদি আপনি বলছেন টেস্টিং_মোডটিকে অন্য কোনও ফাইলে ইন্ট হিসাবে ঘোষণা করা হয় এটি 16 বিট ভেরি মনে করবে এবং রামের কিছু অংশ ওভাররাইট করবে, সম্ভাব্যভাবে অন্য ভেরিয়েবলকে কলুষিত করবে। ডিবাগ করা কঠিন!


0

একটি খুব সংক্ষিপ্ত সমাধান আমি কোনও শিরোনাম ফাইলটিকে কোনও অবজেক্টের বহিরাগত রেফারেন্স বা প্রকৃত বাস্তবায়ন ধারণ করার জন্য ব্যবহার করি। আসলে বস্তুটি ধারণ করে এমন ফাইলটি ঠিক করে#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
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.