strcpy and printf a multidimensional char array C - 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

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;
}

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

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

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]);

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.

Related

Create a string that contains \0 in the middle

I have:
char *var1 = "foo";
char *var2 = "bar";
and I want to create this string: "foo\0bar\0"
How can I do that? I tried this but of course it does not work:
sprintf(buffer, "%s\0%s", var1, var2);
You have two problems here:
Putting \0 (aka NUL) in the middle of any string is legal, but it also means all C string APIs will consider the string as ending early; every C-style string ends with NUL, and there's no way to tell the difference between a new NUL you added and the "real NUL", because it has to assume the first NUL encountered is the end of the string (reading further could read uninitialized memory, or read beyond the end of the array entirely, invoking undefined behavior). So even if you succeed, C APIs that work with strings will never see bar. You'd have to keep track of how long the "real" string was, and use non-string APIs to work with it (sprintf does return how many characters it printed, so you're not operating completely blind).
Trying to put the \0 in the format string itself means that sprintf thinks the format string ends there; from its point of view, "%s\0%s" is exactly the same as "%s", it literally can't tell them apart.
You can work around problem number 2 by inserting the NUL with a format code that inserts a single char (where NUL is not special), e.g.:
sprintf(buffer, "%s%c%s", var1, '\0', var2);
but even when you're done, doing printf("%s", buffer); will only show foo (because the embedded NUL is where scanning stops). The data is there, and can be accessed, just not with C string APIs:
#include <stdio.h>
int main(int argc, char **argv) {
char *var1 = "foo";
char *var2 = "bar";
char buffer[10] = "0123456789";
sprintf(buffer, "%s%c%s", var1, '\0', var2);
for (int i = 0; i < sizeof(buffer); ++i) {
printf("'%c': %hhd\n", buffer[i], buffer[i]);
}
return 0;
}
Try it online!
which outputs:
'f': 102
'o': 111
'o': 111
'': 0
'b': 98
'a': 97
'r': 114
'': 0
'8': 56
'9': 57
The empty quotes contain a NUL byte if you look at the TIO link, but lo and behold, my browser stops the copy/paste at the NUL byte (yay C string APIs), so I can't actually copy it here.
This is a fairly common problem when dealing with binary data.
If you want to manipulate binary data, don't use the string tools of strcat, strcpy, etc., because they use null-termination to determine the length of the string.
Instead use the memcpy library routine that requires you to specify a length. Keep track of every binary string as a pointer and a length.
char *var1="foo";
unsigned len1 = 3;
char *var2="bar";
unsigned len2 = 3;
/* write var1 and var2 to buffer with null-separation */
/* assuming buffer is large enough */
char buffer[10];
unsigned len_buffer = 0;
/* write var1 to start of buffer */
memcpy(buffer, var1, len1);
len_buffer = len1;
/* append null */
buffer[len_buffer++] = '\0';
/* append var2 */
memcpy(buffer+len_buffer, var2, len2);
len_buffer += len2;
Not particulary fast or short, but this should do the job
strcpy (buffer, var1);
strcat (buffer+strlen(var1)+1, var2);

Pointers and char arrays from strings

Hi I have been reading for hours and still can't grasp the conversions between
{
char i ="adf";
char foo[];
char bar[256];
}
and adding * and & makes it more confusing
I have some code that is working.
int TX_SEND(char send[])
{
unsigned char *p_tx_buffer;
p_tx_buffer = &send[0];
strcat(send, "\r");
// Write to the port
int n = write(fd,&send[0],3);
if (n < 0) {
perror("Write failed - ");
return -1;
}
return(0);
}
code is working but I need help with 2 parts.
I want to be able to run this function like kind of like printf IE TX_SEND("AT+CGMSD=STUFF"); but I am stuck
but before hand I do this alot.
char txsend[] = "at";
TX_SEND(txsend);
Also inside my TX_WRITE() I am using write(fd,&send[0],3), but it is hardcoded to send 3 bytes from send[]. I want this to be dynamic so I can just send strings at any length (realistically they will be less than 300 ASCII chars always). I tried to do something with a pointer in there but gave up (*p_tx_buffer was my beginning attempt).
i think you want
int TX_SEND(char *send)
{
int n = write(fd,send,strlen(send));
if (n < 0) {
perror("Write failed - ");
return -1;
}
return(0);
}
you cannot tack on \n to send with strcat. I would add it in the calling function, or declare an intermediate buffer and sprintf to it
like this
int TX_SEND(char *send)
{
char buff[50]; // i dont know a good max size
snprintf(buff, sizeof(buff), "%s\n", send);
int n = write(fd,buff,strlen(buff));
if (n < 0) {
perror("Write failed - ");
return -1;
}
return(0);
}
I'm not going to go through your code line-by-line, but I urge you to focus on these facts:
chars are chars and strings are strings, and never the twain shall meet. (They're totally different.)
'x' is a character constant.
"x" is a string constant.
A string is an array of characters (terminated by '\0').
When you mention an array (including a string) in a context where you need its value, what you get is a pointer to the array's first element.
When you put a & in front of something, what you get is a pointer to that something.
When you put a * in front of a pointer, what you get is the thing that the pointer points to.
Putting this together, we could write
char str[] = "xyz";
char *p = str; /* per rule 5, this is fine, and p gets a pointer to str's first element */
char c = *p; /* per rule 7, c gets the first character of str, which is 'x' */
printf("%c\n", c);
If you're just starting with C, you may not have come across rule 5 yet. It will probably surprise you at first. Learn it well, though: you'll never understand arrays and pointers in C without it.

simple string counter program debugging (pointers)

I am new to C programming and pointers.
I made a simple program where I can read in the string and the program tells you how many characters are there and how many alphabets had appeared how many times.
Somehow, my output is not right. I think it might be my pointer and dereferencing problem.
here is my main:
extern int* count (char* str);
int main(int argc, char* argv[])
{
int numOfChars =0;
int numOfUniqueChars = 0;
char str[80];
int *counts;
strcpy(str, argv[1]);
printf("counting number of characters for \"%s\"..\n", str);
printf("\n");
counts = count(str);
int j;
for (j =0; j<sizeof(counts); j++)
{
if(counts[j])
printf("character %c", *str);
printf("appeared %d times\n", counts[j]);
numOfChars++;
numOfUniqueChars++;
}
printf("\"%s\" has a total of %d character(s)\n", str, numOfChars);
printf(wow %d different ascii character(s) much unique so skill\n", numOfUniqueChars);
}
and this is my count function:
int* count(char* str)
{
int* asctb = malloc(256);
int numOfChars =0;
int i;
int c;
for(i = 0; i<strlen(str); i++)
c = str[i];
asctb[c]++;
numOfChars += strlen(str);
return asctb;
}
and when I compile and run it, my result comes up like this:
./countingCharacter doge
counting number of characters for "doge"...
appeared 0 times
appeared 0 times
appeared 0 times
appeared 0 times
"doge" has a total of 4 character(s)
wow 4 different ascii character(s) much unique so skill
But, I want my result to be like this:
Character d appeared 1 times
Character e appeared 1 times
Character g appeared 1 times
Character o appeared 1 times
"doge" has a total of 4 character(s)
wow 4 different ascii character(s) much unique so skill
Any help will be much appreciated.
Thanks in advance!
EDIT:
i added curly braces for my for loop in the main function.
now i get this result:
./countingCharacter doge
character # appeared 7912 times
character d appeared 1 times
character e appeared 1 times
character g appeared 1 times
character o appeared 1 times
why do I get that "#" in the beginning??
As #kaylum said, one particularly large issue is your use of braces. If you don't use braces with a control flow statement (if, for, while, etc.), only the next line is counted as a part of that statement. As such, this segment:
if (counts[j])
printf("character %c", *str);
printf("appeared %d times\n", counts[j]);
/* ... */
...will only execute the first printf if counts[j] != 0, but will unconditionally execute the following statements.
Your use of malloc is also incorrect. malloc(256) will only allocate 256 bytes; one int is generally 4 bytes, but this differs based on the compiler and the machine. As such, when malloc'ing an array of any type, it's good practice to use the following technique:
type *array = malloc(element_count * sizeof(type));
In your case, this would be:
int *asctb = malloc(256 * sizeof(int));
This ensures you have room to count all the possible values of char. In addition, you'll have to change the way you iterate through counts as sizeof (counts) does not accurately represent the size of the array (it will most likely be 4 or 8 depending on your system).
The variable numOfChars will not behave the way you expect it to. It looks to me like you're trying to share it between the two functions, but because of the way it's declared this will not happen. In order to give global access to the variable, it needs to be declared at global scope, outside of any function.
Also, the line:
printf("character %c ", *str);
...neither keeps track of what characters you've printed nor which you're supposed to, instead just repeatedly printing the first character. *str should be (char)j, since you're printing ASCII values.
That ought to do it, I think.
If you are new to C, there are a number of issues in your code you need to pay attention to. First, if a function returns a value, validate that value. Otherwise, from that point in your code on, you can have no confidence that it is actually operating on the value or memory location you think it is. For example, each of the following should be validated (or changed to stay within allowable array bounds):
strcpy(str, argv[1]);
int* asctb = malloc(256);
counts = count(str);
What if argv[1] had 100 chars? What if malloc returned NULL? How do you know count succeeded? Always include the necessary validations needed by your code.
While not an error, the standard coding style for C avoids caMelCase variables in favor of all lower-case. See e.g. NASA - C Style Guide, 1994 So
int numOfChars =0;
int numOfUniqueChars = 0;
could simply be nchars and nunique.
Next, all your if and for loop syntax fails to encapsulate the required statements in braces, e.g. {...} to create a proper block for your if or for. For example, the following:
for(i = 0; i<strlen(str); i++)
c = str[i];
asctb[c]++;
only loops over c = str[i]; and asctb[c]++; is only executed AFTER the loop exits.
You must initialize your variable, (especially your array elements) before you attempt to reference them otherwise undefined behavior results. (it could seem to work, give weird output like a strange "#" character, or segfault, that's why it is undefined). You have a big problem here:
int* asctb = malloc(256);
None of the values in asctb are initialized. So when you return the array to main() and loop over all values in the array, every element that was not explicitly assigned a value causes undefined behavior. You can either set all values to 0 with memset, or recognize when you need all values initialized and use calloc instead:
int *asctb = calloc (1, 256);
Avoid the use of "magic-numbers" in your code. 256 above is a great example. Don't litter you code with these magic-numbers, instead defined a constant for them at the beginning of your code with either #define or for numerical constants, use an enum instead.
Lastly, in any code your write that dynamically allocates memory, you have 2 responsibilites regarding any block of memory allocated: (1) always preserves a pointer to the starting address for the block of memory so, (2) it can be freed using free when it is no longer needed. You should validate your memory use by running your code though a Memory Error Checking Program, such as valgrind on Linux. It's simple to do and will save you from yourself more times than you can imagine.
Putting all these pieces together and fixing additional logic errors in your code, you look like you were attempting something similar to the following:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* constants for max characters in str and values in asctb */
enum { MAXC = 80, MAXTB = 128 };
int *count (char *str);
int main (int argc, char **argv) {
if (argc < 2) { /* validate str given as argument 1 */
fprintf (stderr, "error: insufficient input, usage: %s str.\n",
argv[0]);
return 1;
}
/* initialize all variables avoid CamelCase names in C */
char str[MAXC] = "";
int j = 0, nchars = 0, nunique = 0;
int *counts = NULL;
strncpy (str, argv[1], MAXC - 1); /* limit copy len */
str[MAXC - 1] = 0; /* nul-terminate str */
printf ("\ncounting number of characters for \"%s\"..\n\n", str);
if (!(counts = count (str))) { /* validate return */
fprintf (stderr, "error: count() returned NULL.\n");
return 1;
}
for (j = 0; j < MAXTB; j++)
if (counts[j]) {
printf ("character '%c' appeared: %d times\n",
(char)j, counts[j]);
nchars += counts[j];
nunique++;
}
free (counts); /* free allocated memory */
printf ("\n\"%s\" has a total of %d character(s)\n", str, nchars);
printf (" wow %d different ascii character(s) much unique so skill\n\n",
nunique);
return 0; /* main is a function of type 'int' and returns a value */
}
int *count (char *str)
{
if (!str) return NULL; /* validate str */
int *asctb = calloc (1, sizeof *asctb * MAXTB);
size_t i; /* you are comparing with size_t in loop */
if (!asctb) { /* validate memory allocation - always */
fprintf (stderr, "count() error: virtual memory exhausted.\n");
return NULL;
}
for(i = 0; i < strlen(str); i++)
asctb[(int)str[i]]++; /* array indexes are type 'int' */
return asctb;
}
(note: the first 30 characters in counts are in the non-printable range, see ASCIItable.com. The indexes were left as you had them, but note, in practice you may want to shift them unless you are interested in counting the non-printable \t, \n, etc. chars).
Example Use/Output
$ ./bin/ccount "address 12234"
counting number of characters for "address 12234"..
character ' ' appeared: 1 times
character '1' appeared: 1 times
character '2' appeared: 2 times
character '3' appeared: 1 times
character '4' appeared: 1 times
character 'a' appeared: 1 times
character 'd' appeared: 2 times
character 'e' appeared: 1 times
character 'r' appeared: 1 times
character 's' appeared: 2 times
"address 12234" has a total of 13 character(s)
wow 10 different ascii character(s) much unique so skill
Look over the logic and syntax corrections and let me know if you have any further questions.

C working with strings and SEGFAULT

Hello and sorry for my bad english.
I am starting with C language, but I didnt get pointers well...
I searched for similar topics, but I didnt get it from them, so I created own topic.
I have got main function, where I call function newSpeak.
There is my code of newSpeak, but there isnt everything...
char * newSpeak ( const char * text, const char * (*replace)[2] )
{
int i;
char * alpha;
for(i=0;i<4;i++)
{
alpha=strstr(text, replace[0][4]);
if(alpha[0])
strncpy (alpha,replace[1][0],10);
}
return 0;
}
Thanks for answering
EDIT: I have found source of the problem.
It works, when I dont use for cycle and run it once. But it doesnt work even when the condition in for cycle is i<1, which should make it run only once...This is strange for me...
alpha=strstr(text, replace[0][4]);
if(alpha[0])
// looks crashy
man strstr:
These functions return a pointer to the beginning of the substring,
or NULL if the substring is not found.
EDIT:
It is difficult to tell what you are trying to do, but below find an arbitrary adaptation of your code. If it were my program, I would write it very differently. I mention that because I do not want someone to read this and think it is the way it should be done.
#include <stdio.h>
#include <string.h>
void newSpeak (char *text, const char *replace[4][2])
{
int i, j;
char *alpha;
for (i = 0; i < 4; i++) {
if (alpha = strstr(text, replace[i][0])) {
for (j = 0; alpha[j] && replace[i][1][j]; j++)
alpha[j] = replace[i][1][j];
}
}
}
int main ()
{
char buf[100] = "abc";
const char *replace[4][2] = {
{ "a", "e" },
{ "b", "f" },
{ "c", "g" },
{ "d", "h" },
};
newSpeak(buf, replace);
puts(buf);
}
The line
strncpy (alpha,replace[1][0],10);
should have generated a compiler warning (and NEVER ignore compiler warnings). The function prototype is
char *strncpy( char *dest, char *source, int n);
But you are passing it replace[1][0] which is a character. It might work if you passed
strncpy( alpha, &replace[1][0], 10);
Even then I still worry. It could be that since alpha is pointing to a block of memory in the block pointed to by text which is a const char*, that you are not allowed to modify that memory.
EDIT I think my first point is wrong - I misread your prototype. But I'm pretty sure the second point is valid (and probably the reason for the segfault).
second edit
It is possible that text does not have sufficient memory allocated to have 10 characters copied into it from replace. Realize that the thing you are matching against (replace[0][4]) and the thing you are copying (replace[1][0]]) are not the same thing; also, you are looping over i but not using that value ... makes me wonder if there is a typo (I am not clairvoyant and cannot figure out what you wanted to change from loop to loop).
You need to check the size of the thing you are copying into:
strncpy(alpha, replace[1][0], (strlen(alpha)<10)?strlen(alpha):10);
would ensure you are copying no more than 10 characters, and no more than there's space in alpha.
This is "on top of" everything else already pointed out (of which using if (alpha!=NULL) instead of if(alpha[0]) is a big one.)
EDIT 3 - I think I figured out the majority of the problems with your code now... see http://codepad.org/YK5VyGAn for a small "working" sample.
Issues with your code included:
You declare text as const char*, then proceed to modify it
You declare replace as const char* (*replace)[2], then address element replace[0][4] (4 > 2...)
You assign the return value of strstr to alpha; this could be NULL (no match), yet you test for alpha[0] (which will fail if alpha == NULL).
When you copied the replacement string, you copied "up to 10 characters" - regardless of whether (a) the target string could accommodate this, and (b) the source string had this many characters. The result might be that you copy the full source string (including the terminating '\0') so that you will not find another match afterwards (you have "deleted" the rest of the string). And then you will run into the "strstr returns NULL" error...
Not sure (without seeing your input string or "replace" strings) which of these actually caused your code to fail - I have written a small program that addresses all of these mistakes. You can find it at http://codepad.org/4jSOnmPy - reproduced here:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define MIN(a,b) (a>b)?(b):(a)
char * newSpeak (const char *text, const char *(*replace)[5] ){
int ii=0, n;
char *alpha, *beta;
printf("length of input string is %d\n", strlen(text));
beta = malloc(strlen(text)+1);
printf("allocated %d bytes\n", strlen(text)+1);
fflush(stdout);
strcpy(beta, text);
printf("copy OK: beta now %s\n", beta);
fflush(stdout);
for(ii = 0; ii < 4; ii++) {
// alpha=strstr(beta, replace[0][0]);
alpha=strstr(beta, "a");
printf("alpha is '%s'\n", alpha);
fflush(stdout);
if(alpha!=NULL) {
char *rs;
rs = replace[1][ii];
printf("ii = %d; alpha now: '%s'\n", ii, alpha);
fflush(stdout);
n = MIN(strlen(alpha), strlen(rs));
printf("n is now %d\n", n);
fflush(stdout);
printf("going to copy at most %d characters from '%s' into '%s'\n", n, rs, alpha);
fflush(stdout);
strncpy (alpha,rs,n);
printf("beta is now '%s'\n", beta);
fflush(stdin);
}
else printf("no match found\n");
}
return beta;
}
int main(void) {
char* r[2][5]={{"a","b","c","d", "e"}, {"o","e","i","u","s"}};
char* myText = "this is a vary sally strang";
printf("NewSpeak: %s\n", "hello world");
printf("converted: %s\n", newSpeak(myText, r));
return 0;
}
Output:
NewSpeak: hello world
length of input string is 27
allocated 28 bytes
copy OK: beta now this is a vary sally strang
alpha is 'a vary sally strang'
ii = 0; alpha now: 'a vary sally strang'
n is now 1
going to copy at most 1 characters from 'o' into 'a vary sally strang'
beta is now 'this is o vary sally strang'
alpha is 'ary sally strang'
ii = 1; alpha now: 'ary sally strang'
n is now 1
going to copy at most 1 characters from 'e' into 'ary sally strang'
beta is now 'this is o very sally strang'
alpha is 'ally strang'
ii = 2; alpha now: 'ally strang'
n is now 1
going to copy at most 1 characters from 'i' into 'ally strang'
beta is now 'this is o very silly strang'
alpha is 'ang'
ii = 3; alpha now: 'ang'
n is now 1
going to copy at most 1 characters from 'u' into 'ang'
beta is now 'this is o very silly strung'
converted: this is o very silly strung
Note - I added lots of "useless" output, including fflush(stdout); statements. This is a good way to ensure that debug printout shows you exactly how far into a program you got, and what was going on before it crashed - without the fflush it's possible you are missing many lines of output (because they never "made it to the screen").
It's obvious from the above that if your replacement strings are a different length than the string they replace, you will get some strange overwriting (I left both search and replace string length at 1 but there is no reason why that should be so).
I hope this helps!

String Padding in C

I wrote this function that's supposed to do StringPadRight("Hello", 10, "0") -> "Hello00000".
char *StringPadRight(char *string, int padded_len, char *pad) {
int len = (int) strlen(string);
if (len >= padded_len) {
return string;
}
int i;
for (i = 0; i < padded_len - len; i++) {
strcat(string, pad);
}
return string;
}
It works but has some weird side effects... some of the other variables get changed. How can I fix this?
It might be helpful to know that printf does padding for you, using %-10s as the format string will pad the input right in a field 10 characters long
printf("|%-10s|", "Hello");
will output
|Hello |
In this case the - symbol means "Left align", the 10 means "Ten characters in field" and the s means you are aligning a string.
Printf style formatting is available in many languages and has plenty of references on the web. Here is one of many pages explaining the formatting flags. As usual WikiPedia's printf page is of help too (mostly a history lesson of how widely printf has spread).
For 'C' there is alternative (more complex) use of [s]printf that does not require any malloc() or pre-formatting, when custom padding is desired.
The trick is to use '*' length specifiers (min and max) for %s, plus a string filled with your padding character to the maximum potential length.
int targetStrLen = 10; // Target output length
const char *myString="Monkey"; // String for output
const char *padding="#####################################################";
int padLen = targetStrLen - strlen(myString); // Calc Padding length
if(padLen < 0) padLen = 0; // Avoid negative length
printf("[%*.*s%s]", padLen, padLen, padding, myString); // LEFT Padding
printf("[%s%*.*s]", myString, padLen, padLen, padding); // RIGHT Padding
The "%*.*s" can be placed before OR after your "%s", depending desire for LEFT or RIGHT padding.
[####Monkey] <-- Left padded, "%*.*s%s"
[Monkey####] <-- Right padded, "%s%*.*s"
I found that the PHP printf (here) does support the ability to give a custom padding character, using the single quote (') followed by your custom padding character, within the %s format.
printf("[%'#10s]\n", $s); // use the custom padding character '#'
produces:
[####monkey]
#include <stdio.h>
#include <string.h>
int main(void) {
char buf[BUFSIZ] = { 0 };
char str[] = "Hello";
char fill = '#';
int width = 20; /* or whatever you need but less than BUFSIZ ;) */
printf("%s%s\n", (char*)memset(buf, fill, width - strlen(str)), str);
return 0;
}
Output:
$ gcc -Wall -ansi -pedantic padding.c
$ ./a.out
###############Hello
You must make sure that the input string has enough space to hold all the padding characters. Try this:
char hello[11] = "Hello";
StringPadRight(hello, 10, "0");
Note that I allocated 11 bytes for the hello string to account for the null terminator at the end.
The argument you passed "Hello" is on the constant data area. Unless you've allocated enough memory to char * string, it's overrunning to other variables.
char buffer[1024];
memset(buffer, 0, sizeof(buffer));
strncpy(buffer, "Hello", sizeof(buffer));
StringPadRight(buffer, 10, "0");
Edit: Corrected from stack to constant data area.
Oh okay, makes sense. So I did this:
char foo[10] = "hello";
char padded[16];
strcpy(padded, foo);
printf("%s", StringPadRight(padded, 15, " "));
Thanks!
#include <iostream>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
using namespace std;
int main() {
// your code goes here
int pi_length=11; //Total length
char *str1;
const char *padding="0000000000000000000000000000000000000000";
const char *myString="Monkey";
int padLen = pi_length - strlen(myString); //length of padding to apply
if(padLen < 0) padLen = 0;
str1= (char *)malloc(100*sizeof(char));
sprintf(str1,"%*.*s%s", padLen, padLen, padding, myString);
printf("%s --> %d \n",str1,strlen(str1));
return 0;
}
The function itself looks fine to me. The problem could be that you aren't allocating enough space for your string to pad that many characters onto it. You could avoid this problem in the future by passing a size_of_string argument to the function and make sure you don't pad the string when the length is about to be greater than the size.
One thing that's definitely wrong in the function which forms the original question in this thread, which I haven't seen anyone mention, is that it is concatenating extra characters onto the end of the string literal that has been passed in as a parameter. This will give unpredictable results. In the example call of the function, the string literal "Hello" will be hard-coded into the program, so presumably concatenating onto the end of it will dangerously write over code. If you want to return a string which is bigger than the original then you need to make sure you allocate it dynamically and then delete it in the calling code when you're done.
#include<stdio.h>
#include <string.h>
void padLeft(int length, char pad, char* inStr,char* outStr) {
int minLength = length * sizeof(char);
if (minLength < sizeof(outStr)) {
return;
}
int padLen = length - strlen(inStr);
padLen = padLen < 0 ? 0 : padLen;
memset(outStr, 0, sizeof(outStr));
memset(outStr, pad,padLen);
memcpy(outStr+padLen, inStr, minLength - padLen);
}

Resources