Creating a dynamic array of characters using C with pointers to pointers - c

I'm trying to create an array of pointers to pointers, at least to my understanding. But I'm getting invalid reads and writes with valgrind running.
char **format_file(FILE *infile) {
char **char_array = malloc(20 * sizeof(char*));
int c;
int cUsed = 0;
while ((c = fgetc(infile)) != EOF) {
char_array[cUsed] = c;
cUsed += 1;
}
printf("%s", *char_array);
return char_array;
}
The code works by reading from an already opened file "infile". First I allocated memory for 20 characters with malloc, then I'm trying to read the file character by character into the allocated memory array until EOF is reached. However, valgrind's output is as follows when I make the code:
==7379== Invalid read of size 1
==7379== at 0x4E7CB36: vfprintf (vfprintf.c:1597)
==7379== by 0x4E85198: printf (printf.c:35)
==7379== by 0x400755: format_file (formatter.c:27)
==7379== by 0x4006C1: main (format265alt.c:21)
==7379== Address 0x6f is not stack'd, malloc'd or (recently) free'd
Line 27 is the printf command that valgrind refers to as an invalid read of size 1.
formatter.c is the file containing the format_file function, while format265alt.c is a file that calls the formatter.c function and opens the file to be read.
I'm confused by the syntax of **, that is, how do I access and read/write the allocated memory?
I apologize if I have not provided enough information about this problem.

valgrind complains because you are storing characters beyond the end of the allocated object. The compiler should complain that you are storing characters into an object of the wrong type, use -Wall -W to enable useful warnings.
A char ** is a pointer to a char pointer, it can point to an array of char pointers, also known as an array of strings. You must allocate the array and each of the strings with the appropriate size for the file contents.
Here there are 2 possibilities:
the function can either load the whole file into a single string, but there would be no need to return a pointer to a char*, just returning the string (char *) would suffice.
the proposed API is more appropriate if the function is to return a pointer to an array of strings, one per line, with an extra NULL at the end, just like the argv array passed as the second argument to the main function.
For this, you must reallocate the string array as more lines are read from the FILE* and each line should be reallocated as it grows. Add a NULL pointer at the end of the string array to indicate its end.
Here is a very inefficient way to do this:
#include <stdlib.h>
#include <stdio.h>
char **format_file(FILE *infile) {
size_t lines = 0;
char **array = malloc(1 * sizeof(char*));
size_t pos = 0;
char *line = malloc(1);
int c;
while ((c = getc(infile)) != EOF) {
if (c == '\n') {
line[pos] = '\0';
array = realloc(array, (lines + 2) * sizeof(char *));
array[lines++] = line;
line = malloc(1);
pos = 0;
continue;
}
line = realloc(line, pos + 2);
line[pos++] = c;
}
if (pos > 0) {
line[pos] = '\0';
array = realloc(array, (lines + 2) * sizeof(char *));
array[lines++] = line;
} else {
free(line);
}
array[lines] = NULL;
return array;
}

If you are just creating an array of characters, a one-dimensional character array is sufficient. No need for the char** business. However, make sure you null terminate the array if you are trying to use it as a string.

Related

C fscanf read pointer to array of characters

