स्ट्रिंग शाब्दिक: वे कहाँ जाते हैं?


161

मुझे दिलचस्पी है कि स्ट्रिंग लिटरल्स को कहाँ आवंटित / संग्रहीत किया जाता है।

मुझे यहाँ एक पेचीदा जवाब मिला , कहा:

एक स्ट्रिंग इनलाइन को परिभाषित करना वास्तव में प्रोग्राम में डेटा को एम्बेड करता है और इसे बदला नहीं जा सकता है (कुछ कंपाइलर इसे स्मार्ट ट्रिक द्वारा अनुमति देते हैं, परेशान न करें)।

लेकिन, यह C ++ के साथ करना था, यह उल्लेख नहीं करने के लिए कि यह परेशान करने के लिए नहीं कहता है।

मैं परेशान कर रहा हूं। = D

तो मेरा सवाल यह है कि मेरा स्ट्रिंग लिटरल कहाँ और कैसे रखा जाता है? मुझे इसे बदलने की कोशिश क्यों नहीं करनी चाहिए? क्या प्लेटफ़ॉर्म द्वारा कार्यान्वयन भिन्न होता है? क्या कोई "स्मार्ट ट्रिक" पर विस्तार से ध्यान देता है?

जवाबों:


125

एक सामान्य तकनीक स्ट्रिंग वाचल्स के लिए "रीड-ओनली-डेटा" सेक्शन में रखी जानी है, जो प्रक्रिया स्थान में मैप-रीड-ओनली हो जाती है (यही वजह है कि आप इसे बदल नहीं सकते हैं)।

यह प्लेटफ़ॉर्म द्वारा भिन्न होता है। उदाहरण के लिए, सरल चिप आर्किटेक्चर रीड-ओनली मेमोरी सेगमेंट का समर्थन नहीं कर सकते हैं ताकि डेटा सेगमेंट राइट हो जाएगा।

इसके बजाय, स्ट्रिंग शाब्दिक परिवर्तनशील बनाने के लिए एक चाल जानने की कोशिश करें (यह आपके मंच पर अत्यधिक निर्भर होगा और समय के साथ बदल सकता है), बस सरण का उपयोग करें:

char foo[] = "...";

कंपाइलर सरणी के लिए शाब्दिक से आरंभ करने की व्यवस्था करेगा और आप सरणी को संशोधित कर सकते हैं।


5
हां, मैं एरे का उपयोग करता हूं, जब मैं म्यूटेबल स्ट्रिंग्स रखना चाहता हूं। मैं बिलकुल उत्सुक था। धन्यवाद।
कूपर

2
आपको म्यूटेबल स्ट्रिंग्स के लिए सरणियों का उपयोग करते समय बफर ओवरफ्लो के बारे में सावधान रहना होगा, हालांकि - केवल सरणी की लंबाई (जैसे foo = "hello"इस मामले में) की तुलना में लंबे समय तक एक स्ट्रिंग लिखना अनपेक्षित साइड-इफेक्ट्स का कारण बन सकता है ... (यह मानते हुए कि आप पुनः नहीं हैं- newया कुछ के साथ स्मृति आवंटित )
जॉनी

2
सरणी स्ट्रिंग का उपयोग करते समय क्या स्टैक या कहीं और जाता है?
सूरज जैन

क्या हम char *p = "abc";म्यूट स्ट्रिंग्स बनाने के लिए उपयोग नहीं कर सकते जैसा कि @ क्रिसहॉपर ने कहा है
KPMG

52

इसका कोई जवाब नहीं है। C और C ++ मानक केवल यह कहते हैं कि स्ट्रिंग शाब्दिक में स्थिर भंडारण अवधि होती है, उन्हें संशोधित करने का कोई भी प्रयास अपरिभाषित व्यवहार देता है, और एक ही सामग्री के साथ एक से अधिक स्ट्रिंग शाब्दिक एक ही भंडारण को साझा नहीं कर सकते हैं।

आपके द्वारा लिखी जाने वाली प्रणाली और उसके द्वारा उपयोग की जाने वाली निष्पादन योग्य फ़ाइल प्रारूप की क्षमताओं के आधार पर, उन्हें पाठ खंड में प्रोग्राम कोड के साथ संग्रहीत किया जा सकता है, या उनके पास आरंभिक डेटा के लिए एक अलग खंड हो सकता है।

विवरण का निर्धारण प्लेटफ़ॉर्म के आधार पर अलग-अलग होगा - सबसे अधिक संभवत: इसमें ऐसे उपकरण शामिल हैं जो आपको बता सकते हैं कि यह कहाँ रखा गया है। कुछ लोग आपको इस तरह के विवरणों पर नियंत्रण भी देंगे, यदि आप इसे चाहते हैं (जैसे कि gnu ld आपको यह बताने के लिए एक स्क्रिप्ट की आपूर्ति करने की अनुमति देता है कि यह समूह डेटा, कोड आदि के बारे में कैसे बताए)


1
मुझे यह संभावना नहीं है कि स्ट्रिंग डेटा को सीधे .text खंड में संग्रहीत किया जाएगा। वास्तव में छोटे शाब्दिक अर्थों के लिए, मैं संकलक कोड को देख सकता हूं जैसे movb $65, 8(%esp); movb $66, 9(%esp); movb $0, 10(%esp)कि स्ट्रिंग के लिए "AB", लेकिन अधिकांश समय, यह गैर-कोड सेगमेंट में होगा जैसे कि .dataया जैसे या .rodata(इस आधार पर कि लक्ष्य समर्थन करता है या नहीं) रीड-ओनली सेगमेंट)।
एडम रोसेनफील्ड

यदि स्ट्रिंग शाब्दिक कार्यक्रम की पूरी अवधि के लिए मान्य हैं, यहां तक ​​कि स्थैतिक वस्तुओं के विनाश के दौरान भी तो स्ट्रिंग स्ट्रिंग के लिए संदर्भ को वापस करना वैध है? यह कार्यक्रम रनटाइम त्रुटि क्यों दिखाता है ideone.com/FTs1Ig
विनाशकारी

@AdamRosenfield: यदि आप कुछ समय के लिए ऊब गए हैं, तो आप विरासत को देखना चाहते हैं (उदाहरण के लिए) UNIX a.out प्रारूप (जैसे, freebsd.org/cgi/… )। एक बात जो आपको जल्दी ध्यान देनी चाहिए, वह यह है कि यह केवल एक डेटा सेगमेंट को सपोर्ट करता है, जो हमेशा राइट रहता है। इसलिए यदि आप केवल-केवल स्ट्रिंग शाब्दिक चाहते हैं, तो अनिवार्य रूप से एकमात्र स्थान वे जा सकते हैं पाठ खंड (और हां, उस समय जब लिंक अक्सर ऐसा ही करते थे)।
जेरी कॉफिन

48

मुझे इसे बदलने की कोशिश क्यों नहीं करनी चाहिए?

क्योंकि यह अपरिभाषित व्यवहार है। C99 N1256 ड्राफ्ट 6.7.8 / 32 " उद्धरण" से उद्धरण :

उदाहरण 8: घोषणा

char s[] = "abc", t[3] = "abc";

परिभाषित "सादे" चार सरणी वस्तुओं sऔरt जिनके तत्वों को वर्ण स्ट्रिंग शाब्दिकों के साथ आरंभ किया जाता है।

यह घोषणा समान है

char s[] = { 'a', 'b', 'c', '\0' },
t[] = { 'a', 'b', 'c' };

सरणियों की सामग्री परिवर्तनीय हैं। दूसरी ओर, घोषणा

char *p = "abc";

