সি প্রোগ্রামিংঃ পয়েন্টার

কম্পিউটার মেমরি এবং মেমরি অ্যাড্রেস

পয়েন্টার প্রোগ্রামিং এ দারুন একটি টুল। পয়েন্টার সম্পর্কে জানার আগে কিছু ব্যাসিক জিনিস জানা যাক, যেগুলো বুঝতে কাজে দিবে।

ভ্যারিয়েবল গুলো কিভাবে কম্পিউটার মেমরিতে/ র‍্যাম এ স্টোর হয়?

র‍্যাম এর এক একটি সেল এক একটি বাইট। আর প্রত্যেকটা বাইট এর একটি করে এড্রেস রয়েছে। আর প্রতিটা বাইটে ৮টি করে বিট রয়েছে।

আমরা যখন বলি আমাদের র‍্যাম 8 Giga byte, তখন আমাদের কম্পিউটারের র‍্যামে মোট 8 000 000 000 bytes ডেটা স্টোর করা যাবে, এবং এদের প্রত্যেকের একটি করে এড্রেস রয়েছে। প্রথমটি ০ পরের টি 1, এর পরের টির এড্রেস 2 এভাবে বাড়তে থাকে।  যদিও কম্পিউটার এ এড্রেস গুলো রিপ্রেজেন্ট করে হেক্সাডেসিমেল নাম্বার সিস্টেমে।

আমরা যখন একটি ভ্যারিয়েবল ডিক্লেয়ার করার পর যখন প্রোগ্রামটি এক্সিকিউট/রান করি তখন কম্পিউটার ঐ ভ্যারিয়েবল এর জন্য কিছু মেমরি এলোকেট করে। কত বাইট মেমরি এলোকেট করবে, তা নির্ভর করে ঐ ভ্যারিয়েবল এর ডেটা টাইপ এবং কম্পাইলার এর উপর।

সাধারনত কম্পাইলার গুলো একটা int এর জন্য 2 byte মেমরি এলোকেট করে। তেমনি একটি char ভ্যারিয়েবলের জন্য 1 byte মেমরি এলোকেট করে। floating-point নাম্বার এর জন্য 4 byte মেমরি এলোকেট করে।

যেমন যখন কম্পিউটার দেখে এমন একটি ডিক্লারেশন int a; তখন এটি বুঝতে পারে এটি একটি ইন্টিজার ভ্যারিয়েবল এবং এর জন্য ২ বাইট মেমরি এলোকেট করা দরকার। তখন র‍্যাম এর খালি যায়গা থেকে এটি এই ইন্টিজারের জন্য ২ বাইট মেমরি এলোকেট করে।

আমরা সহজেই একটি ভ্যারিয়েবলের মেমরি লোকেশন বের করতে পারি, নিচের প্রোগ্রামটি দেখা যাকঃ

#include <stdio.h>
int main()
{
  //!showMemory()
int a =5;
printf("Memory address of variable a is: %p",&a);
return 0;
}
copy C Code & Paste it below the Compiler & Run

উপরের প্রোগ্রামটি রান করালে এমন কিছু দেখাবেঃ Memory address of variable a is: 2686732 । এক কম্পিউটারে এক এক মান দেখাবে। এবং একবার এক এক ভ্যালু দেখাবে। কারণ যতবারই আমরা প্রোগ্রামটি রান করি, প্রতিবারই ভ্যারিয়েবলটির জন্য মেমরিতে একটা জায়গা বরাদ্ধ করা হয়। আর ঐ জায়গার এড্রেসটা প্রতিবারই পরিবর্তন হয়।

কোন ভ্যারিয়েবল এর এর মেমরি এড্রেস জানার জন্য & [ampersend] ব্যবহার করা হয়। যাকে  address-of operator [&] ও বলা হয়।  যা দিয়ে আমরা অন্য একটি ভ্যারিয়েবল এর  এড্রেস বা মেমরি লোকেশন পেতে পারি।

যখন আমরা প্রোগ্রামটি রান করি, তখন কম্পিউটার র‍্যাম এর খালি যায়গা থেকে ভ্যারিয়েবল a এর জন্য ২ বাইট মেমরি এলোকেট করে। কম্পিউটার অটোমেটিকেলি তখন a এর জন্য 2686732 এবং 2686733 নং সেল এলোকেট করে রাখে। আর মেমরি এড্রেস জানার জন্য শুধু মাত্র শুরুর এড্রেস জানলেই হয়। আমরা যখন a এর মেমরি এড্রেস প্রিন্ট করেছি, তখন শুধু শুরুর এড্রেস 2686732 ই পেয়েছি। যদি ও a ভ্যারিয়েবল এর জন্য 2686732 এবং 2686733  মেমরি এলোকেট করা হয়েছে এবং এর মান 5 এই দুই সেলে স্টোর করে রাখা হয়েছে। এখন আমরা যদি a এর মান পরিবর্তন করে অন্য আরেকটা ভ্যালু রাখি, যেমন 8, তখন র‍্যামের 2686732 এবং 2686733 এ দুটো সেল এর মান ও পরিবর্তন হয়ে যাবে এবং এ দুটো সেলে 5 এর পরিবর্থে 8 স্টোর হবে। এবার পয়েন্টার কি জানা যাক।

