Don't understand this unfamiliar syntax: arr1[ arr2[i] - 'a' ]++

Viewed 153

I am looking at a program that finds the frequency of strings entered. Comparison is made based on a string's ASCII value against the ASCII value of lowercase 'a'. I have implemented it; it works, albeit, with a bug, but essentially, I am ignorant of a particular line of code;

for (int i = 0; i < strlen(arr2); i++)
{
    // this line...
    arr1[ arr2[i] - 'a' ]++;
}

arr1 is arr1[26] = {0}, that is, all the letters of the alphabet are assigned an index and the array is initialised to zero, while arr2[] as a function argument, receives the stdin.

How does the mysterious line of code work and what is it saying?

The full code:

#include <stdio.h>
#include <string.h>
    
#define ALEPH   26
    
void freq(char arr2[]);
    
int main ()
{
    char * str;
    
    printf("\nCharacter Frequency\n"
    "--------------------\n");
    
    // user input
    printf("\nEnter a string of characters:\n");
    fgets(str, ALEPH, stdin);
    
    freq(str);
    
    return 0;
}
    
// Function Definiton
void freq (char arr2[])
{
    // array for ascii characters initialised to 0
    int arr1[ALEPH] = {0};
    
    // scan and cycle through the input array
    for  (int i = 0; i < strlen(arr2); i++)
    {
         arr1[ arr2[i] - 'a' ]++;
    }
    
    for (int j = 0; j < 26; j++)
    {
        if ( arr1[j] != 0 )
        {
            printf("\nCharacter: %c - Frequency: %d", 'a'+j, arr1[j]);
        }
    }
    printf("\n");
}
2 Answers

You ask about the line:

arr1[ arr2[i] - 'a' ]++;

In this line:

  • arr1 is the array that will accumulate the histogram
  • arr2 is the input string which will contribute to the histogram
  • i is the index into input string.

This can be rewritten as:

ch = arr2[i];
histogram_slot = ch - 'a';
arr1[histogram_slot ] = arr1[histogram_slot ] + 1;

For each character in the input string, the character is fetched from the string and assigned to "ch". "ch" is converted to the index in the histogram array by subtracting 'a'. In the third line, the histogram_slot is increased by one. histogram_slot 0 is incremented for 'a', 1 for 'b', 2 for 'c', ... , and 25 for 'z'.

A serious bug in this code is that it only works for the lower case letters. An upper case letter, digit, punctuation, Unicode, extended ASCII, or any character not between 'a' and 'z' inclusive will write in an unintended region of memory. At the best, this will cause an unexpected crash. In the medium disaster, it will cause sporatic malfunction that gets through your testing. In the worst case, it creates a security hole allowing someone uncontrolled access to your stack, and thus the ability to take over execution of the thread.

arr1 is an array of 26 ints initialized to 0s. The indexes of its elements are 0..25.

arr2 is assumed to be a string of lowercase letters 'a'..'z' only. The characters are assumed to be using an encoding where lowercase letters are single-byte and sequential in value, such as ASCII (where a=97, ..., z=122). Anything else that does not match these assumptions will cause undefined behavior in this code.

The code loops through arr2, and for each character, calculates an index by subtracting the numeric value of 'a' (ie, ASCII 97) from the character's numeric value:

'a' - 'a' = 97 - 97 = 0
'b' - 'a' = 98 - 97 = 1
...
'z' - 'a' = 122 - 97 = 25

Then the code accesses the arr1 element at that index, and increments that element's value by 1.

Related