Search code examples
carrayspass-by-referencec-strings

Array of c-strings and pass by pointer method does not work


Assigning new values to an array of C strings using pass by pointer method does not work properly.

Inside the "LettersToCapital" method, new values are assigned to the C-string array properly, however, once the C-strings array contents are read outside the method the results are all wrong. All the function is supposed to do is to capitalize all the lowercase letters. I am definitely doing something wrong, but what could it be?

If on line 53 variable tempStr is replaced with a constant string literal e.g. "aqua" then the values remain the same outside the function. But assigning directly from a char array (tempStr) to char array pointer (*(string +i)) does not yield correct results.

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

void LettersToCapital(char **string, int size);
void ReadOutAOC(char **string, int size);

int main()
{
    char *canadianProvinces[] = {"British Columbia", "Alberta", "Saskatchewan", "Manitoba", "Ontario", "Quebec", "New Brunswick", "Nova Scotia", "Prince Edward Island", "Newfoundland", "Yukon", "Northwest Territories", "Nunavut"};

    int numOfCanProv = sizeof(canadianProvinces) / sizeof(int);

    printf("\nNumber of Canadian provinces %d\n", numOfCanProv);

    // printing all provinces before conversion
    printf("\nBefore \"all to capital conversion\"\n\n");
    ReadOutAOC(canadianProvinces, numOfCanProv);    

    LettersToCapital(canadianProvinces, numOfCanProv);
    // Temp(canadianProvinces);

    // printing all provinces after conversion
    printf("\nAfter \"all to capital conversion\"\n");
    ReadOutAOC(canadianProvinces, numOfCanProv);
}

void ReadOutAOC(char **string, int size)
{
    printf("\n");

    for(int i = 0; i < size; i++)
        printf("String outside the assignment method[%d]: %s\n", i + 1, *(string + i));
}

void LettersToCapital(char **string, int size)
{
    char tempStr[256];

    for(int i = 0; i < size; i++)
    {
        for(int j = 0; j < strlen(*(string + i)); j++)
        {
            if(*(*(string + i) + j) > 96 && *(*(string + i) + j) < 123)
                tempStr[j] = *(*(string + i) + j) - 32;
            else
                tempStr[j] = *(*(string + i) + j);
        }
        tempStr[strlen(*(string + i))] = '\0';
        *(string + i) = tempStr; // does not work
        //*(string + i) = "aqua"; // try this instead
        printf("String inside the assignment method[%d]: %s\n", i + 1, *(string + i));
    }
}

Expected output should be:

Before "all to capital conversion"

British Columbia

Alberta

Saskatchewan

...

After "all to capital conversion"

BRITISH COLUMBIA

ALBERTA

SASKATCHEWAN

...


