简体   繁体   中英

How to access elements of array when parameter is double pointer?

First function changes big letter to small. In main I need to have five strings and with function konvertuj I have to go through that array and check for each letter if it's big and convert it to small. The point is that I don't know how to access each character of string in the function. (It's study example so it has to be done with these predefined functions.

char v2m(char z){
        char m = z + 0x20;
        return m;
    }
    void konvertuj(char **niz, int n){
        for (int i = 0; i < n; i++)
            if(*niz[i] > 'A' && *niz[i] < 'Z')
            *niz[i] = v2m(*niz[i]);
    }
    int main(){
        char **niz;
        niz[0] = "Voda";
        niz[1] = "KraISSa";
        niz[2] = "somsssR";
        niz[3] = "aaaaa";
        niz[4] = "WeWeWeW";
        for (int i = 0; i < 5; i++)
        {
            int d = -1;
            while(niz[i][++d]);
            konvertuj(&niz[i], d);
            printf("%s ", niz[i]);
        }
    }
  1. v2m - no need of the variable m

  2. konvertuj no need to iterate through the same letters all over again. You want to convert 1 letter as you iterate in main . Your condition is wrong as you will ignore 'A' and 'Z'

  3. Pointer to pointer does not have allocated space to accommodate 5 pointers. You need to allocate this space. In your code is it UB.

3.a You assign the pointers to the string literals. Attempt to modify the string literal invokes Undefined Behaviour. In my code, I use compound literals which are modifiable.

3.b use correct type for indexes ( size_t ).

char v2m(char z){
    return z + 0x20;
}
void konvertuj(char *niz, size_t n){
    if(niz[n] >= 'A' && niz[n] <= 'Z')
        niz[n] = v2m(niz[n]);
}

int main(void){
    char **niz = malloc(5 * sizeof((*niz)));
    niz[0] = (char[]){"Voda"};
    niz[1] = (char[]){"KraISSa"};
    niz[2] = (char[]){"somsssR"};
    niz[3] = (char[]){"aaaaa"};
    niz[4] = (char[]){"WeWeWeW"};
    for (size_t i = 0; i < 5; i++)
    {
        size_t d = 0;
        while(niz[i][d])
            konvertuj(niz[i], d++);
        printf("%s ", niz[i]);
    }
}

As I( understand you need to keep the function names types and parameters

Sure, it is possible to write it the way you did, but you have to build things accordingly and you did not.

And there some errors, anyway. I will try to show you some points. Please be patient.

        niz[0] = "Voda";
        niz[1] = "KraISSa";
        niz[2] = "somsssR";
        niz[3] = "aaaaa";
        niz[4] = "WeWeWeW";

In C in general you can not just assign a value to a string. You use something like

    strcpy( niz[0], "ANewValue");

v2m()

Instead of

    char v2m(char z){
        char m = z + 0x20;
        return m;
    }

You can just write

    char v2m(char z) { return z + 0x20; }

There is no need to declare a char just for holding the return value.
IIUC you can not change the function prototypes...

konvertuj()

    void konvertuj(char **niz, int n){
        for (int i = 0; i < n; i++)
            if(*niz[i] > 'A' && *niz[i] < 'Z')
            *niz[i] = v2m(*niz[i]);
    }

here I believe you missed the point that the function will receive just a string each time is called, not the whole vector of strings.
The int n is just the length of the string, and even if you could not use strlen() to compute it, it should probably be done inside the function. but I believe you can not change this prototype also.

  • note that you are not including 'A' and 'Z' in your test. Maybe you should use '>=' and '<=' in the tests.
  • since the function gets a single string each call, you must remove many of the '*'
  • note that you had the strings declared as literals, CONSTANTS that you can not change. The first time you try to change a letter your program will abort

This one below should work and you can compare:

void konvertuj(char *niz, int n)
{
        for (int i = 0; i < n; i++)
            if(niz[i] >= 'A' && niz[i] <= 'Z')
                niz[i] = v2m(niz[i]);
}

main()

Instead of

        char **niz;
        niz[0] = "Voda";
        niz[1] = "KraISSa";
        niz[2] = "somsssR";
        niz[3] = "aaaaa";
        niz[4] = "WeWeWeW";

You could write just

        char    niz[][15] = 
        {
            "Voda",
            "KraISSa",
            "somsssR",
            "aaaaa",
            "WeWeWeW"
        };

In C just the last dimension must be declared, eg the '15' above, since the compiler can determine here the other dimension. And this way you can initialize all of them directly. But in order to change one in the code you need to use a loop and replace letter by letter, or call

        strcpy( niz[0], "ANewValue");

Also you can initialize just a few of them, and even out of order, as in

        char    niz[8][15] = 
        {
            [2] = "somsssR",
            [3] = "aaaaa",
            [5] = "AomsssZ",
            [6] = "A",
            [0] = "Voda",
            [1] = "KraISSa",
            [4] = "WeWeWeW",
            [7] = ""
        };

and this is really handy.

Computing the number of strings

Note that you can compute the number of strings and even assign "TheLastValue" to the last one, by writing

        int n = sizeof(niz)/sizeof(niz[0]); // total of strings
        strcpy( niz[n-1], "TheLastValue"); // changes the last string
        printf("Total of %llu values\n", sizeof(niz)/sizeof(niz[0]));

What if char** niz is needed?

This is a really common paradigm in C and C++, and it is very useful. You may recall that the prototype for main() is

    int main( int argc, char** argc) 

for every C program, so you can see how common it is.

Fact is that when you declare niz as char** you are declaring a single variable. What is niz? Well, it is a pointer to a pointer to a char.

  • niz is char**
  • *niz is char*
  • **niz is a single char

But niz is a pointer and it is pointing to nowhere when declared. In your case you want niz pointing to not one but FIVE strings. And if you do nothing the program will abort the first time you try to use it...
You need to build this:
A string is a pointer to the first char , char*

  • so niz needs to point to an area capable of holding 5 pointers to char
  • What about the size of a pointer to char ? That is easy: sizeof(char*)
  • Then you need to make each niz[x] point to the required string.
  • It will not happen by itself. You must build each and every one.

An example: building char** sample from niz as declared above

The code below builds sample as an array of pointers, pointing to the same strings declared and allocated for niz[][] above, and them prints them all

        // building an array of pointers to the strings in niz
        char**      sample = NULL; // ok, points to nothing
        unsigned    area = n * sizeof(char*); // n pointers to strings

        sample = (char**) malloc(area);
        for( int i=0; i<n; i+=1)
            sample[i] = niz[i];

        printf("\n=>\tPrinting the %d strings using the pointers\n", n );
            for( int i=0; i<n; i+=1)
                printf("%2d: '%s'\n", i, sample[i]);

Note that the strings already exists and we are just pointing to them. A new reference only.

A new example: building char** copy as a full copy of niz

It is very very important to see the difference here: copy points to an array of pointers, but each pointer points to a copy of the corresponding string declared in niz and referenced in the sample array.

        // building 'copy' as an array of pointers to the strings once in niz
        char**      copy = NULL; // ok, points to nothing
        copy =      (char**) malloc(area);
        for( int i=0; i<n; i+=1)
        {
            copy[i] = (char*) malloc( (1 + sizeof(niz[i])) * sizeof(char) );
            // large enough to hold it
            int j = 0; // copy each letter
            for ( ; niz[i][j] != 0; j+=1 ) copy[i][j] = niz[i][j];
            copy[i][j] = 0; // this terminates the string
        }

        printf("\n=>\tPrinting the %d copies using the pointers\n", n );
            for( int i=0; i<n; i+=1)
                printf("%2d: '%s'\n", i, copy[i]);

Note: it is in general not recommended to cast the pointers return by malloc() in C, as in the lines

        copy =      (char**) malloc(area);
// or
        copy[i] = (char*) malloc( (1 + sizeof(niz[i])) * sizeof(char) ); 

I just do not care and prefer to write all them down explicitly, as a reminder to myself of what is what. Sure, in C++ you must declare this, but is is rare to allocate memory this way in C++ since C++11. Fell free to use whatever rule you see fit

A complete example

This is the output

PS C:\src\ifdef> gcc -o teste  -Wall -Wextra -Wpedantic -std=c17 so210126.c
PS C:\src\ifdef> ./teste
Total of 8 values  
Before: Voda (4)   
After: voda        
Before: KraISSa (7)
After: kraissa     
Before: somsssR (7)
After: somsssr     
Before: aaaaa (5)  
After: aaaaa       
Before: WeWeWeW (7)
After: wewewew
Before: AomsssZ (7)
After: aomsssz
Before: A (1)
After: a
Before: TheLastValue (12)
After: thelastvalue

=>      Printing the 8 strings using the pointers
 0: 'voda'
 1: 'kraissa'
 2: 'somsssr'
 3: 'aaaaa'
 4: 'wewewew'
 5: 'aomsssz'
 6: 'a'
 7: 'thelastvalue'

=>      Printing the 8 copies using the pointers
 0: 'voda'
 1: 'kraissa'
 2: 'somsssr'
 3: 'aaaaa'
 4: 'wewewew'
 5: 'aomsssz'
 6: 'a'
 7: 'thelastvalue'
PS C:\src\ifdef>

I compiled just on gcc 10.2 on Windows.

This is the example code

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

char v2m(char z) { return z + 0x20; }

void konvertuj(char *niz, int n)
{
        for (int i = 0; i < n; i++)
            if(niz[i] >= 'A' && niz[i] <= 'Z')
                niz[i] = v2m(niz[i]);
}

int main(void)
{
        char    niz[8][15] = 
        {
            [2] = "somsssR",
            [3] = "aaaaa",
            [5] = "AomsssZ",
            [6] = "A",
            [0] = "Voda",
            [1] = "KraISSa",
            [4] = "WeWeWeW",
            [7] = ""
        };

        int n = sizeof(niz)/sizeof(niz[0]); // total of strings
        strcpy( niz[n-1], "TheLastValue"); // changes the last string
        printf("Total of %llu values\n", sizeof(niz)/sizeof(niz[0]));

        for (int i = 0; i < n; i++)
        {
            int d = 0;
            while ( niz[i][d] != 0) d+=1;
            printf("Before: %s (%d)\n", niz[i], d);
            konvertuj( niz[i], d );
            printf("After: %s\n", niz[i]);
        }

        // building an array of pointers to the strings in niz
        char**      sample = NULL; // ok, points to nothing
        unsigned    area = n * sizeof(char*); // n pointers to strings

        sample = (char**) malloc(area);
        for( int i=0; i<n; i+=1)
            sample[i] = niz[i];

        printf("\n=>\tPrinting the %d strings using the pointers\n", n );
            for( int i=0; i<n; i+=1)
                printf("%2d: '%s'\n", i, sample[i]);

        // building 'copy' as an array of pointers to the strings once in niz
        char**      copy = NULL; // ok, points to nothing
        copy =      (char**) malloc(area);
        for( int i=0; i<n; i+=1)
        {
            copy[i] = (char*) malloc( (1 + sizeof(niz[i])) * sizeof(char) ); // large enough to hold it
            int j = 0; // copy each letter
            for ( ; niz[i][j] != 0; j+=1 ) copy[i][j] = niz[i][j];
            copy[i][j] = 0; // this terminates the string
        }

        printf("\n=>\tPrinting the %d copies using the pointers\n", n );
            for( int i=0; i<n; i+=1)
                printf("%2d: '%s'\n", i, copy[i]);

        for( int i=0; i<n; i+=1) free(copy[i]); // destroy each string
        free(copy); // destroy the array;
        free(sample); // sample points to static memory...
                
        return 0;
}

As for the very very long post: I wanted to leave an example of the solution plus an example of the steps involved in order to build a char** vector from existing strings and as an independend copy.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM