In the same file I have two routines. The first will store some bytes from one file. The other will give this information to routines that will process that information.
boolean
adin_memory(char* buffer, int size_chunck, int end_flag){
real_data=(SP16 *)malloc(size_chunck); //real_data -->global
memcpy(&(real_data[0]),&(buffer[0]),size_chunck);
pos_write += size_chunck;
global_size = size_chunck;
global_end_flag = end_flag;
//end_flag = 1 --> end of Stream
//end_flag = 0 --> Streaming
return TRUE;
}
To prevent the possibility of leaking I am using malloc. But this routine is called several times. So, after some repetitions of adin_memory and adin_read (where will be free), I think the memory starts to fragment (I can see a leak with the size of the input file in task manager - increment of RAM). Is that right? How can I prevent this? To see this leak I put one breakpoint at the beginning and at the end of adin_memory an look at task manager.
int
adin_read(SP16 *buf, int sampnum)
{
FILE *fp;
int cnt = 0;
fp = gfp;
//(.......)
if(global_end_flag == 1 || pos_write == pos_read){ return -1;}
for(i = pos_read/sizeof(SP16); i <= sampnum; i++){
if(i >= pos_write/sizeof(SP16)) {
cnt = i;
//(....)
break;
}
buf[i] = real_data[i];
}
pos_write = 0;
//(....)
free(real_data);
return cnt;
}
int
adin_read(SP16 *buf, int sampnum)
{
FILE *fp;
int cnt = 0;
fp = gfp;
//(.......)
if(global_end_flag == 1 || pos_write == pos_read){
/* Leak is possibly here. You return without freeing.
Ensure free is called here also. And it is good practice to
make the freed pointer point to NULL so you can check and
avoid double free problems. */
return -1;
}
for(i = pos_read/sizeof(SP16); i <= sampnum; i++){
if(i >= pos_write/sizeof(SP16)) {
cnt = i;
//(....)
break;
}
buf[i] = real_data[i];
}
pos_write = 0;
//(....)
free(real_data);
return cnt;
}
Difficult to say without further context describing how you use these functions but...
Every time you call your adin_memory() function it will allocate some memory (via a call to malloc) and then set real_data to point to that newly allocated memory.
If real_data was already pointing to some allocated memory then you just leaked it.
So if your main program calls adin_memory() three times and then calls adin_read() then you will leak two blocks of memory and only free the last one.
Change
if(global_end_flag == 1 || pos_write == pos_read){ return -1;}
To
if(global_end_flag == 1 || pos_write == pos_read)
{
free(real_data);
return -1;
}
The problem was indeed in the successive malloc/free (wich cause memory fragmentation). After removed I create a global pointer to the incoming bytes. And make the memcpy in the adin_read()
int adin_read(SP16 *buf, int sampnum)
{
int i;
int cnt = 0;
if(global_end_flag == 1 || pos_write == pos_read){return -1}
memcpy(buf,global_buffer,global_size);
cnt = global_size/sizeof(SP16);
pos_write = 0;
pos_read = 0;
//(....)
return cnt;
}
And then:
boolean
adin_memory(char* buffer, int size_chunck, int end_flag){
pos_write += size_chunck;
global_size = size_chunck;
global_end_flag = end_flag;
global_buffer = buffer;
return TRUE;
}
Related
My program crached on Eclipse when i try to free my object - PokemonTrainer.I have tried the solution in this article, but it didn't help.
PokemonTrainer pokemonTrainerCreate(char* name, Pokemon initial_pokemon,
int max_num_local, int max_num_remote)
{
PokemonTrainer trainer = malloc(sizeof(PokemonTrainer));
if ((name == NULL) || (initial_pokemon == NULL) || (trainer == NULL) ||
(max_num_local < 0) || (max_num_remote < 0))
return NULL;
char tmp_name[strlen(name)];
strcpy(tmp_name, name);
trainer->name = tmp_name;
trainer->max_num_local = max_num_local;
trainer->max_num_remote = max_num_remote;
trainer->pokemons_local = malloc(sizeof(Pokemon)
trainer->max_num_local);
trainer->pokemons_remote = malloc(sizeof(Pokemon)
trainer->max_num_remote);
if (trainer->pokemons_remote == NULL) {
free(trainer->pokemons_local);
return NULL;
} else if (trainer->pokemons_local == NULL) {
free(trainer->pokemons_remote);
return NULL;
}
trainer->pokemons_local[0] = pokemonCopy(initial_pokemon);
trainer->curr_num_local = 1;
trainer->curr_num_remote = 0;
return trainer;
}
void pokemonTrainerDestroy(PokemonTrainer trainer)
{
if (trainer == NULL)
return;
if (trainer->curr_num_local > 0)
for (int i = trainer->curr_num_local - 1; i >= 0; i--)
pokemonDestroy(trainer->pokemons_local[i]);
if (trainer->curr_num_remote > 0)
for (int i = trainer->curr_num_remote - 1; i >= 0; i--)
pokemonDestroy(trainer->pokemons_remote[i]);
free (trainer); // here it's crashed
}
It is during the execution of free() in the stack that I am getting a "No source available for "ntdll!RtlpNtEnumerateSubKey() at 0x77cf04e5" error.
PokemonTrainer trainer = malloc(sizeof(PokemonTrainer)); is unlikely to work properly since you're allocating the size of the pointer, not the real data.
You won't have enough storage => undefined behaviour happens, and for you it happens when freeing the memory (corrupt memory list)
I would do this:
PokemonTrainer trainer = malloc(sizeof(*PokemonTrainer));
so the sizeof takes the size of the structure pointed by PokemonTrainer.
EDIT: for completeness, BLUEPIXY suggests that you've missing 1 byte here (because of null-termination char):
char tmp_name[strlen(name)];
strcpy(tmp_name, name);
and moreover this allocated space is temporary, so I'd suggest:
char *tmp_name = strdup(name);
which will allocate the correct size and performs a dynamic allocation that stays valid even after returning from the routine.
I am trying to make program in C for vertical redundancy check. The Code is given below :
#include<stdio.h>
#include<sys/types.h>
#include<fcntl.h>
#include<unistd.h>
int main()
{
int fd,i;
char *data = "01010101010111110101010101011111";
int count = 0,bit_count=0;
char *parity_bit_array = NULL;
char *data_to_send = NULL;
char *stream_name = "Named Stream";
do
{
if(*data == '1' && bit_count <= 8)
{
count++;
if(bit_count == 8)
{
if( count % 2 == 0)
{
*parity_bit_array = '1';
count = 0;
bit_count = 0;
}
else
{
*parity_bit_array = '0';
count = 0;
bit_count = 0;
}
}
}
bit_count++;
data++;
} while( !data);
do
{
if(bit_count <= 8)
{
*data_to_send++ = *parity_bit_array++;
}
*data_to_send++ = *data;
} while( !data );
printf("%s \n",data_to_send);
mkfifo(stream_name,0666);
fd = open(stream_name,O_WRONLY);
write(fd,data_to_send,sizeof(data_to_send));
close(fd);
unlink(stream_name);
return 0;
}
The file shown below is the sender file of which data is to be read by the receiver.
By using sized array it is working properly but i like to use it with Pointer.
Main Variables in this code :
data : Data on which VRC to be implemented
count : Counting 1 for Even Parity Bit
bit_count : Counting 8 Bits
parity_bit_array : To Collect Parity Bit for Every Single Byte present in data
data_to_send : Combination made by data + parity_bit_array
Ex:
data : 01110000
parity_bit_array : 1
data_to_send : 011100001
You are not allocating memory for your char pointers and you are trying to write to them which will lead to undefined behavior hence segmentation fault.
*parity_bit_array = '1';
There are multiple such cases in this code.
char *data_to_send = NULL;
data_to_send pointer is never allocated memory and you try to write to it
*data_to_send++ = *parity_bit_array++;
Allocate memory to char pointers like
char *data_to_send = malloc(20);
While writing to this array if you see 20 bytes is already written just do realloc() for the same memory
char *temp = realloc(data_to_send,40);
if(temp != NULL)
data_to_send = temp;
There are multiple issues:
} while( !data); is wrong, you should use : } while( *data != 0);
This can indirectly cause segmentation fault(If you are lucky) making the code loop indefinably.
Memory is not allocated to *parity_bit_array and *data_to_send.
Accessing un-allocated memory is undefined behavior and can cause anything including segmentation fault.
write(fd,data_to_send,sizeof(data_to_send)); should be write(fd,data_to_send,sizeof(*data_to_send)); Or something like that as per your logic.
I've got two different arrays that I'm using. With one, I'm getting the exact results that I want, the other, not so much. I'm filing the arrays with by reading from a text file similar to this:
2597
283
4
723
21
82
426
The first five lines would be the customer IDs. There is always 5 lines but they don't always have a value. The next line is the number of vendors, then followed by the vendor ids.
void use_arrays()
{
int
i,
customer_count,
*customer_ids,
vendor_count,
*vendor_ids;
customer_ids = malloc(sizeof(int));
vendor_ids = malloc(sizeof(int));
fill_arrays(&customer_count, customer_ids, &vendor_count, vendor_ids);
for (i = 0; i < customer_count; i++)
{
printf("Customer[%d]: %d\n", i, customer_ids[i]);
}
for (i = 0; i < vendor_count; i++)
{
printf("Vendor[%d]: %d\n", i, vendor_ids[i]);
}
free(customer_ids);
free(vendor_ids);
}
void fill_arrays(int *customer_count, int *customer_ids, int *vendor_count, int *vendor_ids)
{
int
i,
*temp,
customer_id,
vendor_id,
num_cust = 0;
FILE
*inp_file;
char
*endptr = NULL,
buffer[500];
inp_file = fopen(g_filename, "r");
for (i = 0; i < 5; i++) /* Can't be more than 5 customers */
{
fgets(buffer, sizeof(buffer), inp_file);
customer_id = strtol(buffer, &endptr, 0);
if (customer_id != 0)
{
customer_ids[i] = customer_id;
temp = realloc(customer_ids, (i+2)*sizeof(int));
if (temp != NULL)
{
customer_ids = temp;
}
else
{
printf("Couldn't allocate memory\n");
}
num_cust++;
}
}
*customer_count = num_cust;
/* Next is number of vendor ids*/
fgets(buffer, sizeof(buffer), inp_file);
*vendor_count = strtol(buffer, &endptr, 0);
temp = realloc(vendor_ids, *vendor_count*sizeof(int));
if (temp != NULL)
{
vendor_ids = temp;
}
else
{
printf("Couldn't allocate memory\n");
}
for (i = 0; i < *vendor_count; i++)
{
fgets(buffer, sizeof(buffer), inp_file);
vendor_id = strtol(buffer, &endptr, 0);
if (vendor_id != 0)
{
vendor_ids[i] = vendor_id;
}
}
fclose(inp_file);
}
Once the arrays print out, customer_ids is showing the correct numbers but vendor_ids is printing out random numbers from memory. To be more frustrating, it prints the vendors correctly from inside fill_arrays.
If you want to modify vendor_ids the way you do in fill_arrays, then you have to pass it in as a pointer to a pointer:
fill_arrays(int *customer_count, int *customer_ids, int *vendor_count, int **vendor_ids)
Call it like this:
fill_arrays(&customer_count, customer_ids, &vendor_count, &vendor_ids);
Then you can realloc like so:
temp = realloc(*vendor_ids, *vendor_count*sizeof(int));
if (temp != NULL)
{
*vendor_ids = temp;
}
Also, at the end of your function:
vendor_ids[i] = vendor_id;
will have to change to
(*vendor_ids)[i] = vendor_id;
You will also have to make the same changes to customer_ids. The fact that customer_ids was working while vendor_ids wasn't was probably due to your use of realloc. If realloc decides that the memory block has to be reallocated in a new location, you'll run into these problems but if reallocates the memory in the same location, your pointer that you passed in is still pointing there. Since you never know if realloc is going to make that descision or not, both customer_ids and vendor_ids should be passed in as pointers to pointers.
You seem a bit confused how to return memory from a function.
if you have a function that looks like this
void foo(int a);
you can not change a inside of foo, foo only gets a copy of a.
void foo(int *a);
otoh gives foo the address of a, i.e. "i know where you live" it lets you change what a points to. if a points to an array then you can change the contents of that array, if a points to a single integer you can change that.
void foo(int **a);
lets you change not only what a points to, but also where it points. so if you want a to point to somewhere else you can. this is what you need in your function, if you do a malloc/calloc/realloc in your function to return the result you need this
I'm working on a homework assignment and I need to basically create a character buffer. One of the functions I need to create is called "b_reset". It's purpose is to reinitialize the given buffer so that it will point to the first position in the char buffer. This is needed because later on, when a new char is added to the buffer, it needs to be added to the first position in the buffer.
This is the code I have thus far:
The struct:
typedef struct BufferDescriptor {
char * ca_head ;
int capacity ;
char inc_factor;
int addc_offset ;
int mark_offset ;
char r_flag;
char mode;
} Buffer ;
The code:
int b_reset ( Buffer *pB )
{
Buffer *temp = NULL;
int i = 0;
int j = 1;
if (pB == NULL)
{
return R_FAIL_1;
}
else
{
temp = (Buffer*)malloc(sizeof(Buffer*));
if (temp == NULL)
{
return R_FAIL_1;
}
temp->ca_head = (char*)malloc(pB->capacity);
if (!temp->ca_head)
{
temp = NULL;
return R_FAIL_1;
}
for(i = 0;i < ca_getsize(pB);++i)
{
temp->ca_head[j] = pB->ca_head[i];
j++;
}
pB->ca_head = temp->ca_head;
//free(temp->ca_head);
//free(temp);
return 0;
}
}
My goal in this code was to create a temporary buffer that would basically shift over everything 1 time based on the actual given buffer. This would make the first position empty so another char could be added.
The problem I'm running into is that the original buffer doesn't seem to be returning the right values after I reset it.
When I do this for example:
temp->ca_head[0] = 'a';
temp->ca_head[1] = 'b';
temp->ca_head[2] = 'c';
temp->ca_head[3] = 'd';
temp->ca_head[4] = 'e';
b_reset(temp); //this will return the size as 0, when it's actually 5
//temp->ca_head[0] = 'i'; //if this is executed, it returns the size as 6
//and prints out the right values, but if it's not,
//it will not print out anything
printf("%d", ca_getsize(temp));
for(i = 0;i < ca_getsize(temp);++i)
{
printf("%c", temp->ca_head[i]);
}
I know something is going wrong here, but I'm not too sure what. Any suggestions would be greatly appreciated.
This code is based on your followup comment:
well I'm not trying to resize the buffer, I just want to create an
empty space in the first position, so basically shifting everything to
the right 1 time. The assumption is that there is a enough space in
the buffer to handle this process.
I don't think you need to do any malloc() ing beyond the initial one. You can just shift everything up in a loop:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define R_FAIL_1 1
#define BUFFER_SIZE 10
typedef struct BufferDescriptor {
char * ca_head ;
int capacity ;
char inc_factor;
int addc_offset ;
int mark_offset ;
char r_flag;
char mode;
} Buffer ;
void allocate_buffer(Buffer *pB, int size)
{
pB->ca_head = malloc(size);
assert(pB->ca_head);
pB->capacity = size;
}
int ca_getsize( Buffer *pB)
{
return pB->capacity;
}
int b_reset ( Buffer *pB )
{
int i = 0;
if (pB == NULL)
{
return R_FAIL_1;
}
else
{
if ( ca_getsize(pB) <= 0 || pB->ca_head == NULL )
return R_FAIL_1;
}
// shift data up by 1 byte
for( i = ca_getsize(pB) - 1 ; i > 0;i-- )
{
pB->ca_head[i] = pB->ca_head[i-1];
}
pB->ca_head[0] = '\0';
return 0;
}
void print_buffer(Buffer *pB)
{
printf("capacity: %d \n", ca_getsize(pB));
for (int i = 0;i < ca_getsize(pB);++i)
{
printf("buffer(%d): [%d] ",i, pB->ca_head[i]);
}
printf("\n");
}
int main(void)
{
Buffer a_buffer;
allocate_buffer(&a_buffer,BUFFER_SIZE);
strcpy(a_buffer.ca_head,"abcdefgh");
print_buffer(&a_buffer);
int ret = b_reset(&a_buffer);
assert(ret == 0);
print_buffer(&a_buffer);
}
temp = (Buffer*)malloc(sizeof(Buffer*));
You need to allocate enough space to hold a Buffer, but you only allocate enough space to hold a pointer to a buffer. This should be:
temp = (Buffer*)malloc(sizeof(Buffer));
You are managing your memory incorrectly. You are allocating memory for a new Buffer struct when actually you only need to handle the memory of the ca_head member (if my interpretation of your homework problem is correct).
Each time you invoke b_reset, you will allocate memory for this struct that will not be released. If you don't handle your memory correctly, you will experience unexpected results as the one you are reporting in your question.
I suggest you to make a research on the function realloc and use it properly in your b_reset function.
Good luck with your homework.
I am trying to write a Huffman encoding program to compress a text file. Upon completetion, the program will terminate at the return statement, or when I attempt to close a file I was reading from. I assume I have memory leaks, but I cannot find them. If you can spot them, let me know (and a method for fixing them would be appreciated!).
(note: small1.txt is any standard text file)
Here is the main program
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#define ASCII 255
struct link {
int freq;
char ch[ASCII];
struct link* right;
struct link* left;
};
typedef struct link node;
typedef char * string;
FILE * ofp;
FILE * ifp;
int writebit(unsigned char);
void sort(node *[], int);
node* create(char[], int);
void sright(node *[], int);
void Assign_Code(node*, int[], int, string *);
void Delete_Tree(node *);
int main(int argc, char *argv[]) {
//Hard-coded variables
//Counters
int a, b, c = 0;
//Arrays
char *key = (char*) malloc(ASCII * sizeof(char*));
int *value = (int*) malloc(ASCII * sizeof(int*));
//File pointers
FILE *fp = fopen(argv[1], "r");
if (fp == NULL) {
fprintf(stderr, "can't open %s\n", argv[1]);
return 0;
}
//Nodes
node* ptr;//, *head;
node* array[ASCII];
//
int u, carray[ASCII];
char str[ASCII];
//Variables
char car = 0;
int inList = 0;
int placeinList = -1;
int numofKeys;
if (argc < 2) {
printf("Usage: huff <.txt file> \n");
return 0;
}
for (a = 0; a < ASCII; a++) {
key[a] = -1;
value[a] = 0;
}
car = fgetc(fp);
while (!feof(fp)) {
for (a = 0; a < ASCII; a++) {
if (key[a] == car) {
inList = 1;
placeinList = a;
}
}
if (inList) {
//increment value array
value[placeinList]++;
inList = 0;
} else {
for (b = 0; b < ASCII; b++) {
if (key[b] == -1) {
key[b] = car;
break;
}
}
}
car = fgetc(fp);
}
fclose(fp);
c = 0;
for (a = 0; a < ASCII; a++) {
if (key[a] != -1) {
array[c] = create(&key[a], value[a]);
numofKeys = c;
c++;
}
}
string code_string[numofKeys];
while (numofKeys > 1) {
sort(array, numofKeys);
u = array[0]->freq + array[1]->freq;
strcpy(str, array[0]->ch);
strcat(str, array[1]->ch);
ptr = create(str, u);
ptr->right = array[1];
ptr->left = array[0];
array[0] = ptr;
sright(array, numofKeys);
numofKeys--;
}
Assign_Code(array[0], carray, 0, code_string);
ofp = fopen("small1.txt.huff", "w");
ifp = fopen("small1.txt", "r");
car = fgetc(ifp);
while (!feof(ifp)) {
for (a = 0; a < ASCII; a++) {
if (key[a] == car) {
for (b = 0; b < strlen(code_string[a]); b++) {
if (code_string[a][b] == 48) {
writebit(0);
} else if (code_string[a][b] == 49) {
writebit(1);
}
}
}
}
car = fgetc(ifp);
}
writebit(255);
fclose(ofp);
ifp = fopen("small1.txt", "r");
fclose(ifp);
free(key);
//free(value);
//free(code_string);
printf("here1\n");
return 0;
}
int writebit(unsigned char bitval) {
static unsigned char bitstogo = 8;
static unsigned char x = 0;
if ((bitval == 0) || (bitval == 1)) {
if (bitstogo == 0) {
fputc(x, ofp);
x = 0;
bitstogo = 8;
}
x = (x << 1) | bitval;
bitstogo--;
} else {
x = (x << bitstogo);
fputc(x, ofp);
}
return 0;
}
void Assign_Code(node* tree, int c[], int n, string * s) {
int i;
static int cnt = 0;
string buf = malloc(ASCII);
if ((tree->left == NULL) && (tree->right == NULL)) {
for (i = 0; i < n; i++) {
sprintf(buf, "%s%d", buf, c[i]);
}
s[cnt] = buf;
cnt++;
} else {
c[n] = 1;
n++;
Assign_Code(tree->left, c, n, s);
c[n - 1] = 0;
Assign_Code(tree->right, c, n, s);
}
}
node* create(char a[], int x) {
node* ptr;
ptr = (node *) malloc(sizeof(node));
ptr->freq = x;
strcpy(ptr->ch, a);
ptr->right = ptr->left = NULL;
return (ptr);
}
void sort(node* a[], int n) {
int i, j;
node* temp;
for (i = 0; i < n - 1; i++)
for (j = i; j < n; j++)
if (a[i]->freq > a[j]->freq) {
temp = a[i];
a[i] = a[j];
a[j] = temp;
}
}
void sright(node* a[], int n) {
int i;
for (i = 1; i < n - 1; i++)
a[i] = a[i + 1];
}
If your program is crashing on what is otherwise a valid operation (like returning from a function or closing a file), I'll near-guarantee it's a buffer overflow problem rather than a memory leak.
Memory leaks just generally mean your mallocs will eventually fail, they do not mean that other operations will be affected. A buffer overflow of an item on the stack (for example) will most likely corrupt other items on the stack near it (such as a file handle variable or the return address from main).
Probably your best bet initially is to set up a conditional breakpoint on writes to the file handles. This should happen in the calls to fopen and nowhere else. If you detect a write after the fopen calls are finished, that will be where your problem occurred, so just examine the stack and the executing line to find out why.
Your first problem (this is not necessarily the only one) lies here:
c = 0;
for (a = 0; a < ASCII; a++) {
if (key[a] != -1) {
array[c] = create(&key[a], value[a]);
numofKeys = c; // DANGER,
c++; // WILL ROBINSON !!
}
}
string code_string[numofKeys];
You can see that you set the number of keys before you increment c. That means the number of keys is one less than you actually need so that, when you access the last element of code_string, you're actually accessing something else (which is unlikely to be a valid pointer).
Swap the numofKeys = c; and c++; around. When I do that, I at least get to the bit printing here1 and exit without a core dump. I can't vouch for the correctness of the rest of your code but this solves the segmentation violation so anything else should probably go in your next question (if need be).
I can see one problem:
strcpy(str, array[0]->ch);
strcat(str, array[1]->ch);
the ch field of struct link is a char array of size 255. It is not NUL terminated. So you cannot copy it using strcpy.
Also you have:
ofp = fopen("small1.txt.huff", "w");
ifp = fopen("small1.txt", "r");
If small1.txt.huff does not exist, it will be created. But if small1.txt it will not be created and fopen will return NULL, you must check the return value of fopen before you go and read from the file.
Just from counting, you have 4 separate malloc calls, but only one free call.
I would also be wary of your sprintf call, and how you are actually mallocing.
You do an sprintf(buf, "%s%d", buf, c[i]) but that can potentially be a buffer overflow if your final string is longer than ASCII bytes.
I advise you to step through with a debugger to see where it's throwing a segmentation fault, and then debug from there.
i compiled the program and ran it with it's source as that small1.txt file and got "can't open (null)" if the file doesn't exist or the file exist and you give it on the command like ./huf small1.txt the program crashes with:
Program terminated with signal 11, Segmentation fault.
#0 0x08048e47 in sort (a=0xbfd79688, n=68) at huf.c:195
195 if (a[i]->freq > a[j]->freq) {
(gdb) backtrace
#0 0x08048e47 in sort (a=0xbfd79688, n=68) at huf.c:195
#1 0x080489ba in main (argc=2, argv=0xbfd79b64) at huf.c:99
to get this from gdb you run
ulimit -c 100000000
./huf
gdb --core=./core ./huf
and type backtrace
You have various problems in your Code:
1.- mallocs (must be):
//Arrays
char *key = (char*) malloc(ASCII * sizeof(char));
int *value = (int*) malloc(ASCII * sizeof(int));
sizeof(char) == 1, sizeof(char *) == 4 or 8 (if 64 bits compiler is used).
2.- Buffer sizes 255 (ASCII) is too short to receive the contents of array[0]->ch + array[1]->ch + '\0'.
3.- Use strncpy instead of strcpy and strncat instead of strcat.
4.- key is an array of individuals chars or is a null terminated string ?, because you are using this variable in both ways in your code. In the characters counting loop you are using this variables as array of individuals chars, but in the creation of nodes you are passing the pointer of the array and copying as null terminated array.
5.- Finally always check your parameters before used it, you are checking if argc < 2 after trying to open argv[1].