Learn C Programming

lesson ১ / ৯ · Array

Module ৯ · Array

Array: অনেক বাক্স, একটাই নাম

Freeপড়া

এই lesson-এ যা শিখবে

  • নাম দেওয়া size দিয়ে array declare করতে পারবে, সেটা ভরতে পারবে, আর index দিয়ে যেকোনো একটা বাক্স পড়তে বা লিখতে পারবে।
  • Index কেন 0 থেকে n - 1 পর্যন্ত চলে, বুঝিয়ে বলতে পারবে, আর sizeof marks / sizeof marks[0] দিয়ে বাক্স গুনতে পারবে।
  • Capacity আর count আলাদা রাখতে পারবে, আর ভুল size, ভুল index বা ভুল read-এ GCC 12 কী বলে, দেখে চিনতে পারবে।

স্কুল বছরের প্রতি মাসের জন্য Amara একটা করে নম্বর রাখে: বারো মাসে বারোটা নম্বর, তাই বারোটা variable, m1 থেকে m12। Program ঠিকঠাক চলে, কিন্তু গড়ের লাইনটা লিখতে গিয়ে সেটা screen-এর কিনারা পার হয়ে যায়। সামনের বছর Amara চায়, কোন মাস সেটা বললেই সেই মাসের নম্বরটা হাতে আসুক। এই lesson ওকে বারোটা বাক্সের জন্য একটাই নাম দেবে।

বারোটা variable, নাকি একটা array

Amara প্রথমে program-টা এভাবে লিখেছিল:

#include <stdio.h>

int main(void)
{
    int m1 = 72;
    int m2 = 65;
    int m3 = 80;
    int m4 = 91;
    int m5 = 58;
    int m6 = 77;
    int m7 = 84;
    int m8 = 69;
    int m9 = 90;
    int m10 = 73;
    int m11 = 66;
    int m12 = 88;

    printf("average: %.2f\n", (m1 + m2 + m3 + m4 + m5 + m6 + m7 + m8 + m9 + m10 + m11 + m12) / 12.0);
    return 0;
}
average: 76.08

উত্তর ঠিক আছে, কিন্তু এটাকে আর বাড়ানো যায় না। "7 নম্বর মাসে কত পেয়েছিল?" জানতে লাগবে বারোটা if, প্রতিটা নামের জন্য একটা।

Array মানে একই type-এর এক সারি বাক্স, পাশাপাশি, একটাই নামের নিচে। প্রতিটা বাক্স হলো array-এর একটা element। কোন বাক্স চাও, সেটা বলো তার index দিয়ে: square bracket-এর ভিতরে একটা পূর্ণসংখ্যা, যাকে subscript-ও বলে। এবার বারোটা নম্বর একটা array-তে:

#include <stdio.h>

#define MONTHS 12

int main(void)
{
    int marks[MONTHS] = {72, 65, 80, 91, 58, 77, 84, 69, 90, 73, 66, 88};
    int total = 0;

    for (int i = 0; i < MONTHS; i++) {
        total += marks[i];
    }
    printf("average: %.2f\n", (double)total / MONTHS);
    return 0;
}

Run করার আগে loop-টা trace করো, Module 6-এর মতো প্রতি pass-এ এক সারি। প্রতিটা pass একটা করে বাক্স, marks[i], total-এর সাথে যোগ করে।

iযে বাক্স পড়েতার মানpass-এর পরে total
0marks[0]7272
1marks[1]65137
2marks[2]80217
3marks[3]91308
4marks[4]58366
5marks[5]77443
6marks[6]84527
7marks[7]69596
8marks[8]90686
9marks[9]73759
10marks[10]66825
11marks[11]88913
12কোনোটা না: 12 < 12 মিথ্যা, loop শেষ913

913 / 12 হয় 76.083..., আর Module 4-এর (double) cast ভগ্নাংশটা রেখে দেয়।

average: 76.08

তাই একটা array আর একটা loop মিলে বারোটা নাম আর ওই লম্বা লাইনের কাজটা করে দেয়। 7 নম্বর মাস এখন একটাই বাক্স, marks[6]। কেন 6, সেটা পরের অংশগুলো বলবে।

নাম দেওয়া size দিয়ে array declare করা

Array declare করার গড়ন

#define MONTHS 12

