Allegro.cc - Online Community

Allegro.cc Forums » Programming Questions » C string questions

This thread is locked; no one can reply to it. rss feed Print
C string questions
KeithS
Member #11,950
May 2010

Hello once more, been a while.

Picking up coding again, and I have forgotten a lot. So, before anything, apologies for what might be noob questions; I'm just getting to grips with C style strings again. I have not been having too much problem getting reacquainted generally, but certainly am unsure of a few "quirks" regarding strings. There are two topics I want to address here, so I thank you for your patience beforehand.

FIRST TOPIC: DYNAMIC MULTIDIMENSION STRINGS.

What I am looking for here is more of a confirmation that I am doing this the right way. So, I write a program that might have a requirement for an array of strings. Here is a snippet of the first solution;

#SelectExpand
1 2int main(void) 3{ 4 char *strings[4] = {'\0'}; 5 6 string_database[0] = "Florida"; 7 string_database[1] = "Oregon"; 8 string_database[2] = "California"; 9 string_database[3] = "Georgia"; 10 return 0; 11}

Yeah, this works. The array can be printf'ed, passed to functions, and that. But, I assume because it is a string literal (*) declared with no more than the size of the array, it is not very "flexible", so to speak (my understanding so far is that this would be stored in an area of the memory which is read only, to the program). I cannot do many <string.h> operations on this, or modify the string. In other words, not the best solution, and certainly does not satisfy the requirement (ie, an array of strings that can be used with <string.h> functions)...

I worked this out instead, which I am much happier with...

#SelectExpand
1 2int maid(void) 3{ 4 int i_array_size = 5; 5 int i_string_buffer = 50; 6 int i = 0; 7 8 char **string_array = (char **)malloc(i_array_size * sizeof(char *)); 9 10 for (i = 0; i < i_array_size; i++) 11 { 12 *(string_array + i) = (char *)malloc(i_string_buffer * sizeof(char)); 13 } 14 15 16 strcpy(string_array[0], "Boris Yeltsin"); 17 strcpy(string_array[1], "Stone Henge"); 18 strcpy(string_array[2], "Spotted Hyena"); 19 strcpy(string_array[3], "Ford Maverick"); 20 strcpy(string_array[4], "Humbolt Current"); 21 22 // Then use it, modify, pass, etcetera, no issues... 23 24 // Free memory at end... 25 26 for (i = 0; i < i_array_size; i++) 27 { 28 free(*(string_array + i)); 29 } 30 31 free(string_array); 32 33 return 0; 34}

Also, as I want it to be dynamic, it can easily be reallocated thus during program execution...

#SelectExpand
1 // Original array_size = 5; 2 3 for (i = 0; i < i_array_size; i++) 4 { 5 free(*(string_array + i)); 6 } 7 8 array_size = 10; 9 10 (char **)realloc(string_array, i_array_size * sizeof(char *)); 11 // No need for "**" before string_array, as that was only used in originally declaring the pointer. 12 // If used here, it would signify a double dereference on a pointer to a pointer, which is not the intention. 13 // The char ** cast deals with this facet of the issue, anyway... 14 15 for (i = 0; i < i_array_size; i++) 16 { 17 *(string_array + i) = (char *)malloc(i_string_buffer * sizeof(char)); 18 } 19 20 strcpy(string_array[0], "Boris Yeltsin"); 21 strcpy(string_array[1], "Stone Henge"); 22 strcpy(string_array[2], "Spotted Hyena"); 23 strcpy(string_array[3], "Ford Maverick"); 24 strcpy(string_array[4], "Humbolt Current"); 25 strcpy(string_array[5], "Vitamin A"); 26 strcpy(string_array[6], "Angle Falls"); 27 strcpy(string_array[7], "Blue Turnip"); 28 strcpy(string_array[8], "Taj Mahal"); 29 strcpy(string_array[9], "Loch Ness Monster"); 30 31 //..

I left in my original commenting to be corrected, just in case I am way off the mark.

Okay, so it works to my satisfaction, and produces no compiler warnings or errors. I can create a dynamic string array from a text file of strings, even, and the like.

What I would like to know here, please, is; Am I making any serious blunder by doing this? Is there a more "standard" or better way of achieving the same objective, or will this suffice?

SECOND TOPIC: WHAT IS WITH STRCPY()?

There is something happening here that I do not get...

#SelectExpand
1int main(void) 2{ 3 char buffer[250]; 4 char buffer_two[5]; 5 char buffer_three[250]; 6 7 printf("Enter a string.\n"); 8 gets(buffer); 9 10 printf("You wrote: %s\n", buffer); 11 12 //strcpy(buffer_three, buffer); 13 14 int i = 0; 15 for(i = 0; i < 5; i++) 16 { 17 strcpy(buffer_three, buffer); 18 sprintf(buffer_two, "%i). ", i + 1); 19 strcat(buffer_two, buffer_three); 20 21 printf("%s\n", buffer_two); 22 } 23 return 0; 24}

If I enter eg; "This is a string", the output of this is...

1). This is a string
2). his is a string
3). is is a string
4). s is a string
5). is a string