pप्रकार "पॉइंटर टू चार्" के साथ परिभाषित करता है और इसे एक ऑब्जेक्ट के साथ इंगित करने के लिए टाइप करता है "वर्ण का प्रकार" लंबाई 4 के साथ जिसका तत्व एक चरित्र स्ट्रिंग शाब्दिक के साथ आरम्भ किया गया है। यदि pसरणी की सामग्री को संशोधित करने के लिए उपयोग करने का प्रयास किया जाता है , तो व्यवहार अपरिभाषित है।

वह कहाँ जा रहे है?

GCC 4.8 x86-64 ELF Ubuntu 14.04:

  • char s[]: ढेर
  • char *s:
    • .rodata ऑब्जेक्ट फ़ाइल का अनुभाग
    • वही खंड जहां .textऑब्जेक्ट फ़ाइल का अनुभाग डंप हो जाता है, जिसमें रीड एंड एक्सक अनुमति है, लेकिन लिखें नहीं

कार्यक्रम:

#include <stdio.h>

int main() {
    char *s = "abc";
    printf("%s\n", s);
    return 0;
}

संकलन और विघटित:

gcc -ggdb -std=c99 -c main.c
objdump -Sr main.o

आउटपुट में शामिल हैं:

 char *s = "abc";
8:  48 c7 45 f8 00 00 00    movq   $0x0,-0x8(%rbp)
f:  00 
        c: R_X86_64_32S .rodata

तो स्ट्रिंग में संग्रहीत किया जाता है .rodata अनुभाग ।

फिर:

readelf -l a.out

समाहित (सरलीकृत):

Program Headers:
  Type           Offset             VirtAddr           PhysAddr
                 FileSiz            MemSiz              Flags  Align
      [Requesting program interpreter: /lib64/ld-linux-x86-64.so.2]
  LOAD           0x0000000000000000 0x0000000000400000 0x0000000000400000
                 0x0000000000000704 0x0000000000000704  R E    200000

 Section to Segment mapping:
  Segment Sections...
   02     .text .rodata

इसका मतलब यह है कि डिफ़ॉल्ट लिंकर स्क्रिप्ट दोनों को डंप कर देती है .textऔर .rodataएक ऐसे खंड में जिसे निष्पादित किया जा सकता है लेकिन संशोधित नहीं किया जाता है ( Flags = R E)। इस तरह के सेगमेंट को संशोधित करने का प्रयास करने से लिनक्स में सेगफॉल्ट होता है।

यदि हम ऐसा ही करते हैं char[]:

 char s[] = "abc";

हमने प्राप्त किया:

17:   c7 45 f0 61 62 63 00    movl   $0x636261,-0x10(%rbp)

तो यह स्टैक में संग्रहीत हो जाता है (सापेक्ष %rbp), और हम निश्चित रूप से इसे संशोधित कर सकते हैं।


22

FYI करें, बस अन्य उत्तरों का समर्थन:

मानक: आईएसओ / आईईसी 14882: 2003 कहता है:

2.13। स्ट्रिंग शाब्दिक

  1. [...] एक साधारण स्ट्रिंग शाब्दिक प्रकार "सरणी n const char" और स्थिर भंडारण अवधि (3.7) है

  2. क्या सभी स्ट्रिंग शाब्दिक अलग-अलग हैं (जो कि नॉनओवरलैपिंग ऑब्जेक्ट में संग्रहीत हैं) कार्यान्वयन-परिभाषित है। एक स्ट्रिंग शाब्दिक रूप से संशोधित करने के प्रयास का प्रभाव अपरिभाषित है।


2
उपयोगी जानकारी, लेकिन नोटिस लिंक C ++ के लिए है, जबकि प्रश्न c पर
टंगा हुआ है

1
2.13 में # 2 की पुष्टि की। -OOs ऑप्शन के साथ (साइज के लिए ऑप्टिमाइज़ करें), .roata में स्ट्रिंग ओवरलैपल्स को ओवरलैप करता है।
पेंग झांग

