C - Struct string elements are concatenated - c

i have two string elements in my structure
struct mystruct{
char mydate[10];
char mytime[5];
};
They will store strings of type "XX:YY" and "XX-YY-ZZZZ" respectively.
But when I am assigning some value into these variables
struct mystruct *mystruct = (struct mystruct*)malloc(sizeof(struct mystruct));
strcpy(mystruct->mydate, "01-01-1970");
strcpy(mystruct->mytime, "00:01");
mydate variables is printing this:
01-01-197000:01
I am missing something? Can you help me? Thanks in andance!
EDITED with more info
don't works even if I increase the size by one

You have undefined behavior since there is not sufficient room in mydate to contain strings of the format "MM-DD-YYYY" - don't forget the implicit null terminator at the end.
What you're specifically observing is that the lack of the null terminator means that the output function (puts, printf, or whatever you're using) continues to read characters after the string ends. It so happens that there isn't any padding between mydate and mytime in your case, so the value in mytime appears to be part of the string as well.
Remember, since arrays decay to pointers when passed to functions, there is no way for a function with an array parameter to know when it is done reading the array; the null terminator acts as a sentinel value for this purpose.
Solution: Increase the size of both mydate and mytime to accommodate the null terminator as well.

since you complained that your code did not work even with increased sizes, here is an example which works correctly:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct mystruct1 {
char mydate[11];
char mytime[6];
};
void main() {
struct mystruct1 *mystruct1 = (struct mystruct1*)malloc(sizeof(struct mystruct1));
strcpy(mystruct1->mydate, "01-01-1970");
strcpy(mystruct1->mytime, "00:01");
printf("date: %s, time: %s\n", mystruct1->mydate, mystruct1->mytime);
}
In this example every array has enough space to keep the string with the null terminator. So, you can compare it to your code.
In general it is also possible to keep sizes as in your example. But you need to always remember that the end of the string cannot be determined automatically and will require you to use specific function, like strncpy instead of strcpy. printf will not work directly as well. So here is another example:
struct mystruct2 {
char mydate[10];
char mytime[5];
};
void main() {
struct mystruct2 *mystruct2 = (struct mystruct2*)malloc(sizeof(struct mystruct2));
strncpy(mystruct2->mydate, "02-02-2970", 10);
strncpy(mystruct2->mytime, "00:02", 5);
// need top prepare standard strings with terminator for printf
char mydate[11]; // still need [11] for printf to work
char mytime[6];
strncpy(mydate, mystruct2->mydate, 10);
mydate[10] = 0; // make sure to put a string terminator here
strncpy(mytime, mystruct2->mytime, 10);
mytime[5] = 0;
printf("date: %s, time: %s\n", mydate, mytime);
}
The above makes sense in some situation where you are really tight on the memory, but should not be used in generic cases.

The are at least two problems with the code.
1) You are writing too much in the mydate and mytime. The fix is either to allocate more memory in the struct, i.e., char mydate[10+1]; & char mytime[5+1];. Or just don't write the NULL terminator. A solution for that, since you know the size in advance, use memcpy(mystruct->mydate, "01-01-1970", sizeof(mystruct->mydate)); or similar memcpy(mystruct->mytime, "00:01", 5);.
2) The second part, print out, you are not showing (hint). So if you don't store the NULL-terminator then print out needs to be a little more delicate as shown in the example below.
// Possible dynamic max size
printf("date: %.*stime: %.*sThe\n",
(int) sizeof(mystruct->mydate), mystruct->mydate,
(int) sizeof(mystruct->mytime), mystruct->mytime);
// Fixed max size
printf("date: %.10stime: %.5sEnd\n",
mystruct->mydate,
mystruct->mytime);
Either way, the print out will be:
date: 01-01-1970time: 00:01The
date: 01-01-1970time: 00:01End
The printf-syntax used maximizes the length of the string printed.
BTW, your printout, 01-01-197000:01, is likely the result of the compiler placing the memory layout of struct mystruct directly after each other in memory. The the resulting memory layout is equivalent to
struct mystruct {
char my[15];
}
where the compiler knows that mydate starts at offset 0, and mytime starts at offset 10. This, if you filled your struct in the order of your example you first get "01-01-1970\0" followed by "01-01-197000:01\0" (where the last write is out of scope for the struct). So printing the date with printf("%s", mystruct->mydate); gives you your sequence output.
On the other hand, had you decided to write you data in the reverse order,
strcpy(mystruct->mytime, "00:01");
strcpy(mystruct->mydate, "01-01-1970");
the output of
printf("date: %s, time: %sEnd\n", mystruct->mydate, mystruct->mytime);
would be
date: 01-01-1970, time: End
since you time was overwritten my the null terminator of the mydate string copy.

Related

how to I copy structure char variable into another char array [duplicate]

This question already has answers here:
How can I correctly assign a new string value?
(4 answers)
Closed last year.
struct student
{
char name[50];
int rollno;
float marks;
};
int main()
{
char nam[50];
int i = 0, n=5;
struct student s1[n];
for(i=0;i<n;i++)
{
nam[i] = s1[i].name;
}
}
In given code, I am unable to copy s1[i].name in nam[i], I tried all the copy function but it's giving me error every time.
This ...
nam[i] = s1[i].name;
... does not make sense because name[i] is one char, whereas s1[i].name is an array of 50 chars. Moreover, your loop is over the number of elements of s1 (5), which is much less than the number of elements of nam or s1[i].name.
Supposing that you really do want s1 to be an array of struct student as opposed to just one struct, you probably want to use strcpy(). However, you might want or need to use memcpy() (or an element by element copy loop) depending on the nature of the contents of s1 and its elements and any artificial requirements on the exercise.
The strcpy() variation would look like this:
strcpy(nam, s1[i].name);
// do something with nam ...
That depends on the source array to contain a null-terminated string, which is not guaranteed to be the case in the demo program, which never puts any data in s1 or its elements.
Use strcpy() I have tried and it's working
Lots of little things working against you in this code, eg the following...
int i = 0, n=5;
struct student s1[n];
...creates a variable length array of struct student, preventing an initializer from being a viable path. But if for the sake of illustration you can live with a non-VLA version, then try this:
//Use a hard-coded to `5`, to create an array of 5 struct student, initialized as shown:
struct student s1[5] = {{"name1"1, 1.0},("name2",2,2.0},{"name3",3,3.0},{"name4",4,4.0},{"name5",5,5.0}};
Next, your current code: char nam[50]; creates a single uninitialized char array, sufficient for containing only 1 string with up to 49 characters, with a terminating \0. If you can, create it like this. (for the sake of learning to copy strings in a loop.)
char nam[5][50] = {{0}};//created 5 elements of null terminated char array elements,
Then you can do this in your loop:
for(i=0;i<n;i++)
{
strcpy(nam[i], s1[i]name);
}
By now (from comments and answers) you should already know that strings cannot be copied using the = operator like integers or floating numbers. String functions such as strcpy(), or sprintf() can be used to populate strings.

Set to NULL an array of structures on C

