C how to put content of .txt file into 2D array - arrays

Working with C, I am trying to read from a .txt file, line by line, and then put every line into an array. Every line is 200 characters long maximum, and the array can store, lets say 50 lines. If the number of lines exceed 50, I want to dynamically allocate twice as much memory, and so on until it is enough. If I put the if{...} part outside of the while loop, it seems to work, but as soon as I use it inside of the loop it does not. I would appreciate any help.
FILE *fp=fopen(file,"r");
int idx=0;
int row=50;
int col=300;
char temp[row][col];
while (fgets(temp[idx],col,fp)){
if (idx == row) {
char **new = malloc(2 * row * sizeof(char *));
for (int j = 0; j < row; j++) {
new[j] = (char *) malloc(col * sizeof(char) + 1);
}
for (int i = 0; i < row; i++) {
for (int j = 0; j < (col + 1); j++) {
new[i][j] = temp[i][j];
}
}
row = 2 * row;
**temp = **new;
free(new);
}
idx++;
}
fclose(fp);

You cannot change the dimensions of a local array (here temp[row][col]). Instead you need to keep a pointer to such an array. In the following code I use a temp array just to keep one line for fgets, and then copy it immediately into a dynamically allocated storage (arr is the 2d array of lines):
FILE *fp=fopen(file,"r");
int idx=0;
int row=50;
int col=300;
char temp[col];
char **arr = malloc(row*sizeof(char*));
while (fgets(temp,col,fp)){
if (idx == row) {
char **new = realloc(arr, 2 * row * sizeof(char *));
if(!new) abort();
arr = new;
row *= 2;
}
arr[idx++] = strdup(temp);
}
fclose(fp);

Related

C String Allocation Wonkiness

Having trouble understanding and getting to work String operations in the following code.
Please help, me and my study colleagues are losing our minds over this. ty.
This is a simple method to fill a multi dimensional array with custom strings - which for some reason we cannot figure out for the life of us does simply not work - spits out random junk from the memory instead. Also allocation amounts don't seem to be quite right.
#include <stdio.h>
#include <malloc.h>
#include <string.h>
char*** createKeyPad(int rows, int cols, int num_chars) {
if(num_chars <= 0) return NULL;
char needed = 'a';
char** aptr = NULL;
char*** rptr = NULL;
aptr = (char**) malloc(rows * cols * sizeof(char*));
if(aptr == NULL) {
return NULL;
}
rptr = (char***) malloc(rows * sizeof(char**));
if(rptr == NULL) {
free(aptr);
return NULL;
}
for(int row = 0; row < rows; row++) {
rptr[row] = aptr + (row * cols);
}
for(int row = 0; row < rows; row++) {
for(int col = 0; col < cols; col++) {
char* string;
for(int i = 0; i < num_chars; i++) {
string[i] = needed;
}
string[num_chars] = '\0';
rptr[row][col] = string;
printf("%s", string);
}
}
printf("%s", "hallo");
return rptr;
}
int main() {
printf("Hello, World!\n");
char*** keypad = createKeyPad(5, 5, 3);
for(int row = 0; row < 5; row++) {
for(int col = 0; col < 5; col++) {
printf("%s", keypad[row][col]);
}
printf("\n");
}
}
You have plenty problems in this code.
string is a dangling pointer - ie it was not initialized and does not reference a valid char array
even if string was referencing a valid object you assign the same pointer to all (pseudo)array elements.
Do not use *** pointers.
use the correct type for sizes
Use positive checks and try to minimize function returns.
arrays are indexed from 0 in C and even if the string was referencing an array of num_chars elements, string[num_chars] = '\0'; is accessing an element outside the array bounds.
I would use array pointers and use only one allocation to allocate the space for the whole 3D array.
Use objects instead of types in sizeofs
int createKeyPad(size_t rows, size_t cols, size_t numChars, char (**pad)[cols][numChars])
{
int result = 0;
if(numChars > 1)
{
*pad = malloc(rows * sizeof(**pad));
if(*pad)
{
result = 1;
for(size_t row = 0; row < rows; row++)
{
for(size_t col = 0; col < cols; col++)
{
for(size_t i = 0; i < numChars - 1; i++)
{
(*pad)[row][col][i] = row * cols + col + '0';
}
(*pad)[row][col][numChars - 1] = 0;
}
}
}
}
return result;
}
int main(void)
{
printf("Hello, World!\n");
char (*keypad)[5][3];
if(createKeyPad(5, 5, 3, &keypad))
{
for(size_t row = 0; row < 5; row++)
{
for(size_t col = 0; col < 5; col++)
{
printf("%s ", keypad[row][col]);
}
printf("\n");
}
}
free(keypad);
}
https://godbolt.org/z/6zY4zbGW3
The main problem is that char* string; followed by string[i] = needed; is dereferencing an invalid pointer because string is not pointing to anything valid.
In the code's current style of allocating one block for each level and dividing the block up, the memory for all the strings could be allocated in one big block:
char* sptr = (char*) malloc(rows * cols * (num_chars + 1) * sizeof(char));
(Note: The (char*) typecast is not required. Also the * sizeof(char) is not required since sizeof(char) is 1 by definition, but I put it in there in case the code is changed to use something other than char at a later date.)
Then the string variable in the nested loop can be initialized as follows:
char* string = sptr + (row * cols + col) * (num_chars + 1);