I am trying to read lines from a file into a pointer to a character array using fscanf. I am getting segmentation faults when I print. What am I doing wrong? Should I be using a function other than fscanf?
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "stack.h"
#define MAXSTACK 100
#define MAXLENGHT 100
void main(int argc, char * argv[]){
char *filename;
FILE *fp;
char *lines[MAXSTACK];
char * command;
int top = 0;
int numlines = 0;
if(argc < 3){
fprintf(stderr,"error: Not enough arguments provided\n");
exit(1);
}
filename = argv[1];
command = argv[2];
if ( (fp = fopen(filename,"r")) == NULL ){
fprintf(stderr,"error: Cannot open file %s\n",filename);
exit(1);
}
else{
for(int i = 0; i < 3; i++){
fscanf(fp,"%s",lines[i]);
// printf("%s\n",lines[i]);
}
char **ptr2 = lines;
for (int i = 0; i < 2; i++){
printf("%s\n", ptr2[i]);
}
if (strcmp(command,"pop")==0){
//pop(lines);
}else if (strcmp(command,"print_top")==0){
//print_top();
}else if(strcmp(command,"swap_top")==0){
}
}
}
You may want to read lines using fgets:
/* Read a single line into a temporary buffer */
char lineBuffer[MAX_LINE_LENGTH];
while (fgets(lineBuffer, sizeof(lineBuffer), fp) != NULL) {
/* Process the read line */
}
Once you have read a line in the temporary buffer, you can deep-copy the read string into some memory that you allocated on the heap using malloc (or you can just use strdup), and then you can store a pointer to that memory into your lines array:
/* Inside the body of the while loop */
/*
* Deep copy current line into the string pointer array.
* strdup = malloc + strcpy
* Note that free is required to release memory!
*/
lines[currLineIndex] = strdup(lineBuffer);
currLineIndex++;
Note that when you write code like this:
char *lines[MAXSTACK];
you are allocating on the stack an array of MAXSTACK items, each item being a char* pointer. But then you have to give some meaningful value to those pointers (for example: allocating some memory from the heap and pointing to that memory).
Of course, when you are done, you have to scan the whole array and call free on each element pointer to avoid memory leaks.
Moreover, a good coding practice would be to clear the pointers in the array before using it, e.g.:
memset(lines, 0, sizeof(lines));
fscanf("%s", lines[i]) will read a sequence of non-white-space characters (note, not a whole line) into the memory pointed to by lines[i]. The problem is, that you haven't set lines[i] to point to any memory and that is why you get the segfault, you're asking for a sequence of characters to be copied to some undefined location.
If you replace your declaration of an array of character pointers, char *lines[MAXSTACK];, with a declaration of an array of arrays of characters char lines[MAXLENGTH][MAXSTACK] then lines[i] will be an array of MAXLENGTH characters which fscanf("%s", lines[i]) which be able to copy to without seg faulting.
The question remains now, what happens if the string fscanf tries to read is longer than MAXLENGTH? The answer is more characters will be read than can fit into the lines[MAXLENGTH] array and you get what is called a buffer overflow. To safeguard against this you can limit the maximum number of characters fscanf will read from a string, to 100 for example, with fscanf("%100s", lines[i])

Access value of a pointer in dynamically allocated memory

