Reading an array from a file - c

I am back to C from Java and C#. I am stuck with the following simple program attempting to read two arrays from a file with a function. Can anyone point out where am I messing up?
Compiler says:
error: invalid operands to binary * (have ‘int *’ and ‘int *’)
The file format is
4
1 2 3 4
23 23 14 11
My ReadFromFile function needs to fill buffers A and B from the file.
#include<stdio.h>
void ReadFromFile (const char* file_name, int *A, int *B, int *length)
{
FILE* file = fopen (file_name, "r");
fscanf (file, "%d", length);
int i;
for(i = 0; i < length; i++)
{
fscanf (file, "%d", A+i);
}
for(i = 0; i < length; i++)
{
fscanf (file, "%d", B+i);
}
fclose (file);
}
int main()
{
int *A; int *B; int length;
ReadFromFile("input.txt", A, B, &length);
return 0;
}

void ReadFromFile (const char* file_name, int *A, int *B, int *length)
/* ... */
for(i = 0; i < length; i++)
{
fscanf (file, "%d", A+i);
}
You have passed in a single integer A from main(), but here you are trying to access completely unrelated memory. Same goes for B. Did you mean to allocate A and B as arrays?
e.g. change:
int *A; int *B; int length;
to
int A[100], B[100], length;
or something similar. (Perhaps dynamically allocate the arrays once you know how many you need -- or allocate them with malloc(3) and grow them with realloc(3) if you need to.)

int main()
{
int A; int B; int length;
ReadFromFile("input.txt", &A, &B, &length);
return 0;
}
Try using A and B as variables not pointers and call the function with the addresses.
I'm not sure if thats the Issue. But give it a try.
Sorry I was confused by the int pointers. I guess what you want is int[] A what is in fact the same as int* A. But then you have to allocate some memory for the array or initialize it with a given size.