I wrote this C code to set to NULLan array of structures on C. Here it goes,
struct Worker
{
char name[50];
unsigned int salary;
};
int main()
{
int n;
struct Worker number[50];
for(n=0;n<50;n++)
{
number[n].name=NULL;
}
}
Compiler is giving this error: main.c:65:26: error: assignment to expression with array type.
In your struct, the field name is an array of 50 chars. Arrays are not pointers and you cannot change them; you can only change their contents.
Therefore, if you want to initialise the name field, you could make the name an empty string by making the first char a null terminator:
*number[n].name = '\0';
Note that you name will always hold 50 chars, but you are using only the chars up to the first terminator.
Because your array isn't a pointer, the following doesn't work, either:
number[0].name = "Tom";
You must fill the contents to the array, probably with strcpy from <string.h>:
strcpy(number[0].name, "Tom");
If you want to test whether a string is empty, test whether the first character is the null terminator:
if (*number[n].name == '\0') ... // string is empty
You can also initialise the array explicitly:
struct Worker number[50] = {
{"Alice", 3200},
{"Bob", 2700},
{"Charlotte", 3000}
};
This will give you 3 workers with names and a salary and 47 workers with empty strings as names and zero salary. You can, of course, fill them in later or reset the first three.
If you initialise the array with:
struct Worker number[50] = {{""}};
you'll get an array of all empty-named, all zero-salary workers that you can fill.
(That said, if your name member were a pointer like char *name;, you could say number[n].name = "Tom"; or number[n].name = NULL;. But that would mean that you'd have to handle all the memory that the pointer points to yourself, which is not easy.
The advantage of your array of 50 chars is that each worker has already 50 chars that you can use for their name. (Well, up to 49 plus a null terminator.) The disadvantage is that you waste memory on most names, because they are shorter and you are not flexible enough if you need a name that is longer. But before you have learned more about pointers and memory management, the array is the way to go. Just make sure that you don't exceed its limits.)
You can't set an array of characters to null like that; there's no point. Literally there's no pointer. Arrays aren't pointers; their memory is already allocated on the stack, so I can't redirect the array how you're trying to do it. You're trying to manually set the array's address.
If however your struct looked like this
struct Worker{
char *name;
unsigned int salary;
};
Then you would be able to set each worker's name to null.
If your interest is to zero out an array of characters, you could do this
int n, j;
struct Worker number[50];
for(n=0;n<50;n++)
for(j=0;j<50;j++)
number[n].name[j]=0;
But I have no idea why you'd want to do that. I'm assuming you may use strcpy to assign values to each worker's name, which takes care of the null terminating character for you.
Your name is array of 50 chars, if you want to clear it instead of:
num[n].name=NULL;
do:
memset(num[n].name, 0, 50);
You can set to NULL pointer not the array.
You can see if array is empty by examining each of arrays elem, like:
int i;
int non_empty = 0
for( i=0; i < sizeof(array)/sizeof(array[0]); ++i)
{
if(array[i] != 0)
{
non_empty = 1;
break;
}
}

C - Append strings until end of allocated memory

Let's consider following piece of code:
int len = 100;
char *buf = (char*)malloc(sizeof(char)*len);
printf("Appended: %s\n",struct_to_string(some_struct,buf,len));
Someone allocated amount of memory in order to get it filled with string data. The problem is that string data taken from some_struct could be ANY length. So what i want to achieve is to make struct_to_string function do the following:
Do not allocate any memory that goes outside (so, buf has to be allocated outside of the function, and passed)
Inside the struct_to_string I want to do something like:
char* struct_to_string(const struct type* some_struct, char* buf, int len) {
//it will be more like pseudo code to show the idea :)
char var1_name[] = "int l1";
buf += var1_name + " = " + some_struct->l1;
//when l1 is a int or some non char, I need to cast it
char var2_name[] = "bool t1";
buf += var2_name + " = " + some_struct->t1;
// buf+= (I mean appending function) should check if there is a place in a buf,
//if there is not it should fill buf with
//as many characters as possible (without writting to memory) and stop
//etc.
return buf;
}
Output should be like:
Appended: int l1 = 10 bool t1 = 20 //if there was good amount of memory allocated or
ex: Appended: int l1 = 10 bo //if there was not enough memory allocated
To sum up:
I need a function (or couple of functions) that adds given strings to the base string without overwritting base string;
do nothing when base string memory is full
I can not use C++ libraries
Another things that I could ask but are not so important right now:
Is there a way (in C) iterate through structure variable list to get their names, or at least to get their values without their names? (for example iterate through structure like through array ;d)
I do not normally use C, but for now I'm obligated to do, so I have very basic knowledge.
(sorry for my English)
Edit:
Good way to solve that problem is shown in post below: stackoverflow.com/a/2674354/2630520
I'd say all you need is the standard strncat function defined in the string.h header.
About the 'iterate through structure variable list' part, I'm not exactly sure what you mean. If your talking about iterating over the structure's members, a short answer would be : you can't introspect C structs for free.
You need to know beforehand what structure type you're using so that the compiler know at what offset in the memory it can find each member of your struct. Otherwise it's just an array of bytes like any other.
Don't mind asking if I wasn't clear enough or if you want more details.
Good luck.
So basically I did it like here: stackoverflow.com/a/2674354/2630520
int struct_to_string(const struct struct_type* struct_var, char* buf, const int len)
{
unsigned int length = 0;
unsigned int i;
length += snprintf(buf+length, len-length, "v0[%d]", struct_var->v0);
length += other_struct_to_string(struct_var->sub, buf+length, len-length);
length += snprintf(buf+length, len-length, "v2[%d]", struct_var->v2);
length += snprintf(buf+length, len-length, "v3[%d]", struct_var->v3);
....
return length;
}
snprintf writes as much as possible and discards everything left, so it was exactly what I was looking for.

How to make a pointer in a function change the pointee in C

Hee guys,
I have been reading a couple of things about pointers and pointees and started getting curious. The only thing I dont understand is how pointers behave in functions, hence the following code:
#include <stdio.h>
int pointeeChanger(char* writeLocation) {
writeLocation = "something";
return 0;
}
int main(void)
{
char crypted[] = "nothing";
char* cryptedPointer = crypted;
pointeeChanger(cryptedPointer);
printf("The new value is: %s", cryptedPointer);
return 0;
}
What my intention to do is to adjust the pointee, "crypted" var, through a pointer given to a function. The only thing is that it is not working. Could you please explain me what is going wrong in my thought process. I am fairly new to C so my errors could be fairly basic.
Thanks in advance!
Greetings,
Kipt Scriddy
C strings are not the best material to learn pointers, because they are implemented as pointers to char. Let's use int instead:
#include <stdio.h>
void pointeeChanger(int* writeLocation) {
// Using dereference operator "*"
*writeLocation = 42; // something
}
int main(void) {
int crypted = 0; // Nothing
pointeeChanger(&cryptedPointer); // Taking an address with "&"
printf("The new value is: %d", crypted);
return 0;
}
This works as expected.
Modifying strings in place is a lot harder, because you are forced to deal with memory management issues. Specifically, the string into which you copy must have enough space allocated to fit the new string. This wouldn't work with "nothing" and "something", because the replacement is longer by two characters.
Short answer: writeLocation is a local variable and is a copy of cryptedPointer. When you modify writeLocation, cryptedPointer is not modified.
If you want to modify cryptedPointer, you have to pass a pointer to it, like so:
#include <stdio.h>
int pointeeChanger(char** writeLocation) { /* Note: char** */
*writeLocation = "something"; /* Note: *writeLocation */
return 0;
}
int main(void)
{
char crypted[] = "nothing";
char* cryptedPointer = crypted;
pointeeChanger(&cryptedPointer); /* Note: &cryptedPointer */
printf("The new value is: %s", cryptedPointer);
return 0;
}
There are other issues with this code though. After the call to pointeeChanger(), cryptedPointer no longer points to the crypted array. I suspect you actually wanted to change the contents of that array. This code fails to do that.
To change the value of crypted[] you will need to use strcpy() or (preferably) strncpy(). Also you will need to watch the size of the crypted[] array - "something" is longer than "nothing" and will cause a buffer overflow unless crypted[] is made larger.
This code will modify the original crypted[] array:
#include <stdio.h>
#include <string.h>
#define MAX_STR_LEN 64
/*
* Only char* required because we are not modifying the
* original pointer passed in - we are modifying what it
* points to.
*/
int pointeeChanger(char* writeLocation)
{
/*
* In C, you need to use a function like strcpy or strncpy to overwrite a
* string with another string. Prefer strncpy because it allows you to
* specify a maximum size to copy, which helps to prevent buffer overruns.
*/
strncpy(writeLocation, "something", MAX_STR_LEN);
/* strncpy doesn't automatically add a \0 */
writeLocation[MAX_STR_LEN] = '\0';
return 0;
}
int main(void)
{
/*
* The +1 is because an extra character is required for the
* null terminator ('\0')
*/
char crypted[MAX_STR_LEN + 1] = "nothing";
pointeeChanger(crypted);
printf("The new value is: %s", crypted);
return 0;
}
It depends slightly on what you actually want to do:
Do you want to change what cryptedPointer is pointing to, or change the content that cryptedPointer is pointing at?
The second can be done by:
strcpy(writeLocation, "something");
Beware that if something is longer than what the original string's size, you'll overflow the buffer, which is a bad thing. So to fix this, you'd have to have char crypted[10] = "nothing";, to make space for the string "something".
You can clearly also do something like:
writeLocation[2] = 'f';
writeLocation[3] = 'f';
and have the printf print "noffing"
but if you want to do the first variant, then you need to pass a pointer to the pointer:
int pointeeChanger(char** writeLocation) {
*writeLocation = "something";
return 0;
}
And then call:
pointeeChanger(&cryptedPointer);
Note that when this returns, cruptedPointer is pointing at a constant string that can't be modified, where your original crypted can be modified.
Consider that Tom is hired by Sally to break knuckles for the mafia.
Pass-by-value: If Sally tells Tom to count the number of knuckles he breaks at work today, then Sally has no way of knowing which number Tom has come up with until he returns from the road. They both have a copy of the number "zero" in their heads to begin with, but Tom's number might increase throughout the course of the day.
Note the word "copy". When you pass-by-value to a function, you're passing a copy of the object. When you modify the object within a function, you're modifying the copy instead of the original.
Pass-by-reference: If Sally tells Tom to tally the number of knuckles he breaks in the sky, then she (and anyone else who's interested) can refer to the sky. By changing the sky, Tom would also be changing Sally's number.
edit: C doesn't have pass-by-reference, though it does have pointers, which are reference types. Passing a pointer is still pass-by-value, and a copy with the same pointer value is still formed. Hence, your assignment is to the copy, not the original.

Why can't I initialize an array of cstrings like this?

char sXSongBuffer[20][30];
sXSongBuffer = {"Thriller", "Don't Stop Till You Get Enough", "Billy Jean"};
Why does this return the error expected expression before ‘{’ token? The reason I want to initialize my array like this is so that I can change its contents like this later:
sXSongBuffer = {"New Song", "More Music From Me"};
You can't assign to arrays in C. C allows initializing arrays with values that are compile-time constants. If you want to change the values later, or set values that are not compile-time constants, you must assign to a particular index of the array manually.
So, your assignment to sXSongBuffer is disallowed in C. Moreover, since sXSongBuffer[0] to sXSongBuffer[19] are arrays too, you can't even say: sXSongBuffer[0] = "New Song";
Depending upon what you want, this may work for you:
/* declare sXSongBuffer as an array of pointers */
char *sXSongBuffer[30] = {
"Thriller",
"Don't Stop Till You Get Enough",
"Billy Jean",
NULL /* set the rest of the elements to NULL */
};
size_t i;
/* and then later in your code */
sXSongBuffer[0] = "New Song";
sXSongBuffer[1] = "More Music From Me";
for (i=2; i < sizeof sXSongBuffer; ++i)
sXSongBuffer[i] = NULL;
But the above only works if you know all your strings at compile time. If not, you will have to decide if you want "big-enough" arrays, or if you need dynamic memory for the strings and/or the number of strings. In both cases, you will want to use an equivalent of strcpy() to copy your strings.
Edit: To respond to the comment:
You're declaring an array of 30 char pointers with the first three elements pointing to buffers the size of the strings, ie the buff pointed to by sXSongBuffer[0] won't hold any string larger than "Thriller" and if he does sXSongBuffer[0] = malloc(32); He'll get a minor memory leek. Also, he'll have to malloc memory for each of the rest of the slots in the array. He should either use 2d char arrays like in the OP + a designated init, or malloc each buffer at run time and copy in the values. He'll also need to remember to free any memory he mallocs.
sXSongBuffer in char *sXSongBuffer[30]; is an array of size 30, with each element being a char *, a pointer to char. When I do:
char *sXSongBuffer[30];
each of those 30 pointers is uninitialized. When I do:
char *sXSongBuffer[30] = { "Thriller", ... };
I set the pointers to different read-only locations. There is nothing preventing me to then "re-point" the pointers somewhere else. It is as if I had:
char *data = "Hello";
printf("%s\n", data);
data = "Hello, world";
printf("%s\n", data);
In the above snippet, I assign data to "Hello" first, and then change it to point to a longer string later. The code I had above in my answer did nothing more than reassign sXSongBuffer[i] to something else later, and since sXSongBuffer[i] is a pointer, the assignment is OK. In particular, sXSongBuffer[0] is a char *, and can point to any valid location that has a char in it.
As I said later in my answer, if the strings aren't known at compile-time, this scheme doesn't work, and one has to either use arrays with "big enough" sizes, or dynamically allocate memory that's big enough.
C does not have general-purpose array literals. The {} list syntax only works when initializing, i.e. when assigning the value in the same statement that declares the variable.
You cannot just write
char sXSongBuffer[20][30];
sXSongBuffer = {"Thriller", "Don't Stop Till You Get Enough", "Billy Jean"};
You must either initialize array at once (but it will containt only 3 items):
char * sXSongBuffer[]= {"Thriller", "Don't Stop Till You Get Enough", "Billy Jean"};
Or either use stnrcpy on every item:
char sXSongBuffer[20][30];
strncpy(sXSongBuffer[0],"Thriller",29);
strncpy(sXSongBuffer[1],"Don't Stop Till You Get Enough",29);
strncpy(sXSongBuffer[2],"Billy Jean",29);
Take a look at Designated Initializers.
#include <stdio.h>
int main (void) {
char a[6][6] = { [2] = "foo", [4] = "bar" };
for (int i=0; i<6; ++i)
printf("%d == %s\n", i, a[i]);
return 0;
}
This is a c99 feature. Compile with:
gcc -W -std=c99 2dInit.c -o 2dInit
This outputs:
0 ==
1 ==
2 == foo
3 ==
4 == bar
5 ==
In your case you want to do:
char sXSongBuffer[20][30] = {
[0] = "Thriller",
[1] = "Don't Stop Till You Get Enough",
[2] = "Billy Jean"
};

Resources