পয়েন্টার

পয়েন্টার হচ্ছে একটা ভ্যারিয়েবল যার ভ্যালু হচ্ছে আরেকটি ভ্যারিয়েবল এর মেমরি এড্রেস। পয়েন্টার  একটা ডেটা, অ্যারে বা ভ্যারিয়েবল এর কম্পিউটার মেমরি এড্রেস রিপ্রেজেন্ট করে বা পয়েন্ট করে। অন্যান্য ভ্যারিয়েবল এর মত  পয়েন্টার ভ্যারিয়েবল ব্যবহার করার আগে কম্পিউটার/ কম্পাইলারকে বলতে হবে এটা একটি পয়েন্টার ভ্যারিয়েবল। নিচের মত করে একটি পয়েন্টার ভ্যারিয়েবল ডিক্লেয়ার করে।

data_type *name;

যেমন integer পয়েন্টারের জন্যঃ  int *i;

asterisk [*] একটি ভ্যারিয়েবলের আগে ব্যবহার করে পয়েন্টার হিসেবে ডিক্লেয়ার করা হয়। যাকে indirection operator বা value-at-address operator বলা হয়। এখানে আরো কিছু ডেটা টাইপ এর পয়েন্টার ডিক্লারেশন এর উদাহরন দেওয়া হলোঃ

int*ip;/* pointer to an integer */
double*dp;/* pointer to a double */
float*fp;/* pointer to a float */
char*ch /* pointer to a character */

আমরা এখন দেখব কিভাবে পয়েন্টার ব্যবহার করতে হয় একটি প্রোগ্রামে।

#include <stdio.h>
int main ()
{
  //!showMemory()
int a = 5; /* variable declaration */
int *ip; /* pointer variable declaration */
ip = &a; /* store address of "a" in pointer variable*/
printf("Address of a variable: %p\n", &a );
/* address stored in pointer variable */
printf("Address stored in ip variable: %p\n", ip );
return 0;
}
copy C Code & Paste it below the Compiler & Run

এখানে আমরা একটি ভ্যারিয়েবল a ডিক্লেয়ার করেছি। এরপর একটি পয়েন্টার ভ্যারিয়েবল ডিক্লেয়ার করেছি। তারপর পয়েন্টার ভ্যারিয়েবলে a এর মেমরি এড্রেস রেখেছি। তারপর & অপারেটর দিয়ে a ভ্যারিয়েবল এর এড্রেস প্রিন্ট করে দেখলাম। এবং পয়েন্টার ভ্যারিয়েবল এর ভ্যালু প্রিন্ট করে দেখলাম। উভয় এর মান ই একই।

আমরা ইচ্ছে করলে এখন ip  পয়েন্টার ভ্যারিয়েবল দিয়ে a এর মান বের করতে পারি।

#include <stdio.h>
int main ()
{
  //!showMemory()

int a = 5;
int *ip;
ip = &a;
/* access the value using the pointer */
printf("Value of *ip variable: %d\n", *ip );
return 0;
}
copy C Code & Paste it below the Compiler & Run

আমরা যখন প্রগ্রামটি রান করব, তখন ip যে ভ্যারিয়েবলটির এড্রেস শো করবে, তার মান প্রিন্ট করবে। লক্ষকরি, যখন আমরা পয়েন্টার ভ্যারিয়েবল দিয়ে কোন ভ্যারিয়েবল এর এড্রেস বের করতে চাইবো, তখন শুধু পয়েন্টার ভ্যারিয়েবল লিখলেই হবে। কিন্তু যখন আমরা পয়েন্টার ভ্যারিয়েবল দিয়ে মূল ভ্যারিয়েবল এর ভ্যালু বের করতে চাইবো, তখন পয়েন্টার ভ্যারিয়েবল এর আগে * যোগ করতে হবে। যেমন প্রথম প্রোগ্রামে আমরা ip [পয়েন্টার ভ্যারিয়েবল] প্রিন্ট করায় আমরা এড্রেস পেয়েছি। এবং পরের প্রোগ্রামে ip এর আগে একটা * দিয়ে *ip প্রিন্ট করায় আমরা মূল ভ্যারিয়েবলের মান পেয়েছি।

আরেকটি উদাহরণ দিই:

#include <stdio.h>

int main()
{
  int num= 10;
  int *ptr= &num;
  *ptr= 20;
  
  int **ptr1= &ptr; 
  **ptr1= 30;
  printf("Number: %d\n", num);
  return 0;
}
copy C Code & Paste it below the Compiler & Run

