কেন একটি একক যুক্তি (রূপান্তর নির্দিষ্টকরণ ছাড়াই) সহ প্রিন্টফেকে হ্রাস করা হয়?


102

যে বইটিতে আমি পড়ছি, তাতে লেখা আছে যে printfএকটি একক যুক্তি দিয়ে (রূপান্তরকারী নির্দিষ্টকরণ ছাড়াই) হ্রাস করা হয়েছে। এটি বিকল্প প্রস্তাব

printf("Hello World!");

সঙ্গে

puts("Hello World!");

অথবা

printf("%s", "Hello World!");

কেউ আমাকে বলতে পারেন কেন printf("Hello World!");ভুল? বইটিতে লেখা আছে এতে দুর্বলতা রয়েছে। এই দুর্বলতাগুলি কী কী?


34
নোট: printf("Hello World!")হয় না হিসাবে একই puts("Hello World!")puts()সংযোজন ক '\n'। পরিবর্তেprintf("abc")fputs("abc", stdout)
chux এর সাথে

5
কি বই? আমি মনে করি না printfযেভাবে getsসি৯৯-তে অবমূল্যায়ন করা হয়েছে একইভাবে হ্রাস করা হয়েছে, সুতরাং আপনি আপনার প্রশ্ন সম্পাদনাটিকে আরও সুনির্দিষ্ট বলে বিবেচনা করতে পারেন।
el.pescado

14
মনে হচ্ছে আপনি যে বইটি পড়ছেন তা খুব ভাল নয় - একটি ভাল বইয়ের মধ্যে এটি "অবমূল্যায়ন" এর মতো কিছু বলা উচিত নয় (এটি সত্যিকার অর্থে মিথ্যা বলা হয় যদি না লেখক তাদের নিজস্ব মতামত বর্ণনা করার জন্য শব্দটি ব্যবহার না করে) এবং এর ব্যবহারটি ব্যাখ্যা করা উচিত আপনার "করা উচিত নয়" এর উদাহরণ হিসাবে নিরাপদ / বৈধ কোড প্রদর্শন করার চেয়ে আসলে অবৈধ এবং বিপজ্জনক।
আর .. গীটহাব বন্ধ করুন ICE

8
আপনি কি বইটি সনাক্ত করতে পারবেন?
কিথ থমসন

7
দয়া করে বইটির শিরোনাম, লেখক এবং পৃষ্ঠা উল্লেখ উল্লেখ করুন। ধন্যবাদ.
গ্রিননলাইন

উত্তর:


122

printf("Hello World!"); আইএমএইচও কি দুর্বল নয় তবে এটি বিবেচনা করুন:

const char *str;
...
printf(str);

যদি ফর্ম্যাট স্পেসিফায়ারযুক্ত strস্ট্রিংয়ের দিকে ইঙ্গিত করতে দেখা যায় %s, তবে আপনার প্রোগ্রামটি অপরিজ্ঞাত আচরণ (বেশিরভাগ ক্রাশ) puts(str)প্রদর্শন করবে , যেখানে কেবল স্ট্রিংটি প্রদর্শিত হবে।

উদাহরণ:

printf("%s");   //undefined behaviour (mostly crash)
puts("%s");     // displays "%s\n"

21
প্রোগ্রামটি ক্রাশ হওয়ার কারণ ছাড়াও, ফর্ম্যাট স্ট্রিং সহ আরও অনেকগুলি শোষণ সম্ভব। : আরও তথ্যের জন্য এখানে দেখুন en.wikipedia.org/wiki/Uncontrolled_format_string
e.dan

9
আর একটি কারণ putsসম্ভবত এটি দ্রুততর হবে faster
এডিএমজেড

