How do I access data from a dynamic array in c? - c

I've got two different arrays that I'm using. With one, I'm getting the exact results that I want, the other, not so much. I'm filing the arrays with by reading from a text file similar to this:
2597
283
4
723
21
82
426
The first five lines would be the customer IDs. There is always 5 lines but they don't always have a value. The next line is the number of vendors, then followed by the vendor ids.
void use_arrays()
{
int
i,
customer_count,
*customer_ids,
vendor_count,
*vendor_ids;
customer_ids = malloc(sizeof(int));
vendor_ids = malloc(sizeof(int));
fill_arrays(&customer_count, customer_ids, &vendor_count, vendor_ids);
for (i = 0; i < customer_count; i++)
{
printf("Customer[%d]: %d\n", i, customer_ids[i]);
}
for (i = 0; i < vendor_count; i++)
{
printf("Vendor[%d]: %d\n", i, vendor_ids[i]);
}
free(customer_ids);
free(vendor_ids);
}
void fill_arrays(int *customer_count, int *customer_ids, int *vendor_count, int *vendor_ids)
{
int
i,
*temp,
customer_id,
vendor_id,
num_cust = 0;
FILE
*inp_file;
char
*endptr = NULL,
buffer[500];
inp_file = fopen(g_filename, "r");
for (i = 0; i < 5; i++) /* Can't be more than 5 customers */
{
fgets(buffer, sizeof(buffer), inp_file);
customer_id = strtol(buffer, &endptr, 0);
if (customer_id != 0)
{
customer_ids[i] = customer_id;
temp = realloc(customer_ids, (i+2)*sizeof(int));
if (temp != NULL)
{
customer_ids = temp;
}
else
{
printf("Couldn't allocate memory\n");
}
num_cust++;
}
}
*customer_count = num_cust;
/* Next is number of vendor ids*/
fgets(buffer, sizeof(buffer), inp_file);
*vendor_count = strtol(buffer, &endptr, 0);
temp = realloc(vendor_ids, *vendor_count*sizeof(int));
if (temp != NULL)
{
vendor_ids = temp;
}
else
{
printf("Couldn't allocate memory\n");
}
for (i = 0; i < *vendor_count; i++)
{
fgets(buffer, sizeof(buffer), inp_file);
vendor_id = strtol(buffer, &endptr, 0);
if (vendor_id != 0)
{
vendor_ids[i] = vendor_id;
}
}
fclose(inp_file);
}
Once the arrays print out, customer_ids is showing the correct numbers but vendor_ids is printing out random numbers from memory. To be more frustrating, it prints the vendors correctly from inside fill_arrays.

If you want to modify vendor_ids the way you do in fill_arrays, then you have to pass it in as a pointer to a pointer:
fill_arrays(int *customer_count, int *customer_ids, int *vendor_count, int **vendor_ids)
Call it like this:
fill_arrays(&customer_count, customer_ids, &vendor_count, &vendor_ids);
Then you can realloc like so:
temp = realloc(*vendor_ids, *vendor_count*sizeof(int));
if (temp != NULL)
{
*vendor_ids = temp;
}
Also, at the end of your function:
vendor_ids[i] = vendor_id;
will have to change to
(*vendor_ids)[i] = vendor_id;
You will also have to make the same changes to customer_ids. The fact that customer_ids was working while vendor_ids wasn't was probably due to your use of realloc. If realloc decides that the memory block has to be reallocated in a new location, you'll run into these problems but if reallocates the memory in the same location, your pointer that you passed in is still pointing there. Since you never know if realloc is going to make that descision or not, both customer_ids and vendor_ids should be passed in as pointers to pointers.

You seem a bit confused how to return memory from a function.
if you have a function that looks like this
void foo(int a);
you can not change a inside of foo, foo only gets a copy of a.
void foo(int *a);
otoh gives foo the address of a, i.e. "i know where you live" it lets you change what a points to. if a points to an array then you can change the contents of that array, if a points to a single integer you can change that.
void foo(int **a);
lets you change not only what a points to, but also where it points. so if you want a to point to somewhere else you can. this is what you need in your function, if you do a malloc/calloc/realloc in your function to return the result you need this

Related

C : Realloc doesn't work with dynamic double pointer array

I am facing some issues regarding a realloc with a double pointer dynamic array.
What I would like to perform is to add 2 pointers of type Flight* inside the array schedule of type Flight **.
For that, I am relying on the function add_flight in the Functions.c file.
This function asks the user for the airline and flight number values and stores these data in a new Flight* f. If the schedule is null (no flight yet added) it allocates memory for the newly created flight otherwise it realloc the size of schedule in order the add the new flight.
Main.c file:
int main() {
int choice = 1;
Flight** schedule = NULL;
printf("---AIRPORT MANAGER---");
schedule = add_flight(schedule);
printf("\n%s : %d\n", (*schedule)->airline, (*schedule)->flightNumber);
schedule = add_flight(schedule);
printf("\n%s : %d\n", (*schedule + 1)->airline, (*schedule)->flightNumber);
return 0;
}
Functions.c file :
#include "Functions.h"
void mygets(char* s, int maxLength) {
fflush(stdout);
if (fgets(s, maxLength, stdin) != NULL) {
size_t lastIndex = strlen(s) - 1;
if (s[lastIndex] == '\n')
s[lastIndex] = '\0';
}
}
void flush() {
char buffer;
while ((buffer = getchar()) != EOF && buffer != '\n');
}
Flight** add_flight(Flight** schedule) {
Flight* f;
char buffer[100];
if ((f = (Flight*)malloc(sizeof(Flight*))) == NULL) {
exit(1);
}
printf("\n\n---FLIGHT CREATION---");
printf("\nAirline: ");
mygets(buffer, sizeof(buffer));
if ((f->airline = _strdup(buffer)) == NULL) {
exit(1);
}
memset(buffer, 0, 100);
printf("\nFlight number: ");
scanf("%d", &f->flightNumber);
flush();
if (schedule == NULL) {
if ((schedule = malloc(sizeof(Flight*))) == NULL) {
exit(1);
}
*schedule = f;
}
else {
int numberFlights = ((sizeof(*schedule)) / 4) + 1;
if ((schedule = realloc(schedule, numberFlights * sizeof(Flight*))) == NULL) {
exit(1);
}
*(schedule + numberFlights -1) = f;
}
return schedule;
}
The issue comes when the second call of add_flight is performed in the main.c
In the add_flight function, the data are indeed stored in the new Flight* f and then the else statement is considered: the variable numberFlights gets the value 2. However, the realloc doesn't work, the schedule is not enlarged and thus there is still only the first flight stored inside this schedule array. I can't figure out why the second flight is not added inside the schedule.
Can someone explain me why this realloc fails ?
Thanks for your help :)
The sizeof operator is evaluated at compile time. It cannot be used to determine the size of a dynamically allocated array.
C imposes the burden of keeping track of the actual size of an array onto the programmer. You could kee a separate count variable, but because the actual array and its size belong together, it is useful to store them alongside each other in a struct:
typedef struct Flight Flight;
typedef struct Flights Flights;
struct Flight {
char airline[4];
int number;
char dest[4];
};
struct Flights {
Flight *flight;
int count;
};
Instead of operating on the array, operate on the struct:
void add_flight(Flights *fl,
const char *airline, int number, const char *dest)
{
int n = fl->count++; // n is old count; fl->count is new count
fl->flight = realloc(fl->flight,
(fl->count + 1) * sizeof(*fl->flight));
snprintf(fl->flight[n].airline, 4, "%s", airline);
snprintf(fl->flight[n].dest, 4, "%s", dest);
fl->flight[n].number = number;
}
Intialize the flights struct with NULL and a count of zero and don't forget to release the used memory when you're done:
int main(void)
{
Flights fl = {NULL, 0};
add_flight(&fl, "AF", 5512, "CDG");
add_flight(&fl, "AA", 1100, "ATL");
add_flight(&fl, "LH", 6537, "FRA");
add_flight(&fl, "BA", 8821, "LHR");
add_flight(&fl, "IB", 1081, "EZE");
print_flights(&fl);
free(fl.flight);
return 0;
}
You can see it in action here. Some observations:
There is no need to distinguish between adding the first and subsequent flights, because realloc(NULL, size) behaves exactly like malloc(size).
It is not very efficient to reallocate the memory for each added item. Instead, you pick a suitable initial array size like 4 or 8, then double the size when you hit the limit. That means that the allocated size and the count may differ and you need an aditional memsize field in your flights struct.
The code above relies on manual initialization and destruction. Usually, you will write "constructor" and "destructor" functions to do that for you.

My char vector is saving the same value in every position

Im trying to make a shell in linux. I need to save every command the user typed and when the user wants to see the history Ill show him the last 10 commands. Im using a char* historial[10]; but when the user type the second command it is saving in historial[0] and historial[1] the same thing. So if I type 10 different commands, it will save the last command 10 times. I dont know what Im doing wrong.
PS: my condition in the do while is not working neither.
int ejecutarCom(char* comando)
{ //executes the command
pid_t pid;
pid = fork();
int aux = 0;
if (pid < 0)
{
perror("Error");
return -1;
}
else if (pid == 0)
{
aux = execlp(comando, comando, NULL);
}
else
{
wait(NULL);
}
return aux;
}
char* historial[10]; //history vector
int cantidad = 0, tamano = 10; //quantity & size
void guardarEnHistorial(char* c)
{ //saves every command
if (cantidad < tamano)
{
historial[cantidad] = c;
}
if (cantidad == tamano)
{
cantidad = 0;
historial[cantidad] = c;
}
cantidad++;
}
void verHistorial()
{ //shows history
for (int i = 9; i >= 0; i--)
{
if (historial[0] == NULL)
break;
else if (historial[i] == NULL)
{
printf(" ");
}
else
printf("%i: %s\n", i, historial[i]);
}
}
int main()
{
char* comando = (char*) malloc(1024);
int x = 1;
do
{
printf("%s ", prompt);
fflush(stdout);
fgets(comando, sizeof(comando), stdin);
comando[strcspn(comando, "\n")] = '\0';
printf("%s\n", comando);
if (strcmp(comando, "hist") == 0)
{
verHistorial();
}
else
{
x = ejecutarCom(comando);
guardarEnHistorial(comando);
printf("%i\n", x);
}
} while (x != -1);
Modify your main function like below. Every location of your history array is pointing to same memory location hence you are getting the same information in every location. You have to allocate memory for each command like below:-
int main(){
char* comando=(char*) malloc(1024);
int x=1;
do{
printf("%s ",prompt);
fflush (stdout);
fgets(comando,sizeof(comando),stdin);
comando[strcspn(comando, "\n")] = '\0';
printf("%s\n",comando);
if(strcmp(comando,"hist")==0){
verHistorial();}
else{
x=ejecutarCom(comando);
guardarEnHistorial(comando);
comando=(char*) malloc(1024);// You need to allocate memory to store
//each history records. Also consider whether you really need 1024 bytes of memory make it small
printf("%i\n",x);
}}while(x!=-1);
You are storing a pointer to the command buffer, not the command itself. As the address of the command buffer doesn't change, but its contents does, every history entry will point to the same command buffer and thus will show the same command:
char* historial[10]; // history vector has only pointers
//...
historial[cantidad] = c; // stores a pointer
The following allocates and frees memory as appropriate:
void guardarEnHistorial(char* c)
{ //saves every command
if (cantidad == tamano) cantidad = 0;
if (historial[cantidad]) free(historial[cantidad]);
historial[cantidad]= malloc(strlen(c)+1);
strcpy(historial[cantidad], c);
cantidad++;
}
I dont know what Im doing wrong.
You are observing this behavior because the char pointers of historial array are pointing to same memory location comando. Check this statement of your program:
guardarEnHistorial(comando);
and in the guardarEnHistorial () function, you are doing:
historial[cantidad] = c;
Any changes you make to comando will reflect to all the members of historial array in which you have saved the command.
You can use strdup() to resolve this issue. Replace this:
guardarEnHistorial(comando);
with this:
guardarEnHistorial(strdup(comando));
^^^^
The strdup() function returns a pointer to a new string which is a duplicate of the string passed to it. strdup() allocates a memory for the new string using malloc and returns its pointer. Make sure to free it using free() once you are done with it. Like when inserting into historial array, you are rotating the insertion if the condition cantidad == tamano meets. Here, you need to free the element first and then insert the new element into the array otherwise there is a memory leaks in your program.

pass dynamic array of pointers to C function

I want to store each line of the file in an 2D array, and anthor array pointing each line (so I can identify each line), I need to pass this pointers array to a function so I can manipulate my lines, I d'ont know how to do that
I have this code to read and store in the arrays
char ligne[MAX];
//open end test the file
FILE* fichier = fopen("csp.txt","r");
if(fichier == NULL){
printf("can't open the file \n");
return EXIT_FAILURE;
}
//the first line in the file contain number of other lines
fgets(ligne, sizeof(ligne), fichier);
int nbrTaille = strtol(ligne, NULL, 10);
//array of pointers
char (*tab)[nbrTaille] = malloc(nbrTaille * sizeof(ligne));
int i = 0;
//tab array point each line
while(fgets(ligne, sizeof(ligne), fichier)){
if(ligne == NULL) EXIT_FAILURE;
strncpy(tab[i], ligne, strlen(ligne));
printf("line%d : %s\n", i, tab[i]);
i++;
}
//call the funnction by passing array of pointers and the number of lines
allDiff(tab, nbrTaille);
the file I'm reading is
2
1 2
2 3
to receive the array by the function I tried this but it doesn't work
void allDiff(char** T, int taille)
I am always confused with the parenthesies and asterisks. The easier way is to declare a double pointer:
char ** tab;
//you need to take care of errors. I am not doing it for simplicity
//tab = {pointer1, pointer2, pointer3, ...., pointerN} total memory needed N * sizeof(pointer)
tab = malloc(lines* sizeof(tab)); //how many pointers you want
for(i = 0; i < lines; i++){
tab[i] = malloc(MAX); //each string
}
In the end free the memory:
for(i = 0; i < lines; i++){
free(tab[i]);
}
free(tab);
EDIT complete code
char ligne[MAX];
//open end test the file
FILE* fichier = fopen("csp.txt","r");
if(fichier == NULL){
printf("can't open the file \n");
return EXIT_FAILURE;
}
//the first line in the file contain number of other lines
fgets(ligne, sizeof(ligne), fichier);
int nbrTaille = strtol(ligne, NULL, 10);
//array of pointers
//char (*tab)[nbrTaille] = malloc(nbrTaille * sizeof(ligne));
int i = 0;
char **tab;
tab = malloc(nbrTaille * sizeof(tab)); //how many pointers you want
for(i = 0; i < nbrTaille; i++){
//sizeof(ligne) is equal to MAX
tab[i] = malloc(MAX); //each string
}
i = 0;
//tab array point each line
while(fgets(ligne, sizeof(ligne), fichier)){
if(ligne == NULL) EXIT_FAILURE;
strncpy(tab[i], ligne, strlen(ligne));
printf("line%d : %s\n", i, tab[i]);
i++;
}
//call the funnction by passing array of pointers and the number of lines
allDiff(tab, nbrTaille);
void function(char tab[][MAXLEN], ...);
This will do.
For the sake of readbility and sanity, typedef C function pointers before creating arrays or double pointers to them.
/* typedef the function pointer. An ftabptr points to a void function that
takes a char * */
typedef void (*ftabptr)(char *p);
/* create an uninitalised list of twenty of them */
int N = 20;
ftabptr *pointerlist = malloc(N * sizeof(ftabptr));
However I don't think you really want to do this. You can write a
functioning program that does weird things with tables of function
pointers, but normally you use a language other than C if you
want to play that game (Lisp-like languages, etc). The high level
language then often emits C as an intermediate step.

How to convert from string array to int array and then sort it using c

I have string array initialized like that:
char ** strArray;
if ( (strArray = malloc(sizeof(*strArray) + 3)) == NULL ) {
fprintf(stderr, "ls1: couldn't allocate memory");
//exit(EXIT_FAILURE);
}
strArray[0] = NULL;
strArray[0] = "111";
strArray[1] = "222";
strArray[2] = "1";
strArray[3] = "2";
I want to convert this string array to int array, like that:
int * toIntArray(char ** strArray) {
int size = getCharArraySize(strArray);
int intArray[size];
int i;
for ( i = 0; i < size ; ++i)
{
intArray[i] = atoi(strArray[i]);
printf( "r[%d] = %d\n", i, intArray[i]);
}
intArray[size] = '\0';
return intArray;
}
int getCharArraySize(char ** strArray) {
int s = 0;
while ( strArray[s]) {
printf("Char array: %s.\n", strArray[s]);
s++;
}
return s;
}
And then I want to sort this int array.
I must have string array initilized like above (char ** strArray) and then convert this to int array and then sort it. Can anybody help my with that? I would ask about printed sorted integer in main function.
A few minor things to take note of in the question code:
char ** strArray;
if ( (strArray = malloc(sizeof(*strArray) + 3)) == NULL ) {
fprintf(stderr, "ls1: couldn't allocate memory");
//exit(EXIT_FAILURE);
}
If successful, the intention of the above code allocates memory to strArray sufficient for three char *'s. Specifically, strArray[0], strArray1 and strArray[2].
NOTE: As pointed out in Matt McNabb's comment below, it actually incorrectly allocates memory sufficient for one char *, and three extra bytes.
strArray[0] = NULL;
The above line sets sets the first pointer in the **strArray to point at NULL.
strArray[0] = "111";
The above code is odd. After just setting strArray[0] to point at NULL, the above line changes it to point to "111". Kind of makes setting it to NULL (in the first place) seem unnecessary.
strArray[1] = "222";
strArray[2] = "1";
The above two lines initialize the other two pointers in the strArray correctly.
strArray[3] = "2";
The above line attempts to initialize strArray[3], when that element of the array really doesn't exist. So, it is changing something to point to "2", but probably not with the expected result.
Perhaps the intent would be better served by changing the above code to:
char **strArray;
size_t strArrayElements=4;
if(NULL == (strArray = malloc((strArrayElements+1) * sizeof(*strArray))))
{
fprintf(stderr, "ls1: couldn't allocate memory");
exit(EXIT_FAILURE);
}
strArray[strArrayElements] = NULL;
strArray[0] = "111";
strArray[1] = "222";
strArray[2] = "1";
strArray[3] = "2";
As can be observed, the above code allocates 5 elements (strArrayElements+1) to the **strArray. The last element strArray[4] is initialized to NULL; a marker to indicate End-Of-List. Then the other 4 elements [0..3] are initialized.
Now shifting focus to:
int * toIntArray(char ** strArray) {
int size = getCharArraySize(strArray);
int intArray[size];
int i;
for ( i = 0; i < size ; ++i)
{
intArray[i] = atoi(strArray[i]);
printf( "r[%d] = %d\n", i, intArray[i]);
}
intArray[size] = '\0';
return intArray;
}
The above code is successful at converting the strings to their integer forms, and storing them in intArray. However, the code is flawed when it attempts to return intArray to the caller. The intArray variable was declared as a local stack object. The return statement causes all such stack variables to become invalid; and allows the stack memory such variables were using to be used for other things.
Perhaps the the following code better represents what was intended. It allocates memory from the heap for intArray. This allocated memory can outlive the return statement:
int *toIntArray(char **strArray)
{
int size = getCharArraySize(strArray);
int *intArray = malloc(size * sizeof(*intArray));
int i;
for ( i = 0; i < size ; ++i)
{
intArray[i] = atoi(strArray[i]);
printf( "r[%d] = %d\n", i, intArray[i]);
}
intArray[size] = '\0';
return(intArray);
}
Spoiler code may be found here.

How to set all values to NULL struct array within a struct array in C

Basically I have two structs and I want to make an array of A structs, and within each A struct I want an array of 50 B structs. So I assume that we will use double pointers.
struct A{
char* a_word;
struct B** b_list;
};
struct B{
char* b_word;
int b_value;
};
When call initialize function I initialize the structs like this. My goal is to set all the values to NULL when I allocate memory.
struct Word** initialize()
{
int k;
int i;
struct A** A_list = calloc(BUFFSIZE, sizeof(struct A*));
for(k =0; k < BUFFSIZE; k++)
{
A_list[k] = calloc (1, sizeof(struct A));
A_list[k]-> b_list = calloc(50, sizeof(struct B*));
for(i = 0; i < 50; i++)
{
A_list[k]->b_list[i] = calloc(1, sizeof(struct B));
}
}
return hashTable;
}
after initializing all these values I am able to do. . .
if(A[43]->a_word == NULL) //43 unallocated value
{
printf("This is null\n");
//program prints this statement - good
//program knows that this value is NULL
}
But I also want . .
if(A_list[44]->b_list[0] == NULL)
{
printf("This is also null");
//This should be printed but nothing happens
}
For some reason not matter if I set the above if statement to == NULL or != NULL the program outputs absolutely nothing from that if statement. What is going on in memory and how can I allocate everything correctly, and so the value is set to NULL as a default and so I can input a value?
EDIT: Also whenever try to do A_list[value1]->b_list[value2] = strdup("string"); I get a segmentation error, this most likely stems from the same problem.
As mentioned already by WhozCraig in a comment to the question, this code
for(i = 0; i < 50; i++)
{
A_list[k]->b_list[i] = calloc(1, sizeof(struct B));
initialises the first 50 elements of b_list to point to valid memory, that is to be non 0, assuming calloc() never fails. Being that optimistic you better test for those elements being != NULL.
... if I set the above if statement to == NULL or != NULL the program outputs absolutely nothing
The code does not seem to flush stdout here:
if(A_list[44]->b_list[0] == NULL)
{
printf("This is also null");
Change this by adding a final \n:
if(A_list[44]->b_list[0] != NULL)
{
printf("This isn't null\n");
As stdout is line buffered by default, all content will be flushed if a new-line is detected.

Resources