एम्बेडेड कोड के लिए, मुझे "अहस्ताक्षरित int" के बजाय "uint_t" प्रकारों का उपयोग क्यों करना चाहिए?


22

मैं एक STM32F105 के लिए सी में एक आवेदन लिख रहा हूँ, जीसीसी का उपयोग कर।

अतीत (सरल परियोजनाओं के साथ) में, मैं हमेशा की तरह चर को परिभाषित किया है char, int, unsigned int, और इतने पर।

मुझे लगता है कि यह इस तरह के रूप में परिभाषित किया गया stdint.h प्रकार, उपयोग करने के लिए आम बात है int8_t, uint8_t, uint32_t, आदि यह यह कई एपीआई कि मैं उपयोग कर रहा हूँ, और यह भी अनुसूचित जनजाति से एआरएम CMSIS पुस्तकालय में सच।

मेरा मानना ​​है कि मैं समझता हूं कि हमें ऐसा क्यों करना चाहिए; कंपाइलर को मेमोरी स्पेस को बेहतर ऑप्टिमाइज़ करने की अनुमति देना। मुझे उम्मीद है कि अतिरिक्त कारण हो सकते हैं।

हालाँकि, c के पूर्णांक पदोन्नति नियमों के कारण, मैं किसी भी समय दो चेतावनियाँ जोड़ने का प्रयास करता हूँ, किसी भी समय एक बिटवाइज़ ऑपरेशन आदि करने की कोशिश में रूपांतरण चेतावनियों के खिलाफ दौड़ता रहता हूँ conversion to 'uint16_t' from 'int' may alter its value [-Wconversion]। इस मुद्दे पर यहाँ और यहाँ चर्चा की गई है

जब चर के रूप में घोषित का उपयोग कर यह नहीं होता है intया unsigned int

कुछ उदाहरण देने के लिए, यह दिया:

uint16_t value16;
uint8_t value8;

मुझे इसे बदलना होगा:

value16 <<= 8;
value8 += 2;

इसके लिए:

value16 = (uint16_t)(value16 << 8);
value8 = (uint8_t)(value8 + 2);

यह बदसूरत है, लेकिन यदि आवश्यक हो तो मैं यह कर सकता हूं। यहाँ मेरे सवाल हैं:

  1. वहाँ एक मामले में जहां से रूपांतरण है अहस्ताक्षरित के लिए हस्ताक्षर किए और वापस करने के लिए अहस्ताक्षरित परिणाम गलत कर देगा?

  2. वहाँ stdint.h पूर्णांक प्रकार का उपयोग करने के खिलाफ / के लिए कोई अन्य बड़े कारण हैं?

जवाब मैं प्राप्त कर रहा हूँ के आधार पर यह, stdint.h प्रकार की तरह आम तौर पर पसंद कर रहे हैं लग रहा है यहां तक कि ग धर्मान्तरित हालांकि uintकरने के लिए intऔर वापस। यह एक बड़ा सवाल है:

  1. मैं टाइपकास्टिंग (जैसे value16 = (uint16_t)(value16 << 8);) का उपयोग करके संकलक चेतावनी को रोक सकता हूं । क्या मैं सिर्फ समस्या छिपा रहा हूँ? क्या इसके बारे में जाने का एक बेहतर तरीका है?

अहस्ताक्षरित शाब्दिक का उपयोग करें: यानी 8uऔर 2u
मोनिका

धन्यवाद, @OrangeDog, मुझे लगता है कि मैं गलत समझ रहा हूं। मैंने दोनों की कोशिश की value8 += 2u;और value8 = value8 + 2u;, लेकिन मुझे एक ही चेतावनी मिलती है।
बिटस्मैक

जब भी आपके पास पहले से चौड़ी चेतावनियाँ न हों तो हस्ताक्षरित चेतावनियों से बचने के लिए इनका उपयोग करें :)
रोकें मोनिका को रोकें

जवाबों:


11

एक मानक-अनुरूप कंपाइलर जहां int17 से 32 बिट्स तक कहीं भी था, वह वैध रूप से निम्नलिखित कोड के साथ कुछ भी कर सकता है :

uint16_t x = 46341;
uint32_t y = x*x; // temp result is signed int, which can't hold 2147488281