38
@ ব্ল্যাক: puts"সম্ভবত" দ্রুত, এবং এটি সম্ভবত অন্য কারণ হ'ল এটি সুপারিশ করা হয়, তবে এটি আসলে দ্রুত হয় না । আমি সবেমাত্র "Hello, world!"১,০০,০০০ বার, দু'ভাবেই মুদ্রণ করেছি । printfএটি দিয়ে 0.92 সেকেন্ড সময় নিয়েছে। putsএটি দিয়ে 0.93 সেকেন্ড সময় নিয়েছে। এটি যখন দক্ষতার কথা আসে তখন printfচিন্তার putsবিষয় রয়েছে তবে বনাম সেগুলির মধ্যে একটি নয়।
স্টিভ সামিট

10
@KonstantinWeitz কিন্তু (ক) আমি জিসিসি ব্যবহার করছেন না হয়, এবং (খ) এটা কোন ব্যাপার না কেন দাবি " putsদ্রুততর" মিথ্যা, এটা এখনও মিথ্যা।
স্টিভ সামিট

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

75

printf("Hello world");

ভাল এবং কোনও সুরক্ষার ঝুঁকি নেই।

সমস্যাটি রয়েছে:

printf(p);

যেখানে pব্যবহারকারী দ্বারা নিয়ন্ত্রিত কোনও ইনপুটটির পয়েন্টার। স্ট্রিং আক্রমণগুলিকে বিন্যাস করার ঝুঁকি রয়েছে : প্রোগ্রামটি নিয়ন্ত্রণের জন্য ব্যবহারকারী রূপান্তরকরণের নির্দিষ্টকরণ সন্নিবেশ করতে পারে, উদাহরণস্বরূপ, %xমেমরি ডাম্প %nকরতে বা মেমরিটিকে ওভাররাইট করতে rite

লক্ষ্য করুন puts("Hello world")থেকে আচরণের সমতুল্য নয় printf("Hello world")কিন্তু printf("Hello world\n")। সংকলকগণ সাধারণত এটির পরিবর্তে পরবর্তী কলটি অনুকূল করতে যথেষ্ট স্মার্ট puts


10
অবশ্যই printf(p,x)ব্যবহারকারী সমস্যা নিয়ন্ত্রণ করা ঠিক যেমন সমস্যাযুক্ত হবে p। সুতরাং সমস্যা না ব্যবহার printfমাত্র এক আর্গুমেন্ট সহ বরং একটি ব্যবহারকারী নিয়ন্ত্রিত বিন্যাস স্ট্রিং সঙ্গে।
হেগেন ভন ইটজেন

2
@ হ্যাগেনভোন এটজেন প্রযুক্তিগতভাবে এটি সত্য, তবে কয়েকজন ইচ্ছাকৃতভাবে ব্যবহারকারী দ্বারা সরবরাহিত ফর্ম্যাট স্ট্রিংটি ব্যবহার করবেন। লোকেরা যখন লিখেন printf(p), কারণ তারা বুঝতে পারে না যে এটি কোনও ফর্ম্যাট স্ট্রিং, তারা কেবলমাত্র মনে করে যে তারা আক্ষরিক মুদ্রণ করছে।
বর্মার

33

অন্যান্য উত্তরগুলির printf("Hello world! I am 50% happy today")পরেও তৈরি করা সহজ বাগ, এটি সম্ভাব্যরূপে বাজে মেমরির সমস্ত ধরণের সমস্যা তৈরি করে (এটি ইউবি!)।

প্রোগ্রামাররা যখন ভারব্যাটিক স্ট্রিং এবং অন্য কিছু চান না তখন একেবারে পরিষ্কার হওয়া "প্রয়োজনীয়" হওয়া সহজ, সহজ এবং আরও দৃust়

এবং printf("%s", "Hello world! I am 50% happy today")এটিই আপনাকে পায়। এটি পুরোপুরি বোকা।

(স্টিভ, অবশ্যই printf("He has %d cherries\n", ncherries)একেবারে একই জিনিস নয়; এক্ষেত্রে প্রোগ্রামার "ভারব্যাটিম স্ট্রিং" মানসিকতায় নেই; তিনি "ফর্ম্যাট স্ট্রিং" মানসিকতায় রয়েছেন।)