পয়েন্টার ভ্যারিয়েবল দ্বারা মান পরিবর্তন করা

আজ একটা রিয়েল লাইফ অভিজ্ঞতা থেকে কোড করব, ধরো, তোমরা একটা রেস্টোরেন্ট এ গেছো, তোমাদের মাঝে ভুল আইটেম খাবার চলে এসেছে। আমি পয়েন্টার ভ্যারিয়েবলের মাধ্যমে খাবার আইটেমগুলোর মান পরিবর্তন করে তোমাদের পছন্দের খাবার আইটেমগুলো ঠিক করে দিব।

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

int main() {
    char food1[] = "Pizza";
    char food2[] = "Burger";
    char food3[] = "Sushi";
    char food4[] = "Pasta";
    char food5[] = "Ice Cream";

    char* ptr;

    ptr = food1;
    printf("Food 1: %s\n", food1);
    strncpy(ptr, "Spam", sizeof(food1) - 1); 

    ptr = food2;
    printf("Food 2: %s\n", food2);
    strncpy(ptr, "Steak", sizeof(food2) - 1); 
    printf("Food 2 (Updated): %s\n", food2);

    ptr = food3;
    printf("Food 3: %s\n", food3);
    strncpy(ptr, "Pizza", sizeof(food3) - 1); 
    printf("Food 3 (Updated): %s\n", food3);
    
    ptr = food4;
    printf("Food 4: %s\n", food4);
    strncpy(ptr, "Curry", sizeof(food4) - 1); 
    printf("Food 4 (Updated): %s\n", food4);
    
    ptr = food5;
    printf("Food 5: %s\n", food5);
    strncpy(ptr, "Donut", sizeof(food5) - 1); 
    printf("Food 5 (Updated): %s\n", food5);

    return 0;
}

আউটপুট টা কি দারুণ, তাই না?

Food 1: Pizza
Food 1 (Updated): Spam
Food 2: Burger
Food 2 (Updated): Steak
Food 3: Sushi
Food 3 (Updated): Pizza
Food 4: Pasta
Food 4 (Updated): Curry
Food 5: Ice Cream
Food 5 (Updated): Donut
copy C Code & Paste it below the Compiler & Run

ফাংশনে পয়েন্টার পাস করা

আমরা একটি ভ্যারিয়েবলের মত একটি পয়েন্টার বা একটি ভ্যারিয়েবলের মেমরি অ্যাড্রেস  ফাংশনে পাস করতে পারি। নিচের প্রোগ্রামটি দেখুনঃ

#include <stdio.h>
 
void func1(int *pNum) {
   *pNum = 5;
   return;
}
 
int main () 
{
   //!showMemory()
 
   int num = 1;
 
   printf("Before passing: %d\n", num );
 
   func1( &num );
 
   printf("After passing: %d\n", num );
 
   return 0;
}
copy C Code & Paste it below the Compiler & Run

এখানে num নামে একটা ভ্যারিয়েবল নিয়েছি যার মধ্যে এসাইন করেছি 1।  এরপর তা প্রিন্ট করেছি। 1 ই পেয়েছি।

এখন আমরা ফাংশনটিকে কল করেছি। কল করার সময় পাস করেছি num  ভ্যারিয়েবলটির মেমরি অ্যাড্রেস। এরপর ফাংশনে গিয়ে ঐ এড্রেসের ভ্যালু পরিবর্তন করে 5 এসাইন করে দিয়েছি।

এরপর আবার num ভ্যারিয়েবলটি প্রিন্ট করেছি। আমরা পেয়েছি 5। এভাবে আমরা পয়েন্টারকেও ফাংশনে পাস করতে পারি।

স্ট্রাকচারে পয়েন্টার পাস করা

আমার অফিসে 3 জন ব্যক্তি আছে, প্রত্যেক ব্যক্তি পয়েন্টার ভেরিয়েবল ব্যবহার করে 2 জনকে নির্দেশ করে, প্রত্যেক ব্যক্তি অন্য 2 জনের আচরণ সম্পর্কে রিপোর্ট করে। এখন কোড করব।

#include <stdio.h>
#include <string.h>
 
// Define a structure to represent a person
struct Person {
    char name[10];
    char behavior[12];
    struct Person* pointer1;
    struct Person* pointer2;
};
 
void report(struct Person* person) {
    printf("%s reports:\n", person->name);
    printf("%s is %s\n", person->pointer1->name, person->pointer1->behavior);
    printf("%s is %s\n", person->pointer2->name, person->pointer2->behavior);
    printf("\n");
}
 
