I have a doubly linked list data structure which is well formulated, as I have tested it on different inputs.
But, here is a piece of code where I have problem. I am trying to read in a line of string from a file, and store the values by converting them into integers in an array of integer called num. The code is fine unto here as I have printed the entries out and checked them. But when I try storing them into my doubly linked list, I get all the values correct except the second value which is an arbitrary long integer. I have my code and output below:
num_read is a function which reads all the values in a doubly linked list, and takes the array num and its length as arguments.
#include "DlistInterface.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
Dnode *num_read(int *,int *);
int main(){
Dnode *number;
int num[100];
char a[10];
char *lineptr = NULL;
int j = 0;
int i = 0;
int index = 0;
size_t len;
ssize_t read;
FILE *fp;
fp = fopen("Dlist_v_array.txt","r");
while ((read = getdelim(&lineptr,&len,32,fp)) != -1){
i = 0;
index = 0;
while (1){
if ((lineptr[i] == ' ') || (lineptr[i] == '\n')) break;
else a[index++] = lineptr[i++];
}
a[index] = '\0';
num[j] = atoi(a);
printf("%d ",num[j]);
j++;
}
//printf("\n %d",j);
printf("\n");
fclose(fp);
free(lineptr);
number = num_read(num, &j);
while (number != NULL){
printf("%d ",number->data);
number = number->next;
}
printf("\n");
return 0;
}
Dnode *num_read(int *n, int *len){
int k;
for (k=1; k< *len;k++){
//printf("%d ",n[k]);
}
printf("\n");
Dnode *number = (Dnode *)malloc(sizeof(Dnode *));
number->data = n[0];
number->next = NULL;
number->prev = NULL;
Dnode *another_node;
Dnode *temp;
another_node = number;
k = 1;
while(k < *len){
temp = (Dnode *)malloc(sizeof(Dnode));
temp->data = n[k++];
temp->next = NULL;
another_node->next = temp;
temp->prev = another_node;
another_node = temp;
}
return number;
}
I get this as the output:
12 21 33 4 5 6 7 8 9
12 -2109715456 33 4 5 6 7 8 9
The first line of output is because the verification of values read in the number array, which is correct according to the values in the .txt file.
But the same values on the next line have their corresponding second entries to be a weird number, which happens to be their always, no matter what I do.
I desperately need help on this one. I will be very thankful to you all for the help. Please help!!!
Incorrect amount of memory assigned #BLUEPIXY.
// Dnode *number = (Dnode *)malloc(sizeof(Dnode *));
Dnode *number = (Dnode *)malloc(sizeof(Dnode));
To avoid this mistake in the future, and to create simpler original and maintainable code, use;
// pointer = malloc(sizeof *pointer * number_elements)
Dnode *number = malloc(sizeof *number); // number_elements == 1 in OP's case
// check pointer
if (number == NULL) return NULL; // detect and handle out-of-memory somehow
This has advantages
In C, the cast on malloc() is not needed.
by using the sizeof *pointer rather than sizeof(element_type), less chance to get the wrong type, as OP did in this post, and less to update should the type change.
By using the sizeof() first, the memory needs are calculated in at least size_t math. This becomes important with large programs: Consider int h,w; malloc(sizeof *pointer*h*w) vs. malloc(h*w*sizeof *pointer) where h*w overflowed int math, but not size_t math.
Always check for out-of-memory. At a minimum, it saves time in debugging as at least you know, proper memory allocated. Note: on some systems, if number_elements is 0 (and so is sizeof *pointer * number_elements) allocating 0 bytes may return NULL and that is not an out of memory condition.
pointer = malloc(sizeof *pointer * number_elements)
if (pointer == NULL && number_elements > 0) Handle_OOM();
Related
I'm writing a program that is supposed to assign characters from a buffer into a hash-table. I ran valgrind on my program and it signals to a particular line (tmp->word = buffer[i];) and keeps telling me there is a segmentation fault there.
I tried hardcoding the problem line to (tmp->word = 'c';) but the compiler rejected that implementation. I checked to see if the buffer array was initialized, which it was. The program compiles when the problem line is changed to (tmp->word = buffer[i];) but that leads back to a segmentation fault. I have also tried printing the character field in my data structure after I assign it, but the segmentation fault occurs before that can happen. This is what I've written so far. Any help would be greatly appreciated.
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct node
{
struct node* next;
char word;
}
node;
void unload(node* current);
int main(void)
{
node* table[26];
char buffer[5] = "Hello";
printf("%s\n", buffer);
int index = tolower(buffer[0]) - 'a';
node* tmp = table[index];
for(int i = 0, n = strlen(buffer); i < n - 1; tmp = tmp->next)
{
tmp->word = buffer[i];
printf("%c\n", tmp->word);
i++;
}
//follows word that was input
index = tolower(buffer[0]) - 'a';
for(int j = 0; j < 1; j++)
{
tmp = table[index]->next;
unload(tmp);
}
}
void unload(node* current)
{
if (current->next != NULL)
{
unload(current->next);
}
free(current);
}
As was mentioned before in the comments and answer, my array of pointers wasn't initialized. This was a part of the main problem I had which was experiencing a segmentation fault when I tried to assign table[i]->word and table[i]->next a value. No memory was allocated for the nodes in the table so I went and did that which fixed most the problems! Something I learned, however, is that I could not assign a string to table[i]->word which is an array of characters in my now less buggy program, and instead had to use strcpy to read a string into that memory space (please correct me if I'm wrong on that). Thank you all for your help and advice, it was really useful! Posted below is the version of my program that actually works save for a conditional that I need to implement thanks to your guys' help! Again, thank you very much!
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <cs50.h>
#define LENGTH 45
#define CAPACITY 26
typedef struct node
{
struct node* next;
char word[LENGTH + 1];
}
node;
int hash(char* current);
void unload(node* current);
int main(void)
{
//const unsigned int N = 26;
node* table[CAPACITY];
for(int i = 0; i < CAPACITY; i++)
{
table[i] = malloc(sizeof(node)); // this was table[i]->next before. I didn't allocate memory for this node and then tried to defrefernce to a field in a node I though existed
if(table[i] == NULL)
{
printf("Could not allocate memory for hashtable node");
for(int j = 0; j < CAPACITY; j++)
{
unload(table[i]);
}
return 1;
}
table[i]->next = NULL; //just reinitializing the value here so that its not a garbage value
//table[i]->word = "NULL";
}
int q = 0;
while(q < 3) //ths will be chnaged so that we're reading from a file into a buffer and that get_string is gone, hash() stays tho
{
char* name = get_string("Here: ");
int index = hash(name);
if(table[index]->next == NULL)
{
node* cursor = malloc(sizeof(node)); //I'm not executing this if the hash code is the same
table[index]->next = cursor;
strcpy(cursor->word, name); //for some reason you can't just assign a value to an element in an array in a data structure, seems that you need to read the data you want to assign into the location of the element
//cursor->word = name;
cursor->next = NULL;
printf("%s\n", cursor->word);
}
q++;
}
for(int i = 0; i < CAPACITY; i++)
{
unload(table[i]); //wonder why I don't need to do table[i]->next? does this not free the hash table after the first iteration?
// the answer to above is because we are only freeing the node which is that element in the array, not the array itself
}
strcpy(table[6]->word, "Zebra");
printf("%lu\n", sizeof(table[6]));
printf("%s\n", table[6]->word);
/*for(int i = 0; i < CAPACITY; i++)
{
unload(table[i]); //only problem here is that it will eventually free the hash table itself after the first linked list
}*/
}
int hash(char* current)
{
int index = tolower(current[0]) - 'a';
return index;
}
void unload(node* current)
{
if (current->next != NULL)
{
unload(current->next);
}
free(current);
}
As is the comments the main problem is an array of uninitialized pointers. As "intuitive way" while you are coding you may think the once you typed node* table[26]; as int type variables, this will be set to NULL automatically as a pattern behavior or something.But it points to a random location in memory when you declare it. It could be pointing into the system stack, or the global variables, or into the program's code space, or into the operating system.
So, you must give them something to point to and in this case is a NULL. You can do it like this node* table[26] = {NULL};. Another point is when you type char buffer[5] = "Hello";. The char buffer[5] essentially is a pointer pointing to a memory address that the system saves so you can put your string. The memory is saved in blocks so when you type char buffer[1] for example you "jump" to the second part of the entire block which represents the string.
When you do char buffer[5] = "Hello"; " it's sounds like " you are trying to make the Hello string fit in the last piece of the block. To fix this just type char buffer[6] = {"Hello"};(Because you need n+1 of size, you have to include the \0 character). And it will fit properly. Now i think you can figure out how to do the rest.
I am really trying to learn if someone wouldn't mind to educate me in the principles I may be missing out on here. I thought I had everything covered but it seems I am doing something incorrectly.
The following code gives me a segmentation fault, and I cannot figure out why? I am adding the & in front of the arguments name being passed in to fscanf.
int word_size = 0;
#define HASH_SIZE 65536
#define LENGTH = 45
node* global_hash[HASH_SIZE] = {NULL};
typedef struct node {
char word[LENGTH + 1];
struct node* next;
} node;
int hash_func(char* hash_val){
int h = 0;
for (int i = 0, j = strlen(hash_val); i < j; i++){
h = (h << 2) ^ hash_val[i];
}
return h % HASH_SIZE;
}
bool load(const char *dictionary)
{
char* string;
FILE* dic = fopen(dictionary, "r");
if(dic == NULL){
fprintf(stdout, "Error: File is NULL.");
return false;
}
while(fscanf(dic, "%ms", &string) != EOF){
node* new_node = malloc(sizeof(node));
if(new_node == NULL){
return false;
}
strcpy(new_node->word, string);
new_node->next = NULL;
int hash_indx = hash_func(new_node->word);
node* first = global_hash[hash_indx];
if(first == NULL){
global_hash[hash_indx] = new_node;
} else {
new_node->next = global_hash[hash_indx];
global_hash[hash_indx] = new_node;
}
word_size++;
free(new_node);
}
fclose(dic);
return true;
}
dictionary.c:25:16: runtime error: left shift of 2127912344 by 2 places cannot be represented in type 'int'
dictionary.c:71:23: runtime error: index -10167 out of bounds for type 'node *[65536]'
dictionary.c:73:13: runtime error: index -10167 out of bounds for type 'node *[65536]'
dictionary.c:75:30: runtime error: index -22161 out of bounds for type 'node *[65536]'
dictionary.c:76:13: runtime error: index -22161 out of bounds for type 'node *[65536]'
Segmentation fault
Update after OP posted more code
The problem is that your hash_func works with signed integers and that it overflows. Therefore you get a negative return value (or rather undefined behavior).
That is also what these lines tell you:
dictionary.c:25:16: runtime error: left shift of 2127912344 by 2 places cannot be represented in type 'int'
Here it tells you that you have a signed integer overflow
dictionary.c:71:23: runtime error: index -10167 out of bounds for type 'node *[65536]'
Here it tells you that you use a negative index into an array (i.e. global_hash)
Try using unsigned integer instead
unsigned int hash_func(char* hash_val){
unsigned int h = 0;
for (int i = 0, j = strlen(hash_val); i < j; i++){
h = (h << 2) ^ hash_val[i];
}
return h % HASH_SIZE;
}
and call it like:
unsigned int hash_indx = hash_func(new_node->word);
Original answer
I'm not sure this is the root cause of all problems but it seems you have some problems with memory allocation.
Each time you call fscanf you get new dynamic memory allocated for string du to %ms. However, you never free that memory so you have a leak.
Further, this looks like a major problem:
global_hash[hash_indx] = new_node; // Here you save new_node
} else {
new_node->next = global_hash[hash_indx];
global_hash[hash_indx] = new_node; // Here you save new_node
}
word_size++;
free(new_node); // But here you free the memory
So it seems your table holds pointers to memory that have been free'd already.
That is a major problem that may cause seg faults when you use the pointers.
Maybe change this
free(new_node);
to
free(string);
In general I'll suggest that you avoid %ms and also avoid fscanf. Use char string[LENGTH + 1] and fgets instead.
There are multiple issues in the code posted. Here are the major ones:
you should use unsigned arithmetic for the hash code computation to ensure that the hash value is positive. The current implementation has undefined behavior as words longer than 15 letters cause an arithmetic overflow, which may produce a negative value and cause the modulo to be negative as well, indexing outside the bounds of global_hash.
You free the newly allocated node with free(new_node);. It has been stored into the global_hash array: later dereferencing it for another word with the same hash value will cause undefined behavior. You probably meant to free the parsed word instead with free(string);.
Here are the other issues:
you should check the length of the string before copying it to the node structure array with strcpy(new_node->word, string);
fscanf(dic, "%ms", &string) is not portable. the m modifier causes fscanf to allocate memory for the word, but it is an extension supported by the glibc that may not be available in other environments. You might want to write a simple function for better portability.
the main loop should test for successful conversion with while(fscanf(dic, "%ms", &string) == 1) instead of just end of file with EOF. It may not cause a problem in this specific case, but it is a common cause of undefined behavior for other conversion specifiers.
the definition #define HASH_SIZE 65536; has a extra ; which may cause unexpected behavior if HASH_SIZE is used in expressions.
the definition #define LENGTH = 45; is incorrect: the code does not compile as posted.
Here is a modified version:
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#define HASH_SIZE 65536
#define LENGTH 45
typedef struct node {
char word[LENGTH + 1];
struct node *next;
} node;
int word_size = 0;
node *global_hash[HASH_SIZE];
unsigned hash_func(const char *hash_val) {
unsigned h = 0;
for (size_t i = 0, j = strlen(hash_val); i < j; i++) {
h = ((h << 2) | (h >> 30)) ^ (unsigned char)hash_val[i];
}
return h % HASH_SIZE;
}
/* read a word from fp, skipping initial whitespace.
return the length of the word read or EOF at end of file
store the word into the destination array, truncating it as needed
*/
int get_word(char *buf, size_t size, FILE *fp) {
int c;
size_t i;
while (isspace(c = getc(fp)))
continue;
if (c == EOF)
return EOF;
for (i = 0;; i++) {
if (i < size)
buf[i] = c;
c = getc(fp);
if (c == EOF)
break;
if (isspace(c)) {
ungetc(c, fp);
break;
}
}
if (i < size)
buf[i] = '\0';
else if (size > 0)
buf[size - 1] = '\0';
return i;
}
bool load(const char *dictionary) {
char buf[LENGTH + 1];
FILE *dic = fopen(dictionary, "r");
if (dic == NULL) {
fprintf(stderr, "Error: cannot open dictionary file %s\n", dictionary);
return false;
}
while (get_word(buf, sizeof buf, dic) != EOF) {
node *new_node = malloc(sizeof(node));
if (new_node == NULL) {
fprintf(stderr, "Error: out of memory\n");
fclose(dic);
return false;
}
unsigned hash_indx = hash_func(buf);
strcpy(new_node->word, buf);
new_node->next = global_hash[hash_indx];
global_hash[hash_indx] = new_node;
word_size++;
}
fclose(dic);
return true;
}
the following proposed code:
cleanly compiles
still has a major problem with the function: hash_func()
separates the definition of the struct from the typedef for that struct for clarity and flexibility.
properly formats the #define statements
properly handles errors from fopen() and malloc()
properly limits the length of the string read from the 'dictionary' file
assumes that no text from the 'dictionary' file will be greater than 45 bytes.
and now, the proposed code:
#include <stdio.h>
#include <stdlib.h>
#include <stdbool.h>
#include <string.h>
//prototypes
bool load(const char *dictionary);
int hash_func(char* hash_val);
#define HASH_SIZE 65536
#define LENGTH 45
struct node
{
char word[LENGTH + 1];
struct node* next;
};
typedef struct node node;
node* global_hash[HASH_SIZE] = {NULL};
int word_size = 0;
int hash_func(char* hash_val)
{
int h = 0;
for ( size_t i = 0, j = strlen(hash_val); i < j; i++)
{
h = (h << 2) ^ hash_val[i];
}
return h % HASH_SIZE;
}
bool load(const char *dictionary)
{
char string[ LENGTH+1 ];
FILE* dic = fopen(dictionary, "r");
if(dic == NULL)
{
perror( "fopen failed" );
//fprintf(stdout, "Error: File is NULL.");
return false;
}
while( fscanf( dic, "%45s", string) == 1 )
{
node* new_node = malloc(sizeof(node));
if(new_node == NULL)
{
perror( "malloc failed" );
return false;
}
strcpy(new_node->word, string);
new_node->next = NULL;
int hash_indx = hash_func(new_node->word);
// following statement for debug:
printf( "index returned from hash_func(): %d\n", hash_indx );
if( !global_hash[hash_indx] )
{
global_hash[hash_indx] = new_node;
}
else
{
new_node->next = global_hash[hash_indx];
global_hash[hash_indx] = new_node;
}
word_size++;
}
fclose(dic);
return true;
}
I have searched quite a bit, before asking, but I can't seem to make this function work.
I have this array of structs with 2 strings (char*)
and the function put() that adds a new struct, Unless the key already exists in that case it just ovewrites the current value with the new one.
Despite I am passing the array by reference and not making a local copy in the function, the memory still is corrupted (Segmentation Fault).
The source code is compiled under Ubuntu 15.10 on latest version of gcc.
Thanks in advance for your help guys!
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#define N 3
struct store{
char *key;
char *value;
};
void put(char *key, char *value, struct store **store, int size){
int i, found;
struct store *temp = realloc(*store, (size + 1) * sizeof(struct store));
for(i = 0; i < size; ++i){
if(strcmp(key, store[i]->key) == 0){ //Key found, overwrite new value.
store[i]->value = strdup(value); //Assume that every value is null terminated
found = 1;
break;
}
}
if(found) return;
*store = temp;
if(!store){
perror("realloc failed");
exit(EXIT_FAILURE);
}
store[size]->key = strdup(key); //New element
store[size]->value = strdup(value);
return;
}
int main(){
int i = 0;
struct store *store = malloc(N * sizeof(struct store));
if(!store){
perror("malloc failed");
exit(EXIT_FAILURE);
}
store[0].key = strdup("123a");
store[1].key = strdup("456b");
store[2].key = strdup("789c");
store[0].value = strdup("John");
store[1].value = strdup("Sam");
store[2].value = strdup("Mary");
for(i = 0; i < N; ++i)
printf("%s, %s\n\n",store[i].key,store[i].value); //This works fine
put("123a","Jim",&store,N);
for(i = 0; i < N; ++i)
printf("%s, %s\n\n",store[i].key,store[i].value);
put("653a","Tom",&store,N);
for(i = 0; i < N+1; ++i)
printf("%s, %s\n\n",store[i].key,store[i].value);
return 0;
}
struct store *temp = realloc(*store, (size + 1) * sizeof(struct store));
for(i = 0; i < size; ++i){
if(strcmp(key, store[i]->key) == 0){ //Key found, overwrite new value.
store[i]->value = strdup(value); //Assume that every value is null terminated
found = 1;
break;
}
}
if(found) return;
*store = temp;
If the key is found, you don't assign temp to *store. realloc can move the allocated memory to a completely new address, thus leaving *store a dangling pointer. And you really should also check that temp isn't null as well.
There's also the problem of your misuse of store. store is the the address of the pointer you passed into the function, not the first element of an array.
You need to index the array like this (*store)[i].
i have a problem with the initialization of the values inside the first dynamic array of pointers
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char*** GetIndexes()
{
int n = 0;
char ***index;
printf("please insert the number of words you want to add to dictionary\n");
scanf("%d", &n);
index = (char***)calloc(n, sizeof(char));
if (index == NULL)
{
printf("allocation Failed");
return;
}
return index;
}
char** GetDefinitions()
{
int n = 0;
char **definition;
printf("please insert the number of defintions you want to add to the word\n");
scanf("%d", &n);
definition = (char**)calloc(n+1, sizeof(char));
if (definition == NULL)
{
printf("allocation failed");
return;
}
return definition;
}
int main()
{
char *** dptr = GetIndexes();
if (dptr == NULL)
{
printf("memory Allocation failed");
}
int indexcount = sizeof(dptr) / sizeof(char),i;
for (i = 0; i < indexcount; i++)
{
printf("word number %d\n", i + 1);
*dptr[i] = GetDefinitions();
}
printf("%p",dptr);
}
i tried running the debugger in VS2013 and after i enter the number of defintions i want it crashed with this message:
Unhandled exception at 0x01103FB0 in ConsoleApplication1.exe: 0xC0000005: Access violation writing location 0x00000000.
i missed an allocation of something but i cant quite figure out what i missed,
thanks in advance
Your program is very broken
You allocate n char ***s but only request space for n chars and also do it for char **, to prevent this kind of mistake you may use the sizeof operator this way
char ***index;
index = calloc(n, sizeof(*index));
and
char **definition;
definition = calloc(n, sizeof(*definition));
and as you see casting calloc makes it harder and it's not necessary.
You have a return statement that doesn't return anything an GetIndexes() as well as one in GetDefinitions.
They should return NULL if you want to handle failure in the caller function
return NULL;
You erroneously use the sizeof operator to determine the number of char *** pointer allocated in
int indexcount = sizeof(dptr) / sizeof(char)
this will be either 4 or 8 depending on the architecture i.e. the size of a pointer divided by 1 sizeof(char) == 1 always.
You can't compute that value, you simply have to keep track of it. The size
You dereference the triple pointer twice and try to assign a double pointer to it
*dptr[i] = GetDefinitions();
here the operator precedence is also an issue, but regardless of that, this is wrong, may be what you meant was
dptr[i] = GetDefinitions();
This is not going to make your program crash, but it's certainly important to free all malloced pointers before exiting the program.
Here is a suggestion for your code to work, ignore it's purpose since it's not clear what you are trying to do
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char ***GetIndexes(unsigned int *count)
{
char ***index;
printf("please insert the number of words you want to add to dictionary > ");
scanf("%u", count);
index = calloc(*count, sizeof(*index));
if (index == NULL)
{
printf("allocation Failed");
return NULL;
}
return index;
}
char **GetDefinitions(unsigned int *count)
{
char **definition;
printf("please insert the number of defintions you want to add to the word > ");
scanf("%u", count);
definition = calloc(*count + 1, sizeof(*definition));
if (definition == NULL)
{
printf("allocation failed");
return NULL;
}
return definition;
}
int main()
{
unsigned int indexCount, i;
char ***dptr = GetIndexes(&indexCount);
if (dptr == NULL)
{
printf("memory Allocation failed");
}
for (i = 0; i < indexCount; i++)
{
unsigned int definitionsCount;
printf("Word number %u\n", i + 1);
dptr[i] = GetDefinitions(&definitionsCount);
if (dptr[i] != NULL)
{
/* use dptr[i] here or maybe somewhere else, but when you finish */
free(dptr[i]);
}
}
printf("%p", dptr);
/* now if you are done using dptr */
free(dptr);
return 0;
}
As already mentioned in the comment this is a very bad idea and just using double pointers is good here. But the below fixes should be done if you want to use pointers to allocate memory
index = calloc(n, sizeof(char));
should be
index = calloc(n, sizeof(char **));
and
definition = calloc(n+1, sizeof(char));
should be
definition = calloc(n+1, sizeof(char *));
I'm used to PHP, but I'm starting to learn C. I'm trying to create a program that reads a file line by line and stores each line to an array.
So far I have a program that reads the file line by line, and even prints each line as it goes, but now I just need to add each line to an array.
My buddy last night was telling me a bit about it. He said I'd have to use a multidimensional array in C, so basically array[x][y]. The [y] part itself is easy, because I know the maximum amount of bytes that each line will be. However, I don't know how many lines the file will be.
I figure I can make it loop through the file and just increment an integer each time and use that, but I feel that there might be a more simple way of doing it.
Any ideas or even a hint in the right direction? I appreciate any help.
To dynamically allocate a 2D array:
char **p;
int i, dim1, dim2;
/* Allocate the first dimension, which is actually a pointer to pointer to char */
p = malloc (sizeof (char *) * dim1);
/* Then allocate each of the pointers allocated in previous step arrays of pointer to chars
* within each of these arrays are chars
*/
for (i = 0; i < dim1; i++)
{
*(p + i) = malloc (sizeof (char) * dim2);
/* or p[i] = malloc (sizeof (char) * dim2); */
}
/* Do work */
/* Deallocate the allocated array. Start deallocation from the lowest level.
* that is in the reverse order of which we did the allocation
*/
for (i = 0; i < dim1; i++)
{
free (p[i]);
}
free (p);
Modify the above method. When you need another line to be added do *(p + i) = malloc (sizeof (char) * dim2); and update i. In this case you need to predict the max numbers of lines in the file which is indicated by the dim1 variable, for which we allocate the p array first time. This will only allocate the (sizeof (int *) * dim1) bytes, thus much better option than char p[dim1][dim2] (in c99).
There is another way i think. Allocate arrays in blocks and chain them when there is an overflow.
struct _lines {
char **line;
int n;
struct _lines *next;
} *file;
file = malloc (sizeof (struct _lines));
file->line = malloc (sizeof (char *) * LINE_MAX);
file->n = 0;
head = file;
After this the first block is ready to use. When you need to insert a line just do:
/* get line into buffer */
file.line[n] = malloc (sizeof (char) * (strlen (buffer) + 1));
n++;
When n is LINE_MAX allocate another block and link it to this one.
struct _lines *temp;
temp = malloc (sizeof (struct _lines));
temp->line = malloc (sizeof (char *) * LINE_MAX);
temp->n = 0;
file->next = temp;
file = file->next;
Something like this.
When one block's n becomes 0, deallocate it, and update the current block pointer file to the previous one. You can either traverse from beginning single linked list and traverse from the start or use double links.
There's no standard resizable array type in C. You have to implement it yourself, or use a third-party library. Here's a simple bare-bones example:
typedef struct int_array
{
int *array;
size_t length;
size_t capacity;
} int_array;
void int_array_init(int_array *array)
{
array->array = NULL;
array->length = 0;
array->capacity = 0;
}
void int_array_free(int_array *array)
{
free(array->array);
array->array = NULL;
array->length = 0;
array->capacity = 0;
}
void int_array_push_back(int_array *array, int value)
{
if(array->length == array->capacity)
{
// Not enough space, reallocate. Also, watch out for overflow.
int new_capacity = array->capacity * 2;
if(new_capacity > array->capacity && new_capacity < SIZE_T_MAX / sizeof(int))
{
int *new_array = realloc(array->array, new_capacity * sizeof(int));
if(new_array != NULL)
{
array->array = new_array;
array->capacity = new_capacity;
}
else
; // Handle out-of-memory
}
else
; // Handle overflow error
}
// Now that we have space, add the value to the array
array->array[array->length] = value;
array->length++;
}
Use it like this:
int_array a;
int_array_init(&a);
int i;
for(i = 0; i < 10; i++)
int_array_push_back(&a, i);
for(i = 0; i < a.length; i++)
printf("a[%d] = %d\n", i, a.array[i]);
int_array_free(&a);
Of course, this is only for an array of ints. Since C doesn't have templates, you'd have to either put all of this code in a macro for each different type of array (or use a different preprocessor such as GNU m4). Or, you could use a generic array container that either used void* pointers (requiring all array elements to be malloc'ed) or opaque memory blobs, which would require a cast with every element access and a memcpy for every element get/set.
In any case, it's not pretty. Two-dimensional arrays are even uglier.
Instead of an array here, you could also use a linked list, The code is simpler, but the allocation is more frequent and may suffer from fragmentation.
As long as you don't plan to do much random access (Which is O(n) here), iteration is about as simple as a regular array.
typedef struct Line Line;
struct Line{
char text[LINE_MAX];
Line *next;
};
Line *mkline()
{
Line *l = malloc(sizeof(Line));
if(!l)
error();
return l;
}
main()
{
Line *lines = mkline();
Line *lp = lines;
while(fgets(lp->text, sizeof lp->text, stdin)!=NULL){
lp->next = mkline();
lp = lp->next;
}
lp->next = NULL;
}
If you are using C you will need to implement the resizing of the array yourself. C++ and the SDL has this done for you. It is called a vector. http://www.cplusplus.com/reference/stl/vector/
While a multidimensional array can solve this problem, a rectangular 2D array would not really be the natural C solution.
Here is a program that initially reads the file into a linked list, and then allocates a vector of pointers of the right size. Each individual character does then appear as array[line][col] but in fact each row is only as long as it needs to be. It's C99 except for <err.h>.
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
typedef struct strnode {
char *s;
struct strnode *next;
} strnode;
strnode *list_head;
strnode *list_last;
strnode *read1line(void) {
char space[1024];
if(fgets(space, sizeof space, stdin) == NULL)
return NULL;
strnode *node = malloc(sizeof(strnode));
if(node && (node->s = malloc(strlen(space) + 1))) {
strcpy(node->s, space);
node->next = NULL;
if (list_head == NULL)
list_head = node;
else
list_last->next = node;
list_last = node;
return node;
}
err(1, NULL);
}
int main(int ac, char **av) {
int n;
strnode *s;
for(n = 0; (s = read1line()) != NULL; ++n)
continue;
if(n > 0) {
int i;
strnode *b;
char **a = malloc(n * sizeof(char *));
printf("There were %d lines\n", n);
for(b = list_head, i = 0; b; b = b->next, ++i)
a[i] = b->s;
printf("Near the middle is: %s", a[n / 2]);
}
return 0;
}
You can use the malloc and realloc functions to dynamically allocate and resize an array of pointers to char, and each element of the array will point to a string read from the file (where that string's storage is also allocated dynamically). For simplicity's sake we'll assume that the maximum length of each line is less than M characters (counting the newline), so we don't have to do any dynamic resizing of individual lines.
You'll need to keep track of the array size manually each time you extend it. A common technique is to double the array size each time you extend, rather than extending by a fixed size; this minimizes the number of calls to realloc, which is potentially expensive. Of course that means you'll have to keep track of two quantities; the total size of the array and the number of elements currently read.
Example:
#define INITIAL_SIZE ... // some size large enough to cover most cases
char **loadFile(FILE *stream, size_t *linesRead)
{
size_t arraySize = 0;
char **lines = NULL;
char *nextLine = NULL;
*linesRead = 0;
lines = malloc(INITIAL_SIZE * sizeof *lines);
if (!lines)
{
fprintf(stderr, "Could not allocate array\n");
return NULL;
}
arraySize = INITIAL_SIZE;
/**
* Read the next input line from the stream. We're abstracting this
* out to keep the code simple.
*/
while ((nextLine = getNextLine(stream)))
{
if (arraySize <= *linesRead)
{
char **tmp = realloc(lines, arraysSize * 2 * sizeof *tmp);
if (tmp)
{
lines = tmp;
arraySize *= 2;
}
}
lines[(*linesRead)++] = nextLine;
)
return lines;
}