2
এটি কোনও যুক্তিযুক্ত নয়, এবং আমি বুঝতে পারি যে আপনি ভারব্যাটিম বনাম ফর্ম্যাট স্ট্রিং মানসিকতা সম্পর্কে কী বলছেন, তবে, ভাল, সকলেই সেভাবে ভাবেন না, এটি একটি কারণ যা এক-আকারের-ফিটস-সমস্ত নিয়মই র‌্যাঙ্ক করতে পারে। "কখনই ধ্রুব স্ট্রিং প্রিন্ট না করে" বলা printfঠিক যেমনটি "সবসময় লিখুন" ঠিক এইরকমই if(NULL == p)These এই নিয়মগুলি কিছু প্রোগ্রামারদের পক্ষে কার্যকর হতে পারে তবে সমস্ত নয় And এবং উভয় ক্ষেত্রেই (মেলানো printfফর্ম্যাট এবং যোদা শর্তসাপেক্ষ), আধুনিক সংকলকরা যাইহোক ভুল সম্পর্কে সতর্ক করে, সুতরাং কৃত্রিম নিয়মগুলি আরও কম গুরুত্বপূর্ণ
স্টিভ সামিট

1
@ স্টিভ যদি কিছু ব্যবহার করার ক্ষেত্রে ঠিক শূন্যপদ থাকে তবে বেশ কিছুটা ডাউনসাইড থাকে তবে হ্যাঁ এটি ব্যবহার করার সত্যিই কোনও কারণ নেই। অন্যদিকে Yoda অবস্থার Do যে তারা কোড কঠিন করে তুলতে (আপনি intuitively, বলতে চাই না "যদি পি শূন্য হয়" "যদি শূন্য পি হয়") পড়তে downside হয় না।
ভু

2
@ ভু এর printf("%s", "hello")থেকে ধীর হতে চলেছে printf("hello"), তাই একটি খারাপ দিক রয়েছে। একটি ছোট্ট কারণ আইও প্রায় সবসময় এ জাতীয় সরল বিন্যাসের চেয়ে ধীরে ধীরে ধীরে ধীরে থাকে।
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

1
@ ইয়াক্ক আমি সন্দেহ করি যে এটি ধীর হবে
এমএম

gcc -Wall -W -Werrorএই ধরনের ভুল থেকে খারাপ পরিণতি রোধ করবে।
chqrlie

17

আমি এখানে দুর্বলতার অংশ সম্পর্কে কিছুটা তথ্য যুক্ত করব ।

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

কেউ যদি আপনার প্রিন্টফে নিয়মিত স্ট্রিংয়ের পরিবর্তে ফর্ম্যাট স্ট্রিং চরিত্রটি রাখেন (বলুন, আপনি যদি প্রোগ্রামটি স্টিডিন প্রিন্ট করতে চান), প্রিন্টফ স্ট্যাকের মধ্যে যা খুশি নিতে পারবেন।

এটি লুকানো তথ্য অ্যাক্সেস করতে বা উদাহরণস্বরূপ অনুমোদনের বাইপাস অ্যাক্সেস করার জন্য স্ট্যাকগুলি অন্বেষণে প্রোগ্রামগুলি কাজে লাগাতে (এবং এখনও রয়েছে) খুব ব্যবহৃত হয়েছিল।

উদাহরণ (সি):

int main(int argc, char *argv[])
{
    printf(argv[argc - 1]); // takes the first argument if it exists
}

যদি আমি এই প্রোগ্রামটির ইনপুট হিসাবে রাখি "%08x %08x %08x %08x %08x\n"

printf ("%08x %08x %08x %08x %08x\n"); 