एक कार्यान्वयन जो ऐसा करना चाहता था वह वैध रूप से एक प्रोग्राम उत्पन्न कर सकता था जो कि हर कल्पनाशील प्रोटोकॉल का उपयोग करके प्रत्येक पोर्ट पिन पर स्ट्रिंग "फ्रेड" के अलावा कुछ भी नहीं करेगा। एक कार्यक्रम के कार्यान्वयन की संभावना को लागू करने की संभावना है जो ऐसा काम करेगा जो असाधारण रूप से कम है, लेकिन यह सैद्धांतिक रूप से संभव है। यदि आप उपर्युक्त कोड लिखना चाहते हैं ताकि यह निर्धारित किया जाए कि अपरिभाषित व्यवहार में संलग्न न हों, तो बाद की अभिव्यक्ति को (uint32_t)x*xया तो लिखना आवश्यक होगा 1u*x*x। एक संकलक पर जहां int17 और 31 बिट्स के बीच है, बाद की अभिव्यक्ति ऊपरी बिट्स को बंद कर देगी, लेकिन अपरिभाषित व्यवहार में संलग्न नहीं होगी।

मुझे लगता है कि जीसीसी चेतावनियां यह सुझाव देने की कोशिश कर रही हैं कि जैसा लिखा गया कोड पूरी तरह से 100% पोर्टेबल नहीं है। ऐसे समय होते हैं जब कोड को वास्तव में उन व्यवहारों से बचने के लिए लिखा जाना चाहिए जो कुछ कार्यान्वयनों पर अनिर्धारित होंगे, लेकिन कई अन्य मामलों में किसी को बस यह पता लगाना चाहिए कि कोड को कार्यान्वयन पर उपयोग करने की संभावना नहीं है जो कि अत्यधिक कष्टप्रद चीजों को करेगा।

ध्यान दें कि प्रकारों का उपयोग करना intऔर shortकुछ चेतावनियों को समाप्त कर सकता है और कुछ समस्याओं को ठीक कर सकता है, लेकिन संभवत: दूसरों को पैदा करेगा। uint16_tसी और पूर्णांक-प्रमोशन नियमों जैसे प्रकारों के बीच की बातचीत icky है, लेकिन इस प्रकार के विकल्प अभी भी किसी भी विकल्प से बेहतर हैं।


6

1) यदि आप केवल एक ही लंबाई के हस्ताक्षरित पूर्णांक के बिना अहस्ताक्षरित से कास्ट करते हैं, तो बिना किसी ऑपरेशन के, आपको हर बार एक ही परिणाम मिलेगा, इसलिए यहां कोई समस्या नहीं है। लेकिन विभिन्न तार्किक और अंकगणितीय ऑपरेशन हस्ताक्षरित और अहस्ताक्षरित ऑपरेंड पर अलग-अलग कार्य कर रहे हैं।
2) stdint.hप्रकारों का उपयोग करने का मुख्य कारण यह है कि इस प्रकार के बिट आकार को सभी प्लेटफार्मों पर परिभाषित और समान किया जाता है, जो कि int, longइत्यादि के लिए सही नहीं है , साथ ही साथ charकोई मानक संकेत नहीं है, यह हस्ताक्षर या अहस्ताक्षरित हो सकता है। चूक। यह अतिरिक्त जाँच और मान्यताओं का उपयोग किए बिना सटीक आकार जानने वाले डेटा में हेरफेर करना आसान बनाता है।


2
के आकार int32_tऔर uint32_tसभी प्लेटफ़ॉर्म पर समान हैं, जिस पर वे परिभाषित हैं । यदि प्रोसेसर के पास बिल्कुल मिलान हार्डवेयर प्रकार नहीं है, तो ये प्रकार परिभाषित नहीं हैं। इसलिए intआदि और, शायद, int_least32_tआदि का लाभ
पीट बेकर

1
@PeteBecker - यह यकीनन एक फायदा है, हालांकि, परिणामस्वरूप संकलित त्रुटियां आपको समस्या से तुरंत अवगत कराती हैं। मैं अपने बजाय मेरे आकार को बदलने के बजाय बहुत कुछ करूँगा।
सपि

@ सपी - कई स्थितियों में अंतर्निहित आकार अप्रासंगिक है; सी प्रोग्रामर कई वर्षों के लिए तय आकार के बिना ही ठीक हो गए।
पीट बेकर

6

चूंकि यूजीन का # 2 शायद सबसे महत्वपूर्ण बिंदु है, मैं सिर्फ यह जोड़ना चाहूंगा कि यह एक सलाहकार है

MISRA (directive 4.6): "typedefs that indicate size and signedness should be used in place of the basic types".