My assignment is to read words from a text file and store them in character arrays which are stored in an array of char*. All memory in these arrays needs to be dynamically allocated.
What I am doing is reading in each word with fscanf() and storing it into the variable str. I am then calculating the length of the word in str and dynamically allocating memory to store the value of str in the character array new_word. new_word is then inserted into the array of char* named words. When words runs out of space, I double its size and continue.
My problem lies in the commented code starting on line 62. I'm going to need to read these words later from words, so I'm testing my ability to access the pointers and their values. I can index new_word fine (in the lines above), but when I then store new_word in words and try to read from words, I get the following error:
hw1.c:63:25: error: subscripted value is not an array, pointer, or vector
while (*(words[count])[k] != '\0'){
on lines 63 and 64. I know it has something to do with dereferencing the pointer, but I have tried a bunch of variations with no success. How can I fix this?
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char* argv[]){
if (argc != 3){
fprintf(stderr, "Incorrect number of arguments\n");
exit(1);
}
char* infile = argv[1];
FILE* finp = fopen(infile, "r");
if (finp == NULL){
fprintf(stderr, "Unable to open input file\n");
exit(1);
}
char* prefix = argv[2];
int count = 0;
int size = 20;
char* words = calloc(size, sizeof(char));
printf("Allocated initial array of 20 character pointers.\n");
char* str = malloc(30*sizeof(char));
while (fscanf(finp, "%s", str) == 1){
if (count == size){
words = realloc(words, 2 * size);
size *= 2;
printf("Reallocated array of %d character pointers.\n", size);
}
int i = 0;
while (str[i] != '\0'){
i++;
}
char* new_word = malloc((i+1)*sizeof(char));
int j = 0;
while (str[j] != '\0'){
new_word[j] = str[j];
j++;
}
new_word[j] = '\0';
int k = 0;
while (new_word[k] != '\0'){
printf("%c", new_word[k]);
k++;
}
printf("\n");
words[count] = *new_word;
/*k = 0;
while (*(words[count])[k] != '\0'){
printf("%c", *(words[count])[k]);
k++;
}
printf("\n");*/
count++;
}
}
Ok, dissecting that a bit:
char* words = calloc(size, sizeof(char));
this should probably read:
char **words = calloc(size, sizeof(char *));
Why? What you want here is a pointer to an array of pointers to char ... words points to the first char *, which points to your first "string".
char* str = malloc(30*sizeof(char));
while (fscanf(finp, "%s", str) == 1){
Buffer overflow here. Make sure to read at maximum 30 characters if you define your buffer not to hold more. Btw, just for convention, call your buffer buffer or buf (not str) and there's really no need to dynamically allocate it. Hint: Use a field size for fscanf() or, even better, some other function like fgets().
if (count == size){
words = realloc(words, 2 * size);
size *= 2;
printf("Reallocated array of %d character pointers.\n", size);
}
The realloc here will not work, should read
words = realloc(words, 2 * size * sizeof(char *));
You need to multiply the size of a single element, which, in this case, is a pointer to char.
No guarantee this will be all errors, but probably the most important ones. On a sidenote, strlen() and strncpy() will help you stop writing unnecessary code.
A pointer to "A [dynamically-allocated] array of char*" would need to be recorded in a variable of type char **. That is, a pointer to the first element of the array, which element is of type char *. Thus ...
char **words;
If you want to have sufficient space for size words, then you could allocate it as ...
words = calloc(size, sizeof(char *));
(note the difference from your code), though it's harder to make a mistake with this form:
words = calloc(size, sizeof(*words));
Note in that case that the sizeof operator does not evaluate its operand, so it does not matter that words is not yet allocated.
Most importantly, be aware that the elements of array words are themselves pointers, not the ultimately pointed-to strings. Thus you assign a new word to the array by
words[count] = new_word;
(Again, note the difference from your version.) Other adjustments are needed as well.
The problematic while loop, though, is not fixed even then. Remember that the expression pointer[index] is equivalent to *((pointer) + (index)), so the expression *(words[count])[k] attempts to triply derference words. Even with the type correction, you want only to doubly dereference it: words[count][k].
But why re-invent the wheel? As Olaf observed with respect to strlen() and some of your earlier code, C already has perfectly good functions in its standard library for dealing with strings. In this case ...
printf("%s", words[count]);
... would be so much simpler than that while loop.

reading a file of strings to a multidimensional array to access later

I am really having a problem understanding dynamically allocated arrays.
I am attempting to read a text file of strings to a 2d array so I can sort them out later. right now as my code stands it throws seg faults every once in a while. Which means I'm doing something wrong. I've been surfing around trying to get a better understanding of what malloc actually does but I want to test and check if my array is being filled.
my program is pulling from a text file with nothing but strings and I am attempting to put that data into a 2d array.
for(index = 0; index < lines_allocated; index++){
//for loop to fill array 128 lines at a time(arbitrary number)
words[index] = malloc(sizeof(char));
if(words[index] == NULL){
perror("too many characters");
exit(2);
}
//check for end of file
while(!feof(txt_file)) {
words = fgets(words, 64, txt_file);
puts(words);
//realloc if nessesary
if (lines_allocated == (index - 1)){
realloc(words, lines_allocated + lines_allocated);
}
}
}
//get 3rd value placed
printf("%s", words[3]);
since this just a gist, below here ive closed and free'd the memory, The output is being displayed using puts, but not from the printf from the bottom. an ELI5 version of reading files to an array would be amazing.
Thank you in advance
void *malloc(size_t n) will allocate a region of n bytes and return a pointer to the first byte of that region, or NULL if it could not allocate enough space. So when you do malloc(sizeof(char)), you're only allocating enough space for one byte (sizeof(char) is always 1 by definition).
Here's an annotated example that shows the correct use of malloc, realloc, and free. It reads in between 0 and 8 lines from a file, each of which contains a string of unknown length. It then prints each line and frees all the memory.
#include <stdio.h>
#include <stdlib.h>
/* An issue with reading strings from a file is that we don't know how long
they're going to be. fgets lets us set a maximum length and discard the
rest if we choose, but since malloc is what you're interested in, I'm
going to do the more complicated version in which we grow the string as
needed to store the whole thing. */
char *read_line(void) {
size_t maxlen = 16, i = 0;
int c;
/* sizeof(char) is defined to be 1, so we don't need to include it.
the + 1 is for the null terminator */
char *s = malloc(maxlen + 1);
if (!s) {
fprintf(stderr, "ERROR: Failed to allocate %zu bytes\n", maxlen + 1);
exit(EXIT_FAILURE);
}
/* feof only returns 1 after a read has *failed*. It's generally
easier to just use the return value of the read function directly.
Here we'll keep reading until we hit end of file or a newline. */
while ('\n' != (c = getchar())) {
if (EOF == c) {
/* We return NULL to indicate that we hit the end of file
before reading any characters, but if we've read anything,
we still want to return the string */
if (0 == i) return NULL;
break;
}
if (i == maxlen) {
/* Allocations are expensive, so we don't want to do one each
iteration. As such, we're always going to allocate more than
we need. Exactly how much extra we allocate depends on the
program's needs. Here, we just add a constant amount. */
maxlen += 16;
/* realloc will attempt to resize the memory pointed to by s,
or copy it to a newly allocated region of size maxlen. If it
makes a copy, it will free the old version. */
char *p = realloc(s, maxlen + 1);
if (!p) {
/* If the realloc fails, it does not free the old version, so we do it here. */
free(s);
fprintf(stderr, "ERROR: Failed to allocate %zu bytes\n", maxlen + 1);
exit(EXIT_FAILURE);
}
s = p;//set the pointer to the newly allocated memory
}
s[i++] = c;
}
s[i] = '\0';
return s;
}
int main(void) {
/* If we wanted to, we could grow the array of strings just like we do the strings
themselves, but for brevity's sake, we're just going to stop reading once we've
read 8 of them. */
size_t i, nstrings = 0, max_strings = 8;
/* Each string is an array of characters, so we allocate an array of char*;
each char* will point to the first element of a null-terminated character array */
char **strings = malloc(sizeof(char*) * max_strings);
if (!strings) {
fprintf(stderr, "ERROR: Failed to allocate %zu bytes\n", sizeof(char*) * max_strings);
return 1;
}
for (nstrings = 0; nstrings < max_strings; nstrings++) {
strings[nstrings] = read_line();
if (!strings[nstrings]) {//no more strings in file
break;
}
}
for (i = 0; i < nstrings; i++) {
printf("%s\n", strings[i]);
}
/* Free each individual string, then the array of strings */
for (i = 0; i < nstrings; i++) {
free(strings[i]);
}
free(strings);
return 0;
}
I haven't looked too closely so I could be offering an incomplete solution.
That being said, the error is probably here:
realloc(words, lines_allocated + lines_allocated);
realloc if succesful returns the new pointer, if you're lucky it can allocate the adjacent space (which wouldn't cause a segfault).
words = realloc(words, lines_allocated + lines_allocated);
would solve it, although you probably need to check for errors.

How to store fgets string results into an char array?

I am currently getting the following error
Process terminated with status -1073741819
and I suspect its my fgets() but I have no idea why this is happening, any help would be much appreciated.
//Gets Dictionary from file
char* GetDictionary() {
int ArraySize;
int i = 0;
FILE * DictionaryFile;
//Gets first line (in this case it is the amount of Lines)
DictionaryFile = fopen("dictionary.txt", "r");
fscanf(DictionaryFile,"%d", &ArraySize);
ArraySize = ArraySize + 1;
printf("%d", ArraySize);
fclose(DictionaryFile);
//Gets the array
char* Dictionary = malloc(sizeof(char)*ArraySize);
char Temp[ArraySize];
char TempArray[ArraySize];
DictionaryFile = fopen("dictionary.txt", "r");
while(fgets(Temp, sizeof Temp, DictionaryFile)!=NULL) {
Dictionary[i] = Temp;
//Check The array
printf("%s", Dictionary[i]);
i++;
}
fclose(DictionaryFile);
return Dictionary;
}
-1073741819 --> C0000005 and likely has some significance. Maybe use below to discern its meaning.
puts(strerror(-1073741819));
Code has many issues: Here are some corrected to get you going.
1) Allocate an array of pointers, not an array of char
// char* Dictionary = malloc(sizeof(char)*ArraySize);
char** Dictionary = malloc(ArraySize * sizeof *Dictionary);
2) Form a big buffer to read each line
char Temp[100];
3) After reading each line, get rid of the likely trailing '\n'
size_t len = strlen(Temp);
if (len && Temp[len-1] == '\n') Temp[--len] = 0;
4) Allocate memory for that word and save
Dictionary[i] = malloc(len + 1);
assert(Dictionary[i]);
memcpy(Dictionary[i], Temp, len + 1);
5) Robust code frees it allocations before completion
6) Code reads "amount of Lines" twice as file is opened twice. Just leave file open (and not re-open it). #user3386109
You likely want Dictionary to be an array of char strings. That is, Dictionary is an array, and each element in the array is a char *. That makes Dictionary a char **.
For this example, it may be most straightforward to allocate memory for the Dictionary array itself, then allocate memory for its contents. You'll need to free all this when you're done, of course.
char **Dictionary = malloc(sizeof(char *) * ArraySize);
for (int i = 0; i < ArraySize; i++) {
Dictionary[i] = malloc(ArraySize);
}
There are better ways to do this. For one, you might only allocate memory when you need it, for each fgets() return. You could also use strdup() to allocate only the memory you need. You could also pass in Dictionary from the caller, already allocated, so you don't worry about allocating it here.
Later in your program, as #WhozCraig pointed out, you need to copy the string in Temp, like strcpy(Dictionary[i], Temp), in place of Dictionary[i] = Temp. I too am surprised that's not generating a compiler warning!