int main() {
    // Create three persons
    struct Person person1, person2, person3;
 
    // Assign names to each person
    strcpy(person1.name, "Person 1");
    strcpy(person2.name, "Person 2");
    strcpy(person3.name, "Person 3");
 
    // Initialize real behaviors for each person
    strcpy(person1.behavior, "Industrious");
    strcpy(person2.behavior, "Expert");
    strcpy(person3.behavior, "Creative");
 
    // Point each person to the other two persons
    person1.pointer1 = &person2;
    person1.pointer2 = &person3;
 
    person2.pointer1 = &person1;
    person2.pointer2 = &person3;
 
    person3.pointer1 = &person1;
    person3.pointer2 = &person2;
 
    // Report on behaviors
    report(&person1);
    report(&person2);
    report(&person3);
 
    return 0;
}

আউটপুটঃ

Person 1 reports:
Person 2 is Expert
Person 3 is Creative

Person 2 reports:
Person 1 is Industrious
Person 3 is Creative

Person 3 reports:
Person 1 is Industrious
Person 2 is Expert

copy C Code & Paste it below the Compiler & Run

আরেকটা উদাহরণ দিচ্ছিঃ

#include <stdio.h>

// Function to swap values using pointers
void swap(int *x, int *y) {
    int temp = *x;
    *x = *y;
    *y = temp;
}

int main() {
    // Declare an array with a null terminator
    int arr[] = {1, 2, 3, 4, 5, '\0'};

    // Declare variables
    int a = 10;
    int b = 20;
    int *ptr_a, *ptr_b;

    // Initialize pointers
    ptr_a = &a;
    ptr_b = &b;

    // Print array using pointers and ptr++
    printf("Array elements using ptr++:\n");
    int *ptr_arr = arr;
    while (*ptr_arr != '\0') {
        printf("%d ", *ptr_arr);
        ptr_arr++;
    }

    // Reset pointer to the first element
    ptr_arr = arr;

    // Print array using pointers and ptr--
    printf("\nArray elements using ptr--:\n");
    while (ptr_arr >= arr) {
        printf("%d ", *ptr_arr);
        ptr_arr--;
    }

    // Update array element using pointer
    *(arr + 2) = 99;

    // Print updated array
    printf("\nArray after update:\n");
    for (int i = 0; i < 5; ++i) {
        printf("%d ", arr[i]);
    }

    // Use function to swap values
    printf("\n\nBefore swapping:\n");
    printf("Value of a: %d\n", *ptr_a);
    printf("Value of b: %d\n", *ptr_b);

    swap(ptr_a, ptr_b);

    printf("After swapping:\n");
    printf("Value of a: %d\n", *ptr_a);
    printf("Value of b: %d\n", *ptr_b);

    // Pointer to pointer
    int c = 30;
    int *ptr_c = &c;
    int **ptr_to_ptr_c = &ptr_c;

    printf("\nPointer to pointer example:\n");
    printf("Value of c: %d\n", **ptr_to_ptr_c);

    return 0;
}

copy C Code & Paste it below the Compiler & Run

কল বাই ভ্যালু এবং কল বাই রেফারেন্স

মূলত কল বাই রেফারেন্সই হলো পয়েন্টাের। অর্থাৎ কোন কিছুর ঠিকানা বা এড্রেস বলে দেয়া বা পয়েন্ট করা বা রেফারেন্স করাই হলো পয়েন্টাের।

মনে কর, তুমি জাহাঙ্গীনগর বিশ্ববিদ্যালয় ২১০ নাম্বার রুমে যেতে চাও, আমি বলে দিলাম নিচে গিয়ে গার্ড কে জিজ্ঞাসা করলে বলে দিবে। (এটা হলো কল বাই রেফারেন্স)।

আবার বলতে পারি ২য় তালায় ডান দিকে ২০৯ এর বিপরীতে ২১০ নাম্বার রুমে যেখানে রেজিস্টার বসে সেখানকার রেজিস্টার হলো একটা ভ্যালু( এটা হলো কল বাই ভ্যালু)। সেই ভ্যালু তোমাকে বলে দিলে যে কোন ভাবে বললেই তুমি সেখানে যেতে পারবে অর্থাৎ একই জায়গায় যাবে। আমরা ফাংশনকে ডাটা নেয়ার জন্য এই দুই ভাবে বলে দিতে পারি । যেমন,

কল বাই ভ্যালু

আমরা বলতে পারি, যে ভ্যালু মেথড দ্বারা ফাংশন কলে ভেরিয়েবলের মান ব্যবহার করা হয়। আমরা আনুষ্ঠানিক প্যারামিটার দ্বারা প্রকৃত পরামিতির মান পরিবর্তন করতে পারি না।প্রকৃত এবং আনুষ্ঠানিক পরামিতির জন্য ভিন্ন মেমরি বরাদ্দ করা হয় যেহেতু প্রকৃত পরামিতির মান আনুষ্ঠানিক প্যারামিটারে অনুলিপি করা হয়।

#include<stdio.h>  
void change(int num) {    
    printf("Before adding value inside function num=%d \n",num);    
    num=num+100;    
    printf("After adding value inside function num=%d \n", num);    
} 
    