First of all A and B are pointers to junk, you need to allocate space if you actually want to store something (or expect a Segmentation fault or memory corruption).
It's complaining about your for loops. length is never initialized so you're sending a pointer to junk data anyway (or it's never populated in read from file).
i < length isn't valid (in that sense) because you're comparing an int value to an address or int * (which doesn't make sense). You might mean i < *length; because length is a pointer and needs to be dereferenced for its actual value.

When calling your function you should give an address reference e.g. &A and &B
And this is how you should read a text file appropriately :)
FILE *file = fopen(file_name, "r");
while(file != EOF){
fscanf(...)
}
EDIT:
You don't need to use double pointers. Simply initialize in main() integers Int A,B and give your method them addresses.

In your function you use A+i that accesses places in memory not allocated by you.
In other words: you need a call to malloc first to get the memory.
Think about your structure here:
You have address variables A and B but you never pointed them to the address of allocated memory. If you want to do this BEFORE your function you need to know the length of the array. If you want to do it IN your function, you need to pass the address of A and B to assign to it:
void ReadFromFile (const char* file_name, int** A, int** B, int* length)
{
FILE* file = fopen (file_name, "r");
fscanf (file, "%d", length);
*A = (int*) malloc(length * sizeof(int))
// now use (*A)+i
You would then change main
int* A; int* B; int length;
ReadFromFile("input.txt", &A, &B, &length);

Related

How do I modify a char** in another function

This is what I expect my string array s to be after the program is run: {"#0", "#1", "2"}.
This is what I am getting: {"#2", "#2", "2"}.
How do I modify this code so that I can get {"#0", "#1", "#2"} in the main after the function is executed?
What am I doing wrong? Please help.
#include <stdio.h>
void func(char **s){
for(int i=0; i<3; i++){
char buf[10];
snprintf(buf, 10, "#%d",i);
s[i]=buf;
}
}
int main()
{
char *s[3];
func(s);
for(int i=0; i<3; i++){
printf("%s", s[i]);
}
return 0;
}
First, you have every element in your array pointing to the same character array.
Second, they are all pointing to a local character array. This leads to undefined behavior.
Local variables in functions get allocated on the stack. When the function call is over, that memory may be overwritten with some other value.
Given two pointers p and q, the statement:
p = q;
doesn't copy the contents of the memory pointed to by q to the contents of the memory pointed to by p. It copies the pointer values, such that both p and q now point to the same memory, and any change to the memory via p is reflected when q is used.
That being said, the statement:
char buf[10];
declares buf to be an array of 10 chars. It has a lifetime corresponding to the execution of its block of definition. Once the function returns, it's destroyed and s is now indeterminate. Indeterminate pointers lead to undefined behaviour.
Possible Solutions:
standard strcpy
POSIX's strdup (which will be included in C23)
Note that the strdup() function returns a pointer to a new string which is
a duplicate of the provided string. Memory for the new string is obtained with malloc, and must be freed by the calling process with free.
#Chris’s answer tells you what is wrong.
To fix it, you have options. The simplest is to make the argument array have strings (char arrays) that are big enough for your uses:
#define MAX_STR_LEN (9+1) // Every string’s capacity is 9+1 characters
void func(size_t n, char array_of_string[][MAX_STR_LEN])
{
for (size_t i=0; i<n; i++)
{
snprintf(array_of_string[i], MAX_STR_LEN, "#%d", (int)i); // use the extant string
}
}
int main(void)
{
char array_of_string[3][MAX_STR_LEN] = {{0}}; // Array of 3 strings
func(3, array_of_string);
...
return 0;
}
If you want to play with dynamic char * strings, life gets only a little more complicated:
void func(size_t n, char *array_of_string[])
{
for (size_t i=0; i<n; i++)
{
free(array_of_string[i]); // free any pre-existing string
array_of_string[i] = calloc( 10, 1 ); // allocate our new string
if (!array_of_string[i]) fooey(); // always check for failure
snprintf(array_of_string[i], 10, "#%d", (int)i); // use the new string
}
}
int main(void)
{
char *array_of_string[3] = {NULL}; // Array of 3 dynamic strings, all initially NULL
func(3, array_of_string);
...
for (size_t i=0; i<3; i++) // Don’t forget to clean up after yourself
free(array_of_string[i]);
return 0;
}
Ultimately the trick is to manage the size of your strings, remembering that a string is itself just an array of char. You must ensure that there is enough room in your character array to store all the characters you wish. (Good job on using snprintf()!
Also remember that in C any argument of the form array[] is the same as *array. So our functions could have been written:
void func(size_t n, char (*array_of_string)[MAX_STR_LEN])
or
void func(size_t n, char **array_of_string)
respectively. The first is an uglier (harder to read) syntax. The second is nicer, methinks, but YRMV.
Finally, if you are using C99 (or later) you can tell the compiler that those arguments are, actually, arrays:
void func(size_t n, char array_of_string[n][MAX_STR_LEN])
or
void func(size_t n, char *array_of_string[n])
MSVC does not support that syntax, though, and probably never will, alas.
{ // start of a new scope
char buf[10]; // a variable with automatic storage duration
// ...
} // end of scope - all automatic variables end their life
In your code, you make pointers point at buf which has seized to exist (3 times) at the } in the for loop. Dereferencing (reading from the memory those pointers point at) those pointers afterwards makes your program have undefined behavior (anything could happen).
What you can do is to allocate and release memory dynamically using malloc and free.
When sending in a pointer to the first element in an array of elements to a function like you do, it's also customary to provide the length of the array (the number of elements in the array) to the function.
It could look like this:
#include <stdio.h>
#include <stdlib.h>
// A macro to calculate the number of elements in an array
// sizeof (x) - the number of bytes the whole array occupies
// sizeof *(x) - the size of the first element in the array
// (all elements have equal size)
// The result of the division is the number of elements in the array.
#define SIZE(x) (sizeof (x) / sizeof *(x))
void func(char *s[], size_t len) {
for (size_t i = 0; i < len; i++) {
// calculate the required length for this string
size_t req = snprintf(NULL, 0, "#%zu", i) + 1; // +1 for '\0'
// and allocate memory for it
s[i] = malloc(req);
if(s[i] == NULL) exit(1); // failed to allocate memory
snprintf(s[i], req, "#%zu", i);
} // dynamically allocated memory is _not_ released at the end of the scope
}
int main() {
char *s[3];
func(s, SIZE(s));
for (size_t i = 0; i < SIZE(s); i++) {
puts(s[i]);
free(s[i]); // free what you've malloc'ed when you are done with it
}
}
Note that with the use of the macro there is only one hardcoded 3 in the program. Even that could be made into a named constant (#define CHAR_PTRS (3) or enum { CHAR_PTRS = 3 };) to further the ease of reading and maintaining the code.
A non-idiomatic version only accepting a pointer to an array of a fixed (at compile time) size could look like like below. In this example you couldn't accidentally provide a pointer to an array with only 2 char* (which would cause the function to write out of bounds). Instead, it'd result in a compilation error.
#include <stdio.h>
#include <stdlib.h>
// Here `s` is a pointer to an array of 3 char*
void func(char *(*s)[3]) {
for (int i = 0; i < 3; i++) {
(*s)[i] = malloc(10);
if((*s)[i] == NULL) exit(1);
snprintf((*s)[i], 10, "#%d", i);
}
}
int main() {
char *s[3];
func(&s); // &s is a char*(*)[3]
for (int i = 0; i < 3; i++) {
printf("%s\n", s[i]);
free(s[i]);
}
}
#include <stdio.h>
#include <string.h>
void func(char **s){
for(int i=0; i<3; i++){
s[i]=malloc(sizeof(char) * 100);
char buf[10];
snprintf(buf, 10, "#%d",i);
strcpy(s[i], buf);
}
}
int main()
{
char *s[3];
func(s);
for(int i=0; i<3; i++){
printf("%s", s[i]);
}
return 0;
}
This fixed my problem. My understanding is that I assigned memory and then copied the contents of buf to s to the now-present memory.

how do I assign individual string to an element in one array of pointer?

I am new to C and still trying to figure out pointer.
So here is a task I am stuck in: I would like to assign 10 fruit names to a pointer of array and print them out one by one. Below is my code;
#include <stdio.h>
#include <string.h>
int main(){
char *arr_of_ptrs[10];
char buffer[20];
int i;
for (i=0;i<10;i++){
printf("Please type in a fruit name:");
fgets(buffer,20,stdin);
arr_of_ptrs[i]= *buffer;
}
int j;
for (j=0;j<10;j++){
printf("%s",*(arr_of_ptrs+j));
}
}
However after execution this, it only shows me the last result for all 10 responses. I tried to consult similar questions others asked but no luck.
My understanding is that
1) pointer of array has been allocated memory with [10] so malloc() is not needed.
2) buffer stores the pointer to each individual answer therefore I dereference it and assign it to the arr_of_ptrs[i]
I am unsure if arr_of_ptrs[i] gets me a pointer or a value. I thought it is definitely a pointer but I deference it with * the code and assign it to *buffer, program would get stuck.
If someone could point out my problem that would be great.
Thanks in advance
Three erros, 1. You must allocate memory for elements of elements of arr_of_ptrs, now you only allocate the memory for elements of arr_of_ptrs on stack memory. 2. arr_of_ptrs[i]= *buffer; means all of the arr_of_ptrs elements are pointed to same memory address, which is the "buffer" pointer. So all the elements of arr_of_ptrs will be same to the last stdin input string. 3. subsequent fgets() call has potential problem, one of the explaination could be here
A quick fix could be,
#include <stdio.h>
#include <string.h>
int main(){
const int ARR_SIZE = 10, BUFFER_SIZE = 20;
char arr_of_ptrs[ARR_SIZE][BUFFER_SIZE];
char *pos;
int i, c;
for (i = 0; i < ARR_SIZE; ++i) {
printf ("Please type in a fruit name: ");
if (fgets (arr_of_ptrs[i], BUFFER_SIZE, stdin) == NULL) return -1;
if ((pos = strchr(arr_of_ptrs[i], '\n')))
*pos = 0;
else
while ((c = getchar()) != '\n' && c != EOF) {}
}
for (i = 0; i < ARR_SIZE; ++i)
printf("%s\n", arr_of_ptrs[i]);
return 0;
}
The misunderstanding is probably that "Dereferencing" an array of characters, unlike dereferencing a pointer to a primitive data type, does not create a copy of that array. Arrays cannot be copied using assignment operator =; There a separate functions for copying arrays (especially for 0-terminated arrays of char aka c-strings, and for allocating memory needed for the copy):
Compare a pointer to a primitive data type like int:
int x = 10;
int *ptr_x = &x;
int copy_of_x = *ptr_x; // dereferences a pointer to x, yielding the integer value 10
However:
char x[20] = "some text"; // array of characters, not a single character!
char *ptr_x = &x[0]; // pointer to the first element of x
char copy_of_first_char_of_x = *ptr_x; // copies the 's', not the entire string
Use:
char x[20] = "some text";
char *ptr_x = &x[0];
char *copy_of_x = malloc(strlen(ptr_x)+1); // allocate memory large enough to store the copy
strcpy(copy_of_x,ptr_x); // copy the string.
printf("%s",copy_of_x);
Output:
some text

Dynamic memory and pointers arguments

I have this two functions, meant to do the same thing - read every line from a file with only integers and store them on an array:
I call them like this on the main() function:
StoreinArray1(X, size, f);
StoreinArray2(X, size, f);
The First works but the Second doesn't.
First
int StoreinArray1(int X[], int *size, char *file)
{
int i=0;
FILE *f;
f = fopen(file, "r");
X = (int*) realloc (X, *size * sizeof(int));
for (i=0;i<*size;i++)
{
fscanf(f, "%d", &X[i]);
}
return 1;
}
Second
int StoreinArray2(int X[], int *size, char *file)
{
FILE *f;
f = fopen(file, "r");
if (f == NULL)
return -1; // failed opening
*size = 0;
while (!feof(f))
{
if (fscanf(f, "%d", &X[*size]) == 1)
*size = *size + 1;
}
fclose(f);
return 1;
}
For the First I used dynamic memory allocation and actually calculated size:
X = malloc(0);
while ((ch = fgetc(f)) != EOF)
{
if (ch == '\n')
lines++;
}
size = &lines;
For the Second I can't do the same. Visual Studio Code crashes when I try.
So I tried to do *size = 0 and then StoreinArray2(X, size, f); but it didn't work either.
So my question is about the second function:
Is it calculating the size while it is scanning the file? Supposedly it isn't necessary to use dynamic memory allocation (my teacher said).
If so then how can I pass some "size" argument correctly? As a pointer or just a simple integer?
Thank you in advance!
Edit:
Here is the full First program:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *f;
int *size=0, *X, lines=1;
char *file = {"file.txt"};
char ch;
X = malloc(0);
f = fopen(file, "r");
while ((ch = fgetc(f)) != EOF)
{
if (ch == '\n')
lines++;
}
size = &lines;
StoreinArray(X, size, file);
}
int StoreinArray(int X[], int *size, char *file)
{
int i=0;
FILE *f;
f = fopen(file, "r");
X = (int*) realloc (X, *size * sizeof(int));
for (i=0;i<*size;i++)
{
fscanf(f, "%d", &X[i]);
}
for (i=0;i<*size;i++)
printf("%d\n",X[i]);
return 1;
}
And the Second:
int main()
{
int X[100];
int *size;
char *file = {"file.txt"};
*size = 0;
StoreinArray(X, size, file);
}
int StoreinArray(int X[], int *size, char *file)
{
FILE *f;
f = fopen(file, "r");
if (f == NULL)
return -1;
*size = 0;
while (!feof(f))
{
if (fscanf(f, "%d", &X[*size]) == 1)
*size = *size + 1;
}
fclose(f);
return 1;
}
In first I had to open the file in main to count the number of lines. I know I forgot fclose(f) and free(X) in main, but with those instructions VSC crashes.
int StoreinArray (int X[], int *size, char *file)
{
FILE *f;
int i=0;
f = fopen(file, "r");
if (f == NULL)
return -1;
*size = 0;
while (!feof(f))
{
if (fscanf(f, "%d", &X[*size]) == 1)
{
*size = *size + 1;
X = (int *) realloc (X , *size * sizeof(int));
}
}
fclose(f);
return 1;
}
int main()
{
int *X, size=0;
char *file = {"f.txt"};
X=malloc(0);
StoreinArray(X, &size, file);
free(X);
}
The problem with the second version of your program is the declaration of size in main. Declare it as an int, not a pointer to an int. Your current program is crashing because you didn't allocate any space for size, and when StoreInArray tried to update it, you got an access violation. So, main should look like this:
int main()
{
int X[100];
int size;
char *file = {"file.txt"};
size = 0;
StoreinArray(X, &size, file);
}
OK, I'll try to be through about it and explain all the things I can find.
First of all, we need to talk about variables, pointers and memory, because it seems that you don't have a very firm grasp of these concepts. Once that clicks, the rest should follow easily.
First up, simple variables. That part is easy, I think you more or less understand that.
int x; // This is an integer variable. It's not initialized, so its value could be anything
int meaning = 42; // This is another integer variable. Its value will be 42.
double pi = 3.14; // A variable with digits after the decimal point
char c = 15; // Another inte... well, yes, actually, char is also an integer.
char c2 = 'a'; // Nice, this also counts. It's converted to an integer behind the scenes.
Etc.
Similarly with arrays:
int arr[10]; // Array with 10 values. Uninitialized, so they contain garbage.
int arr2[3] = { 1, 2, 3 }; // Array with 3 values, all initialized
int arr3[] = {1, 2, 3, 4, 5}; // Array with 5 values.
Arrays are basically just a bunch of variables created at once. When you make an array, C needs to know the size, and the size must be a fixed number - you can't use another variable. There's a reason for this, but it's technical and I won't go into that.
Now about memory. Each of these variables will be stored somewhere in your computer's RAM. The precise location is unpredictable and can vary each time you run your program.
Now, RAM is like a huuuge array of bytes. There's byte number 0, byte number 1, etc. An int variable takes up 4 bytes, so it could, for example, end up in bytes number 120, 121, 122 and 123.
All the bytes in a single variable (or in a single array) will be next to each other in RAM. Two different variables could end up in the opposite ends of your RAM, but the bytes in each of those variables will be together.
Now we come to the concept of a pointer. A pointer is basically just an integer variable. It contains the RAM-number of the first byte of some other variable. Let's look at an example:
int i = 42;
int *p = &i;
Suppose that the variable i got stored in the bytes number 200...203 (that's 4 bytes). In those bytes we have the value 42. Then let's suppose that the variable p got stored in the bytes number 300...303 (that's another 4 bytes). Well, these 4 bytes will contain the value 200, because that's the first byte of the i variable.
This is also what programmers mean when they say "(memory) address of <variable>" or "a pointer to <variable>. It's the number of the first byte in RAM of <variable>. Since all bytes of the same variable stick together, then by knowing the first byte (and knowing the type of the variable), you can figure out where the rest of <variable> is in memory.
Now let's add one more line in our example:
*p = 5;
What the computer does in this case is it takes the address which was stored in p, goes to that location in memory, treats the following 4 bytes as an integer, and puts the value 5 there. Since we had previously set p to "point" at the address of i, this has the same effect as simply setting the i variable itself.
Ok, did you get all of this? This is a bit tricky and usually takes a while to wrap your head around it. Feel free to re-read it as many times as necessary to understand it. You'll need it to move on.
Ready? Ok, let's talk a bit about stack and dynamic memory.
When the program starts, the OS automatically allocates a bit of memory for it, just to make it easier for it to start. It's like a big array of bytes, all together in memory. Nowadays it's usually about 1MB, but it can vary. This memory is called "The Stack". Why is it called that? Eh, I'll explain some other time.
Anyways, When your main() function starts, the OS goes like "Here you go my good fellow, a pointer to The Stack. It's all yours to use as you see fit! Have a nice day!"
And your main() function then uses it to store all the variables you make in it. So when you go like p = &i; then the address you get stored in p is somewhere within The Stack.
Now when main() calls another function, such as StoreinArray(), it also gives it a pointer to the stack and says "OK, here's a pointer to the stack. Careful, I've already used the first XXX bytes of it, but feel free to use the rest".
And then StoreinArray() uses the stack to put its variables there. And when StoreinArray() calls something else, it does the same, and on and on.
Now, there are a few things to note here:
This scheme is nice, because nobody needs to "allocate" or "deallocate" any memory. It's easier, it's faster.
However, when a function returns, all its variables are considered to be gone and that memory is fair game to anyone else later wanting it. So be careful about pointers pointing to it. They will only be valid while the function is running. When it returns - well, the pointer will still "work", but who knows when something will overwrite that memory... And if you write to that memory, who can tell what you will mess up? Many subtle bugs have been created this way.
The stack is fairly limited and can be exhausted. When all bytes in it are used up, your program WILL crash. The typical way this happens is either when you try to create a very large array, or you go into some sort of infinite loop where the function keeps calling itself over and over again. Try that. :)
So, for these cases you use "dynamic" memory. In C that mostly means malloc(). You tell malloc() how many bytes of memory you need, and malloc() finds a large enough unclaimed space in the RAM, marks it as used, and gives you a pointer to it. Well, that's a simplified view of things anyway.
The same approach works when you don't know beforehand how much memory you will need.
The downside is that you need to free() the memory when you're done with it, or you may run out of available memory, and then malloc() will fail. Be also aware that after you've freed the memory, all pointers to it should be considered invalid, because you're not the owner of that particular piece of memory anymore. Anything can happen if you keep messing with it.
Phew, that's a lot. OK, I need a break. I'll come back and analyze your programs a bit later. But if you've understood all of this, you should now be able to spot the mistakes in your programs. Try going through them, line by line, and narrating to yourself what each line does.
Many, many hours later:
OK, so let's take a look at your programs. The first one:
#include <stdio.h>
#include <stdlib.h>
int main()
{
FILE *f;
int *size=0, *X, lines=1;
char *file = {"file.txt"};
char ch;
X = malloc(0);
f = fopen(file, "r");
while ((ch = fgetc(f)) != EOF)
{
if (ch == '\n')
lines++;
}
size = &lines;
StoreinArray(X, size, file);
}
int StoreinArray(int X[], int *size, char *file)
{
int i=0;
FILE *f;
f = fopen(file, "r");
X = (int*) realloc (X, *size * sizeof(int));
for (i=0;i<*size;i++)
{
fscanf(f, "%d", &X[i]);
}
for (i=0;i<*size;i++)
printf("%d\n",X[i]);
return 1;
}
There are two things that can be improved here. First - the size and lines variables. There's no need for both of them. Especially since you set size to point to lines anyway. Just keep lines and all will be fine. When you pass it to StoreinArray(), pass it as a simple integer. There's no need for a pointer.
Second, the X array. You're doing something odd with it and it seems like you're fumbling in the dark. There's no need for the malloc(0) and then later realloc(X, *size*sizeof(int). Keep it simple - first count the lines, then allocate the memory (as much as needed). Also, keep the memory allocation in the main() method and just pass the final X to the StoreinArray. This way you can avoid another subtle bug - when inside the StoreinArray() function you execute the line X = (int*) realloc (X, *size * sizeof(int)); the value of X changes only inside the StoreinArray() function. When the function returns, the variable X in the main() function will still have its old value. You probably tried to work around this with the reallocate() dance, but that's not how it works. Even worse - after the realloc(), whatever value the X used to be, isn't a valid pointer anymore, because realloc() freed that old memory! If you had later tried to do anything with the X variable in the main() function, your program would have crashed.
Let's see how your program would look with the changes I proposed (plus a few more small cosmetic tweaks):
#include <stdio.h>
#include <stdlib.h>
int main()
{
char *file = "file.txt";
FILE *f = fopen(file, "r");
int *X, lines=1;
char ch;
while ((ch = fgetc(f)) != EOF)
{
if (ch == '\n')
lines++;
}
fclose(f);
X = (int *)malloc(lines * sizeof(int));
StoreinArray(X, lines, file);
}
void StoreinArray(int X[], int lines, char *file)
{
int i=0;
FILE *f = fopen(file, "r");
for (i=0;i<lines;i++)
{
fscanf(f, "%d", &X[i]);
}
fclose(f);
for (i=0;i<lines;i++)
printf("%d\n",X[i]);
}
OK, now the second program.
int main()
{
int X[100];
int *size;
char *file = {"file.txt"};
*size = 0;
StoreinArray(X, size, file);
}
int StoreinArray(int X[], int *size, char *file)
{
FILE *f;
f = fopen(file, "r");
if (f == NULL)
return -1;
*size = 0;
while (!feof(f))
{
if (fscanf(f, "%d", &X[*size]) == 1)
*size++;
}
fclose(f);
return 1;
}
Right off the bat, the size variable will crash your program. It's a pointer that isn't initialized so it points to some random place in memory. When a little lower you try to write to the memory it points to (*size = 0), that will crash, because most likely you won't own that memory. Again, you really don't need a pointer here. In fact, you don't need the variable at all. If you need to know in the main program how many integers the StoreinArray() read, you can simply have it return it.
There's another subtle problem - since the size of X array is fixed, you cannot afford to read more than 100 integers from the file. If you do, you will go outside the array and your program will crash. Or worse - it won't crash, but you'll be overwriting the memory that belongs to some other variable. Weird things will happen. C is lenient, it doesn't check if you're going outside the allowed bounds - but if you do, all bets are off. I've spent many hours trying to find the cause of a program behaving weirdly, only to find out that some other code in some completely unrelated place had gone outside its array and wreaked havoc upon my variablers. This is VERY difficult to debug. Be VERY, VERY careful with loops and arrays in C.
In fact, this kind of bug - going outside an array - has it's own name: a "Buffer Overrun". It's a very common security exploit too. Many security vulnerabilities in large, popular programs are exactly this problem.
So, the best practice would be to tell StoreinArray() that it can store at most 100 integers in the X array. Let's do that:
#include <stdio.h>
#include <stdlib.h>
#define MAX_X 100
int main()
{
int X[MAX_X];
char *file = "file.txt";
int lines;
lines = StoreinArray(X, MAX_X, file);
}
int StoreinArray(int X[], int maxLines, char *file)
{
FILE *f;
int lines;
f = fopen(file, "r");
if (f == NULL)
return -1;
while (!feof(f))
{
if (fscanf(f, "%d", &X[lines]) == 1)
lines++;
if (lines == maxLines)
break;
}
fclose(f);
return lines;
}
So, there you are. This should work. Any more questions? :)

About qsort() in C, difference between ** buf and buf[][]

When I use qsort() in the C on my Mac, these code works well, It can sort every lines in one file well.
int compare(const void *p, const void *q) {
return strcmp(p,q);
}
void function_name(){
char buf[1024][1024];
int i=0;
FILE * fp;
if(!(fp=fopen(filename,"r"))){
perror("Open error!");
exit(0);
}
while(fgets(buf[i],1024,fp)){
//printf("%s",buf[i]);
i++;
}
qsort(buf, i, sizeof(buf[0]), compare);
}
However, when I use malloc to assign the space, it sucks. Why is that?
The code below shows I use malloc to make one two-dimension array. I print the content of buf before and after. It seems that lose all the information.
int i=0;
FILE * fp;
char ** buf;
buf = (char **)malloc(sizeof(char*)*1024);
if(!(fp=fopen(filename,"r"))){
perror("Open error!");
exit(0);
}
buf[0]=(char *)malloc(sizeof(char)*1024);
while(fgets(buf[i],1024,fp)){
i++;
buf[i]=(char *)malloc(sizeof(char)*1024);
}
for(int j=0;j<i;j++){
printf("%s",buf[j]);
}
printf("hehe%ld\n",sizeof(char)*1024);
qsort(buf, i, sizeof(char)*1024, compare);
printf("hehe\n");
for(int j=0;j<i;j++){
printf("%s",buf[j]);
}
output:
a
A
b
c
d
D
C
E
e
B
d
e
f
a
hehe1024
hehe
(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)(null)
Most important, how to fix my malloc version?
When you attempt to qsort buf declared as char **buf, you are using the wrong comparison function. As I noted in the comment, char buf[x][y] is an x array of character arrays of y chars, e.g. char (*)[y] when passed as a parameter. When you declare buf as char **buf;, you declare a pointer to a pointer to type char. In either case, you have a pointer to string, and you must dereference each value passed to qsort one additional level of indirection., e.g.
int cmpstrings (const void *a, const void *b) {
return strcmp (*(char * const *)a, *(char * const *)b);
}
A short example using char **buf; would be:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* qsort string comparison - for pointer to pointer to char */
int cmpstrings (const void *a, const void *b) {
return strcmp (*(char * const *)a, *(char * const *)b);
}
int main (void) {
char *ap[] = { "This is a tale",
"of captian Jack Sparrow",
"a pirate so brave",
"on the seven seas." },
**buf = NULL;
int n = sizeof ap/sizeof *ap;
if (!(buf = malloc (n * sizeof *buf))) { /* allocate pointers */
fprintf (stderr, "error: virtual memory exhausted.\n");
return 1;
}
for (int i = 0; i < n; i++)
buf[i] = strdup (ap[i]); /* allocate/copy strings */
qsort (buf, n, sizeof *buf, cmpstrings);
for (int i = 0; i < n; i++) { /* print and free */
printf ("buf[%d] : %s\n", i, buf[i]);
free (buf[i]);
}
free (buf);
return 0;
}
Example
$ ./bin/qsortptp
buf[0] : This is a tale
buf[1] : a pirate so brave
buf[2] : of captian Jack Sparrow
buf[3] : on the seven seas.
qsort, memcpy and similar functions assumes that the pointer passed points at an array. The very definition of an array is x number of items allocated contiguously, in adjacent memory cells. For example char array [x][y];.
There is wide-spread confusion about the use of pointer-to-pointers. There's an old trick you can use to declare a look-up table, where each item points to an array of variable length, which goes like this:
char** ptr = malloc(x * sizeof(char*));
for(int i=0; i<x; i++)
{
ptr[i] = malloc(y * sizeof(char));
}
This allows you to access the look-up table with array-like syntax, ptr[i][j]. But that does not mean that this is an array, there is no relation whatsoever beween a pointer-to-pointer and a 2D array! This is rather x number of segments, where each segment allocated at any place on the heap.
The only reason why you would ever use the above pointer-to-pointer trick is when you must have variable length for each item in the look-up table. If you don't need that, then the above method is plain bad and always incorrect - it is much slower than a real array, both in terms of allocation overhead, heap fragmentation and poor data cache use. And as you discovered, it can't even be used as an array... because it isn't one.
Now there are unfortunately numerous incompetent would-be gurus all over the world incorrectly teaching the above as the way to allocate a multi-dimensional array dynamically. Which is complete nonsense. It is not a multi-dimensional array and can't be used as one. See this for an explanation of how you actually should allocate multi-dimensional arrays dynamically.