type name[MONTHS] = {v0, v1, ...};
  • type হলো প্রতিটা বাক্সের type; একটা array-তে কখনো দুই রকম type মেশে না।
  • name পুরো সারিটার একটাই নাম।
  • [MONTHS] বলে কয়টা বাক্স বানাতে হবে; program compile হওয়ার সময়েই এটা ঠিক হয়ে যায়।
  • = {v0, v1, ...} হলো initialiser: বাক্সগুলো যে মান নিয়ে শুরু করে।

এই track বাক্সের সংখ্যাটা লেখে একটা #define constant দিয়ে, যার নাম দেখেই বোঝা যায় সে কী গোনে, যেমন MONTHS বা MAX_N। const int কেন চলবে না, সেটা এখানে বলার কথা দিয়েছিল Module 3।

const int আসলে একটা variable, যেটা বদলাবে না বলে তুমি কথা দিয়েছ; এটা সত্যিকারের constant না। main-এর ভিতরে const int months = 12; লিখে তারপর int marks[months]; লেখো, কোনো command line-এই কোনো বার্তা আসবে না। কিন্তু তৈরি হবে একটা variable length array, সংক্ষেপে VLA: যে array-এর size ঠিক হয় program চলার সময়।

VLA এসেছে C99-এ, আর C11 এটাকে compiler-এর জন্য ঐচ্ছিক করে দিয়েছে। GCC 12 এখনো VLA চালায়, তবে এর এমন কিছু নিয়ম আছে যা সাধারণ array-এর নেই। ওই দুইটা লাইন সব function-এর বাইরে সরিয়ে দাও, GCC 12 প্রতিটা command line-এ মানা করে দেবে: error: variably modified 'marks' at file scope।

তাই এই track কখনো VLA ব্যবহার করে না। প্রতিটা size একটা #define, program শুরুর আগেই ঠিক করা।

Index চলে 0 থেকে n - 1 পর্যন্ত

প্রথম বাক্সটা বাক্স 0, বাক্স 1 না; Module 6 ঠিক এই কথাটাই দিয়ে রেখেছিল। n-টা বাক্সের array-তে index চলে 0 থেকে n - 1 পর্যন্ত। তাই marks[0] হলো জানুয়ারি, আর marks[11] ডিসেম্বর।

Index-কে একটা দূরত্ব হিসেবে পড়ো: প্রথম বাক্স থেকে কয় বাক্স সামনে। জুলাই বছরের সপ্তম মাস, প্রথম থেকে ছয় বাক্স সামনে, তাই সেটা marks[6]। একটা বাক্স ঠিক একটা int variable-এর মতোই কাজ করে।

#include <stdio.h>

#define MONTHS 12

int main(void)
{
    int marks[MONTHS] = {72, 65, 80, 91, 58, 77, 84, 69, 90, 73, 66, 88};

    printf("July: %d\n", marks[6]);
    marks[6] = 90;
    printf("July now: %d\n", marks[6]);
    printf("January: %d, December: %d\n", marks[0], marks[MONTHS - 1]);
    return 0;
}
July: 84
July now: 90
January: 72, December: 88

Assignment-টা শুধু বাক্স 6 বদলেছে, আর কোনোটা না। তাই n-টা বাক্সের শেষটা marks[n - 1], এখানে marks[MONTHS - 1]। এই lesson শুধু সামনের দিকে হাঁটে; marks[n - 1] থেকে পিছিয়ে marks[0] পর্যন্ত যাওয়াটা অনুশীলন 1-এর কাজ।

Initialiser: বাক্সগুলো কোন মান নিয়ে শুরু করে

=-এর পরে braces-এর ভিতরের তালিকাটাই initialiser। এটা বাক্সগুলো ভরে বাঁ থেকে ডানে, বাক্স 0 থেকে শুরু করে। নিচে এর চার রকম চেহারা, একটা table হিসেবে print করা।

#include <stdio.h>

#define BOXES 5

int main(void)
{
    int full[BOXES] = {3, 1, 4, 1, 5};
    int part[BOXES] = {9, 8};
    int zeros[BOXES] = {0};
    int picked[BOXES] = {[3] = 7};

    printf("box  full  part  zeros  picked\n");
    for (int i = 0; i < BOXES; i++) {
        printf("%3d  %4d  %4d  %5d  %6d\n", i, full[i], part[i], zeros[i], picked[i]);
    }
    return 0;
}

Run করার আগে প্রতিটা কলাম আন্দাজ করো। নিচের table-টাই trace।