এটি স্ট্যাক থেকে পাঁচটি পরামিতি পুনরুদ্ধার করতে এবং এটিকে 8-সংখ্যার প্যাডেড হেক্সাডেসিমাল সংখ্যা হিসাবে প্রদর্শন করতে প্রিন্টফ-ফাংশনকে নির্দেশ দেয়। সুতরাং একটি সম্ভাব্য আউটপুট যেমন দেখতে পারে:

40012980 080628c4 bffff7a4 00000005 08059c04

দেখুন এই আরো একটি সম্পূর্ণ ব্যাখ্যা এবং অন্যান্য উদাহরণ জন্য।


13

printfআক্ষরিক বিন্যাসের স্ট্রিংগুলির সাথে কল করা নিরাপদ এবং দক্ষ এবং printfব্যবহারকারীদের দেওয়া বিন্যাসের স্ট্রিংগুলির সাথে যদি আপনার অনুরোধটি সুরক্ষিত না থাকে তবে স্বয়ংক্রিয়ভাবে আপনাকে সতর্ক করার জন্য এমন সরঞ্জাম রয়েছে ।

ফর্ম্যাট স্পেসিফায়ারের printfসুবিধা নেওয়ার ক্ষেত্রে সবচেয়ে গুরুতর আক্রমণ attacks %nঅন্যান্য সমস্ত ফর্ম্যাট স্পেসিফায়ারের বিপরীতে, যেমন %d, %nপ্রকৃত পক্ষে বিন্যাসের একটি আর্গুমেন্টে সরবরাহ করা মেমরি ঠিকানার মান লিখায়। এর অর্থ হ'ল কোনও আক্রমণকারী মেমরিটিকে ওভাররাইট করতে পারে এবং এইভাবে আপনার প্রোগ্রামটির নিয়ন্ত্রণ নিতে পারে। উইকিপিডিয়া আরও বিশদ সরবরাহ করে।

আপনি যদি printfআক্ষরিক বিন্যাসের স্ট্রিং দিয়ে কল করেন তবে আক্রমণকারী %nআপনার ফর্ম্যাট স্ট্রিংয়ের দিকে ঝাঁপিয়ে পড়ে না এবং আপনি নিরাপদে থাকেন are প্রকৃতপক্ষে, জিসিসি আপনার কলকে printfএকটি কলকে পরিবর্তিত করবে puts, সুতরাং লিটারেরালি কোনও পার্থক্য নেই (চলমান দ্বারা এটি পরীক্ষা করুন gcc -O3 -S)।

আপনি যদি printfকোনও ব্যবহারকারীর সরবরাহিত ফর্ম্যাট স্ট্রিংয়ের সাথে কল করেন তবে আক্রমণকারী কোনও সম্ভাব্যরূপে %nআপনার ফর্ম্যাট স্ট্রিংয়ের দিকে ছিনিয়ে নিতে পারে এবং আপনার প্রোগ্রামটির নিয়ন্ত্রণ নিতে পারে। আপনার সংকলক সাধারণত আপনাকে সতর্ক করে দেবে যে তিনি অনিরাপদ, দেখুন -Wformat-security। আরও আরও উন্নত সরঞ্জাম রয়েছে যা নিশ্চিত করে যে printfব্যবহারকারীর সরবরাহিত বিন্যাসের স্ট্রিংয়ের সাহায্যেও প্রার্থনা নিরাপদ এবং তারা এমনকি সঠিক সংখ্যা এবং যুক্তির ধরণটি আপনি পাস করেছেন কিনা তাও পরীক্ষা করতে পারে printf। উদাহরণস্বরূপ, জাভার জন্য গুগলের ত্রুটি প্রবণ এবং পরীক্ষক ফ্রেমওয়ার্ক রয়েছে


12

এটি বিপথগামী পরামর্শ। হ্যাঁ, মুদ্রণের জন্য আপনার যদি রান-টাইম স্ট্রিং থাকে,

printf(str);

বেশ বিপজ্জনক, এবং আপনার সর্বদা ব্যবহার করা উচিত

printf("%s", str);

পরিবর্তে, কারণ সাধারণভাবে আপনি কখনই জানতে পারবেন না যে strকোনও %চিহ্ন থাকতে পারে কিনা । যাইহোক, আপনার যদি একটি সংকলন-সময় ধ্রুব স্ট্রিং থাকে তবে এতে দোষের কিছু নেই

printf("Hello, world!\n");

(অন্যান্য জিনিসের মধ্যে, এটি আদিপুস্তে সর্বাধিক ক্লাসিক সি প্রোগ্রাম, আক্ষরিক অর্থে জেনেসিসের সি প্রোগ্রামিং বই থেকে So


because printf's first argument is always a constant stringআপনি এর সাথে কী বোঝাতে চাইছেন তা সম্পর্কে আমি ঠিক নিশ্চিত নই।
সেবাস্তিয়ান মাচ

আমি যেমন বলেছি, "He has %d cherries\n"একটি ধ্রুব স্ট্রিং, এর অর্থ এটি একটি সংকলন-সময় ধ্রুবক। তবে, সত্যি বলতে, লেখকের পরামর্শটি " printfপ্রথম যুক্তি হিসাবে ধ্রুব স্ট্রিংগুলি পাস করবেন না ", এটি ছিল " প্রথম যুক্তি %হিসাবে স্ট্রিংগুলি পাস করবেন না printf"।
স্টিভ সামিট

literally from the C programming book of Genesis. Anyone deprecating that usage is being quite offensively heretical- আপনি সাম্প্রতিক বছরগুলিতে কে-আরআর আসলে পড়েন নি। সেখানে এক টন পরামর্শ এবং কোডিং স্টাইল রয়েছে যা কেবল হ্রাস করা হয়নি তবে এই দিনগুলিতে কেবল সাধারণ খারাপ অভ্যাস।
ভু

@ ভু: ঠিক আছে, আমরা কেবল বলে রাখি যে খারাপ অভ্যাস হিসাবে বিবেচিত সমস্ত কিছুই আসলে খারাপ অভ্যাস নয়। ("কখনই সরল ব্যবহার করবেন না int" স্প্রিংসটি মনে মনে রাখবেন))
স্টিভ সামিট

1
@ স্টিভ আমি জানি না আপনি কোথায় এটি শুনেছেন, তবে এটি সেখানে যে খারাপ (খারাপ?) অনুশীলনের কথা বলছি তা অবশ্যই নয়। আমাকে ভুল বুঝবেন না, সময়ের জন্য কোডটি পুরোপুরি ঠিক ছিল, তবে আপনি সত্যিকার অর্থে আজকের দিনে খুব বেশি কিছু দেখতে চাইছেন না তবে aতিহাসিক নোট হিসাবে। "এটা K & R এর" শুধু না এই দিন ভাল মানের একটি সূচক, সব যে হয়
Voo

9

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


8

যেহেতু কেউ উল্লেখ করেনি, আমি তাদের সম্পাদনা সম্পর্কিত একটি নোট যুক্ত করব।

সাধারণ পরিস্থিতিতে, কোনও সংকলক অপটিমাইজেশন ব্যবহার করা হয় না ধরে নিয়ে (যেমন printf()আসলে কল printf()এবং না fputs()), আমি printf()কম দক্ষতার সাথে পারফর্ম করার আশা করব , বিশেষত দীর্ঘ স্ট্রিংয়ের জন্য। এটি printf()কোনও রূপান্তর নির্দিষ্টকারক রয়েছে কিনা তা পরীক্ষা করার জন্য স্ট্রিংকে বিশ্লেষণ করতে হবে।

এটি নিশ্চিত করার জন্য, আমি কয়েকটি পরীক্ষা চালিয়েছি। পরীক্ষাগুলি উগুন্টুতে 14.04, জিসিসি 4.8.4 সহ সঞ্চালিত হয়। আমার মেশিনটি একটি ইন্টেল আই 5 সিপিইউ ব্যবহার করে। প্রোগ্রামটি পরীক্ষা করা হচ্ছে:

#include <stdio.h>
int main() {
    int count = 10000000;
    while(count--) {
        // either
        printf("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM");
        // or
        fputs("qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM", stdout);
    }
    fflush(stdout);
    return 0;
}

দুটোই সংকলিত gcc -Wall -O0। সময় ব্যবহার করে পরিমাপ করা হয় time ./a.out > /dev/null। নিম্নলিখিতটি একটি সাধারণ রানের ফলাফল (আমি এগুলি পাঁচবার চালিয়েছি, সমস্ত ফলাফল 0.002 সেকেন্ডের মধ্যে রয়েছে)।

জন্য printf()বৈকল্পিক:

real    0m0.416s
user    0m0.384s
sys     0m0.033s

জন্য fputs()বৈকল্পিক:

real    0m0.297s
user    0m0.265s
sys     0m0.032s

যদি আপনার খুব দীর্ঘ স্ট্রিং থাকে তবে এই প্রভাবটি প্রশস্ত করা হবে ।

#include <stdio.h>
#define STR "qwertyuiopasdfghjklzxcvbnmQWERTYUIOPASDFGHJKLZXCVBNM"
#define STR2 STR STR
#define STR4 STR2 STR2
#define STR8 STR4 STR4
#define STR16 STR8 STR8
#define STR32 STR16 STR16
#define STR64 STR32 STR32
#define STR128 STR64 STR64
#define STR256 STR128 STR128
#define STR512 STR256 STR256
#define STR1024 STR512 STR512
int main() {
    int count = 10000000;
    while(count--) {
        // either
        printf(STR1024);
        // or
        fputs(STR1024, stdout);
    }
    fflush(stdout);
    return 0;
}

জন্য printf()বৈকল্পিক (তিনবার, বাস্তব প্লাস / মাইনাস 1.5s দৌড়ে):

real    0m39.259s
user    0m34.445s
sys     0m4.839s

জন্য fputs()বৈকল্পিক (তিনবার, বাস্তব প্লাস / মাইনাস 0.2s দৌড়ে):

real    0m12.726s
user    0m8.152s
sys     0m4.581s

নোট: পরিদর্শন সমাবেশ জিসিসি দ্বারা উত্পন্ন পর, আমি বুঝলাম যে জিসিসি সেরা অনুকূল রূপ দেয় fputs()একটি কল fwrite()কল, এমনকি -O0। ( printf()কলটি অপরিবর্তিত রয়েছে)) কম্পাইলার fwrite()সংকলন-সময়ে স্ট্রিংয়ের দৈর্ঘ্য গণনা করায় এটি আমার পরীক্ষাটি অকার্যকর করবে কিনা তা আমি নিশ্চিত নই ।


2
এটি আপনার পরীক্ষাকে অকার্যকর করে তুলবে না, কারণ fputs()প্রায়শই স্ট্রিং ধ্রুবকগুলির সাথে ব্যবহৃত হয় এবং সেই অনুকূলকরণের সুযোগটি আপনি যে পয়েন্টটি তৈরি করতে চেয়েছিলেন তার অংশ part এটি বলেছে, একটি গতিশীলভাবে উত্পন্ন স্ট্রিংয়ের সাথে একটি পরীক্ষা রান যোগ করা হবে fputs()এবং fprintf()এটি একটি দুর্দান্ত পরিপূরক ডেটা পয়েন্ট হবে ।
প্যাট্রিক Schlüter

@ প্যাট্রিকস্ল্যাটার টেস্টিং গতিশীলভাবে উত্পন্ন স্ট্রিংগুলির সাথে এই প্রশ্নের উদ্দেশ্যকে পরাভূত করছে বলে মনে হচ্ছে ... ওপি কেবল স্ট্রিং লিটারাল মুদ্রণ করতে আগ্রহী বলে মনে হচ্ছে।
ব্যবহারকারী 12205