int main() {    
    int x=100;    
    printf("Before function call x=%d \n", x);    
    change(x);//passing value in function    
    printf("After function call x=%d \n", x);    
return 0;  
}    
copy C Code & Paste it below the Compiler & Run

কল বাই রেফারেন্স

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

#include<stdio.h>  
void change(int *num) {    
    printf("Before adding value inside function num=%d \n",*num);    
    (*num) += 100;    
    printf("After adding value inside function num=%d \n", *num);    
}      
int main() {    
    int x=100;    
    printf("Before function call x=%d \n", x);    
    change(&x);//passing reference in function    
    printf("After function call x=%d \n", x);    
return 0;  
}   
copy C Code & Paste it below the Compiler & Run

ডাইনামিক মেমরি এলোকেশন

রানটাইমে মেমরি এলোকেট করার প্রসেসকে ডাইনামিক মেমরি এলোকেশন বলে।

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

সি প্রোগ্রামিং stdlib.h এ চারটি লাইব্রেরী ফাংশন রয়েছে যে গুলো দিয়ে আমরা ডাইন্যামিকেলি মেমরি এলোকেট করতে পারি। ফাংশন গুলো হচ্ছেঃ

* malloc()
* calloc()
* free()
* realloc()

সবার আগে free ()এর কাজ বলে নি। free নাম থেকেই বুঝা যায় এটা দিয়ে এলোকেটকৃত মেমরি মুক্ত/dellocate করে দেওয়া হয়।

malloc() ব্যবহারের সিনট্যাক্সঃ

var = malloc(byte-size)

যদি আমাদের র‍্যামে মেমরি এলোকেট করার মত মেমরি না থাকে, তাহলে এলোকেশন করতে পারবে না। এবং একটা নাল পয়েন্টার রিটার্ন করবে। যদিও এখনকার কম্পিউটারে এ সমস্যা হওয়ার কথা না… যথেষ্ট র‍্যাম থাকে। একটি উদাহরণঃ

#include <stdio.h>
#include <stdlib.h>

int main() {
  //!showMemory()
  char *dynamic_var;
  
  dynamic_var = (char *)malloc(200 * sizeof(char));
  
  if (dynamic_var == NULL) {
    printf("Couldn't able to allocate requested memory\n");
  } else {
    char text[30] = "Dynamically allocated string.";
    int length = 0;
    
    while (text[length] != '\0') {
      dynamic_var[length] = text[length];
      length++;
    }
    
    dynamic_var[length] = '\0'; // Null-terminate the string
  }
  
  printf("%s\n", dynamic_var);
  
  free(dynamic_var);
  
  return 0;
}

copy C Code & Paste it below the Compiler & Run

dynamic_var নামে একটি ডাইনামিক ভ্যারিয়েবল নিয়েছি। যার কোন সাইজ আমরা সেট করে দি নাই। আমরা malloc দিয়ে এই dynamic_var এর সাইজ সেট করেছি। if else দিয়ে আমরা dynamic_var চেক করে নিয়েছি। যদি মেমরি এলোকেট করতে না পারে, তাহলে আমাদের লেখা উঠবে “Couldn’t able to allocate requested memory” আর যদি মেমরি এলোকেট করতে পারে, তাহলে স্ট্রিং ফাংশন ব্যবহার করে dynamic_var এ Dynamically allocated string. এ স্ট্রিংটি এসাইন করবে। এরপর আমরা dynamic_var ভ্যারিয়েবলটি প্রিণ্ট করেছি।

শেষে free ব্যবহার করে এলোকেটকৃত মেমরি মুক্ত করে দিয়েছি।

#include <stdio.h>
#include <stdlib.h>

int main() {
    //!showMemory()
    int *dynamic_var;
    int n, i;
    printf("How many numbers will you store? ");
    scanf("%d", &n);

    dynamic_var = (int *)malloc(n * sizeof(int));
    if (dynamic_var == NULL) {
        printf("Couldn't allocate requested memory\n");
    } else {
        // Read dynamically allocated values
        for (i = 0; i < n; i++) {
            printf("Enter number %d: ", i + 1);
            scanf("%d", &dynamic_var[i]);
        }
    }

    // Printing dynamically allocated values
    for (i = 0; i < n; i++) {
        printf("Number %d = %d\n", i + 1, dynamic_var[i]);
    }

    free(dynamic_var);

    return 0;
}

copy C Code & Paste it below the Compiler & Run

এখানে আমরা dynamic_var নামক একটা ইন্টিজার পয়েন্টার ভ্যারিয়েবল নিয়েছি। আমরা dynamic_var এ কয়েকটি নাম্বার স্টোর করতে চাই, তা ইনপুট নিয়েছি n নামক ভ্যারিয়েবল দিয়ে। এরপর এটার সাইজ ডাইনামিক্যালি এলোকেট করেছি, n * sizeof(int) দিয়ে।
এরপরের কাজ সহজ। আমরা ফর লুপ চালিয়ে নাম্বার গুলো ইনপুট নিয়েছি। এরপর সে গুলো আবার আউটপুট দিয়েছি।