Arrayবাক্স 0 থেকে 4কেন
full3 1 4 1 5পাঁচটা বাক্সে পাঁচটা মান
part9 8 0 0 0দুইটা মান বাক্স 0 আর 1 ভরে; বাকিগুলো পায় 0
zeros0 0 0 0 0একটাই মান, 0, বাক্স 0 ভরে; বাকিগুলো পায় 0
picked0 0 0 7 0[3] = 7 নাম ধরে বাক্স 3-কে বেছে নেয়; বাকিগুলো পায় 0
box  full  part  zeros  picked
  0     3     9      0       0
  1     1     8      0       0
  2     4     0      0       0
  3     1     0      0       7
  4     5     0      0       0

part-এর পেছনের নিয়মটার নাম zero-fill নিয়ম। তালিকা array-এর চেয়ে ছোট হলে, তালিকা যে বাক্সগুলো পর্যন্ত পৌঁছায় না, সেগুলো শুরু করে 0 দিয়ে। C17 standard-এর section 6.7.9, paragraph 21-এ এই নিয়ম লেখা আছে। তাই = {0} কোনো বিশেষ command না। এটা একটামাত্র মানের তালিকা, বাকিটা নিয়মই করে দেয়।

picked-এ আছে একটা designated initialiser, যেটা এসেছে C99-এ: [3] = 7 index দিয়ে একটা বাক্সকে নাম ধরে বেছে নেয়। Module 12 একই কায়দায় structure-এর field ভরবে।

int scores[] = {120, 95, 310}; size-টা বাদ রাখে, তখন compiler প্রতিটা মানের জন্য একটা করে বাক্স বানায়। বেশি মান দিলেও compile হয়ে যায়: int a[3] = {1, 2, 3, 4};-এর জন্য Playground-এর Compile output tab দেখায় warning: excess elements in array initializer, আর 4-টা বাদ পড়ে যায়।

তাই তালিকা ভরে বাক্স 0 থেকে, আর যে বাক্স বাদ পড়ে, সেটা শুরু করে 0 দিয়ে। এই track-এর প্রতিটা array-তে initialiser থাকে, শুধু = {0} হলেও।

বাক্সগুলো পাশাপাশি বসে, আর sizeof ওদের গোনে

Module 2-এ sizeof-কে marks নামের একটা int-এর কথা জিজ্ঞেস করা হয়েছিল, উত্তর এসেছিল 4 byte। এবার একটা array-এর কথা জিজ্ঞেস করো, সে পুরো array-এর উত্তর দেবে।

#include <stdio.h>

#define MONTHS 12

int main(void)
{
    int marks[MONTHS] = {0};

    printf("one box:   %zu bytes\n", sizeof marks[0]);
    printf("the array: %zu bytes\n", sizeof marks);
    printf("boxes:     %zu\n", sizeof marks / sizeof marks[0]);
    return 0;
}
one box:   4 bytes
the array: 48 bytes
boxes:     12

4 byte করে বারোটা বাক্সে হয় 48, আর sizeof marks ঠিক 48-ই বলে। মানে বাক্সগুলোর মাঝে কোনো ফাঁক নেই। ওরা memory-র একটা টানা অংশে পরপর বসে থাকে, একে বলে contiguous। বাক্স i শুরু হয় বাক্স 0-এর i x 4 byte পরে: index যে একটা দূরত্ব, সেই কথাটাই, এবার byte-এ মাপা। বাক্সগুলোর আসল অবস্থান print করে দেখাবে Module 11।

পুরোটাকে একটা বাক্সের মাপ দিয়ে ভাগ করো, 48 / 4, পাবে 12। sizeof marks / sizeof marks[0] এটুকুই করে, আর ওর উত্তর একটা size_t, তাই Module 2-এর মতো %zu দিয়ে print হয়। এটা প্রতিটা বাক্স গোনে, ব্যবহার হোক বা না হোক, আর কোথায় গিয়ে এই কৌশল আর কাজ করে না, সেটা দেখাবে lesson 05।

Capacity আর count দুইটা আলাদা সংখ্যা

সাধারণত input-ই বলে দেয় কয়টা মান আসছে, কিন্তু প্রথম read-এর আগেই array-টা থাকা চাই। তাই array বানাও সবচেয়ে বড় যে input আসতে পারে তার মাপে, আর ব্যবহার করো শুধু সামনের অংশটা।

এতে প্রতিটা array-এর দুইটা সংখ্যা থাকে। Capacity মানে কয়টা বাক্স আছে: MAX_N-এর মতো একটা #define। Count মানে কয়টা বাক্সে আসল data আছে: program চলার সময় পড়া একটা int n। Data-র উপর দিয়ে যাওয়া প্রতিটা loop চলে count পর্যন্ত।

