c - স্ট্রাকচার প্রোগ্রামিং




গৃহস্থালি অ্যারে বনাম চার পয়েন্টার মধ্যে পার্থক্য কি সি? (6)

আমি সি পয়েন্টার বুঝতে চেষ্টা করছি কিন্তু আমি বর্তমানে নিম্নলিখিত সঙ্গে বিভ্রান্ত করছি:

  • char *p = "hello"

    এই অক্ষর অ্যারের দিকে ইঙ্গিত একটি গৃহস্থালি পয়েন্টার, এইচ থেকে শুরু।

  • char p[] = "hello"

    এটি একটি অ্যারে যা হ্যালো সংরক্ষণ করে

এই ফাংশনে উভয় ভেরিয়েবল পাস করলে পার্থক্য কী?

void printSomething(char *p)
{
    printf("p: %s",p);
}

গৃহস্থালি অ্যারে বনাম চার পয়েন্টার মধ্যে পার্থক্য কি সি?

C99 N1256 খসড়া

চরিত্র স্ট্রিং লিখন দুটি ভিন্ন ব্যবহার আছে:

  1. char[] :

    char c[] = "abc";      

    এটি "আরো যাদু" এবং 6.7.8 / 14 "সূচনা" এ বর্ণিত:

    চরিত্র টাইপের একটি অ্যারে একটি অক্ষর স্ট্রিং আক্ষরিক দ্বারা শুরু করা যেতে পারে, বিকল্পভাবে বন্ধনীগুলিতে আবদ্ধ। ক্যারেক্টার স্ট্রিং আক্ষরিক ক্রমিক অক্ষর (যদি রুম থাকে বা অ্যারে অজানা আকারের থাকে তবে সমাপ্ত নাল চরিত্র সহ) অ্যারের উপাদানগুলি সূচনা করে।

    সুতরাং এই জন্য শুধু একটি শর্টকাট হয়:

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

    অন্য কোন নিয়মিত অ্যারের মত, c সংশোধন করা যেতে পারে।

  2. অন্য সব জায়গায়: এটি একটি জেনারেট করে:

    সুতরাং যখন আপনি লিখুন:

    char *c = "abc";

    এটি অনুরূপ:

    /* __unnamed is magic because modifying it gives UB. */
    static char __unnamed[] = "abc";
    char *c = __unnamed;

    char[] থেকে char * , যা সবসময় বৈধ থেকে নিখুঁত কাস্ট লক্ষ্য করুন।

    তারপর যদি আপনি c[0] সংশোধন করেন, তবে আপনি __unnamed পরিবর্তন __unnamed , যা UB হয়।

    এটি 6.4.5 "স্ট্রিং লিটারেলস" -এ নথিভুক্ত করা হয়েছে:

    5 অনুবাদ পর্যায়ে 7, একটি বাইট বা মানের মান শূন্যটি প্রতিটি মাল্টিবিট অক্ষর ক্রমকে যুক্ত করে যা একটি স্ট্রিং আক্ষরিক বা আক্ষরিক থেকে পাওয়া যায়। Multibyte চরিত্র ক্রম তারপর স্টিক ধারণ করার জন্য পর্যাপ্ত স্ট্যাটিক সময়কাল এবং দৈর্ঘ্য একটি অ্যারের আরম্ভ করতে ব্যবহৃত হয়। চরিত্র স্ট্রিং লিটারেলগুলির জন্য, অ্যারের উপাদানের টাইপ গৃহস্থালি থাকে এবং মাল্টিবিট অক্ষর ক্রমটির পৃথক বাইটগুলির সাথে সূচনা হয় [...]

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

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";

"pointer to char" টাইপ দিয়ে p সংজ্ঞায়িত করে এবং দৈর্ঘ্যের 4 টি অক্ষর সহ "বস্তুর অ্যারে" টাইপের সাথে একটি বস্তুর দিকে নির্দেশ করে যার উপাদানগুলিকে একটি অক্ষর স্ট্রিং আক্ষরিক রূপে সূচনা করা হয়। অ্যারের বিষয়বস্তু সংশোধন করতে p ব্যবহার করার প্রচেষ্টা করা হলে, আচরণটি অনির্ধারিত হয়।

জিसीसी 4.8 x86-64 এলএলএফ বাস্তবায়ন

কার্যক্রম:

#include <stdio.h>

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

কম্পাইল এবং decompile:

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

উপসংহার: GCC স্টোর char* এটি .rodata বিভাগে, .text

আমরা char[] জন্য একই কাজ করতে হলে char[] :

 char s[] = "abc";

আমরা প্রাপ্ত:

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

তাই এটি স্ট্যাকের মধ্যে সংরক্ষণ করা হয় ( %rbp আপেক্ষিক)।

উল্লেখ্য, ডিফল্ট লিঙ্কার স্ক্রিপ্ট একই সেগমেন্টে .rodata এবং .text রাখে, যা কার্যকর হয়েছে তবে কোনও লেখার অনুমতি নেই। এই সঙ্গে পালন করা যেতে পারে:

readelf -l a.out

যেটা বহন করে:

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

আপনি একটি স্ট্রিং ধ্রুবক বিষয়বস্তু পরিবর্তন করার অনুমতি দেওয়া হয় না, যা প্রথম p পয়েন্ট কি। দ্বিতীয় p একটি স্ট্রিং ধ্রুবক সঙ্গে শুরু একটি অ্যারে, এবং আপনি তার বিষয়বস্তু পরিবর্তন করতে পারেন