malloc এর String Library সহ আরেকটা উদাহরণ দিচ্ছিঃ

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
int main()
{
char *dynamic_var;
 
dynamic_var = (char*)malloc( 30 * sizeof(char) );
 
if( dynamic_var== NULL )
{
printf("Couldn't able to allocate requested memory\n");
}
else
{
strcpy( dynamic_var,"Dynamically allocated string.");
}
printf("%s\n", dynamic_var );
 
free(dynamic_var);
 
return 0;
}

calloc()

calloc() এবং malloc() এর সিনট্যাক্সে তেমন কোন পার্থক্য নেই। দুটাই একই ভাবেই ব্যবহার করা হয়। calloc() করে কি, প্রথমে ০ দিয়ে ইনিশিয়ালাইজ করে নেয়। পরে মেমরিতে পয়েন্টার রিটার্ন করে। একই উদাহরন calloc() দিয়ে লিখলেঃ

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
int main()
{
char *dynamic_var;
 
dynamic_var =(char*)calloc( 200 , sizeof(char) );
 
if( dynamic_var== NULL )
{
printf("Couldn't able to allocate requested memory\n");
}
else
{
strcpy( dynamic_var,"Dynamically allocated string.");
}
printf("%s\n", dynamic_var );
 
free(dynamic_var);
 
return 0;
}
copy C Code & Paste it below the Compiler & Run

realloc()

calloc() এবং malloc() দিয়ে এলোকেট করা মেমরি যদি ইনসাফিশিয়েন্ট হয়, তাহলে realloc() দিয়ে প্রয়োজন অনুযায়ী আবার মেমরি এলোকেট করে নেওয়া যায়। উদারহণঃ

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
int main()
{
char *dynamic_var;
//allocate
dynamic_var = (char*)malloc( 30 * sizeof(char) );
 
if( dynamic_var== NULL )
{
printf("Couldn't able to allocate requested memory\n");
}
else
{
strcpy( dynamic_var,"Dynamically allocated string.");
}
printf("%s\n", dynamic_var );
 
//reallocate
dynamic_var = (char* )realloc( dynamic_var, 60 * sizeof(char) );
 
if( dynamic_var== NULL )
{
printf("Couldn't able to allocate requested memory\n ");
}
else
{
strcat( dynamic_var,"With more reallocated content");
}
printf("%s\n", dynamic_var );
 
free(dynamic_var);
 
return 0;
}
copy C Code & Paste it below the Compiler & Run

উপরের প্রোগ্রামের প্রথম অংশ malloc() এর প্রথম উদারহণটির মতই। এর পরবর্তিতে আমাদের আরো কিছু যোগ করা দরকার, তাই আমরা realloc() ব্যবহার করে মেমরি বাড়িয়ে নিলাম।আরেকটা উদাহরণঃ

#include <stdio.h>
#include <stdlib.h>
int main(){
int *dynamic_var ,i,n1,n2;
printf("Enter size of array: ");
scanf("%d",&n1);
 
dynamic_var =(int*)malloc(n1*sizeof(int));
 
printf("Address of previously allocated memory: ");
 
for(i=0;i<n1;++i)
printf("%u\t",dynamic_var +i);
 
printf("\nEnter new size of array: ");
scanf("%d",&n2);
 
dynamic_var =realloc(dynamic_var ,n2);
 
for(i=0;i<n2;++i)
printf("%u\t",dynamic_var +i);
return 0;
}
copy C Code & Paste it below the Compiler & Run

এখানে প্রথমে জিজ্ঞেস করবে কত সাইজে অ্যারে দরকার। পরে তত সাইজের একটি অ্যারে তৈরি করবে। এবং ঐ অ্যারের এড্রেস গুলো প্রিন্ট করবে। আমরা চাইলে ঐ অ্যারেতে ইনপুট নিয়ে রাখতে পারি। প্রিন্ট করতে পারি।

এরপর আবার জিজ্ঞেস করবে নিউ অ্যারে সাইজ। এখন যদি আগের থেকে বড় কোন মান দিয়ে থাকি, তাহলে আগের থেকে বড় একটা অ্যারে আমাদের জন্য তৈরি করবে। ছোট ভ্যালু দিলে আগের অ্যারেটা ছোট করে দিবে।এবং অ্যারের এড্রেস গুলো প্রিন্ট করবে। যেমন প্রথম বার ৫ ইনপুট করে দেখুন। এরপরে ২ বা ৮ ইনপুট করে দেখতে পারেন।

malloc, calloc and realloc এর একসাথে উদাহরণ:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
 