इसके अलावा जैक गन्सल उस नियम के समर्थक प्रतीत होते हैं: http://www.ganssle.com/tem/tem2365.html


2
यह बहुत बुरा है "एन-बिट अहस्ताक्षरित पूर्णांक को निर्दिष्ट करने के लिए कोई भी प्रकार नहीं हैं जो समान आकार के परिणाम प्राप्त करने के लिए किसी अन्य समान आकार के पूर्णांक द्वारा सुरक्षित रूप से गुणा किया जा सकता है"। इंटेगर प्रमोशन नियम मौजूदा प्रकारों के साथ बुरी तरह से बातचीत करते हैं uint32_t
सुपरकैट

3

चेतावनियों को खत्म करने का एक आसान तरीका है, जीसीसी में -Wconversion का उपयोग न करना। मुझे लगता है कि आपको इस विकल्प को मैन्युअल रूप से सक्षम करना होगा, लेकिन यदि नहीं, तो आप इसे अक्षम करने के लिए -W रूपांतरण का उपयोग कर सकते हैं। यदि आप अभी भी चाहते हैं तो आप अन्य विकल्पों के माध्यम से साइन और FP सटीक रूपांतरण के लिए चेतावनी सक्षम कर सकते हैं।

-Wconversion चेतावनियाँ लगभग हमेशा झूठी सकारात्मक हैं, जो शायद इसलिए भी नहीं -Wextra डिफ़ॉल्ट रूप से इसे सक्षम करता है। एक स्टैक ओवरफ़्लो सवाल अच्छा विकल्प सेट के लिए सुझावों की एक बहुत है। मेरे अपने अनुभव के आधार पर, यह शुरू करने के लिए एक अच्छी जगह है:

-std = c99 -pedantic -Wall -Wextra -Wshadow

यदि आपको उनकी आवश्यकता है, तो अधिक जोड़ें, लेकिन संभावना है कि आप नहीं होंगे।

यदि आपको रखना चाहिए -Wconversion, तो आप केवल सांख्यिक ऑपरेंड टाइप करके अपने कोड को थोड़ा छोटा कर सकते हैं:

value16 <<= (uint16_t)8;
value8 += (uint8_t)2;

हालांकि सिंटैक्स हाइलाइटिंग के बिना पढ़ना आसान नहीं है।


2

किसी भी सॉफ्टवेयर परियोजना में, पोर्टेबल प्रकार की परिभाषाओं का उपयोग करना बहुत महत्वपूर्ण है। (यहां तक ​​कि एक ही संकलक के अगले संस्करण को भी इस विचार की आवश्यकता है।) एक अच्छा उदाहरण, कई साल पहले मैंने एक परियोजना पर काम किया था जहां वर्तमान संकलक ने 'इंट' को 8 बिट्स के रूप में परिभाषित किया था। संकलक के अगले संस्करण ने 'इंट' को 16 बिट्स के रूप में परिभाषित किया। चूंकि हमने 'int' के लिए किसी भी पोर्टेबल परिभाषा का उपयोग नहीं किया था, इसलिए रैम (प्रभावी रूप से) आकार में दोगुनी हो गई और 8 बिट पर निर्भर कई कोड अनुक्रम विफल हो गए। एक पोर्टेबल प्रकार की परिभाषा के उपयोग ने समस्या को ठीक करने के लिए सैकड़ों (सैकड़ों घंटे आदमी) को टाला होगा।


int8 बिट प्रकार को संदर्भित करने के लिए किसी भी उचित कोड का उपयोग नहीं करना चाहिए । यहां तक ​​कि अगर एक गैर-सी संकलक जैसे कि सीसीएस ऐसा करता है, तो उचित कोड को char8 बिट्स के लिए या टाइप किए गए एक प्रकार का उपयोग करना चाहिए , और 16 बिट्स के लिए एक टाइप किए गए प्रकार ("लंबा" नहीं)। दूसरी ओर, CCS से वास्तविक कंपाइलर की तरह कुछ करने के लिए पोर्टिंग कोड समस्याग्रस्त होने के लिए उपयुक्त है, भले ही वह उचित टाइपराइफ़ का उपयोग करता हो, क्योंकि ऐसे कंपाइलर अक्सर अन्य तरीकों से "असामान्य" होते हैं।
सुपरकैट