14

gcc एक ऐसा .rodataखंड बनाता है जो पता स्थान में "कहीं" मैप किया जाता है और केवल पढ़ने के लिए चिह्नित किया जाता है,

दृश्य C ++ ( cl.exe) .rdataसमान उद्देश्य के लिए एक अनुभाग बनाता है ।

आप अपने निष्पादन योग्य वर्गों को देखने के लिए ( dumpbinया objdumpलिनक्स पर) आउटपुट को देख सकते हैं।

उदाहरण के लिए

>dumpbin vec1.exe
Microsoft (R) COFF/PE Dumper Version 8.00.50727.762
Copyright (C) Microsoft Corporation.  All rights reserved.


Dump of file vec1.exe

File Type: EXECUTABLE IMAGE

  Summary

        4000 .data
        5000 .rdata  <-- here are strings and other read-only stuff.
       14000 .text

1
मैं यह नहीं देख सकता कि obddump के साथ rdata सेक्शन का डिस्प्रेशन कैसे हो सकता है।
user2284570

@ user2284570, ऐसा इसलिए है क्योंकि उस अनुभाग में असेंबली नहीं है। इसमें डेटा शामिल है।
एलेक्स बुडोव्स्की

1
बस अधिक पठनीय आउटपुट प्राप्त करने की बात है। मेरा मतलब है कि मैं उन अनुभागों के लिए पते के बजाय डिसएफ़ीड के साथ इनलाइन किए गए तार प्राप्त करना चाहूंगा। (हेम को आप C के printf("some null terminated static string");बजाय जानते हैं printf(*address);)
user2284570

4

यह आपके निष्पादन योग्य के प्रारूप पर निर्भर करता है । इसके बारे में सोचने का एक तरीका यह है कि यदि आप असेंबली प्रोग्रामिंग कर रहे थे, तो आप अपने असेंबली प्रोग्राम के डेटा सेगमेंट में स्ट्रिंग शाब्दिक डाल सकते हैं। आपका सी कंपाइलर कुछ ऐसा करता है, लेकिन यह सब इस बात पर निर्भर करता है कि आप किस सिस्टम के लिए बाइनरी संकलित कर रहे हैं।


2

स्ट्रिंग लिटरल्स को अक्सर केवल-पढ़ने के लिए मेमोरी में आवंटित किया जाता है, जिससे उन्हें अपरिवर्तनीय बनाया जा सकता है। हालाँकि, कुछ कंपाइलरों में संशोधन एक "स्मार्ट ट्रिक" द्वारा संभव है .. और स्मार्ट ट्रिक "मेमोरी को इंगित करने वाले कैरेक्टर पॉइंटर का उपयोग करके" है .. कुछ कंपाइलरों को याद रखें, हो सकता है कि यह अनुमति न दे..उनका डेमो है

char *tabHeader = "Sound";
*tabHeader = 'L';
printf("%s\n",tabHeader); // Displays "Lound"

0

चूंकि यह कंपाइलर से कंपाइलर तक भिन्न हो सकता है, इसलिए खोज स्ट्रिंग शाब्दिक के लिए ऑब्जेक्ट डंप को फ़िल्टर करने का सबसे अच्छा तरीका है:

objdump -s main.o | grep -B 1 str

जहाँ सभी वर्गों की पूर्ण सामग्री प्रदर्शित करने के लिए -sबल है, ऑब्जेक्ट फ़ाइल है, बलों को मैच से पहले एक लाइन प्रिंट करने के लिए भी (ताकि आप अनुभाग नाम देख सकें) और वह स्ट्रिंग शाब्दिक है जिसे आप खोज रहे हैं।objdumpmain.o-B 1grepstr

Windows मशीन पर gcc के साथ, और एक वैरिएबल mainजैसे घोषित किया गया

char *c = "whatever";

चल रहा है

objdump -s main.o | grep -B 1 whatever

रिटर्न

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