#include <stdio.h>

#define MAX_N 100

int main(void)
{
    int marks[MAX_N] = {0};
    int n = 0;

    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &marks[i]);
    }

    printf("marks:");
    for (int i = 0; i < n; i++) {
        printf(" %d", marks[i]);
    }
    printf("\n");
    printf("%d of %d boxes used, the last one is marks[%d]\n", n, MAX_N, n - 1);
    return 0;
}

Input 5 আর 70 85 62 91 48 দিয়ে পড়ার loop-টা trace করো।

ii < n?scanf যা পড়েযেখানে store হয়
00 < 5, হ্যাঁ70marks[0]
11 < 5, হ্যাঁ85marks[1]
22 < 5, হ্যাঁ62marks[2]
33 < 5, হ্যাঁ91marks[3]
44 < 5, হ্যাঁ48marks[4]
55 < 5, না: loop শেষকিছু নাকোথাও না

Print করার loop-টাও একই পাঁচটা বাক্সের উপর দিয়ে একই পাঁচটা pass দেয়।

marks: 70 85 62 91 48
5 of 100 boxes used, the last one is marks[4]

ওই output-টা input 5 আর 70 85 62 91 48-এর জন্য। পড়া শেষ হওয়ার পরে array-টা দেখতে এরকম:

Capacity আর count: বাক্স আছে 100টা, data আছে 5টায় 5টা নম্বর পড়ার পরে int marks[MAX_N] count: n = 5, যে বাক্সগুলোতে data আছে মান index byte 70 0 0 85 1 4 62 2 8 91 3 12 48 4 16 0 5 20 0 6 24 0 7 28 ... 0 99 396 capacity: MAX_N = 100টা বাক্স, 400 byte, একটাই নাম: marks বাক্স 5 থেকে 99 আছে, = {0} থেকে 0 পেয়েছে, কিন্তু ওদের মধ্যে কোনো নম্বর পড়া হয়নি। শেষ নম্বরটা marks[n - 1], মানে marks[4]। marks[100] হতো শেষেরও বাইরে।

ড্যাশ দিয়ে আঁকা বাক্সগুলো array-এর ভিতরে, কিন্তু data-র বাইরে।

Read-টা আবার দেখো, scanf("%d", &marks[i])। int array-এর একটা বাক্স মানে একটা int, তাই এটা ampersand নেয়, Module 3 যেমন শিখিয়েছিল। Module 3-এর ব্যতিক্রম ছিল bracket ছাড়া খালি নামটা, marks, যেটা নিজেই বোঝায় প্রথম বাক্সটা কোথায়। তাই নিয়মটা আসলে কখনো ভাঙেনি: & জানতে চায় বাক্সটা কোথায়, আর খালি array-এর নাম সেটা আগে থেকেই বলে দেয়। খালি নামটা কাজে লাগবে Module 10 আর 11-এ; bracket থাকলে সেটা একটা বাক্স, আর একটা বাক্স সবসময় নিজের & নেয়।

তাই capacity হলো তুমি কতটা জায়গা বানিয়েছ, আর count হলো তোমার হাতে কতটা data আছে।

শেষ বাক্সের এক ঘর পরে

C কোনো index check করে না। 100 বাক্সের array-তে marks[100] লিখলেও compile হয়, run-ও হয়, আর program গিয়ে পড়ে এমন memory-তে, যেটা অন্য কারও, বা কারোরই না। এটাই undefined behaviour (Module 4): output ঠিকঠাক দেখালেও program-টা ভুল।

Bob 10 বাক্সের একটা array-তে 1 থেকে 10 ভরে, শুরু করে বাক্স 1 থেকে, তারপর দশটা বাক্সই print করে। ওর array-তে কোনো initialiser নেই, আর SIZE হলো 10।

int a[SIZE];

for (int i = 1; i <= SIZE; i++) {
    a[i] = i;
}

Playground-এ ওর program শেষ হয়েছে সফল badge নিয়ে, আর print করেছে 0 1 2 3 4 5 6 7 8 9। একমাত্র ইঙ্গিত ছিল Compile output tab, যেটার রং amber হয়ে গিয়েছিল। GCC 12 প্রতিটা command line-এ, Playground-সহ, এই warning দেয়: warning: iteration 9 invokes undefined behavior [-Waggressive-loop-optimizations]।