Is it necessary to free sub-pointer before calling realloc on pointer-to-pointer array to shrink its size?

For example, I have this C code:
int len = 100;
int i;
// arr is pointer-to-pointer 2d array of char
char **arr = malloc(len * sizeof(char*));
for (i = 0; i < len; i++)
{
// Allocate the sub-pointer
arr[i] = malloc(len * sizeof(char));
...
}
...
// Is this part necessary?
for (i = 0; i < len; i++)
{
// Freeing the sub-pointer
free(arr[i]);
}
// Shrink the arr's size from 100 to 50
char** temp = realloc(arr, 50 * sizeof(char*));
...
Before to do realloc for arr to shrink its size (from 100 to 50), is it necessary to free the arr's sub-pointer?
for (i = 0; i < len; i++)
{
free(arr[i]);
}
Yes, you have to free all pointers if the reallocated array will have size that is less than the original size. For example
for (i = 50; i < len; i++)
{
free(arr[i]);
}
char** temp = realloc(arr, 50 * sizeof(char*));
C does not have destructors so you have manually to free all objects pointed to by the removed elements. Otherwise there will be memory leaks.

Freeing malloc of unknown size

I'm trying to free the malloc that is generated with a not fixed number of arrays.
char ** get_moves(){
// some code
char **moves = malloc(sizeof(char *) * k); // 'k', could ranges between 1~9
if (!moves){
return NULL;
}
for(int i = 0; i < k; i++){
moves[i] = malloc(82);
if (!moves[i]) {
free (moves);
return NULL;
}
// more code
return moves;
}
int main(){
//some code
char **res = get_moves(some_input);
//more code
for (int i = 0; i < (sizeof(res)/sizeof(res[0)); i ++){
free(res[i]);
}
free(res);
}
In one of the inputs to get_move, res should have 2 arrays but the sizeof(res)/sizeof(res[0) gives me just 1.
How is the proper way to handle this?
The only way is to keep track of the element count of the array, if you don't want to pass it to every function when passing the array, you can combine both pieces of information in a struct, like here
#include <stdlib.h>
struct ArrayOfStrings
{
int count;
char **data;
};
struct ArrayOfStrings get_moves()
{
struct ArrayOfStrings result;
char **moves;
// some code
result.count = 0;
result.data = malloc(sizeof(char *) * k); // 'k', could ranges between 1~9
if (result.data == NULL)
return result;
result.count = k;
moves = result.data;
for (int i = 0; i < k; i++)
{
moves[i] = malloc(82);
if (moves[i] == NULL)
{
/* also free succesfully allocated ones */
for (int j = i - 1 ; j >= 0 ; --j)
free(moves[j]);
free(moves);
}
result.count = 0;
result.data = NULL;
return result;
}
// more code
return result;
}
int main(){
//some code
struct ArrayOfStrings res = get_moves(some_input);
//more code
for (int i = 0; i < res.count ; i ++)
free(res.data[i]);
free(res.data);
return 0; // you should return from main.
}
sizeof is not for the length of an object's content but for the size of a data type, it is computed at compile time.
So in your case
sizeof(res) / sizeof(res[0]) == sizeof(char **) / sizeof(char *) == 1
since sizeof(char **) == sizeof(char *) it's just the size of a pointer.
sizeof(res)
Returns the sizeof(double-pointer);
So if you intend to get the number of pointers stored then you might not get this by doing what you are doing.
You need to do something like
for(i=0;i<k;i++) /* As I see you are allocating k no of pointer Keep track of it*/
free(res[i]);
free(res);
res is in fact not an array of arrays of char type. Instead it is a pointer to pointer to char type. sizeof(res) will give you the size of char**. You need to keep track of the number of allocations.
Since the maximum number of arrays to allocate is small (9), you can simplify your code by allocating the maximum number. Fill the unused elements with NULL:
#define MAX_K 9
char **moves = malloc(sizeof(char *) * MAX_K);
for(int i = 0; i < k; i++){
...
}
for(int i = k; i < MAX_K; i++){
moves[i] = NULL;
}
To deallocate, just ignore the NULL pointers:
for (int i = 0; i < MAX_K; i ++){
if (res[i])
free(res[i]);
}
free(res);

Dynamically allocate a string matrix in C

I'm trying to read a map from a text file and create a string array according to the number of rows and columns in the map. Every cell in the grid is a 2 character string.
For instance,
**--**--**--
--**--**--**
should create a 2*6 matrix. The number of rows and columns are ROWS and COLS respectively. I used
char ***map = malloc(ROWS * sizeof(char *));
for (i = 0; i < ROWS; i++)
{
map[i] = malloc(COLS * sizeof(char) * 2);
}
But when I try to use a map[x][y], it will segfault.
char ***map; could be interpreted as an "Array of arrays of strings", so the inner array actually contains char pointers. Therefore, your loop needs to look like this:
for(i = 0; i < ROWS; i++) {
int j;
map[i] = malloc(COLS * sizeof(char*));
for(j = 0; j < COLS; j++) map[i][j] = malloc(3 * sizeof(char)); // 3 chars because a string has to be terminated by \0
}
Alternatively, you could declare map as char **map, then your initialization code would work, but then you'd need to use map[i][j] and map[i][j+1] to access the elements of the individual cells.
It could look like this:
int i, j, ROWS = 2, COLS = 6;
char ***map = malloc(ROWS * sizeof(char **));
for (i = 0; i < ROWS; ++i)
{
map[i] = malloc(COLS * sizeof(char*));
for (j = 0; j < COLS; ++j)
map[i][j] = malloc(2 * sizeof(char));
}
Note that 2 chars allow you to store these characters, but it could cause you some troubles if you are going to work with them as a string (printf("%s, strcpy ...). In that case I would rather allocate memory for 3 chars so that terminating character can be stored as well.
Also note that you should clean this memory once it is allocated and cleaning should be done in reverse order according to allocation. It could look like this:
for (i = 0; i < ROWS; ++i)
{
for (j = 0; j < COLS; ++j)
free(map[i][j]);
free(map[i]);
}
free(map);
Hope this helps.
It needs to be
char ***map = malloc(ROWS * sizeof(char**));
for (i = 0; i < ROWS; i++)
{
map[i] = malloc(COLS * sizeof(char*));
for (int j=0; i<COLS; ++j)
map[i][j] = malloc(3*sizeof(char);
}
Edit: As pointed out in another answer and a comment, should be 3 not 2 malloc'ed chars.
The first line should be:
char **map = malloc(ROWS * sizeof(char *));
As a rule of thumb, add one * to the return type of malloc(). If you allocate an array of five ints with malloc(5 * sizeof(int)) then you would get back an int *.
Or, you can think of each * as adding a dimension—char * is a 1-D array of characters, and char ** is a 2-D array.
If you want 2D array, way do you declare map as char***? Change it to char**.
(If I misunderstoop, and you want 2D array of char*, you should change the allocation, to use sizeof(char**) and sizeof(char*), and allocate memory to the string separately.)
Edit: If you know the size of the map when you declare it, make it char map[ROWS][COLS][2];
If you don't (or you want to simply pass it to another functions), you can declare it as char (**map)[2], and keep your allocations as they are.
(Change the 2 to 3 if you want to terminate them by \0 (To print it, for example))

Allocating memory to a 3-dimensional char array causes segmentation fault

As a spin of of an earlier question, I have run into some problems regarding allocating memory to a 3 dimensional array.
I am working on a project where we need to do some work on a text. To do this, we need to split the text into smaller sections, and process the text word for word. To save these smaller pieces of text, we have a 3D array, a list of sections each containing a list of the words in the section.
But I get a segmentation fault when I try to allocate memory for the individual words using malloc().
localText->list[i][n] = malloc(100 * sizeof(char));
Here is the entire code.
typedef struct {
char name[100];
char ***list;
}text;
int main(){
int i = 0, n, z,wordCount, sections;
FILE *file;
text *localText;
openFile(&file, "test.txt");
wordCount = countWords(file);
sections = (wordCount / 50) + 1;
localText = malloc(sizeof(text));
localText->list = malloc(sections * sizeof(char **));
for(i = 0; i < sections; i++)
localText->list[i] = malloc(50 * sizeof(char *));
for(n = 0; n < 50; n++)
localText->list[i][n] = malloc(100 * sizeof(char));
readFileContent(file, localText->list, 50);
freeText(localText);
return 1;
}
You're missing some braces:
for(i = 0; i < sections; i++) {
// ...
}

Resources