1
এমনকি তার উদাহরণে স্ট্রিং লিটারেল ব্যবহার করা হলেও তিনি এটিকে স্পষ্টভাবে উল্লেখ করেন না। আসলে, আমি মনে করি বইয়ের পরামর্শ সম্পর্কে তাঁর বিভ্রান্তি উদাহরণে স্ট্রিং লিটারেল ব্যবহারের ফলস্বরূপ। স্ট্রিং লিটারেল সহ, বইগুলির পরামর্শ কোনওভাবে সন্দেহজনক, গতিশীল স্ট্রিং সহ এটি ভাল পরামর্শ।
প্যাট্রিক Schlüter

1
/dev/nullসাজানোর এটিকে খেলনা বানায়, সাধারণত ফর্ম্যাট করা আউটপুট জেনারেট করার সময় আপনার লক্ষ্য আউটপুটটি কোথাও যাওয়ার জন্য ফেলে দেওয়া হবে না। একবার আপনি "ডেটা আসলে ছাড়ছেন না" সময় যুক্ত করলে তারা কীভাবে তুলনা করবেন?
ইয়াক্ক - অ্যাডাম নেভ্রামামন্ট

7
printf("Hello World\n")

স্বয়ংক্রিয়ভাবে সমতুল্য সংকলিত

puts("Hello World")

আপনি নিজের নির্বাহযোগ্যকে ডাইসেম্বল করে এটি পরীক্ষা করতে পারেন:

push rbp
mov rbp,rsp
mov edi,str.Helloworld!
call dword imp.puts
mov eax,0x0
pop rbp
ret

ব্যবহার

char *variable;
... 
printf(variable)

সুরক্ষার সমস্যাগুলির দিকে পরিচালিত করবে, প্রিন্টফাকে কখনও সেভাবে ব্যবহার করবেন না!

সুতরাং আপনার বইটি আসলে সঠিক, একটি ভেরিয়েবলের সাথে প্রিন্টফ ব্যবহার অবমূল্যায়ন করা হয়েছে তবে আপনি এখনও প্রিন্টফ ("আমার স্ট্রিং \ n") ব্যবহার করতে পারেন কারণ এটি স্বয়ংক্রিয়ভাবে পুটে যাবে


12
এই আচরণটি সম্পূর্ণরূপে সংকলকের উপর নির্ভর করে।
জ্যাবারওয়াকি

6
এটি বিভ্রান্তিকর। আপনি রাষ্ট্র A compiles to B, কিন্তু বাস্তবে আপনি বোঝাতে চেয়েছিলেন A and B compile to C
সেবাস্তিয়ান মাচ

6

জিসিসির জন্য এটি পরীক্ষা করার জন্য নির্দিষ্ট সতর্কতাগুলি সক্ষম করা printf()এবং scanf()

সিসি ডকুমেন্টেশন বলে:

-Wformatঅন্তর্ভুক্ত করা হয় -Wall। চেক, বিকল্প বিন্যাস কিছু দিক উপর আরো বেশি নিয়ন্ত্রণ জন্য -Wformat-y2k, -Wno-format-extra-args, -Wno-format-zero-length, -Wformat-nonliteral, -Wformat-security, এবং -Wformat=2অন্তর্ভুক্ত নয় পাওয়া যায়, কিন্তু -Wall

-Wformatযার মধ্যে সক্রিয় করা হয় -Wallবিকল্প বেশ কয়েকটি বিশেষ সতর্কবার্তা যে সাহায্যের এই ক্ষেত্রে এটি সক্রিয় করে না:

  • -Wformat-nonliteral আপনি যদি স্ট্রিং লিটারেরালটিকে ফর্ম্যাট সুনির্দিষ্ট হিসাবে পাস না করেন তবে সতর্ক করবে will
  • -Wformat-securityআপনি যদি এমন স্ট্রিং পাস করেন যাতে একটি বিপজ্জনক নির্মাণ থাকতে পারে তা সতর্ক করে দেবে। এটা তোলে একটি উপসেট এর -Wformat-nonliteral

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


1

যে কোনও পার্শ্ব-উদ্বেগকে আচ্ছাদিত করে অন্যান্য সুস্পষ্ট বর্ণিত উত্তরগুলির পাশাপাশি, আমি প্রদত্ত প্রশ্নের একটি সুনির্দিষ্ট এবং সংক্ষিপ্ত উত্তর দিতে চাই।


কেন printfএকটি একক যুক্তি দিয়ে (রূপান্তর নির্দিষ্টকরণ ছাড়াই) অবহেলা করা হচ্ছে?

একজন printfসাধারণ একটি একক আর্গুমেন্ট সহ ফাংশন কল করা হয় না অবচিত এবং কোন দুর্বলতা আছে যখন সঠিকভাবে ব্যবহৃত হিসাবে আপনি সবসময় কোড হইবে।

সি ব্যবহারকারীগণ, পুরো বিশ্বের মধ্যে, স্ট্যাটাস শুরুর থেকে শুরু করে স্ট্যাটাস বিশেষজ্ঞ printfপর্যন্ত কনসোলকে আউটপুট হিসাবে একটি সাধারণ পাঠ্য বাক্যাংশ দেওয়ার জন্য সেই উপায়টি ব্যবহার করে।

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

অবশ্যই, এটিও সঠিক এবং যথাযথ নয় যে স্ট্রিংয়ের একমাত্র এবং কেবল যুক্তি হিসাবে সরবরাহ করা কোনও ফর্ম্যাট বা রূপান্তর নির্দিষ্টকরণকারী রয়েছে, যেহেতু কোনও রূপান্তর ঘটবে না।

এটি বলেছিল "Hello World!"যে স্ট্রিংটির ভিতরে কোনও বিন্যাসের নির্দিষ্টকরণ ছাড়াই কেবল যুক্তি হিসাবে একটি সাধারণ স্ট্রিংকে আক্ষরিক দেওয়া আপনার প্রশ্নের মতো করে দেওয়া হয়েছে:

printf("Hello World!");

হয় না অবচিত অথবা " খারাপ অভ্যাস " এ সব কিংবা কোনো দুর্বলতা আছে।

প্রকৃতপক্ষে, অনেক সি প্রোগ্রামাররা হ্যালো ওয়ার্ল্ড-প্রোগ্রাম এবং এই printfধরণের প্রথম বিবরণ হিসাবে সাধারণভাবে সি বা এমনকি প্রোগ্রামিং ভাষাগুলি শিখতে এবং ব্যবহার করতে শুরু করে ।

তারা যদি হতাশ হয় তবে তা হবে না।

যে বইটিতে আমি পড়ছি, তাতে লেখা আছে যে printfএকটি একক যুক্তি দিয়ে (রূপান্তরকারী নির্দিষ্টকরণ ছাড়াই) হ্রাস করা হয়েছে।

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

আমি উপরে যা বলেছি তার অনুসারে printfকেবলমাত্র একটি যুক্তি (একটি স্ট্রিং আক্ষরিক) দিয়ে এবং কোনও বিন্যাসের নির্দিষ্টকরণ ছাড়াই ব্যবহার কোনও ক্ষেত্রেই অবহিত বা "খারাপ অনুশীলন" হিসাবে বিবেচিত নয় ।

আপনার লেখককে জিজ্ঞাসা করা উচিত, তিনি এর অর্থ কী বা তার চেয়েও ভাল, তার পরবর্তী সংস্করণ বা সাধারণভাবে ছাপগুলির জন্য আপেক্ষিক বিভাগটি স্পষ্ট করে বা সংশোধন করার জন্য মনে করুন।


আপনি যোগ হতে পারে printf("Hello World!");হয় না সমতূল্য puts("Hello World!");যাহাই হউক না কেন, যা সুপারিশ লেখক সম্পর্কে কিছু বলে।
chqrlie
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.