দেখা যাক:

#include <stdio.h>
#include <string.h>

int main()
{
    char *p = "hello";
    char q[] = "hello"; // no need to count this

    printf("%zu\n", sizeof(p)); // => size of pointer to char -- 4 on x86, 8 on x86-64
    printf("%zu\n", sizeof(q)); // => size of char array in memory -- 6 on both

    // size_t strlen(const char *s) and we don't get any warnings here:
    printf("%zu\n", strlen(p)); // => 5
    printf("%zu\n", strlen(q)); // => 5

    return 0;
}

foo * এবং foo [] বিভিন্ন ধরনের এবং তারা কম্পাইলার দ্বারা ভিন্নভাবে পরিচালিত হয় (পয়েন্টার = ঠিকানা + পয়েন্টারের ধরন উপস্থাপনা, অ্যারে = পয়েন্টার + অ্যারের ঐচ্ছিক দৈর্ঘ্য, যদি জানা থাকে, উদাহরণস্বরূপ, অ্যারে স্থিরভাবে বরাদ্দ করা হয় ), বিস্তারিত মান পাওয়া যাবে। এবং রানটাইম পর্যায়ে তাদের মধ্যে কোন পার্থক্য নেই (অ্যাডমলারের মধ্যে, ভাল, প্রায়, নীচে দেখুন)।

এছাড়াও, সি প্রশ্নাবলীতে একটি সম্পর্কিত question রয়েছে:

প্রশ্নঃ এই প্রাথমিকীকরণের মধ্যে পার্থক্য কী?

char a[] = "string literal";   
char *p  = "string literal";   

আমি পি [আই] একটি নতুন মান নির্ধারণ করার চেষ্টা করে আমার প্রোগ্রাম ক্র্যাশ।

উত্তর : একটি স্ট্রিং আক্ষরিক (C উৎসের একটি ডবল উদ্ধৃত স্ট্রিংয়ের আনুষ্ঠানিক শব্দ) দুটি সামান্য ভিন্ন উপায়ে ব্যবহার করা যেতে পারে:

  1. গৃহস্থালি একটি অ্যারের জন্য প্রারম্ভিক হিসাবে, গৃহস্থালি ঘোষণা [] হিসাবে, [], এটি যে অ্যারের অক্ষর প্রাথমিক মান নির্ধারণ করে (এবং, প্রয়োজন হলে, তার আকার)।
  2. অন্য কোথাও, এটি অক্ষরগুলির একটি নামহীন, স্ট্যাটিক অ্যারে রূপে পরিণত হয় এবং এই অনামী অ্যারে কেবল-পঠনযোগ্য মেমরিতে সংরক্ষণ করা যেতে পারে, এবং এর ফলে এটি অগত্যা সংশোধন করা যাবে না। একটি অভিব্যক্তি প্রেক্ষাপটে, অ্যারেরটি একবারের মতো একটি পয়েন্টারে রূপান্তরিত হয় (স্বাভাবিক হিসাবে দেখুন 6), তাই দ্বিতীয় ঘোষণাপত্রটি অনামী অ্যারের প্রথম উপাদানটিকে নির্দেশ করতে পিকে শুরু করে।

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

প্রশ্ন 1.31, 6.1, 6.2, 6.8, এবং 11.8 বি দেখুন।

রেফারেন্স: কে আর আর 2 সেকেন্ড। 5.5 পি। 104

আইএসও সেকেন্ড। 6.1.4, সেকেন্ড। 6.5.7

রেশন সেকেন্ড। 3.1.4

এইচ এবং এস সেকেন্ড। 2.7.4 পিপি 31-2


যতদূর আমি মনে করতে পারি, একটি অ্যারে আসলে পয়েন্টার একটি গ্রুপ। উদাহরণ স্বরূপ

p[1]== *(&p+1)

একটি সত্য বিবৃতি


char* এবং char[] বিভিন্ন ধরনের , কিন্তু এটি সব ক্ষেত্রে অবিলম্বে স্পষ্ট নয়। এটি কারণ কারণ পয়েন্টারগুলিতে অ্যারে ক্ষয় হয় , যার মানে যদি char[] টাইপের একটি অভিব্যক্তি প্রদান করা হয় যেখানে char* একটি প্রত্যাশা করা হয়, কম্পাইলার স্বয়ংক্রিয়ভাবে অ্যারেটিকে একটি পয়েন্টারে তার প্রথম উপাদানতে রূপান্তরিত করে।

আপনার উদাহরণ ফাংশন printSomething একটি পয়েন্টার প্রত্যাশা করে, তাই যদি আপনি এটির মতো একটি অ্যারে পাস করার চেষ্টা করেন:

char s[10] = "hello";
printSomething(s);

কম্পাইলার আপনি এই লিখেছেন যে জাহির করে:

char s[10] = "hello";
printSomething(&s[0]);

এপিইউ থেকে, অনুচ্ছেদ 5.14:

char    good_template[] = "/tmp/dirXXXXXX"; /* right way */
char    *bad_template = "/tmp/dirXXXXXX";   /* wrong way*/

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

উদ্ধৃত টেক্সট ম্যাচ @ কীর Santilli এর ব্যাখ্যা।





pointers