from static array assignment to array from file

I have this piece of code outside the main function
mystr * arrstr[] = {
"rec",
"cent",
"ece",
"ce",
"recent",
"nt",
};
I modified it so that it can read the values from a text file. for this purpose i modified this working code to read line from file into array named string.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(void) {
int i=0,j;
char* string[100];
char line[100];
FILE *file;
file = fopen("patt", "r");
while(fgets(line, sizeof(line), file)!=NULL) {
printf("%s", line);
string[i] = (char*)malloc(strlen(line));
strcpy(string[i], line);
i++;
}
fclose(file);
return 0;
}
so the final code is now something like this.
..
..
char *getpatterns(const char *filename) {
int i=0;
char* string[100];
char line[100];
FILE *file;
file = fopen(filename, "r");
while(fgets(line, sizeof(line), file)!=NULL) {
//printf("%s", line);
string[i] = (char*)malloc(strlen(line));
strcpy(string[i], line);
i++;
}
fclose(file);
return(string);
}
mystr * arrstr[] = getpatterns("patt");/*{
"rec",
"cent",
"ece",
"ce",
"recent",
"nt",
};*/
..
..
But i get errors like this.
example1.c: In function ‘getpatterns’:
example1.c:43:2: warning: return from incompatible pointer type [enabled by default]
example1.c:43:2: warning: function returns address of local variable [enabled by default]
example1.c: At top level:
example1.c:45:1: error: invalid initializer
make: *** [example1.o] Error 1
Here line 45 is this line
mystr * arrstr[] = getpatterns("patt");/*{
Please suggest corrective action.
The first warnings are that you are trying to return a char ** as a char * (which is not a good idea), and that you are returning a local variable which is deallocated when the function returns (also not a good idea). The last is telling you that you can't use function calls in initializers of global variables in C (you can do some of that in C++, though I'm not convinced you can do this one).
Fixing it will take some rethinking. You need the function to return allocated memory, or you need to pass the memory to the function. And you'll have to change the type of the global variable. And you'll need to know how many entries there are in the array, somehow.
mystr **arrstr = 0; // Either
mystr *arrstr[100]; // Or
On the whole, I'd probably go with memory allocation and the 'either' declaration:
mystr **arrstr = 0;
char **getpatterns(const char *file)
{
char **array = 0;
...code similar to yours that allocates entries in the array...
...include space for a null pointer to mark the end of the list of strings...
return(array);
}
int main(void)
{
arrstr = getpatterns("patt");
...
}
(Another 'cheat' mechanism would use static char *string[100]; in getpatterns(); you still have to fix the return type and the type of the global variable.)
I tried these but, errors were not resolved: ...
It's impossible to tell exactly what was wrong without your code. However, the code below works for me. The source code was in a file gp.c; the source code prints itself, and releases the memory. Checked under valgrind with a clean bill of health.
Note that your original code did not allocate enough space for the strings it was copying (because you retained the newline read by fgets() — but you were at least using fgets() and not gets(), which is very important). This code uses memmove() — it could use memcpy() instead since there's guaranteed to be no overlap, but memmove() always works and memcpy() doesn't necessarily work when the source data overlaps the target data. It knows how long the string is, so the copy function doesn't need to test for whether the character being copied is a NUL '\0'. The code carefully ensures that there's a null pointer at the end of the list of pointers; that's how you know when you've reached the end of the list of strings. The code also works when gp.c is an empty file.
The algorithm using three items num_xxx, max_xxx, and xxx is a typical way to handle incremental allocation. It typically over-allocates slightly; if you're concerned about the space, you could use strings = realloc(strings, (num_strings+1) * sizeof(*strings)); max_strings = num_strings + 1; at the end of the loop to release the extra space. The + 1 is to allow for the null pointer. By roughly doubling the size allocated each time you allocate, you avoid quadratic behaviour compared with incrementing by one each time.
Notice too that the code carefully avoids losing the allocated space if the realloc() fails. You should 'never' use space = realloc(space, new_size); to avoid losing your pointer. The code carefully avoids dereferencing null pointers, and simply stops reading when there is a memory shortage.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
extern char **getpatterns(const char *filename);
char **getpatterns(const char *filename)
{
size_t num_strings = 0;
size_t max_strings = 0;
char **strings = 0;
FILE *file = fopen(filename, "r");
if (file != 0)
{
char line[4096];
while (fgets(line, sizeof(line), file) != NULL)
{
if (max_strings == 0 || num_strings >= max_strings - 1)
{
size_t new_num = max_strings * 2 + 2;
char **new_space = realloc(strings, new_num * sizeof(*new_space));
if (new_space == 0)
break;
strings = new_space;
max_strings = new_num;
}
size_t len = strlen(line); /* Includes '\n' at end */
strings[num_strings] = (char*)malloc(len);
memmove(strings[num_strings], line, len - 1);
strings[num_strings][len] = '\0';
strings[++num_strings] = 0; /* Null terminate list of strings */
}
fclose(file);
}
return(strings);
}
int main(void)
{
char **data = getpatterns("gp.c");
char **argp = data;
if (argp != 0)
{
/* Print data */
while (*argp != 0)
puts(*argp++);
/* Free space */
argp = data;
while (*argp != 0)
free(*argp++);
free(data);
}
return(0);
}

Resources