I have a function whichtakes a file, reads it line by line, puts every line in a *char[], puts this twodimensional array in a struct, and returns this struct:
wordlist.h:
#ifndef H_WORDLIST
#define H_WORDLIST
typedef struct {
char **chWordsList;
int listlen;
}Wordlist;
Wordlist getWordlistFromFile(char *chFilename);
char *getRandomWord();
#endif
The function (plus headers):
#include "wordlist.h"
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <string.h>
#define WORDSIZE 100
Wordlist getWordlistFromFile(char *chFilename){
FILE *file = fopen(chFilename,"r");
if (file == NULL){
printf("Unable to open file %s. Check if the file exists and can be read by this user.\n",chFilename);
exit(1);
}
char chWord[WORDSIZE];
int intFileSize = 0;
//First: coundt the amount of lines in the file
while((fgets(chWord,WORDSIZE,file) != NULL)){
++intFileSize;
}
rewind(file);
char *chWordList[intFileSize];
for (int count = 0; (fgets(chWord,WORDSIZE,file) != NULL); ++count){
chWordList[count] = malloc( strlen(chWord +1));
strcpy(chWordList[count],chWord);
chWordList[count][strlen(chWord) -1] = 0;
}
fclose(file);
Wordlist wordlist;
wordlist.chWordsList = chWordList;
wordlist.listlen = intFileSize;
for (int i = 0; i < wordlist.listlen; ++i){
printf("%s\n", wordlist.chWordsList[i]);
}
return wordlist;
}
So far this works great. The last for loop prints exactly every line of the given file, all fully expected behaviour, works perfect. Now, I actually want to use the function. So: in my main.c:
Wordlist list = getWordlistFromFile(strFilePath);
for (int i = 0; i < list.listlen; ++i){
printf("%s\n", list.chWordsList[i]);
}
This gives me the weirdest output:
abacus
wordlist
(null)
(null)
��Ⳏ
E����H�E
gasses
While the output should be:
abacus
amused
amours
arabic
cocain
cursor
gasses
It seems to me almost like some pointers get freed or something, while others stay intact. What is going on? Why is wordlist perfect before the return and broken after?
char *chWordList[intFileSize]
This array of strings is allocated on stack since it's declared as a local of getWordlistFromFile. Upon exiting the function the stack pointer is decreased and the array is no longer valid.
You should use the same approach used for the single string: allocate in on heap.
char **chWordList = malloc(intFileSize*sizeof(char*))
In this way the array will persist the scope of the function and you will be able to use it after the call to the function.
Because you are returning pointers to objects whose lifetime has expired. In particular, chWordsList inside the return value points to an object whose lifetime ends when the function returns. When you dereference that pointer you get undefined behavior (UB); therefore any result would not be surprising.
What you need to do is malloc memory for the chWordList instead of declaring it as a local array:
char **chWordList = malloc(intFileSize * sizeof(char*))
Change
char *chWordList[intFileSize];
to
char **chWordList = malloc(sizeof(char *) * intFileSize);
i.e allocated chwordList and set that in the WordList.
Your code is returning array variable chWordList allocated on stack, so it will not be valid once the function getWordlistFromFile() completes and returns to main().
Related
I'm writing a simple c program that reads lines from a text file into a char **. In my main function, I create the char * array, allocate memory for it, and pass a pointer to the array to another function to populate each index in the array with a char * representing the content of each line in the text file.
For some reason related to my memory management I'm guessing, I'm receiving a segmentation fault on the third iteration of my while loop, which copies the string into the array of strings. Why is this?
My code:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
void getRestaurants(char ***restaurantsArray) {
FILE *restaurantsFile = fopen("./restaurants.txt", "r");
char *restaurant = (char *)malloc(50 * sizeof(char));
char *restaurantCopy = restaurant;
//fopen will return null if it is unable to read the file
if (restaurantsFile == NULL) {
free(restaurant);
return;
}
int index = 0;
while (fgets(restaurantCopy, 50, restaurantsFile)) {
// segfault occurs the third time the following line is executed
*restaurantsArray[index] = (char*)malloc(50 * sizeof(char));
strcpy(*restaurantsArray[index], restaurantCopy);
printf("%s", restaurantCopy);
printf("%s", *restaurantsArray[index]);
index++;
}
fclose(restaurantsFile);
free(restaurant);
}
void main() {
char **restaurantsArray = (char **)malloc(100 * sizeof(char *));
char **restaurantsArrayCopy = restaurantsArray;
getRestaurants(&restaurantsArrayCopy);
}
Expected Result:
firstline
firstline
secondline
secondline
thirdline
thirdline
and so on, if the provided restaurants.txt file contains:
firstline
secondline
thirdline
In getRestaurants, restaurantsArray is declared as char ***Array. In the line *restaurantsArray[index] = …;, it takes restaurantsArray[index] and attempts to use it as a pointer (by applying the * operator). But restaurantsArray is merely a pointer to the restaurantsArrayCopy in main. restaurantsArrayCopy is merely a single object, not an array. It is just one char **. In getRestaurants, using restaurantsArray[index] with anything but zero for index uses some undefined thing.
There is no need to pass &restaurantsArrayCopy from main to getRestaurants. Just pass restaurantsArray. This is a pointer to allocated space.
Then, in getRestaurants, instead of *restaurantsArray[index] = …;, use restaurantsArray[index] = …;, without the *. This will assign a value to an element in restaurantsArray, which is what you want to do. Similarly, remove the * in the strcpy and the printf.
Im not clearly sure what is the difference between those 2. My professor wrote that **array is same as *array[] and we were presented an example where he used **array (so after classes I tried exchanging that with *array[] and it didn't work), could anyone tell me if those 2 are actually the same as he wrote?? Anyway the class was about dynamic memory allocation
#As soon as I changed the double pointer, this line started throwing error
lines = malloc(sizeof(char*));
and a few others where the memory is beeing reallocated
#2 Hell yeah, here's the whole code
And for those comments bellow, nope there is nothing inside [] because his statement was
**array = *array[]
BIG UPDATE
I am so sorry for any inconvenience, I was just too tired at the time of writing this post, here is the whole code with no edits
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char **lines; // global text buffer, organized as an array of lines
// --------------------------------------------------------------------------------
// initialize global buffer
void initialize()
{
lines = malloc(sizeof(char*));
lines[0] = NULL;
}
// --------------------------------------------------------------------------------
// return number of lines in buffer
int countLines()
{
int count = 0;
while(lines[count++]) ;
return count-1;
}
// --------------------------------------------------------------------------------
// print one line
void printLine(int line)
{
printf("Line %d: %p %p %s\n",line, &lines[line], lines[line], lines[line]);
}
// --------------------------------------------------------------------------------
// print all lines
void printAll()
{
int num_lines = countLines();
int line = 0;
printf("----- %d line(s) ----\n",num_lines);
while (line < num_lines)
printLine(line++);
printf("---------------------\n");
}
// --------------------------------------------------------------------------------
// free whole buffer
void freeAll()
{
int line = countLines();
while (line >= 0)
free(lines[line--]);
free(lines);
}
// --------------------------------------------------------------------------------
// insert a line before the line specified
void insertLine(int line, char *str)
{
int num_lines = countLines();
// increase lines size by one line pointer:
lines = realloc(lines, (num_lines+2) * sizeof(char*));
// move line pointers backwards:
memmove(&lines[line+1], &lines[line], (num_lines-line+1)*sizeof(char*));
// insert the new line:
lines[line] = malloc(strlen(str)+1);
strcpy(lines[line],str);
}
// --------------------------------------------------------------------------------
// remove the specified line
void removeLine(int line)
{
int num_lines = countLines();
// free the memory used by this line:
free(lines[line]);
// move line pointers forward:
memmove(&lines[line], &lines[line+1], (num_lines-line+1)*sizeof(char*));
// decrease lines size by one line pointer:
lines = realloc(lines, num_lines * sizeof(char*));
}
// --------------------------------------------------------------------------------
// insert a string into specified line at specified column
void insertString(int line, int col, char *str)
{
// make room for the new string:
lines[line] = realloc(lines[line], strlen(lines[line])+strlen(str)+1);
// move characters after col to the end:
memmove(lines[line]+col+strlen(str), lines[line]+col, strlen(lines[line])-col);
// insert string (without terminating 0-byte):
memmove(lines[line]+col, str, strlen(str));
}
// --------------------------------------------------------------------------------
// MAIN program
int main()
{
initialize();
printAll();
insertLine(0,"Das ist");
printAll();
insertLine(1,"Text");
printAll();
insertLine(1,"ein");
printAll();
insertLine(2,"kurzer");
printAll();
printf("lines[2][4] = %c\n",lines[2][4]);
insertString(2,0,"ziemlich ");
printAll();
removeLine(2);
printAll();
freeAll();
return 0;
}
If the code you reference in your question was given to you by your professor as an example of the use of pointer arrays of pointers to pointers, I'm not sure how much good that class will actually do. I suspect it was either provided as a debugging exercise or it may have been your attempt at a solution. Regardless, if you simply compile with Warnings enabled, you will find a number of problems that need attention before you advance to debugging your code.
Regarding the code you reference, while you are free to use a global text buffer, you are far better served by not using a global buffer and passing a pointer to your data as required. There are some instances, various callback functions, etc. that require global data, but as a rule of thumb, those are the exception and not the rule.
Your question basically boils down to "How do I properly use an array of pointers and double-pointers (pointer-to-pointer-to-type) variables. There is no way the topic can be completely covered in one answer because there are far too many situations and contexts where one or the other can be (or should be) used and why. However, a few examples will hopefully help you understand the basic differences.
Starting with the array of pointers to type (e.g. char *array[]). It is generally seen in that form as a function argument. When declared as a variable it is followed with an initialization. e.g.:
char *array[] = { "The quick",
"brown fox",
"jumps over",
"the lazy dog." };
char *array[]; by itself as a variable declaration is invalid due to the missing array size between [..]. When used globally, as in your example, the compiler will accept the declaration, but will warn the declaration is assumed to have one element.
The elements of array declared above are pointers to type char. Specifically, the elements are pointers to the string-literals created by the declaration. Each of the strings can be accessed by the associated pointer in array as array[0], ... array[3].
A pointer to pointer to type (double-pointer), is exactly what its name implies. It is a pointer, that holds a pointer as its value. In basic terms, it is a pointer that points to another pointer. It can be used to access the members of the array above by assigning the address of array like:
char **p = array;
Where p[1] or *(p + 1) points to "brown fox", etc.
Alternatively, a number of pointer to pointer to type can be dynamically allocated and used to create an array of pointers to type, that can then be allocated and reallocated to handle access or storage of an unknown number of elements. For example, a brief example to read an unknown number of lines from stdin, you might see:
#define MAXL 128
#define MAXC 512
...
char **lines = NULL;
char buf[MAXC] = {0};
lines = malloc (MAXL * sizeof *lines);
size_t index = 0;
...
while (fgets (buf, MAXC, stdin)) {
lines[index++] = strdup (buf);
if (index == MAXL)
/* reallocate lines */
}
Above you have lines, a pointer-to-pointer-to-char, initially NULL, that is use to allocate MAXL (128) pointers-to-char. Lines are then read from stdin into buf, after each successful read, memory is allocated to hold the contents of buf and the resulting start address for each block of memory is assigned to each pointer line[index] where index is 0-127, and upon increment of index to 128, index is reallocated to provide additional pointers and the read continues.
What makes the topic larger than can be handled in any one answer is that an array of pointers or pointer to pointer to type can be to any type. (int, struct, or as a member of a struct to different type, or function, etc...) They can be used linked-lists, in the return of directory listings (e.g opendir), or in any additional number of ways. They can be statically initialized, dynamically allocated, passed as function parameters, etc... There are just far too many different contexts to cover them all. But in all instances, they will follow the general rules seen here and in the other answer here and in 1,000's more answers here on StackOverflow.
I'll end with a short example you can use to look at the different basic uses of the array and double-pointer. I have provided additional comments in the source. This just provides a handful of different basic uses and of static declaration and dynamic allocation:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main (void) {
/* array is a static array of 4 pointers to char, initialized to the
4 string-literals that a part of the declaration */
char *array[] = { "The quick",
"brown fox",
"jumps over",
"the lazy dog." };
/* p is a pointer-to-pointer-to-char assigned the address of array */
char **p = array;
/* lines is a pointer-to-pointer-to-char initialized to NULL, used
below to allocate 8 pointers and storage to hold 2 copes of array */
char **lines = NULL;
size_t narray = sizeof array/sizeof *array;
size_t i;
printf ("\nprinting each string-literal at the address stored by\n"
"each pointer in the array of ponters named 'array':\n\n");
for (i = 0; i < narray; i++)
printf (" %s\n", array[i]);
printf ("\nprinting each string using a pointer to pointer to char 'p':\n\n");
for (i = 0; i < narray; i++, p++)
printf (" %s\n", *p);
p = array;
printf ("\nprinting each line using a pointer to pointer"
" to char 'p' with array notation:\n\n");
for (i = 0; i < narray; i++)
printf (" %s\n", p[i]);
/* allocate 8 pointers to char */
lines = malloc (2 * narray * sizeof *lines);
/* allocate memory and copy 1st 4-strings to lines (long way) */
for (i = 0; i < narray; i++) {
size_t len = strlen (array[i]);
lines[i] = malloc (len * sizeof **lines + 1);
strncpy (lines[i], array[i], len);
lines[i][len] = 0;
}
/* allocate memory and copy 1st 4-strings to lines
(using strdup - short way) */
// for (i = 0; i < narray; i++)
// lines[i] = strdup (array[i]);
/* allocate memory and copy again as last 4-strings in lines */
p = array;
for (i = 0; i < narray; i++, p++)
lines[i+4] = strdup (*p);
p = lines; /* p now points to lines instead of array */
printf ("\nprinting each allocated line in 'lines' using pointer 'p':\n\n");
for (i = 0; i < 2 * narray; i++)
printf (" %s\n", p[i]);
/* free allocated memory */
for (i = 0; i < 2 * narray; i++)
free (lines[i]);
free (lines);
return 0;
}
Let me know if you have any questions. It a large topic with a relatively small set of rules that can be applied in whole lot of different ways and in different contexts.
My professor wrote that **array is same as *array[]
That is true in some contexts and not true in other contexts.
If used in a function as argument,
void foo(int **array) {}
is the same as
void foo(int *array[]) {}
When declared as variables,
int **array;
is not the same as
int *array[];
To Explain in short:
if you want to use a pointer to type, you use *array or *ptr, or whatever variable name .
if you want to use an array of pointers and you already know how many pointers you need before execution (for eg. 10 pointers), then you use *array[10] or *ptr[10];
if you want to use an array of pointers but yet you don't know how many pointers you need before execution, then you use **array or **ptr;
after that you can dynamically allocate memory for every pointer using malloc or new operator to allocate memory for whatever data type you are using, (the data type maybe primitive like int, char ,etc, or it maybe user-defined like struct, etc.
I am pretty new to C and memory allocation in general. Basically what I am trying to do is copy the contents of an input file of unknown size and reverse it's contents using recursion. I feel that I am very close, but I keep getting a segmentation fault when I try to put in the contents of what I presume to be the reversed contents of the file (I presume because I think I am doing it right....)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int recursive_back(char **lines, int lineNumber, FILE *input) {
char *input_line = malloc(sizeof(char) * 1000);
lines = realloc(lines, (lineNumber) * 1000 * sizeof(char));
if(fgets(input_line, 201, input) == NULL) {
*(lines + lineNumber) = input_line;
return 1;
}
else {
printf("%d\n", lineNumber);
return (1+recursive_back(lines, ++lineNumber, input));
}
}
void backward (FILE *input, FILE *output, int debugflag ) {
int i;
char **lines; //store lines in here
lines = malloc(1000 * sizeof(char *) ); //1000 lines
if(lines == NULL) { //if malloc failed
fprintf(stderr, "malloc of lines failed\n");
exit(1);
}
int finalLineCount, lineCount;
finalLineCount = recursive_back(lines, 0, input);
printf("test %d\n", finalLineCount);
for(i = finalLineCount; i > 0; i--) {
fputs(*(lines+i), output); //segfault here
}
}
I am using a simple input file to test the code. My input file is 6 lines long that says "This is a test input file". The actual input files are being opened in another function and passed over to the backward function. I have verified that the other functions in my program work since I have been playing around with different options. These two functions are the only functions that I am having trouble with. What am I doing wrong?
Your problem is here:
lines = realloc(lines, (lineNumber) * 1000 * sizeof(char));
exactly as #ooga said. There are at least three separate things wrong with it:
You are reallocating the memory block pointed to by recursive_back()'s local variable lines, and storing the new address (supposing that the reallocation succeeds) back into that local variable. The new location is not necessarily the same as the old, but the only pointer to it is a local variable that goes out of scope at the end of recursive_back(). The caller's corresponding variable is not changed (including when the caller is recursive_back() itself), and therefore can no longer be relied upon to be a valid pointer after recursive_back() returns.
You allocate space using the wrong type. lines has type char **, so the object it points to has type char *, but you are reserving space based on the size of char instead.
You are not reserving enough space, at least on the first call, when lineNumber is zero. On that call, when the space requested is exactly zero bytes, the effect of the realloc() is to free the memory pointed to by lines. On subsequent calls, the space allocated is always one line's worth less than you think you are allocating.
It looks like the realloc() is altogether unnecessary if you can rely on the input to have at most 1000 lines, so you should consider just removing it. If you genuinely do need to be able to reallocate in a way that the caller will see, then the caller needs to pass a pointer to its variable, so that recursive_back() can modify it via that pointer.
I need to read some data from text file and store it in 2D-array.
This code works good:
#include <string.h>
#include <stdio.h>
int main() {
FILE *f = fopen("Read.txt", "r");
char buff[100][100];
char str[100];
int i = 0;
while(fgets(str, 100, f)) {
strcpy(buff[i], str);
i++;
}
return 0;
}
But why doesn't it work when I try to change buff definition in line 5 to:
char (*buff)[100];
I expected this definition to work too.
The error I get:
Run-Time Check Failure #3 - The variable 'buff' is being used without being defined
char (*buff)[100];
Here buff is a pointer to an array of 100 characters. So first you should make the pointer point to valid memory location before storing some value in it.
Presuming you want to go for dynamic memory allocation then you can have
char *buff[100];
Now in the fgets() loop allocate memory to each pointer individually like
buff[i] = malloc(100);
Note here buff is an array of 100 char pointers.
I have a program that accepts a char input using argv from the command line. I copy the input argv[1] using strcpy to a pointer called structptr(it goes to structptr->words from struct) where memory has been allocated. I then copy character by character from the memory that the pointer structptr points to another pointer called words that points to memory that has been allocated. After i've copied one character i print that element [c] to make sure that it has been copied correctly(which it has). I then finish copying all of the characters and return the result to a char pointer but for some reason it is blank/null. After each copying of the characters i checked if the previous elements were correct but they don't show up anymore([c-2], [c-1], [c]). Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct StructHolder {
char *words;
};
typedef struct StructHolder Holder;
char *GetCharacters(Holder *ptr){
int i=0;
char *words=malloc(sizeof(char));
for(i;i<strlen(ptr->words);i++){
words[i]=ptr->words[i];
words=realloc(words,sizeof(char)+i);
}
words[strlen(ptr->words)]='\0';
return words;
}
int main(int argc, char **argv){
Holder *structptr=malloc(sizeof(Holder));
structptr->words=malloc(strlen(argv[1]));
strcpy(structptr->words, argv[1]);
char *charptr;
charptr=(GetCharacters(structptr));
printf("%s\n", charptr);
return 0;
At first I thought this was the problem:
char *words=malloc(sizeof(char)) is allocating 1 byte (sizeof 1 char). You probably meant char *words = malloc(strlen(ptr->words)+1); - You probably want to null check the ptr and it's member just to be safe.
Then I saw the realloc. Your realloc is always 1 char short. When i = 0 you allocate 1 byte then hit the loop, increment i and put a char 1 past the end of the realloced array (at index 1)
Also your strcpy in main is has not allocated any memory in the holder.
In these two lines,
structptr->words=malloc(strlen(argv[1]));
strcpy(structptr->words, argv[1]);
need to add one to the size to hold the nul-terminator. strlen(argv[1]) should be strlen(argv[1])+1.
I think the same thing is happening in the loop, and it should be larger by 1. And sizeof(char) is always 1 by definition, so:
...
words=realloc(words,i+2);
}
words=realloc(words,i+2); // one more time to make room for the '\0'
words[strlen(ptr->words)]='\0';
FYI: Your description talks about structptr but your code uses struct StructHolder and Holder.
This code is a disaster:
char *GetCharacters(Holder *ptr){
int i=0;
char *words=malloc(sizeof(char));
for(i;i<strlen(ptr->words);i++){
words[i]=ptr->words[i];
words=realloc(words,sizeof(char)+i);
}
words[strlen(ptr->words)]='\0';
return words;
}
It should be:
char *GetCharacters(const Holder *ptr)
{
char *words = malloc(strlen(ptr->words) + 1);
if (words != 0)
strcpy(words, ptr->words);
return words;
}
Or even:
char *GetCharacters(const Holder *ptr)
{
return strdup(ptr->words);
}
And all of those accept that passing the structure type makes sense; there's no obvious reason why you don't just pass the const char *words instead.
Dissecting the 'disaster' (and ignoring the argument type):
char *GetCharacters(Holder *ptr){
int i=0;
OK so far, though you're not going to change the structure so it could be a const Holder *ptr argument.
char *words=malloc(sizeof(char));
Allocating one byte is expensive — more costly than calling strlen(). This is not a good start, though of itself, it is not wrong. You do not, however, check that the memory allocation succeeded. That is a mistake.
for(i;i<strlen(ptr->words);i++){
The i; first term is plain weird. You could write for (i = 0; ... (and possibly omit the initializer in the definition of i, or you could write for (int i = 0; ....
Using strlen() repeatedly in a loop like that is bad news too. You should be using:
int len = strlen(ptr->words);
for (i = 0; i < len; i++)
Next:
words[i]=ptr->words[i];
This assignment is not a problem.
words=realloc(words,sizeof(char)+i);
This realloc() assignment is a problem. If you get back a null pointer, you've lost the only reference to the previously allocated memory. You need, therefore, to save the return value separately, test it, and only assign if successful:
void *space = realloc(words, i + 2); // When i = 0, allocate 2 bytes.
if (space == 0)
break;
words = space;
This would be better/safer. It isn't completely clean; it might be better to replace break; with { free(words); return 0; } to do an early exit. But this whole business of allocating one byte at a time is not the right way to do it. You should work out how much space to allocate, then allocate it all at once.
}
words[strlen(ptr->words)]='\0';
You could avoid recalculating the length by using i instead of strlen(ptr->words). This would have the side benefit of being correct if the if (space == 0) break; was executed.
return words;
}
The rest of this function is OK.
I haven't spent time analyzing main(); it is not, however, problem-free.