GCC pass গোনে 0 থেকে, তাই iteration 9 মানে সেই pass, যেখানে i = 10, আর সেটা লেখে a[10]-এ। Compiler Explorer-এ GCC 12-এর -O2 listing দেখায়, লেখাটা সত্যিই হয়েছে, array-এর ঠিক পরের কিছু ফাঁকা byte-এ। ওখানে আর কিছু ছিল না, তাই চোখে পড়ার মতো কিছু ভাঙেনি। শুরুর 0-টা হলো a[0], যেখানে Bob কখনো কিছু লেখেনি।

Warning এসেছে শুধু এজন্য যে loop-এর সীমাটা ছিল একটা constant। Index যদি input থেকে আসে, GCC 12 কোনো command line-এই কিছু বলে না, -Wall -Wextra-তেও না। তাই শেষ বাক্স a[n - 1], আর data-র loop check করে i < n। ভুল জায়গায় লেখা আর কী কী ঘটাতে পারে, মেপে দেখাবে lesson 06।

Example 1: প্রতি মাসে কত দিন, তার একটা lookup table

Lookup table হলো এমন একটা array, যেটা একবার ভরে রাখো, তারপর index দিয়ে পড়ো। মাস গোনা হয় 1 থেকে, তাই m নম্বর মাস থাকে বাক্স m - 1-এ।

#include <stdio.h>

#define MONTHS 12

int main(void)
{
    int days[MONTHS] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
    int month = 0;

    scanf("%d", &month);
    printf("month %d has %d days\n", month, days[month - 1]);
    return 0;
}
month 2 has 28 days

ওই output-টা input 2-এর জন্য: ফেব্রুয়ারি হলো days[1]। Table-টা leap year-এর হিসাব রাখে না। 1 থেকে 12-এর বাইরের কোনো মাস দিলে program array-এর বাইরে থেকে পড়বে, আর C তাকে থামাবে না।

Run in Compiler
Example 2: Kenji-র score গোনার কাজটা compiler-কে দাও

Kenji bracket-টা খালি রাখে। Compiler ওর score-গুলো গোনে, আর sizeof program-কে জানিয়ে দেয় কয়টা আছে।

#include <stdio.h>

int main(void)
{
    int scores[] = {120, 95, 310, 87, 205};
    int count = (int)(sizeof scores / sizeof scores[0]);

    printf("%d scores:", count);
    for (int i = 0; i < count; i++) {
        printf(" %d", scores[i]);
    }
    printf("\n");
    printf("first %d, last %d\n", scores[0], scores[count - 1]);
    return 0;
}
iযে বাক্স পড়েএ পর্যন্ত লাইনটা
0scores[0], 1205 scores: 120
1scores[1], 955 scores: 120 95
2scores[2], 3105 scores: 120 95 310
3scores[3], 875 scores: 120 95 310 87
4scores[4], 2055 scores: 120 95 310 87 205
5কোনোটা না: 5 < 5 মিথ্যাnewline লাইনটা শেষ করে
5 scores: 120 95 310 87 205
first 120, last 205

(int) cast count-কে i-এর মতো একই type-এর বানিয়ে দেয় (Module 4)। Braces-এর ভিতরে ষষ্ঠ একটা score যোগ করে আবার run করো: আর কিছু না বদলালেও count আর শেষ score দুইটাই বদলে যাবে।

Run in Compiler
Example 3: Amara একটা খাতার নম্বর ঠিক করে

শিক্ষক একটা খাতা আবার দেখেছেন। Amara-র program আগে n-টা নম্বর পড়ে, তারপর খাতার ক্রম আর নতুন নম্বরটা। তারপর তালিকাটা print করে দুইবার, বদলের আগে আর পরে। k নম্বর খাতা থাকে marks[k - 1]-এ।

#include <stdio.h>

#define MAX_N 100

int main(void)
{
    int marks[MAX_N] = {0};
    int n = 0;
    int paper = 0;
    int new_mark = 0;

    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &marks[i]);
    }
    scanf("%d %d", &paper, &new_mark);

    printf("before:");
    for (int i = 0; i < n; i++) {
        printf(" %d", marks[i]);
    }
    printf("\n");

    marks[paper - 1] = new_mark;

    printf("after: ");
    for (int i = 0; i < n; i++) {
        printf(" %d", marks[i]);
    }
    printf("\n");
    return 0;
}
বাক্স01234
পড়া শেষে7085629148
marks[3 - 1] = 68-এর পরে7085689148
before: 70 85 62 91 48
after:  70 85 68 91 48

