I'm getting the following error with memcpy. It doesn't give compilation error but doesn't give the result I would imagine. I've never used memcpy before so I'm sure I'm making a simple mistake. I've looked around previous questions but couldn't find one with structures. I can use memcpy on independent variables but just not on structs.
If someone can point out my mistake it'll be great.
#include <stdio.h>
#include <string.h>
int main() {
struct st{
char c1[12];
char c2[32];
char c3[3];
char c4[7];
char c5[13];
char c6[5];
char c7[10];
};
struct st s;
char s1[] = "part number";
char s2[] = "j9uijd9d09fj";
char s3[] = "abc";
char s4[] = "seven";
char s5[] = "aaaaaaaa";
char s6[] = "ptype";
char s7[] = "user";
memcpy(s.c1,s1,sizeof(s.c1));
memcpy(s.c2,s2,sizeof(s.c2));
memcpy(s.c3,s3,sizeof(s.c3));
memcpy(s.c4,s4,sizeof(s.c4));
memcpy(s.c5,s5,sizeof(s.c5));
memcpy(s.c6,s6,sizeof(s.c6));
memcpy(s.c7,s7,sizeof(s.c7));
printf("%s\n",s.c1);
printf("%s\n",s.c2);
printf("%s\n",s.c3);
printf("%s\n",s.c4);
printf("%s\n",s.c5);
printf("%s\n",s.c6);
printf("%s\n",s.c7);
return 0;
}
OUTPUT I'm getting :
part number
j9uijd9d09fj
abcseven
seven
aaaaaaaa
ptypeuser
user
Thanks!!!
Change the size of c3 in your struct to 4 and c6 to 6 to allow for the NULL terminator.
struct st{
char c1[12];
char c2[32];
char c3[4]; /* putting 'abc' which is 4 chars */
char c4[7];
char c5[13];
char c6[6]; /* putting 'ptype' which is 6 chars */
char c7[10];
};
printf with %s prints a null-terminated string. s3 (and s6) in this case has the null-terminating character overwritten by c so printf stops printing it when it reaches the next one, which is after seven.
Your code is wrong for two things:
s.c3 is 3 characters in length, so there is no room for the extra NUL byte. That's why you get it concatenated with the next one on printing.
In some cases, you are copying more bytes than there is in the original string: memcpy(s.c2,s2,sizeof(s.c2)) is copying 32 bytes, but the original string is far shorter. That is undefined behaviour.
Probabl you want to use strcpy().
Or even strncpy, but beware! this functions does not do what most people think... Read the documentation at least twice before using it.
In the structure the member c3 is an array of three characters. You then copy four characters into it! Remember that strings have an extra character that ends the string, so the string "abc" is actually four characters: 'a', 'b', 'c' and the terminator '\0'.
The same for the c6 member of the structure.
The problem you are facing is the missing NUL-terminator of the character sequences.
A character sequence as you constructor them, is always one character longer then the amount of characters you types in. The additional character at the end is the NUL-terminator.
So the arrays you copy your thing in, need to be one character longer then the amount of characters you want to copy in. For c3 the amount of characters is too small, causing the NUL-terminator to be missing.
printf then prints your string character by character until it sees the NUL-terminator. In case its missing, printf just continues reading the memory until it hits the first 0x00-byte. In this case you are lucky because you are using a structure. Structures are written in one block of memory so printf just drops into the next field.
You solve your problem simply by ensuring that the arrays sizes in the structure are always larger then the character sequences you want to copy in.
Related
I have a structure like this:
typedef struct {
char gpsTime[6];
char gpsStatus[1];
char gpsLat[10];
char gpsLong[10];
char gpsSpeed[5];
char gpsDate[6];
} gpsData;
and also a string at which I would like to concatenate the elements of this structure:
#define MESSAGESIZE 100
// Final message to be sent
char message[MESSAGESIZE]="$STARTOFMSG,23,";
The problem is, when I use "strcat" function like this:
strcat(message,data.gpsTime);
I expect to have something like this:
"$STARTOFMSG,23,083559";
but the total elements of structure are concatenated, i.e.:
"$STARTOFMSG,23,,083559A4717.1143700833.91520.004091202##";
I guess this is a problem with pointers. How should I solve this?
I guess gpsTime is not properly terminated, i.e. it's not a valid C string. This will cause undefined behavior.
If you expect it to be "083559" as shown, it cannot be char gpsTime[6] since that leaves no room for the terminator. It has to be char gpsTime[7] or more. This of course goes for all of the strings; a C string with n characters has to have n + 1 chars worth of space since the final one must be \0 to terminate the string.
strcat copies the string up to the /0 termination character. Add the termination char and it should work.
083559 is already 6 character, so you need to define gpsTime as char gpsTime[7] and add the /0 termination character.
I need help :/
On textfile, I have something like this:
MyName: 15 3
and I want to read each element until ':' and store it in variable name of the struct, and read the next elements and store 15 and 3 in another variables.
typedef struct STRUCT_PLAYER{char name[20];
int sucess;
int numQuest;
int wrongs;} PLAYER;
int readTop(PLAYER playTop[]){
char *classify = "classificacao.txt";
FILE *fl;
fl = fopen(classify, "r");
char c;
int n=0;
if(cl==NULL){
printf("Error reading the file %s\n", classify);
exit(1);
}
else{
while(c!=EOF){
while(c!=':'){
c=getc(fl);
if(c!=':') playTop[n].name=c;
}
if(c==':')fscanf(fl, " %d %d\n", &playTop[n].numQuest, &playTop[n].wrongs);
n++;
}
}
return n;
this is my code, but it appears me an error, on this line
if(c!=':') playerTOP[n].nome=c;
error: assignment to expression with array type
but I don't understand what it is
c is char, playTop->name is a char[], so your are assigning incompatible types. Also
coderredoc answer
Array name is not a modifiable lvalue
When you initialize your structure do:
int n=0;
playTop[n].name[0] = 0;
...
while(c!=':'){
char cs[] = { getc(fl), 0 };
if(c!=':')
strcat(playTop[n].name, cs);
}
With this cs is a C-String containing one letter only and strcat appends
the string to playTop[n].name, thus saving the name
EDIT
Ricardo's comment
thank you. but I din't understant why you put a zero on char cs[] = { getc(fl), 0}
That is what I was talking about in the comment. In C a string must be
'\0'-terminated.
A C-String is a sequence of bytes. This sequence must end with the value 0.
Every value in the sequence represents a character based on the
ASCII encoding, for example the
character 'a' is 97, 'b' is 98, etc. The character '\0' has
the value 0 and it's the character that determines the end of the string.
That's why you hear a lot that C-Strings are '\0'-terminated.
In C you use an array of chars (char string[], char string[SOME VALUE]) to
save a string. For a string of length n, you need an array of dimension n+1, because
you also need one space for the terminating '\0' character.
When dealing with strings, you always have to think about the proper type,
whether your are using an array or a pointer. A pointer
to char doesn't necessarily mean that you are dealing with a C-String!
Let's take a look at
char str1[] = "Hallo";
char str2[] = { 'H', 'a', 'l', 'l', 'o', 0 };
Both declarations do the same thing, they initialized the arrays str1 and
str2 with 6 elements. str1[5] and str2[5] will be the same: 0 (or '\0'
in its char notation).
A string literal is the text that is wrapped in quotes, "Hello" is a string
literal. Somewhere in the memory of the process the linker puts the sequence
'H', 'a', 'l', 'l', 'o', 0 in memory, usually read-only memory (note the 0
at the end). Even though you don't write the '\0' explicitly, it will have
one.
The '\0' at the end of the sequence of chars is what makes the sequence a
C-String, not the type of the variable. If there is no '\0' at the end, it is not a C-String.
char cs[] = { getc(fl), 0};
is a shorthand of
char cs[2];
cs[0] = getc(fl);
cs[1] = '\0';
By doing the last assignments, I ensure that cs holds a C-String. Most
functions defined in string.h (standard library) expect C-Strings, so they
must be '\0'-terminated.
man strcat
#include <string.h>
char *strcat(char *dest, const char *src);
DESCRIPTION
The strcat() function appends the src string to the dest string,
overwriting the terminating null byte ('\0') at the end of dest, and
then adds a terminating null byte.
From the description you'll see that both dest and src must be strings,
thus '\0'-terminated. That's why I said you should initialize
playTop->name[0] with 0, so that is will be a valid (and empty) string. Only
then I can use strcat.
Luckily there are many ways to archive the same results. If you don't want to
use strcat you could do it also this way:
int name_idx = 0;
while(c!=EOF){
while(c!=':'){
c=getc(fl);
if(c!=':') playTop[n].name[name_idx++]=c;
}
if(c == ':')
{
// make sure that playTop[n].name is a c-string
playTop[n].name[name_idx] = 0;
fscanf(...);
}
...
}
One last thing:
It's not bad using an array for saving strings, the problem is that the
maximum length of the string is dimension or array - 1. In your case the
name must not be longer than 19 characters, otherwise you will have a buffer
overflow and you are going to write over memory that is not allocated for the
name, you will have undefined behaviour and anything could happen.
When you know that the max. length of your string won't pass certain value
(let's say 15), then it's fine to use char name[20]. If there's no guarantee
of a max. length, then you have to dynamically allocate memory for it using
malloc/realloc (and later you would have to free that memory).
Also, a better way to parse your line would be to use fgets to get the while
line and then parse it:
typedef struct STRUCT_PLAYER{char *name;
int sucess;
int numQuest;
int wrongs;} PLAYER;
int readTop(PLAYER playTop[]){
...
char line[1024];
fgets(line, sizeof line, fl);
int colon_index = strchr(line, ':');
// color_index is the index where : is in the line,
// the length of the name is colon_index - 1
// because you have to save \0 as well, you need to
// allocate one more space for the c-string
// name length + 1 ==> (colon_index - 1) + 1
// which equeals to colon_index
playTop[n].name = malloc(colon_index);
// copy the name from line into name. strncpy
// copies at most `colon_index - 1` characters, if
// \0 is not among them, it won't be appended at the end
// you have to do that
strncpy(playTop[n].name, line, colon_index - 1);
// making sure that name becomes a c-string
playTop[n].name[colon_index] = 0;
// sscanf is like fscan, only it takes the content from
// a string and not from a FILE object.
// line + colon_index + 1 ensures that sscanf reads only
// after the colon
sscanf(line + colon_index + 1, "...", ...);
}
Doing it this way, you ensure that the name can have any length. Note that
all these functions may fail: for example malloc might return NULL if
there is not enough memory, strchr might return NULL if a colon is not
found in the line (the line has a wrong format or is empty). The line itself could be
longer than 1024 bytes. For sake of simplicity I've omitted all those checks.
If you've understood my code, then you could make it better by checking for
the errors mentioned above. Read carefully the documentation of the functions
used here.
name is an array of characters and since you are copying character by character, need to use playTop[n].name[x]=c; since name is a character pointer doesn't make sense to compiler when you try to assign a character to it.
Array name is not a modifiable lvalue. You are assigning to it. Violating rules. That's why compiler complained. Even if it was modifiable you are having an incompatibility in type. Assigning a char to char[].
More logical would be to do this
playerTOP[n].nome[some_index]=c;
You have to store those characters in the nome member array of the struct.(which is a char array not char).
Also make int c and then do the check c!=EOF.
the whole, bug ridden:
while(c!=EOF){
while(c!=':'){
c=getc(fl);
if(c!=':') playTop[n].name=c;
}
if(c==':')fscanf(fl, " %d %d\n", &playTop[n].numQuest, &playTop[n].wrongs);
can be easily reduced (and should be reduced) to the clean:
while( 3 == fscanf( fl, " %19[^:]: %d %d",
playTop[n].name,
&playtop[n].numQuest,
&playTop[n].wrongs ) )
{
n++;
}
I am appending a string using single character, but I am not able to get it right. I am not sure where I am making mistake. Thank you for your help in advance. The original application of the method is in getting dynamic input from user.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void main(){
int j;
char ipch=' ';
char intext[30]="What is the problem";
char ipstr[30]="";
printf("Input char: ");
j=0;
while(ipch!='\0'){
//ipch = getchar();
ipch = intext[j];
printf("%c", ipch);
strcat(ipstr,&ipch);
j++;
}
puts("\n");
puts(ipstr);
return;
}
Following is the output I am getting.
$ ./a.out
Input char: What is the problem
What is h e
p
oblem
change
strcat(ipstr,&ipch);
to
strncat(ipstr, &ipch, 1);
this will force appending only one byte from ipch. strcat() will continue appending some bytes, since there's no null termination character after the char you are appending. as others said, strcat might find somewhere in memory \0 and then terminate, but if not, it can result in segfault.
from manpage:
char *strncat(char *dest, const char *src, size_t n);
The strncat() function is similar to strcat(), except that
it will use at most n characters from src; and
src does not need to be null-terminated if it contains n or more characters.
strcat requires its second argument to be a pointer to a well-formed string. &ipch does not point to a well-formed string (the character sequence of one it points to lacks a terminal null character).
You could use char ipch[2]=" "; to declare ipch. In this case also use:
strcat(ipstr,ipch); to append the character to ipstr.
ipch[0] = intext[j]; to change the character to append.
What happens when you pass &ipch to strcat in your original program is that the function strcat assumes that the string continues, and reads the next bytes in memory. A segmentation fault can result, but it can also happen that strcat reads a few garbage characters and then accidentally finds a null character.
strcat() is to concatenate strings... so passing just a char pointer is not enough... you have to put that character followed by a '\0' char, and then pass the pointer of that thing. As in
/* you must have enough space in string to concatenate things */
char string[100] = "What is the problem";
char *s = "?"; /* a proper '\0' terminated string */
strcat(string, s);
printf("%s\n", string);
strcat function is used to concatenate two strings. Not a string and a character. Syntax-
char *strcat(char *dest, const char *src);
so you need to pass two strings to strcat function.
In your program
strcat(ipstr,&ipch);
it is not a valid statement. The second argument ipch is a char. you should not do that. It results in Segmentation Fault.
i ve tried almost everything but i cant solve this problem. I have a struct:
typedef struct{
char firstname[15];
char lastname[20];
char status[1];
char dateOfBirth[10];
} Rec;
and a main:
int main()
{
Rec rec;
strcpy(rec.status,"M");
strcpy(rec.dateOfBirth,"14-11-2000");
strcpy(rec.firstname,"Peter");
strcpy(rec.lastname,"Something");
printf("%s, %s, %s, %s\n", rec.status,rec.dateOfBirth,rec.firstname,rec.lastname);
return 0;
}
So this, should print -> M, 14-11-2000, Peter, Something ..but it doesnt!
It prints -> M14-11-2000, 14-11-2000, Peter, Something.
In other words the "status" member gets "corrupted".
I noticed that if i change the order of the struct's data , the output changes but there is always a data that gets corrupted.
Is there anything wrong in the way that i define my struct? Thx!
You need to make the char arrays 1 byte longer to allow for the null terminator. In particular, the status and dateOfBirth fields should be 2 and 11 bytes respectively based on how you are using them. strcpy copies the given data plus 1 null terminator byte. Without that, the strcpy ends up writing one byte past that member (which would likely be the very next member in this case since char arrays would likely end up with one byte alignment).
In C a string is a char array terminated with a 0-byte, so your char arrays need to be one byte longer.
Your char arrays status and dateOfBirth are too short; you need to account for the terminating null character.
It's because your char arrays are missing null terminators (the status buff doesn't have space for one). printf continues printing til it encounters a null terminator, it does no bounds checking.
I am trying to copy certain parts of a string into other, new strings, but when i try to do it and print the results it gives me weird output.. I really hope someone can help. I have a feeling that it is something about missing pointers.. Here is my source;
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void getData(char code[], char ware[], char prod[], char qual[])
{
printf("Bar code: %s\n", code);
/* Copy warehouse name from barcode */
strncpy(ware, &code[0], 3);
ware[4] = "\0";
strncpy(prod, &code[3], 4);
prod[5] = "\0";
strncpy(qual, &code[7], 3);
qual[4] = "\0";
}
int main(){
/* allocate and initialize strings */
char barcode[] = "ATL1203S14";
char warehouse[4];
char product[5];
char qualifier[4];
getData(&barcode, &warehouse, &product, &qualifier);
/* print it */
printf("Warehouse: %s\nID: %s\nQualifier: %s", warehouse, product, qualifier);
return 0;
}
EDIT:
The wierd output is:
Bar code: ATL1203S14
Warehouse: ATL
ID: ♫203(♫>
Qualifier: S14u♫203(♫>
I think you meant '\0' instead of "\0" and 3 instead of 4:
ware[4] = "\0";
Try:
ware[3] = 0;
Also the & in getData(&barcode, &warehouse...) are useless. Just use getData(barcode, warehouse...);.
You're writing past the end of the chars in your getData() function. You've defined char product[5], which allocates 5 bytes of memory. That gives you array indexes 0,1,2,3,4. In getData, you write the product's null terminator to index 5, which is past the end of product, and will overwrite the next var's first character.
The same applies for barecode, warehouse, and qualifier.
Arrays in C and C++ are zero-based. The last index is one less than the length. You're setting a value in the memory after the array, for each of the arrays ware, prod and qual.
For example, instead of
char warehouse[4];
ware[4] = "\0";
you'd want:
char warehouse[4];
ware[3] = "\0";
getData(&barcode, &warehouse, &product, &qualifier);
This is not the way you should call getData. getData takes pointers, arrays are automatically converted to pointers, so theres no need to use the address-of operator &.
You should use
getData(barcode, warehouse, product, qualifier);
The sizes of the strings inside main() don't include a place for the sentinel.
You need to have:
char warehouse[5];
char product[6];
char qualifier[5];
Also, You are assigning a pointer to the string "\0" into a character, where you should be assigning the character '\0' itself.
I think I'd do things a bit differently. In particular, strncpy is almost never really useful (I'm reasonably certain it was invented for file names in the original Unix FS, and while it fits their specific requirements quite nicely, those requirements are sufficiently unusual that it's rarely good for much of anything else).
Instead, I'd use sscanf: sscanf(code, "%4c%5c%4c", ware, prod, qual);
Your question does not make it clear whether this is really correct. As others have pointed out, you're writing past the ends of the space you've allocated. Above, I've assumed you specified the number of characters you want to copy, so you'd have to expand each of the allocations by one character to make room for the terminator. Alternative, if you've already left room for the terminator and want one fewer character copied, you'd have to reduce each of the lengths above by one so the format string would be "%3c%4c%3c".