Solution

  • The following code:

    1. cleanly compiles
    2. performs the desired functionality
    3. note the proper method of accessing the data strings
    4. note the appropriate usage of the header file: ctype.h and function: toupper()
    5. unused variables, like char tempStr[256]; and all references to that variable are eliminated
    6. will result in a 'seg fault' event due to trying to modify data in readonly memory

    And now the faulty code:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    
    void LettersToCapital(char **string, size_t size);
    void ReadOutAOC(char **string, size_t size);
    
    int main( void )
    {
        char *canadianProvinces[] = 
        {
            "British Columbia", 
            "Alberta", 
            "Saskatchewan", 
            "Manitoba", 
            "Ontario", 
            "Quebec", 
            "New Brunswick", 
            "Nova Scotia", 
            "Prince Edward Island", 
            "Newfoundland", 
            "Yukon", 
            "Northwest Territories", 
            "Nunavut"
        };
    
        size_t numOfCanProv = sizeof(canadianProvinces) / sizeof(canadianProvinces[0]);
    
        printf("\nNumber of Canadian provinces %lu\n", numOfCanProv);
    
        // printing all provinces before conversion
        printf("\nBefore \"all to capital conversion\"\n\n");
        ReadOutAOC(canadianProvinces, numOfCanProv);    
    
        LettersToCapital(canadianProvinces, numOfCanProv);
        // Temp(canadianProvinces);
    
        // printing all provinces after conversion
        printf("\nAfter \"all to capital conversion\"\n");
        ReadOutAOC(canadianProvinces, numOfCanProv);
    }
    
    void ReadOutAOC(char **string, size_t size)
    {
        printf("\n");
    
        for( size_t i = 0; i < size; i++)
            printf("String outside the assignment method[%lu]: %s\n", i + 1, string[i] );
    }
    
    void LettersToCapital(char **string, size_t size)
    {
        for( size_t i = 0; i < size; i++)
        {
            size_t j;
            for( j = 0; j < strlen( string[i] ); j++)
            {
                string[i][j] = (char)toupper( string[i][j] );
            }
    
            printf("String inside the assignment method[%lu]: %s\n", i + 1, string[i] );
        }
    }
    

    Running the above code results in the following output:

    Before "all to capital conversion"
    
    
    String outside the assignment method[1]: British Columbia
    String outside the assignment method[2]: Alberta
    String outside the assignment method[3]: Saskatchewan
    String outside the assignment method[4]: Manitoba
    String outside the assignment method[5]: Ontario
    String outside the assignment method[6]: Quebec
    String outside the assignment method[7]: New Brunswick
    String outside the assignment method[8]: Nova Scotia
    String outside the assignment method[9]: Prince Edward Island
    String outside the assignment method[10]: Newfoundland
    String outside the assignment method[11]: Yukon
    String outside the assignment method[12]: Northwest Territories
    String outside the assignment method[13]: Nunavut
    Segmentation fault (core dumped)
    

    using gdb to step through the program shows the cause of the seg fault event is this line:

    string[i][j] = (char)toupper( string[i][j] );
    

    because it is trying to change a value/byte/char in readonly memory

    Suggest modifying the definition of the data to:

    #define MAX_PROV_NAME_LEN 50
    char canadianProvinces[][ MAX_PROV_NAME_LEN ] = 
        {
            "British Columbia", 
            "Alberta", 
            "Saskatchewan", 
            "Manitoba", 
            "Ontario", 
            "Quebec", 
            "New Brunswick", 
            "Nova Scotia", 
            "Prince Edward Island", 
            "Newfoundland", 
            "Yukon", 
            "Northwest Territories", 
            "Nunavut"
        };
    

    Then adjusting the rest of the code to match would correct the problem.

    The corrected code:

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <ctype.h>
    
    #define MAX_PROV_NAME_LEN 50
    
    void LettersToCapital(char string[][ MAX_PROV_NAME_LEN ], size_t size);
    void ReadOutAOC(char string[][ MAX_PROV_NAME_LEN ], size_t size);
    
    int main( void )
    {
        char canadianProvinces[][ MAX_PROV_NAME_LEN ] = 
        {
            "British Columbia", 
            "Alberta", 
            "Saskatchewan", 
            "Manitoba", 
            "Ontario", 
            "Quebec", 
            "New Brunswick", 
            "Nova Scotia", 
            "Prince Edward Island", 
            "Newfoundland", 
            "Yukon", 
            "Northwest Territories", 
            "Nunavut"
        };
    
        size_t numOfCanProv = sizeof(canadianProvinces) / sizeof(canadianProvinces[0]);
    
        printf("\nNumber of Canadian provinces %lu\n", numOfCanProv);
    
        // printing all provinces before conversion
        printf("\nBefore \"all to capital conversion\"\n\n");
        ReadOutAOC(canadianProvinces, numOfCanProv);    
    
        LettersToCapital(canadianProvinces, numOfCanProv);
        // Temp(canadianProvinces);
    
        // printing all provinces after conversion
        printf("\nAfter \"all to capital conversion\"\n");
        ReadOutAOC(canadianProvinces, numOfCanProv);
    }
    
    void ReadOutAOC(char string[][ MAX_PROV_NAME_LEN ], size_t size)
    {
        printf("\n");
    
        for( size_t i = 0; i < size; i++)
            printf("String outside the assignment method[%lu]: %s\n", i + 1, string[i] );
    }
    
    void LettersToCapital(char string[][ MAX_PROV_NAME_LEN ], size_t size)
    {
        for( size_t i = 0; i < size; i++)
        {
            size_t j;
            for( j = 0; j < strlen( string[i] ); j++)
            {
                string[i][j] = (char)toupper( string[i][j] );
            }
    
            printf("String inside the assignment method[%lu]: %s\n", i + 1, string[i] );
        }
    }
    

    a run of the corrected code results in the following output:

    Number of Canadian provinces 13
    
    Before "all to capital conversion"
    
    
    String outside the assignment method[1]: British Columbia
    String outside the assignment method[2]: Alberta
    String outside the assignment method[3]: Saskatchewan
    String outside the assignment method[4]: Manitoba
    String outside the assignment method[5]: Ontario
    String outside the assignment method[6]: Quebec
    String outside the assignment method[7]: New Brunswick
    String outside the assignment method[8]: Nova Scotia
    String outside the assignment method[9]: Prince Edward Island
    String outside the assignment method[10]: Newfoundland
    String outside the assignment method[11]: Yukon
    String outside the assignment method[12]: Northwest Territories
    String outside the assignment method[13]: Nunavut
    String inside the assignment method[1]: BRITISH COLUMBIA
    String inside the assignment method[2]: ALBERTA
    String inside the assignment method[3]: SASKATCHEWAN
    String inside the assignment method[4]: MANITOBA
    String inside the assignment method[5]: ONTARIO
    String inside the assignment method[6]: QUEBEC
    String inside the assignment method[7]: NEW BRUNSWICK
    String inside the assignment method[8]: NOVA SCOTIA
    String inside the assignment method[9]: PRINCE EDWARD ISLAND
    String inside the assignment method[10]: NEWFOUNDLAND
    String inside the assignment method[11]: YUKON
    String inside the assignment method[12]: NORTHWEST TERRITORIES
    String inside the assignment method[13]: NUNAVUT
    
    After "all to capital conversion"
    
    String outside the assignment method[1]: BRITISH COLUMBIA
    String outside the assignment method[2]: ALBERTA
    String outside the assignment method[3]: SASKATCHEWAN
    String outside the assignment method[4]: MANITOBA
    String outside the assignment method[5]: ONTARIO
    String outside the assignment method[6]: QUEBEC
    String outside the assignment method[7]: NEW BRUNSWICK
    String outside the assignment method[8]: NOVA SCOTIA
    String outside the assignment method[9]: PRINCE EDWARD ISLAND
    String outside the assignment method[10]: NEWFOUNDLAND
    String outside the assignment method[11]: YUKON
    String outside the assignment method[12]: NORTHWEST TERRITORIES
    String outside the assignment method[13]: NUNAVUT