I narrowed that down to strcpy(). For some reason I don't understand, in the for loop the pointer to the first character of the string advances according to the loop. I have not made any code to instruct it to do this.

On the other hand, if I do this (note, strcpy() now outside of the loop)...

#SelectExpand
1int main(void) 2{ 3 char buffer[250]; 4 char buffer_two[5]; 5 char buffer_three[250]; 6 7 printf("Enter a string.\n"); 8 gets(buffer); 9 10 printf("This is the string: %s\n", buffer); 11 12 strcpy(buffer_three, buffer); 13 14 int i = 0; 15 for(i = 0; i < 5; i++) 16 { 17 //strcpy(buffer_three, buffer); 18 sprintf(buffer_two, "%i). ", i + 1); 19 strcat(buffer_two, buffer_three); 20 21 printf("%s\n", buffer_two); 22 } 23 return 0; 24}

... the output is as expected...

1). This is a string
2). This is a string
3). This is a string
4). This is a string
5). This is a string

I cannot for the life of me see why this happens. Why does the pointer advance if strcpy() is inside the loop? Is this some undocumented behavior of strcpy()?

Again, thanks for your patience! All the best.

PS: Using MinGW, on Windows 10, with CODE::BLOCKS IDE.

PPS: My sincere apologies, I unwittingly posted this twice, first an incomplete version.

* * * * * * * * * * *

My Noobish Blog

relpatseht
Member #5,034
September 2004
avatar

Your first topic is using strings correctly. No real problems. There are different ways to do the same thing, but they're all functional.

The second is broken because buffer_two is only of length 5. You're concatenating buffer_three to buffer_two. The result won't fit, so it's overwriting memory.

KeithS
Member #11,950
May 2010

FACEPALM! :D

Thanks again, relpatseht!

* * * * * * * * * * *

My Noobish Blog

bamccaig
Member #7,536
July 2006
avatar

As a rule, you should prefer to use functions that can be told buffer sizes so they'll guard against an overflow like that. Those kinds of overflows are the kinds of things that can lead to vulnerabilities in your software. strncpy and strncat will use the size passed to them to prevent overflowing.

Note, the number passed to strncat is not the size of the destination buffer, but the number of characters that can be appended to it. I think you'd have to calculate that as (destination_size - strlen(destination) - 1). Unfortunately, it doesn't seem to indicate errors at all so if you wanted to handle an error you'd have to detect that yourself:

if(strlen(destination) + strlen(source) >= destination_size) {
    ...
}

Otherwise, your resulting string will just be truncated.

raynebc
Member #11,908
May 2010

I don't know why they thought it was a good idea to design strncat that way when the other strn... functions accepted a buffer size parameter.

bamccaig
Member #7,536
July 2006
avatar

I can think of two explanations:

1. Perhaps they assumed that the caller may need to do these calculations anyway and doing them twice would be wasteful.

2. Perhaps it wasn't so much a design decision as it was an implementation detail leaking through.

Chris Katko
Member #1,881
January 2002
avatar

Sidenote: That's why D uses ranges, instead of iterators. A range is two pointers, the beginning and end. Whereas in many places in C++ you have to pass two separate iterators when really, you're talking about the same "object"--so why not bundle them together?

"On iteration"

"Iterators must go" [PDF]

-----sig:
“Programs should be written for people to read, and only incidentally for machines to execute.” - Structure and Interpretation of Computer Programs
"Political Correctness is fascism disguised as manners" --George Carlin

KeithS
Member #11,950
May 2010

bamccaig said:

Note, the number passed to strncat is not the size of the destination buffer, but the number of characters that can be appended to it. I think you'd have to calculate that as (destination_size - strlen(destination) - 1). Unfortunately, it doesn't seem to indicate errors at all so if you wanted to handle an error you'd have to detect that yourself:

if(strlen(destination) + strlen(source) >= destination_size) {
...
}

Thank you, this is all good stuff! That, combined with the dynamic string, would allow you to quietly do a quick routine to resize the destination string and fix the problem.

Nice!

EDIT:

Hmmm. Thinking about that, it is probably not a good idea to realloctae memory for just one element of a dynamic array; it would probably overwrite part of the next, contiguous element. I'm asking for all sorts of problems...

* * * * * * * * * * *

My Noobish Blog

Go to: