strcat giving unexpected result - c

This is a very small portion of a large project...
These are typedefs defined in standard header file for the project.
typedef uint16_t u_int16_t;
typedef uint32_t u_int32_t;
typedef uint8_t u_int8_t;
Now this is the actual function causing problem ...
void function(u_int8_t *data1, u_int32_t data1len,
u_int8_t *data2, u_int32_t data2len)
{
FILE *fq,*fr,*fs;
char *data3;
int data3len;
data3len=data1len+data2len;
printf("\n%d",data1len);
printf("\n%d",data2len);
printf("\n%d",data3len);
fq=fopen("data1.txt","wb");
fwrite((char *)data1,data1len,1,fq);
fr=fopen("data2.txt","wb");
fwrite((char *)data2,data2len,1,fr);
data3=(char *)data1;
strcat(data3,(char *)data2);
fs=fopen("data3.txt","wb");
fwrite((char *)data3,data3len,1,fs);
}
Some output Snapshots ...
40
14
54
udit#udit-Dabba ~$ hexdump -C data1.txt
00000000 60 00 00 00 00 8c 06 00 00 00 00 00 00 00 00 00 |`...............|
00000010 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 02 |........|
00000028
udit#udit-Dabba ~$ hexdump -C data2.txt
00000000 00 26 00 26 00 00 00 01 00 00 00 02 34 12 00 65 |.&.&........4..e|
00000010 00 34 00 00 61 62 63 64 |.4..abcd|
00000018
udit#udit-Dabba ~$ hexdump -C data3.txt
00000000 60 00 00 00 00 8c 06 00 00 00 00 00 00 00 00 00 |`...............|
00000010 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 00 |................|
00000020 00 00 00 00 00 00 00 02 00 78 f8 65 00 00 00 02 |.........x.e....|
00000030 f4 1f 96 00 18 34 a6 bf 1c 03 96 00 88 f1 90 08 |.....4..........|
00000040
Why contents of data2.txt are not copied to data3.txt ??? If there is any other possible way then please tell me !!!! Thanx in advance ...

Why contents of data2.txt are not copied to data3.txt?
strcat is specifically for concatenating C strings and only copies up until the null terminator character. So it stops copying from the source once it encounters 00 which it considers the end of the string. Notice how data2 begins with a 00, so it stops immediately.
You'll instead need to memcpy with the destination being 1 past the last byte in data3 and the source being data2. You'll also need to fail gracefully if data3 (which is really data1) does not have enough space to hold data2.

I think you could do it much easier, saving you from ensuring that the respective memory block is large enough.
void function(u_int8_t *data1, u_int32_t data1len,
u_int8_t *data2, u_int32_t data2len)
{
FILE *fq,*fr,*fs;
char *data3;
fq=fopen("data1.txt","wb");
fwrite((char *)data1, data1len, 1, fq);
fclose(fq);
fr=fopen("data2.txt","wb");
fwrite((char *)data2, data2len, 1, fr);
fclose(fr);
fs=fopen("data3.txt","wb");
fwrite((char *)data1, data1len, 1, fs);
fwrite((char *)data2, data2len, 1, fs);
fclose(fs);
}

You try to append data2 to the end of data1.
You intend to put the result into a third string (data3), but in fact data3 is just data1; they point to the same location.
Your code is equivalent to
strcat (data1, data2);
which of course writes beyond data1's end.
(there is another problem: if the caller of the function uses a "string_literal" as argument#1,
data1 will not be writable. )
You probably have to allocate some memory for the resulting string and make point data3 to it.

Related

C hexadecimal formatting giving me extra digits

I've been working on a small program to dump the hex values of a file, similar to od or hexdump, and I've ran into an issue when printing the values. For certain hex values, mostly ones with alphabetical characters as their second digit, it prints with 6 extra f digits before printing the actual number, instead of just printing a 2-width value like I had specified. I have confirmed that the values themselves are not unexpected in any way, it's the printing that's messing it up.
Code:
int main(int argc, char* argv[]) {
FILE* dataFile = fopen(argv[1], "rb");
int byteCount = 0;
char currentByte = fgetc(dataFile);
while (currentByte != EOF) {
printf("%08d", byteCount);
do {
//print as hex
printf(" %02x ", currentByte);
//set up for next loop
currentByte = fgetc(dataFile);
byteCount++;
} while (currentByte != EOF && (byteCount) % 16 != 0);
printf("\n");
}
printf("%08d\n", byteCount);
}
Output:
00000000 7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00
00000016 03 00 3e 00 01 00 00 00 10 6b 00 00 00 00 00 00
00000032 40 00 00 00 00 00 00 00 08 23 02 00 00 00 00 00
00000048 00 00 00 00 40 00 38 00 0d 00 40 00 1f 00 1e 00
00000064 06 00 00 00 04 00 00 00 40 00 00 00 00 00 00 00
00000080 40 00 00 00 00 00 00 00 40 00 00 00 00 00 00 00
00000096 ffffffd8 02 00 00 00 00 00 00 ffffffd8 02 00 00 00 00 00 00
00000112 08 00 00 00 00 00 00 00 03 00 00 00 04 00 00 00
00000128 18 03 00 00 00 00 00 00 18 03 00 00 00 00 00 00
00000144 18 03 00 00 00 00 00 00 1c 00 00 00 00 00 00 00
00000160 1c 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
00000176 01 00 00 00 04 00 00 00 00 00 00 00 00 00 00 00
00000192 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00
00000208 fffffff8 34 00 00 00 00 00 00 fffffff8 34 00 00 00 00 00 00
Does anyone know what's going on here?
There are several problems here.
char is entirely unsuitable to hold raw binary data, because it has implementation-defined signedness and it often a signed type, see Is char signed or unsigned by default?. In fact char should only ever be used for text strings. Instead, you would normally use uint8_t. However... :
The EOF constant is of type int, so if you intend to compare something against EOF, it must be type int. fgetc is guaranteed to return characters as if they were unsigned char but converted to int - as it happens, int is large enough to hold all values of an unsigned char so int can be used here.
%x specifier for printf expects an unsigned int.
Fixes:
char currentByte -> int currentByte
printf(" %02x ", currentByte); -> printf(" %02x ", (unsigned int)currentByte);.
You must always check if fopen was successful and also fclose the file pointer when done. Also note that calling fopen directly on argv with no input sanitation is not acceptable in a professional program. You don't even know if argv[1] exists - the program needs a whole lot more error handling overall.

Write() in a file, the words occur twice

I am current writing a file system, and init the disk. I am trying to write into the disk block.
int main(){
char name[] = "disk";
create_disk(name); // fd already define in global var;
open_disk(name);
write(fd,"one",16);
write(fd,"two",16);
write(fd,"three",16);
close(fd);
}
Here is the outcome in the file
IN HEX
Offset: 00 01 02 03 04 05 06 07 08 09 0A 0B 0C 0D 0E 0F
00000000: 6F 6E 65 00 74 77 6F 00 74 68 72 65 65 00 00 00 one.two.three...
00000010: 74 77 6F 00 74 68 72 65 65 00 00 00 00 14 00 00 two.three.......
00000020: 74 68 72 65 65 00 00 00 00 14 00 00 00 00 00 00 three...........
00000030: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
00000040: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
My question: I only write "one", "two", "three" once each, why it giving me multiple copies of "three", "two".
How can I make my outcome looks like this?
one.............
two.............
three...........
................
................
You're writing 16 bytes from a much shorter string (i.e. reading memory you should not be). That's undefinable behavior.
Beware the nasal demons.
What you really want is something like this:
char buffer[16] = { 0 };
strcat(buffer, "one");
write(fd,buffer,16);
Now you are writing 16 chars out of a 16 char buffer which is perfectly ok.

Cannot read struct from binary file

I am trying to read the contents of a binary file into a struct, but each time I print out the contents of my struct, I get a pretty strange output. Below is the struct I am trying to set:
struct student {
char name[32];
unsigned int age;
SEX sex;
float gpa;
struct course *courses;
struct student *next;
}
With the course struct defined as:
struct course {
char grade;
unsigned int number;
struct course *next;
}
And SEX defined as:
typedef enum _SEX {MALE = 'M', FEMALE = 'F', OTHER = 'O'} SEX;
Right now, my function is as follows:
void read_bin(char *filename){
FILE *file;
struct student myStudent;
file = fopen(filename, "rb");
if(file == NULL){
printf("Unable to open file!");
return;
}
fread(&myStudent, sizeof(struct student), 1, file);
printf("\nName: %s, Age: %d", myStudent.name, myStudent.age);
fclose(file);
}
But I keep on getting this strange output:
Age: 0
With name not even showing up and age set to an incorrect number. I thought this might be due to padding, so I tried using
fread(&myStudent.name, sizeof myStudent.name, 1, file);
fread(&myStudent.age, sizeof myStudent.age, 1, file);
So that it reads each individual element and pads as needed. However, I get the same output. When I ran hexdump to see what exactly I was dealing with, this is what came out:
f0 0d 03 00 00 00 46 72 65 64 00 00 00 00 00 00 |......Fred......|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00 00 00 00 00 00 1d 00 00 00 4d 00 00 00 cd cc |..........M.....|
4c 40 01 00 00 00 44 02 00 00 41 4a 6f 65 00 00 |L#....D...AJoe..|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00 00 00 00 00 00 00 00 00 00 00 16 00 00 00 4d |...............M|
00 00 00 33 33 73 40 03 00 00 00 6e 00 00 00 42 |...33s#....n...B|
dc 00 00 00 41 54 01 00 00 41 53 61 72 61 68 00 |....AT...ASarah.|
00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00 00 00 00 00 00 00 00 00 00 16 00 00 00 46 00 |..............F.|
00 00 00 00 40 40 03 00 00 00 78 00 00 00 42 dc |....##....x...B.|
00 00 00 41 4a 01 00 00 43 |...AJ...C|
Any help would be greatly appreciated -- I've been stuck on this function for awhile.
Depending on the filetype the first few bytes can be a fileheader, here information can be stored like version numbers, length of the data etc.
As for the pointers, that won't work like others pointed out since pointers are adresses, not actual relevant data.
You want to fseek to sizeof(fileheader), before you start reading the file.
Also be wary that structs are padded in C and that the order in which you declared the elements in the struct might not be the order the compiler decided for it.

Linker adds extra padding in section?

I am using GCC and am trying to get the linker to create an array of data objects from different files in a custom section. I have added this section to the linker script:
.mydata :
{
__mydata_start = .;
KEEP (*(.mydata))
__mydata_end = .;
}
Here is my data structure:
struct my_type_t {
unsigned char a;
size_t b;
void *c;
const void *d;
const void *e;
const char *f;
};
I am on a 64-bit platform, and sizeof(struct my_type_t) gives 48. However, the linker seems to want to align these structures to 64-bytes.
My declarations:
struct my_type_t a1 __attribute__((section(".mydata"))) = {
1, 2, (void *)3, (void *)4, (void *)5, (void *)6,
};
struct my_type_t a2 __attribute__((section(".mydata"))) = {
7, 8, (void *)9, (void *)10, (void *)11, (void *)12,
};
Linker output:
$ gcc -o ldtest ldtest.c -Wl,-Tlink.ld
$ objcopy -Obinary -j .mydata ldtest mydata
$ hd mydata
00000000 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 |................|
00000010 03 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 |................|
00000020 05 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 |................|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 07 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 |................|
00000050 09 00 00 00 00 00 00 00 0a 00 00 00 00 00 00 00 |................|
00000060 0b 00 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 |................|
If I instead declare it as an array:
struct my_type_t a1[2] __attribute__((section(".mydata"))) = {
{
1, 2, (void *)3, (void *)4, (void *)5, (void *)6,
},
{
7, 8, (void *)9, (void *)10, (void *)11, (void *)12,
},
};
There is no padding, as expected:
00000000 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00 |................|
00000010 03 00 00 00 00 00 00 00 04 00 00 00 00 00 00 00 |................|
00000020 05 00 00 00 00 00 00 00 06 00 00 00 00 00 00 00 |................|
00000030 07 00 00 00 00 00 00 00 08 00 00 00 00 00 00 00 |................|
00000040 09 00 00 00 00 00 00 00 0a 00 00 00 00 00 00 00 |................|
00000050 0b 00 00 00 00 00 00 00 0c 00 00 00 00 00 00 00 |................|
Why is the linker adding extra padding beyond what the compiler thinks is necessary, and how can I get rid of it?
I don't know why it wants it aligned on 64-byte, but you can do:
struct my_type_t {
unsigned char a;
size_t b;
void *c;
const void *d;
const void *e;
const char *f;
} __attribute__ ((aligned (64)));
To make gcc automatically pad each struct to the nearest higher 64-byte address and make sizeof(struct my_type_t) also include the padding. This makes operations like this work correctly:
struct my_type_t *s = &__mydata_start;
s++; // Jump to next entry
s[0]; // First entry
s[1]; // Second entry

How can I write a structure to a file ?

I have tried to look this up in multiple places and I cannot understand why fwrite doesn't work.
If I had a structure with 100 fields I would not want to use fprintf with 100 format specifiers.
struct emp
{
char name[15];
int age;
int salary;
char address[30];
};
int main()
{
char str[60];
struct emp emp1[5] = {{"Yoda",23,45000,"Asia"},{"Darth",34,2344,"NAmerica"},{"Jabba",22,5566,"Africa"},{"Luke",33,3399,"SAmerica"},{"Laya",44,6677,"Europe"}};
FILE *fp;
fp = fopen("C:/.../sampleText.txt","w");`
int i=0;
for(i=0; i<5; i++)
{
fwrite(&emp1[i],sizeof(emp1[i]),1,fp);
//fprintf(fp,"%s, %d, %d, %s\n",&emp1[i].name,emp1[i].age,emp1[i].salary,emp1[i].address);
}
fclose(fp);
getch();
}
There are two answers:
It does work, if everything is set correctly and porting the written data to other machines is not an issue.
It doesn't work if you have any of a large number of common features in data structures, or if you need to move the data from one type of machine (say an Intel machine) to another type (say PowerPC or SPARC).
In your example structure, you have no pointers, so you could write the structure verbatim to a file, and then in another invocation of the program running on the same (type of) machine, you could read it back in, and you would see the same data.
However, if your structure contained pointers, you could not meaningfully write the structure to disk. The pointers in one invocation of the program need not have any significance in another invocation of the program. If you needed to port the data between a little-endian (Intel) and big-endian (PowerPC, SPARC) machine, you'd have to use a platform-neutral way of accessing the data; simply writing the data to disk would not work.
So, where portability is not an issue, this code should work — Unix or Windows. It uses the "wb" and "rb" arguments to fopen() because the data is binary data, not plain text. The b is optional but harmless on Unix; it is crucial on Windows. The code also fixes the file name to sampledata.bin so it can be run on either platform, writing in the current directory. It writes the data; it then reads the data; it then compares the read data with the written data, reporting any problems. If the program says nothing, all is OK.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct emp
{
char name[15];
int age;
int salary;
char address[30];
};
int main(void)
{
char const filename[] = "sampledata.bin";
struct emp emp1[5] =
{
{ "Yoda", 23, 45000, "Asia" },
{ "Darth", 34, 2344, "N America" },
{ "Jabba", 22, 5566, "Africa" },
{ "Luke", 33, 3399, "S America" },
{ "Leia", 44, 6677, "Europe" },
};
struct emp emp2[5];
FILE *ifp;
FILE *ofp;
int i;
ofp = fopen(filename, "wb");
if (ofp != 0)
{
if (fwrite(emp1, sizeof(emp1), 1, ofp) != 1)
{
fprintf(stderr, "Failed to write to %s\n", filename);
exit(1);
}
fclose(ofp);
}
ifp = fopen(filename, "rb");
if (ifp != 0)
{
if (fread(emp2, sizeof(emp2), 1, ifp) != 1)
{
fprintf(stderr, "Failed to read from %s\n", filename);
exit(1);
}
fclose(ifp);
}
for (i = 0; i < 5; i++)
{
if (emp1[i].age != emp2[i].age ||
emp1[i].salary != emp2[i].salary ||
strcmp(emp1[i].name, emp2[i].name) != 0 ||
strcmp(emp1[i].address, emp2[i].address) != 0)
printf("Difference in record %d\n", i);
}
return 0;
}
Content of the file sampledata.bin:
0x0000: 59 6F 64 61 00 00 00 00 00 00 00 00 00 00 00 00 Yoda............
0x0010: 17 00 00 00 C8 AF 00 00 41 73 69 61 00 00 00 00 ........Asia....
0x0020: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0030: 00 00 00 00 00 00 00 00 44 61 72 74 68 00 00 00 ........Darth...
0x0040: 00 00 00 00 00 00 00 00 22 00 00 00 28 09 00 00 ........"...(...
0x0050: 4E 20 41 6D 65 72 69 63 61 00 00 00 00 00 00 00 N America.......
0x0060: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0070: 4A 61 62 62 61 00 00 00 00 00 00 00 00 00 00 00 Jabba...........
0x0080: 16 00 00 00 BE 15 00 00 41 66 72 69 63 61 00 00 ........Africa..
0x0090: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00A0: 00 00 00 00 00 00 00 00 4C 75 6B 65 00 00 00 00 ........Luke....
0x00B0: 00 00 00 00 00 00 00 00 21 00 00 00 47 0D 00 00 ........!...G...
0x00C0: 53 20 41 6D 65 72 69 63 61 00 00 00 00 00 00 00 S America.......
0x00D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x00E0: 4C 65 69 61 00 00 00 00 00 00 00 00 00 00 00 00 Leia............
0x00F0: 2C 00 00 00 15 1A 00 00 45 75 72 6F 70 65 00 00 ,.......Europe..
0x0100: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
0x0110: 00 00 00 00 00 00 00 00 ........
0x0118:
You don't specify what you mean by fwrite doesn't work, but I'll assume you're working on Windows, in which case you need to specify "wb" to fopen. By default on Windows, it's writing in text mode (i.e. "wt").
not a good idea to write struct to file or sockets as it is. It is inviting complex to solve problems. the best approach is to use serialization before writing. Also, as Jim pointed out above, make sure to open the file in binary.
Take a look in this question and the answers. there is a pretty good answer and explanation for your question.
Passing a structure through Sockets in C
Data serialization is a non-trivial task. As some others have pointed out, it is possible in some cases to write the contents of your struct to disk as binary data. It's the simplest to write, but it is unlikely to be stable. Each time you recompile your code, it can potentially change the format the data is written and read in.
Your best option is to use a standard data interchange format, such as CSV, XML, or JSON. There are many existing tools to utilize these formats, so you should look into using one of them.

Resources