Passing pointer of pointers to function, returning both an int and an address (by parameter)

I've got a function which, as is, works correctly. However the rest of the program has a limitation in that I've preset the size of the array (the space to be allocated). Obviously, this is problematic should an event arise in which I need extra space for that array. So I want to add dynamic allocation of memory into my program.
But I'm having an issue with the whole pointer to a pointer concept, and I've utterly failed to find an online explanation that makes sense to me...
I think I'll want to use malloc(iRead + 1) to get an array of the right size, but I'm not sure what that should be assigned to... *array? **array? I'm not at all sure.
And I'm also not clear on my while loops. &array[iRead] will no longer work, and I'm not sure how to get a hold of the elements in the array when there's a pointer to a pointer involved.
Can anyone point (heh pointer pun) me in the right direction?
I can think of the following approaches.
First approach
Make two passes through the file.
In the first pass, read the numbers and discard them but keep counting the number of items.
Allocate memory once for all the items.
Rewind the file and make a second pass through it. In the second pass, read and store the numbers.
int getNumberOfItems(FILE* fp, int hexi)
{
int numItems = 0;
int number;
char const* format = (hexi == 0) ? "%X" : "%d";
while (fscanf(fp, format, &number) > 0) {
++numItems;
return numItems;
}
void read(int *array, FILE* fp, int numItems, int hexi)
{
int i = 0;
char const* format = (hexi == 0) ? "%X" : "%d";
for ( i = 0; i < numItems; ++i )
fscanf(fp, format, &array[i]);
}
int main(int argc, char** argv)
{
int hexi = 0;
FILE* fp = fopen(argv[1], "r");
// if ( fp == NULL )
// Add error checking code
// Get the number of items in the file.
int numItems = getNumberOfItems(fp, hexi);
// Allocate memory for the items.
int* array = malloc(sizeof(int)*numItems);
// Rewind the file before reading the data
frewind(fp);
// Read the data.
read(array, fp, numItems, hexi);
// Use the data
// ...
// ...
// Dealloate memory
free(array);
}
Second approach.
Keep reading numbers from the file.
Every time you read a number, use realloc to allocate space the additional item.
Store the in the reallocated memory.
int read(int **array, char* fpin, int hexi)
{
int number;
int iRead = 0;
// Local variable for ease of use.
int* arr = NULL;
char const* format = (hexi == 0) ? "%X" : "%d";
FILE *fp = fopen(fpin, "r");
if (NULL == fp){
printf("File open error!\n");
exit(-1);
}
while (fscanf(fp, format, &number) > 0) {
arr = realloc(arr, sizeof(int)*(iRead+1));
arr[iRead] = number;
iRead += 1;
}
fclose(fp);
// Return the array in the output argument.
*array = arr;
return iRead;
}
int main(int argc, char** argv)
{
int hexi = 0;
int* array = NULL;
// Read the data.
int numItems = read(&array, argv[1], hexi);
// Use the data
// ...
// ...
// Dealloate memory
free(array);
}
int read(int **array, char* fpin, int hexi) {
int iRead = 0;
int i, *ary;
char *para;
FILE *fp;
fp = fopen(fpin, "r");
if (NULL == fp){
printf("File open error!\n");
exit(-1);
}
para = (hexi == 0) ? "%*X" : "%*d";
while (fscanf(fp, para)!= EOF)
++iRead;
ary = *array = malloc(iRead*sizeof(int));
if(ary == NULL){
printf("malloc error!\n");
exit(-2);
}
rewind(fp);
para = (hexi == 0) ? "%X" : "%d";
for(i = 0; i < iRead; ++i)
fscanf(fp, para, &ary[i]);
fclose(fp);
return iRead;
}
I'd suggest something like this:
int read(int **array_pp, char* fpin, int hexi) {
...
int *array = malloc (sizeof (int) * n);
for (int i=0; i < n; i++)
fscanf(fp, "%X",&array[i]);
...
*array_pp = array;
return n;
}
Notes:
1) You must use "**" if you want to return a pointer in a function argument
2) If you prefer, however, you can declare two pointer variables (array_pp and array) to simplify your code.
I think you wouldn't call it an array. Arrays are of fixed size and lie on the stack. What you need (as you already said), is dynamically allocated memory on the heap.
maybe that's why you didn't find much :)
here are some tutorials:
http://en.wikibooks.org/wiki/C_Programming/Arrays (and following pages)
http://www.eskimo.com/~scs/cclass/int/sx8.html
you got the function declaration correctly:
int read(int **array, char* fpin, int hexi)
What you need to do:
find out how much memory you need, eg. how many elements
allocate it with *array = malloc(numElements * sizeof(int)) (read "at the address pointed by array allocate memory for numElements ints")
now you can (*array)[idx] = some int (read "at the address pointed by array, take the element with index idx and assign some int to it")
call it with int* destination; int size = read(&destination, "asdf", hexi)
hope it helps..

Resources