int main()
{
    char *dynamic_var;
 
    // Step 1: Allocate memory using malloc
    dynamic_var = (char*)malloc(30 * sizeof(char));
   
    if (dynamic_var == NULL)
    {
        printf("Couldn't allocate requested memory with malloc\n");
        return 1; // Exit the program if allocation fails
    }
   
    // Copy a string into the malloc allocated memory
    strcpy(dynamic_var, "Dynamically allocated string.");
    printf("Using malloc: %s\n", dynamic_var);
 
    // Step 2: Allocate new memory using calloc
    char *calloc_var = (char*)calloc(30, sizeof(char)); // Allocate with calloc
   
    if (calloc_var == NULL)
    {
        printf("Couldn't allocate requested memory with calloc\n");
        return 1; // Exit the program if allocation fails
    }
 
    // Copy the string from malloc_var to calloc_var
    strcpy(calloc_var, dynamic_var);
    printf("Using calloc: %s\n", calloc_var);
 
    // Step 3: Reallocate memory using realloc
    char *realloc_var = (char*)realloc(calloc_var, 60 * sizeof(char)); // Resize memory with realloc
 
    if (realloc_var == NULL)
    {
        printf("Couldn't reallocate requested memory\n");
        free(calloc_var);
        return 1; // Exit the program if reallocation fails
    }
 
    // Copy the string again and add more content to the reallocated memory
    strcat(realloc_var, " With more reallocated content");
    printf("After realloc: %s\n", realloc_var);
 
    // Free all allocated memory
    free(realloc_var);
    // realloc_var is free. So no data will display.
   //printf("After Free Realloc: %s\n", realloc_var);
    free(dynamic_var);
    // dynamic_var is free. So no data will display.
   //printf("After Free Malloc: %s\n", dynamic_var);
   
   
    return 0;
}
copy C Code & Paste it below the Compiler & Run

malloc এবং calloc ব্যবহারের সময় ১৬ বাইটের জন্য ২০ বাইটের মতো বেশি মেমরি বরাদ্দ হতে পারে:

মেমরি বরাদ্দের পার্থক্য:
১. এলাইমেন্টের প্রয়োজনীয়তা: অনেক সিস্টেম মেমরি প্রাপ্তির জন্য নির্দিষ্ট সীমার মধ্যে (যেমন ৮ বাইট বা ১৬ বাইট) সঠিকভাবে অ্যালাইনমেন্ট রাখার প্রয়োজন হয়। তাই, যখন আপনি ১৬ বাইট মেমরি ব্যবহার করেন, মেমরি অ্যালোকেটর সেই মেমরিকে সঠিকভাবে অ্যালাইনমেন্ট করার জন্য অতিরিক্ত কিছু বাইট বরাদ্দ করতে পারে।
২. মেটাডেটা ওভারহেডিং: মেমরি অ্যালোকেটর সাধারণত মেমরি ব্যবস্থাপনার জন্য কিছু মেটাডেটা অন্তর্ভুক্ত করে (যেমন বরাদ্দের আকার ট্র্যাক করার জন্য)। এই মেটাডেটার জন্য অতিরিক্ত কিছু বাইট বরাদ্দ করা হতে পারে। উদাহরণস্বরূপ, ৪ বা ৮ বাইটের অতিরিক্ত মেমরি হতে পারে।
৩. malloc বনাম calloc: malloc(sizeof) নির্দিষ্ট সংখ্যক বাইট বরাদ্দ করে কিন্তু সেই মেমরিকে ইনিশিয়ালাইজ করে না।

calloc(sizeof) নির্দিষ্ট সংখ্যক উপাদান এবং প্রতিটি উপাদানের জন্য নির্দিষ্ট বাইট (size) বরাদ্দ করে এবং মেমরি শূন্য দিয়ে ইনিশিয়ালাইজ করে।
যদিও বরাদ্দকৃত মেমরি আকার একই (যেমন ১৬ বাইট), calloc অতিরিক্ত মেমরি বরাদ্দ করতে পারে ইনিশিয়ালাইজেশন বা অ্যালাইনমেন্ট প্রয়োজনীয়তার কারণে।

উদাহরণ:
malloc দিয়ে ১৬ বাইট ব্যবহার করলে, ব্যবহারযোগ্য ১৬ বাইট মেমরি পাবেন, কিন্তু calloc দিয়ে ১৬ বাইট ব্যবহার করলে, মোট বরাদ্দিত মেমরি ২০ বাইটও হতে পারে, যেহেতু অ্যালাইনমেন্ট এবং মেটাডেটার জন্য অতিরিক্ত মেমরি বরাদ্দ করা হতে পারে বা ইনিশিয়ালাইজ করার জন্য অথবা অ্যালাইনমেন্টের কারণে অতিরিক্ত মেমরি বরাদ্দ করা হতে পারে।
এটি নির্ভর করে আপনার সিস্টেমের মেমরি অ্যালোকেটরের কিভাবে কাজ করে তার উপর।

Array of Pointer, Function of Pointer and File Pointer

এগুলো একসাথে নিয়ে আরেকটি উদাহরণ দেখাচ্ছিঃ

#include<stdio.h>
int recursiveToThree(int n){
  printf("%d th\n", n + 1);
  if(n < 3){
      int r = recursiveToThree(n + 1);
      n = r;
  }
  return n;
}
int main(){
  int n = 0;//variable declaration

  n = recursiveToThree(0);//recursive function

  int arr[5] = {1, 2, 3};//array variable

  int* ptr = &arr[2];//pointer variable
  *ptr = 5;

  //dynamic memory allocation
  int* d_arry = malloc(sizeof(int) * 3);

  //two-dimensional dynamic array
  int* pd_arr[2];
  pd_arr[0] = malloc(sizeof(int) * 2);
  pd_arr[1] = malloc(sizeof(int) * 2);

  printf("Hello,world!\n");//standard output

  free(pd_arr[0]);//memory leak

  //File Output
  {
    FILE* fp=NULL;
    fp = fopen("PLIVET.txt", "w");
    fputs("PLIVET", fp);
    fclose(fp);
  }

  //File Input
  {
    FILE* fp=NULL;
    char buf[7];
    fp = fopen("PLIVET.txt", "r");
    while(fgets(buf,10,fp) != NULL) {
      printf("%s",buf);
    }
    fclose(fp);
  }
  return 0;
}
copy C Code & Paste it below the Compiler & Run

Stack And Heap Memory

#include <stdio.h>
#include <stdlib.h>

int main() {
    // Stack memory allocation
    int stackVar1 = 10;
    int stackVar2 = 20;
    int stackVar3 = 30;

    // Pointers pointing to stack variables
    int *ptr1 = &stackVar1;
    int *ptr2 = &stackVar2;
    int *ptr3 = &stackVar3;

    // Displaying stack memory values
    printf("Stack Variables:\n");
    printf("stackVar1 = %d, Address = %p\n", *ptr1, (void *)ptr1);
    printf("stackVar2 = %d, Address = %p\n", *ptr2, (void *)ptr2);
    printf("stackVar3 = %d, Address = %p\n", *ptr3, (void *)ptr3);

    // Heap memory allocation
    int *heapVar1 = (int *)malloc(sizeof(int));
    int *heapVar2 = (int *)malloc(sizeof(int));
    int *heapVar3 = (int *)malloc(sizeof(int));

    // Check if memory allocation was successful
    if (heapVar1 == NULL || heapVar2 == NULL || heapVar3 == NULL) {
        printf("Memory allocation failed\n");
        return 1; // Exit with an error code
    }

    // Assigning values to heap variables
    *heapVar1 = 40;
    *heapVar2 = 50;
    *heapVar3 = 60;

    // Displaying heap memory values
    printf("\nHeap Variables:\n");
    printf("heapVar1 = %d, Address = %p\n", *heapVar1, (void *)heapVar1);
    printf("heapVar2 = %d, Address = %p\n", *heapVar2, (void *)heapVar2);
    printf("heapVar3 = %d, Address = %p\n", *heapVar3, (void *)heapVar3);

    // Freeing heap memory
    free(heapVar1);
    free(heapVar2);
    free(heapVar3);

    return 0;
}
copy C Code & Paste it below the Compiler & Run

Local variable as a global variable using Dynamic Memory Allocation

#include <stdio.h>
#include <stdlib.h>  // Include for malloc and free
 
int global_var = 10; // global variable but memory can not be freed
 
void Function() {
    int local_var = 20;
    printf("local_var in Function: %d\n", local_var);
}
 
int main() {
    int local_var = 30; 
    printf("local_var in main: %d\n", local_var);

    //local_var1 but memory can be freed using malloc
    int *local_var1 = (int*) malloc(4 * sizeof(int));  // Allocate memory for 4 integers
    
    if (local_var1 == NULL) {  // Always check if malloc succeeded
        printf("Memory allocation failed\n");
        return 1;  // Return a non-zero value to indicate failure
    }
 
    // Optionally initialize the allocated memory
    for (int i = 0; i < 4; i++) {
        local_var1[i] = i + 1;  // Initialize with some values
    }
 
    printf("global_var: %d\n", global_var);
    Function();
 
    // Print values from the allocated memory
    for (int i = 0; i < 4; i++) {
        printf("local_var1[%d]: %d\n", i, local_var1[i]);
    }
 
    free(local_var1);  // Free the allocated memory
    printf("local_var1 after freed in main: %d\n", local_var1); // gurbadge value will print because memory is free
    printf("global_var: %d\n", global_var);
    Function();
    return 0;
}
opy C Code & Paste it below the Compiler & Run