2

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

Community
  • 1
  • 1
user1611172
  • 45
  • 1
  • 6
  • 1
    If you have a fixed-length array, `strcpy` looks like suicide. Use `strncpy` if at all, or just `memcpy` the whole thing. – Kerrek SB Aug 21 '12 at 08:47
  • Had not looked up the difference of those until now. Thank you to everyone that answered. I learnt something from each and every one of those answers. – user1611172 Aug 21 '12 at 11:13

5 Answers5

1

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;
}
Ben Stephens
  • 563
  • 3
  • 8
  • strncpy usually needs some extra handling. If the message length is reached it leaves the null terminator out! Which in most cases is not what the programmer wants. Either make sure the null terminator is there or use snprintf(buf,"%s",str) which terminates even when it truncates. – Tommi Kyntola Aug 21 '12 at 09:46
  • Yah this is true actually, with strncpy need to check if your string is bigger than your destination and deal with it if it is (just set the last char to '\0' simple solution). snprintf(buf, size, "%s", str) will do that work for you. However, if this is something your program is going to repeat many times then there's probably a performance gain in using strncpy and wrapping that check in a define macro or inline function – Ben Stephens Aug 21 '12 at 10:07
  • Thanks Tommi, I've added some of this to the example. – Ben Stephens Aug 21 '12 at 10:34
  • Thank you. That is an exceptionally thorough answer. – user1611172 Aug 21 '12 at 11:10
0

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

spin_eight
  • 3,925
  • 10
  • 39
  • 61
0

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

0x90
  • 39,472
  • 36
  • 165
  • 245
  • Perhaps I worded my question incorrectly. I have the above array of strings (3D char array) and need to populate that given individual strings (or 1D char arrays). Would a similar approach work? – user1611172 Aug 21 '12 at 08:59
0

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]);
Josh Greifer
  • 3,151
  • 24
  • 25
  • I think probably best not to put assert into production code. Instead better to use strncpy and then check the return value to see whether less chars were copied than expected – Ben Stephens Aug 21 '12 at 09:02
  • Yes, one usually places asserts in debug builds only, typically using #ifdef DEBUG around them – Josh Greifer Aug 21 '12 at 09:05
  • Actually asserts are typically compiled out using the NDEBUG define prior to including assert.h – Tommi Kyntola Aug 21 '12 at 09:44
0

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.

Bharat Sharma
  • 3,926
  • 2
  • 17
  • 29