1
  1. हाँ। एक एन-बिट हस्ताक्षरित पूर्णांक एन-बिट अहस्ताक्षरित पूर्णांक के रूप में गैर-नकारात्मक संख्याओं की लगभग आधी संख्या का प्रतिनिधित्व कर सकता है, और अतिप्रवाह विशेषताओं पर भरोसा करना अपरिभाषित व्यवहार है इसलिए कुछ भी हो सकता है। वर्तमान और पिछले प्रोसेसर का विशाल बहुमत ट्वोस पूरक का उपयोग करता है इसलिए बहुत सारे ऑपरेशन हस्ताक्षरित और अहस्ताक्षरित अभिन्न प्रकारों पर एक ही काम करने के लिए होते हैं, लेकिन फिर भी सभी ऑपरेशन बिट-वार समान परिणाम उत्पन्न नहीं करेंगे। आप वास्तव में बाद में अतिरिक्त परेशानी के लिए पूछ रहे हैं जब आप यह पता नहीं लगा सकते हैं कि आपका कोड इरादा के अनुसार काम क्यों नहीं कर रहा है।

  2. हालांकि int और अहस्ताक्षरित में कार्यान्वयन परिभाषित आकार होते हैं, इन्हें अक्सर आकार या गति कारणों से कार्यान्वयन द्वारा "स्मार्टली" चुना जाता है। जब तक मेरे पास अन्यथा करने के लिए एक अच्छा कारण नहीं होता है तब तक मैं आम तौर पर इनसे चिपके रहता हूं। इसी तरह, जब विचार किया जाता है कि int या अहस्ताक्षरित का उपयोग करना है तो मैं आम तौर पर int पसंद करता हूं जब तक कि मेरे पास ऐसा करने का एक अच्छा कारण न हो।

ऐसे मामलों में जहां मुझे वास्तव में एक प्रकार के आकार या हस्ताक्षर-नेस पर बेहतर नियंत्रण की आवश्यकता होती है, मैं आमतौर पर या तो एक सिस्टम-डिफाइंड टाइपडेफ (size_t, intmax_t, आदि) का उपयोग करना पसंद करूंगा या अपना खुद का टाइपडेफ बनाऊंगा जो किसी दिए गए कार्य को इंगित करता है। प्रकार (prng_int, adc_int, आदि)।


0

अक्सर कोड का उपयोग एआरएम अंगूठे और एवीआर (और x86, पावरपीसी और अन्य आर्किटेक्चर) पर किया जाता है, और 16 या 32 बिट अधिक कुशल हो सकता है (दोनों तरीके: फ्लैश और साइकिल) एसटीएम 32 एआरएम पर भी एक चर के लिए जो 8 बिट्स में फिट होता है ( 8 बिट AVR पर अधिक कुशल है) । हालांकि अगर SRAM लगभग भरा हुआ है, वैश्विक संस्करण के लिए 8 बिट पर निर्भर करना उचित हो सकता है (लेकिन स्थानीय संस्करण के लिए नहीं)। पोर्टेबिलिटी और रखरखाव के लिए (विशेष रूप से 8 बिट संस्करण के लिए), इसके फायदे हैं (बिना किसी नुकसान के) न्यूनतम आकार निर्दिष्ट करने के लिए, एक आकार पर सटीक आकार और टाइप की जगह के बजाय .h जगह (आमतौर पर ifdef के तहत) को ट्यून करने के लिए (संभवतः uint_fast8_t/। uint_least8_t) पोर्टिंग / निर्माण समय के दौरान, जैसे:

// apparently uint16_t is just as efficient as 32 bit on STM32, but 8 bit is punished (with more flash and cycles)
typedef uint16_t uintG8_t; // 8bit if SRAM is scarce (use fol global vars that fit in 8 bit)
typedef uint16_t uintL8_t; // 8bit on AVR (local var, 16 or 32 bit is more efficient on STM + less flash)
// might better reserve 32 bits on some arch, STM32 seems efficient with 16 bits:
typedef uint16_t uintG16_t; // 16bit if SRAM is scarce (use fol global vars that fit in 16 bit)
typedef uint16_t uintL16_t; // 16bit on AVR (local var, 16 or 32 bit whichever is more efficient on other arch)

GNU लाइब्रेरी थोड़ी मदद करती है, लेकिन आम तौर पर टाइपराइफ़ किसी भी तरह से समझ में आता है:

typedef uint_least8_t uintG8_t;
typedef uint_fast8_t uintL8_t;

// लेकिन uint_fast8_t BOTH के लिए जब SRAM चिंता का विषय नहीं है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.