ওই output-টা input 5, তারপর 70 85 62 91 48, তারপর 3 68-এর জন্য। বদলেছে শুধু বাক্স 2। প্রতিটা loop বাক্স 0 থেকে 4 পর্যন্ত হাঁটে, উপরে trace করা পড়ার loop-টার মতোই।

Zara আগে edge-গুলো চালিয়ে দেখে: খাতা 1 বদলায় marks[0], আর খাতা 5 বদলায় marks[4]। খাতা 0 দিলে লেখা হতো marks[-1]-এ, প্রথম বাক্সেরও আগে, আর C সেটাও থামাত না। Print-এর loop-টা এখানে দুইবার আছে; lesson 05 এটাকে একটা function বানিয়ে দেবে।

Run in Compiler

এটা কোথায় কাজে লাগে

  • ছবি। 24-bit BMP file ছবির প্রতিটা সারি রাখে byte-এর একটা array হিসেবে। প্রতিটা pixel নেয় তিনটা byte: নীল, সবুজ, লাল।
  • শব্দ। CD-র audio-তে প্রতি channel-এ সেকেন্ডে 44,100টা sample থাকে, প্রতিটা একটা 16-bit সংখ্যা। WAV file এগুলো রাখে একটা লম্বা array-তে, বাঁ আর ডান channel-এর sample পালা করে।
  • Character যাচাই। GNU C Library isdigit(c)-এর উত্তর দেয় আগে থেকে বানানো একটা table-এর একটা বাক্স পড়ে। সেখানে index হলো character-টার code, Module 1-এর ASCII table থেকে।
  • ক্যালেন্ডার। Python-এর calendar module মাসের দিনসংখ্যা রাখে mdays নামের একটা list-এ, যার প্রথম ঘরে একটা বাড়তি 0। তাই জানুয়ারি হয় mdays[1], যে কাজটা Example 1 করেছিল month - 1 দিয়ে।

যে ভুলগুলো সবাই করে

১. Data-র loop-এ i <= n।

printf("marks:");
for (int i = 0; i <= n; i++) {
    printf(" %d", marks[i]);
}

কোনো command line-এই বার্তা নেই, কারণ n আসে input থেকে। Input 5 আর 70 85 62 91 48 দিলে Bob-এর loop print করে marks: 70 85 62 91 48 0। বাড়তি 0-টা হলো marks[5], capacity-র ভিতরে কিন্তু data-র বাইরে। n যদি MAX_N-এর সমান হয়, loop পড়ে marks[100], আর সেটা undefined behaviour। লেখো i < n; তুমি <= লিখবে, কারণ "n পর্যন্ত" শুনলে মনে হয় n-ও তার মধ্যে পড়ে।

২. Size হিসেবে const int, সাথে initialiser।

const int months = 12;
int marks[months] = {0};

প্রতিটা command line-এ error, Playground-সহ: error: variable-sized object may not be initialized। months একটা variable, তাই marks একটা VLA, আর VLA-কে initialiser দেওয়া যায় না। এর বদলে লেখো #define MONTHS 12। তুমি const-এর দিকে হাত বাড়াবে, কারণ Module 3 এটাকে সাবধানী পছন্দ বলেছিল, আর একটা মানের জন্য সেটা সত্যিই তাই।

৩. = দিয়ে array copy করা।

int marks[MONTHS] = {72, 65, 80, 91, 58, 77, 84, 69, 90, 73, 66, 88};
int backup[MONTHS] = {0};

backup = marks;

প্রতিটা command line-এ error, Playground-সহ: error: assignment to expression with array type। C কখনো এক ধাপে পুরো array copy করে না, তাই বাক্স ধরে ধরে copy করো, একটা loop-এ backup[i] = marks[i]। তুমি = দিয়ে চেষ্টা করবে, কারণ ওটা একটা int copy করে, আর array দেখতে বারোটা int-এর মতোই।

৪. Ampersand ছাড়া read।

for (int i = 0; i < n; i++) {
    scanf("%d", marks[i]);
}

Playground-এ নীরব। নিজের মেশিনে GCC 12-এর gcc -Wall বলে warning: format '%d' expects argument of type 'int *', but argument 2 has type 'int' [-Wformat=]; star চিহ্নটার মানে বুঝিয়ে বলবে Module 11। & না থাকলে scanf বাক্সের ভিতরের সংখ্যাটাকেই লেখার জায়গা ভেবে বসে। Playground-এ এভাবে তিনটা নম্বর পড়া একটা program মেপে দেখা হয়েছে: badge এসেছে Runtime error, সাথে (no output)। বাংলা page-এ একই badge আর লেখা বাংলায় দেখায়।

লেখো &marks[i]। তুমি এটা বাদ দেবে, কারণ marks[i] দেখতে এমনিতেই একটা জায়গার মতো।

মাথা খাটাও

Bob দেখেছে, = {0} পাঁচটা বাক্সই 0 দিয়ে ভরে দেয়। ও চায় পাঁচটা বাক্সেই 1, তাই একই জিনিস 1 দিয়ে লেখে।

#include <stdio.h>

#define BOXES 5

int main(void)
{
    int a[BOXES] = {1};

    printf("a:");
    for (int i = 0; i < BOXES; i++) {
        printf(" %d", a[i]);
    }
    printf("\n");
    return 0;
}

এটা compile হয়, কোনো command line-এই কোনো বার্তা আসে না। Bob আশা করে a: 1 1 1 1 1। এটা আসলে কী print করে, আর কেন?

Zero-fill নিয়মটা আবার পড়ো, শব্দ ধরে ধরে। Bob-এর তালিকায় কয়টা মান আছে, আর সেগুলো কোন কোন বাক্স পর্যন্ত পৌঁছায়?

অনুশীলন ১সহজ

খাতাগুলো যে ক্রমে এসেছে, Amara নম্বরগুলো সেই ক্রমেই তুলেছে। এখন ও চায় শেষ খাতা থেকে পিছিয়ে প্রথম খাতা পর্যন্ত। নম্বরগুলো int marks[MAX_N]-এ store করো, যেখানে MAX_N হলো 1000, তারপর উল্টো ক্রমে print করো।

Input. এক লাইনে n, তারপর n-টা পূর্ণসংখ্যা।

Output. n-টা পূর্ণসংখ্যা উল্টো ক্রমে, এক লাইনে, মাঝে একটা করে space।

Constraints. 1 <= n <= 1000। প্রতিটা পূর্ণসংখ্যা -1000000000 থেকে 1000000000-এর মধ্যে।

Sample. Input 5 আর 70 85 62 91 48 দিলে 48 91 62 85 70।

#include <stdio.h>

#define MAX_N 1000

int main(void)
{
    int marks[MAX_N] = {0};
    int n = 0;

    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &marks[i]);
    }

    /* Print the n marks from the last box back to the first,
       on one line, separated by single spaces. */

    return 0;
}

reverse-print নামে গ্রেড হয়। Hidden test-এ আছে n = 1 আর n = 1000, আর সেগুলো ধরে ফেলে এমন loop, যেটা marks[n] থেকে শুরু হয় বা marks[0]-এর আগেই থেমে যায়।

Run in Compiler
অনুশীলন ২সহজ

Alice তালিকা চোখে দেখে মিলিয়ে নেয়, তাই ও চায় প্রতিটা মান তার বাক্সের নম্বরের পাশে print হোক।

Input. এক লাইনে n, তারপর n-টা পূর্ণসংখ্যা।

Output. n-টা লাইন। বাক্স i-এর লাইনে থাকবে a[i] = v, যেখানে v হলো ওই বাক্সের মান, আর i গোনা শুরু 0 থেকে।

Constraints. 1 <= n <= 100। প্রতিটা পূর্ণসংখ্যা -1000000 থেকে 1000000-এর মধ্যে।

Sample. Input 3 আর 70 85 62 দিলে তিন লাইনে a[0] = 70, a[1] = 85 আর a[2] = 62।

#include <stdio.h>

#define MAX_N 100

int main(void)
{
    int a[MAX_N] = {0};
    int n = 0;

    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &a[i]);
    }

    /* Print one line per box, in the form a[0] = 70.
       Which boxes hold data, and which index is the last one? */

    return 0;
}

আলাদা করে গ্রেড হয় না। n-টা মান array-তে পড়ার কাজটা reverse-print-ই গ্রেড করে।

Run in Compiler
অনুশীলন ৩মাঝারি

Zara-র বৃষ্টি মাপার যন্ত্রের sensor মাঝে মাঝে গোলমাল করে, তখন একটা negative reading লিখে ফেলে। বৃষ্টি তো negative হতে পারে না, তাই ও প্রতিটা negative বাক্সে 0 বসায়। তারপর পরিষ্কার করা log আর কয়টা reading ঠিক করেছে, দুইটাই print করে।

Input. এক লাইনে n, তারপর n-টা পূর্ণসংখ্যা।

