user1611172
user1611172

Reputation: 45

strcpy and printf a multidimensional char array C

Say I have an array

char messages[10][2][50];

What is the correct syntax for strcpy, in order to get the data into one of the strings (inner most char array of size 50) and then the corresponding convention to supply it to printf via %s?

For that matter, am I declaring the array subscripts in the correct order? It is intended to be 10 lots of, pairs (of 2) strings. Each string being 50 chars wide.

01{{50 chars},{50 chars}}
02{{50 chars},{50 chars}}
...
09{{50 chars},{50 chars}}
10{{50 chars},{50 chars}}

Various internet sources seem to conflict on which subscript to omit and, whatever I try seems to produce unintended results.

e.g. Could you fill in the blanks to the following

strcpy(message???, "Message 1 Part 1");
strcpy(message???, "m1 p2");
strcpy(message???, "m2 p1");
strcpy(message???, "m2 p2");
strcpy(message???, "m3 p1");
strcpy(message???, "m3 p1");
//So on...

int i;
for(i=0;i<10;i++)
    printf("%s, %s\n", message???, message???);

Such that the array has a structure of and holds:

01{{"Message 1 Part 1\0"},{"m1 p2\0"}}
02{{"m2 p1\0"},{"m2 p2\0"}}
01{{"m3 p1\0"},{"m3 p2\0"}}
//So on...

And outputs as such

Message 1 part 1, m2 p2

m2, p2

m3, p3

and so on

Upvotes: 2

Views: 4362

Answers (5)

Ben Stephens
Ben Stephens

Reputation: 563

I just wrote a quick program to show the things you've asked about... loading them up at declaration, strncpy into one of them, and then printing them out.

Hope it helps

edit: I kind of hate magic numbers so I almost totally removed them
edit: I've added alternatives Tommi Kyntola and I were talking about in the comments

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

// safe string copy macro, terminates string at end if necessary
// note: could probably just set the last char to \0 in all cases
// safely if intending to just cut off the end of the string like this

#define sstrcpy(buf, src, size) strncpy(buf, src, size); if(strlen(src) >= size) buf[size-1] = '\0';

#define MSGLIMIT 10
#define MSGLENGTH 30
#define MSGFIELDS 2
#define MSGNAME 0
#define MSGTEXT 1


int main(void) {
    char messages[MSGLIMIT][MSGFIELDS][MSGLENGTH] = { {"bla", "raa"},
                                                      {"foo", "bar"}
                                                    };
    int i;

    char *name1 = "name16789012345678901234567890";
    char *text1 = "text16789012345678901234567890";

    char *name2 = "name26789012345678901234567890";
    char *text2 = "text26789012345678901234567890";

    char *name3 = "name36789012345678901234567890";
    char *text3 = "text36789012345678901234567890";


    // doesn't set last char to \0 because str overruns buffer
    // undocumented result of running this, but likely to just get the name2 string
    // as that'll be the very next thing in memory on most systems

    strncpy(messages[2][MSGNAME], name1, MSGLENGTH); // 2 because it's the next empty one
    strncpy(messages[2][MSGTEXT], text1, MSGLENGTH);

    // alternative suggested by Tommi Kyntola
    // printf family are more complicated and so cost more cpu time than strncpy
    // but it's quick and easy anywhere you have string.h and fine most of the time

    snprintf(messages[3][MSGNAME], MSGLENGTH, "%s", name2);
    snprintf(messages[3][MSGTEXT], MSGLENGTH, "%s", text2);

    // uses the define macro at the top of the page to set the last char to \0 if
    // otherwise not set by strncpy, adds a little weight but still the better option
    // if performance of this section of code is important

    sstrcpy(messages[4][MSGNAME], name3, MSGLENGTH);
    sstrcpy(messages[4][MSGTEXT], text3, MSGLENGTH);


    for(i = 0; i < 5; i++) // 5 because that's how many I've populated
            printf("%s : %s\n", messages[i][MSGNAME], messages[i][MSGTEXT]);

    return 0;
}

Upvotes: 1

Bharat Sharma
Bharat Sharma

Reputation: 3966

It will be

strcpy(message[0][0], "Message 1 Part 1");
strcpy(message[0][1], "m1 p2");
strcpy(message[2][0], "m2 p1");
strcpy(message[2][1], "m2 p2");
strcpy(message[3][0], "m3 p1");
strcpy(message[3][1], "m3 p2");


for(i=0;i<10;i++)
    printf("%s, %s\n", message[i][0], message[i][1]);

try to get the concept.

Upvotes: 0

Josh Greifer
Josh Greifer

Reputation: 3231

As has been pointed out, best to use strncpy, or as in my example below, use asserts, to prevent possible buffer overruns. There's a tiny increase in performance in strcpy vs strncpy.

    #define FIRST_OF_PAIR 0
#define SECOND_OF_PAIR 1

    int message_num = 7;

    char messages[10][2][50];

    char *string = "hello";

    assert(strlen(string) < 50);
    assert(message_num > 0 &&  message_num < 10);

    strcpy(messages[message_num][SECOND_OF_PAIR], "Hello");

    printf("%s", messages[message_num][SECOND_OF_PAIR]);

Upvotes: 0

0x90
0x90

Reputation: 41002

I would use:

assuming you want to copy to char* new_buff

memcpy(new_buff, messages, 10*2*50);

you can do a 3 nested loop and use strncpy.

don't use strcpy... it is unsecured

Upvotes: 0

spin_eight
spin_eight

Reputation: 4025

You can ommit greatest subscription(in you ex. it is 10), as it can be calculated by compiler according to remained subscriptions. To pass layers of 50 elements use pointers: (*messages)[10][2] - will be pointer on layer of 50 elements

Upvotes: 0

Related Questions