I am sending a string formatted like so from one program to another: number lastname firstname middlei ID GPA
.
The receiving program sees the correct string and this string is stored into a buffer. The string is tokenized and the number used in a switch statement. The corresponding case's code is shown below.
tok = strtok( arg, " " );
printf( "the first token is %s\n", tok );
memcpy( ptr->student.lname, tok, 10 );
tok = strtok( NULL, " " );
memcpy( ptr->student.fname, tok, 10 );
tok = strtok( NULL, " " );
ptr->student.initial = *tok;
tok = strtok( NULL, " " );
sscanf( tok, "%lu", &ptr->student.SID );
tok = strtok( NULL, " " );
sscanf( tok, "%f", &ptr->student.GPA );
// few more pointer and location initializations...
printf( "lu %s %s %c %f\n", ptr->student.SID, ptr->student.lname,
ptr->student.fname, ptr->student.initial, ptr->student.GPA );
The input string is: 5 lastnamereallylong,firstnamereallylong,X,1234,4.0
Which the first program shortens to: 5 lastnamere firstnamer X 1234 4.0
The second program's output is as follows:
the first tok is lastnamere
1234 lastnamereXfirstnamer firstnamer X 4.000000
Can someone please help me figure out what is going on here? I could understand not using memcopy
correctly but I wouldn't think that that would change the order of the original information.
What's going on here is undefined behavior: you are telling memcpy
to copy the first ten characters of whatever the token is, but when the token is more than ten characters long, it means that the string is not going to be null-terminated. Passing such strings to printf("%s", ...)
is undefined behavior.
To fix this problem, you should either force termination by setting ptr->student.lname[9] = '\0'
if you want to stay with fixed-length strings, or use strdup
to allow variable-length strings.
Finally, there is a thread-safe alternative to using strtok
- you can use strtok_r
.