Output. দুই লাইন: ঠিক করার পরের n-টা মান, মাঝে একটা করে space, তারপর কয়টা বাক্স বদলেছে।

Constraints. 1 <= n <= 100। প্রতিটা পূর্ণসংখ্যা -1000000 থেকে 1000000-এর মধ্যে।

Sample. Input 4 আর 3 -1 5 -7 দিলে দুই লাইনে 3 0 5 0 আর 2।

#include <stdio.h>

#define MAX_N 100

int main(void)
{
    int rain[MAX_N] = {0};
    int n = 0;

    scanf("%d", &n);
    for (int i = 0; i < n; i++) {
        scanf("%d", &rain[i]);
    }

    /* Set every negative box to 0, and count the boxes you changed.
       Then print the n values on one line, and the count on the next. */

    return 0;
}

আলাদা করে গ্রেড হয় না। Judge শুধু output দেখে, আর প্রতিটা মান পড়ার সাথে সাথে 0-তে নামিয়ে print করলে array ছাড়াই একই output আসে। তবু array দিয়েই করো, একটা বাক্স বদলানোর হাত পাকাতে।

Run in Compiler

সচরাচর যে প্রশ্নগুলো আসে

  • সব array বিশাল করে বানালেই তো হয়, size নিয়ে আর ভাবতে হয় না?

    এটা Kenji-র বুদ্ধি, কিন্তু প্রতিটা বাক্সই memory খায়, ব্যবহার হোক বা না হোক। int marks[1000000] পাঁচটা নম্বর রাখতে 4,000,000 byte দখল করে বসে থাকে। Capacity নাও problem-এ দেওয়া n-এর সীমা থেকে। Function-এর ভিতরের বড় array ঠিক কোথায় গিয়ে ভাঙে, সেটা মেপে দেখাবে lesson 03।

  • printf কি একবারে পুরো array print করতে পারে?

    না। printf("%d\n", marks); Playground-এ নীরবে compile হয়, আর এমন একটা সংখ্যা print করে, যেটা কোনো নম্বরই না। Compiler Explorer-এ একবার run করে পাওয়া গেছে -1316912256। নিজের মেশিনে GCC 12-এর gcc -Wall বলে warning: format '%d' expects argument of type 'int', but argument 2 has type 'int *' [-Wformat=]। বাক্সগুলো একটা একটা করে print করো, একটা loop দিয়ে।

  • int marks[12]; আর marks[11] = 88;, দুই জায়গার bracket কি একই কথা বলে?

    না। Declaration-এ সংখ্যাটা বলে কয়টা বাক্স বানাতে হবে। বাকি সব জায়গায় সেটা index, মানে প্রথম বাক্স থেকে দূরত্ব। তাই declaration-এ marks[12] বারোটা বাক্স বানায়, আর অন্য যেকোনো জায়গায় marks[12] মানে শেষ বাক্সের এক ঘর পরে।

  • বেশি মান এলে array কি বড় হতে পারে?

    না। Program compile হওয়ার সময়েই এর size ঠিক হয়ে যায়। Program চলতে চলতে বাড়তে পারে, এমন memory হলো Module 14-এর বিষয়।

মূল কথা

  • Array হলো একই type-এর এক সারি বাক্সের একটাই নাম; এর size একটা #define, কখনো VLA না।
  • Index চলে 0 থেকে n - 1, কারণ index মানে দূরত্ব; শেষ বাক্স a[n - 1]।
  • Initialiser বাক্স 0 থেকে ভরে, যে বাক্স বাদ পড়ে সেটা শুরু করে 0 দিয়ে, আর [3] = 7 নাম ধরে একটা বাক্স বেছে নেয়।
  • বাক্সগুলো পাশাপাশি বসে, তাই sizeof a / sizeof a[0] ওদের গোনে, ব্যবহার হোক বা না হোক।
  • Capacity হলো কয়টা বাক্স আছে, count হলো কয়টায় data আছে, আর data-র loop check করে i < n।
  • C কখনো index check করে না: শেষের পরে লেখা undefined behaviour, আর বেশিরভাগ সময় সেটা নীরব।

এরপর Zara-র তিনটা negative তাপমাত্রার সর্বোচ্চটা আসে 0, আর lesson 02 খুঁজে বের করে কেন।

lesson ১ শেষ

শেষ হলে চিহ্ন দিন, অগ্রগতি আপনার সাথে থাকবে।

পরেরটা: Array ঘোরার pattern: যোগফল, সর্বোচ্চ, গণনা