How to initialize these pointers? - c

I need to make a list of employees and I can't change these structures, I'm having trouble in how to initialize each of tab[10] to NULL and how to set values
#include <stdio.h>
#include <stdlib.h>
typedef struct employee Employee;
struct employee{
char name[81];
float salary;
};
Employee *tab[10]; /*a table with employee*/
void set(Employee **tab, int i, char *name, float salary){
tab[i]->name = name;
tab[i]->salary = salary;
}
int main(){
Employee *e;
int i = 0;
for(; i < 10; i++) init(i,&e);
return 0;
}
/*a table with an employee, each position must have a name and a salary*/
Employee *tab[10];
void init(int n, Employee **tab);

Everaldo
With commentators helping you, it seems you are getting there. I would like to sum up the suggests given so far and add a couple of my own.
Declaring the Employee array
Declaring the array as a global variable and then passing it as a parameter to functions makes things a little confusing. I usually prefer declaring a local variable and then passing it to the various functions that uses it. Also as suggested by David C. Rankin, to initialize every array element to 0 just requires you to initial the first element in the declaration statement. No FOR loop needed. The compiler will auto initialize the rest of the array elements for you.
main()
{
Employee* tab[10] = { NULL };
. . . .
}
Array memory allocation
As mention by Patrick87, you need to add code to assign memory to every element in the array. An example initialization routine could be coded as follows:
int init(int len, Employee** tab) {
int i = 0;
for (i = 0; i < len; i++)
{
if ( (tab[i] = (Employee*) calloc (1,sizeof(Employee))) == NULL )
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Function usage:
if (init(10, &tab) == EXIT_FAILURE)
{
puts("CALLOC Failed, aborting....");
exit(EXIT_FAILURE);
}
Things to note:
Check to ensure the memory was allocated. On failure return some
type of failure status to alert the caller of the function.
The status codes that are being returned are define in stdlib.h.
They are not necessary but do give a clear indication to the reader
of your code the success and failure paths your code takes.
The FOR loop was moved inside the initialization function. Function
calls are expensive when it comes to processing time. Since the
array size is known, it is faster to perform the loop inside the
function.
Try to always write functions that return a status. This will enable
the caller to perform any error handling if the function's operations
fail.
Set array element values
The following statement is not valid. You cannot directly copy the content from a string pointer to an array of characters. You will need to use statements like strcpy, strncpy, or memcpy to copy the data.
tab[i]->name = name;
There is a method I prefer for copying strings.
sprintf(tab[i]->name, "%.80s", name);
This will copy up to 80 characters from name into tab[i]->name, then insert a null character. The beauty of this statement is that the designation variable does not have to be the same size as the source. If the source variable (in this case name) is shorter, spirntf will simply stop when it encounter a null character and then null terminate the destination string. If the source is longer than 80 characters or if it is missing the null terminator character, sprintf will stop coping at the 80st character position and then auto insert a null character in the 81st character position.
An example SET routine could look like the following:
void set(Employee** tab, int i, char* name, float salary) {
sprintf(tab[i]->name, "%.80s", name);
tab[i]->salary = salary;
}
Usage:
for (i = 0; i < 10; i++)
{
set(&tab, i, "Bob", 35000. + i); // bogus values, demo purposes only
}
Main program logic
Your main program as you currently have outline will need to change. For starters, the declaration of variable “e” should be replace with the declaration of variable “tab” (see Patrick87 comments) . On initializing the array, see my suggestion above. To set values to the array elements see SET function comments above.
Free memory
Every time you allocate memory, you must free it when you are done. Forgetting to free allocated memory will create memory leaks in your program. Note technically, in this demonstration program, the system will free the memory when your code exits, so you do not need to free it. But it is good practice so when you start writing real applications you will not forget to do so.
Here is an example on how this could be done:
for (i = 0; i < 10; i++)
free (tab[i]);

tab is an array of pointers, so to initialize them all to NULL, you can use a for loop, e.g.
for (i = 0; i < n; i++)
tab[i] = 0;
To set a value, allocate some space for an instance of your struct (either on the stack via function parameter or local variable, or else on the heap by with malloc/calloc/realloc) and then set one of the tab[k] to the address of the memory you allocated (using & or just the pointer directly if allocated).

Related

Why does printf behave this way when using struct?

I am writing a code while making the use of structures. I am new to structs so I am practicing to get used to it. Anyway while trying to use printf on a variable of type string which is a variable of a struct type, printf only prints '#' instead of the whole string.
...
void signPlayers(struct player players[9], int playersC) // players is an array declared on main and playersC is the size of it.
{
for (int i = 0; i < playersC; i++)
{
char tname[22];
printf("Please enter the name of the player #%d: \n",i+1);
int res = scanf(" %s",&tname);
while(res != 1)
{
printf("Please enter a valid name for player #%d: \n",i+1);
res = scanf(" %s",&tname);
}
players[i].name = tname;
printf("Player #%d signed as %s!\n\n",players[i].id,players[i].name); // this printf actually works fine
}
}
int checkForWinner(struct player players[], int playersC)
{
for (int i = 0; i < playersC; i++)
{
if (players[i].pos == 10)
return 0;
printf("%s\n",players[i].name); // prints "#" instead of the name
}
return 1;
}
...
so If I entered the name Joey, At first printf it actually prints "Joey", then when I call the checkForWinner function (Its called after signPlayers function), the printf now prints only "#" instead of the whole name again.
What could be wrong?
Your code is attempting to access stack memory after the function has returned. When you do that, you get just whatever garbage was left over after the function call, or perhaps some part of a new stack frame belonging to a different function. Either way, it's undefined behavior.
There are several problems with the assignment players[i].name = tname; in function signPlayers:
It is assigning the address of the local array variable tname. This variable goes out of scope on every iteration through the loop. That means accessing that pointer after the loop will be undefined behavior.
Even if tname was moved out of the loop's scope up to the function scope, it will still go out of scope as soon as the function returns. Either way, accessing it after the function call is undefined behavior.
It's likely that the address of tname doesn't change from one iteration of the loop to the next. So that means that the .name member of every player in the players array will probably be identical (as well as being invalid).
There are many ways to fix this. Here are three ways:
Make a copy of tname each time through the loop using strdup(tname), and assign that to players[i].name:
players[i].name = strdup(tname);
The strdup function allocates memory, so if you use this approach, you will need to remember to free the .name member of each player when you're done.
Allocate memory dynamically to each players[i].name prior to calling signPlayers:
for (i=0; i<playersC; ++i) players[i].name = malloc(22);
signPlayers(players, playersC);
// Don't forget to call free() on each .name member after you're done
Inside signPlayers, you would then get rid of tname altogether and do:
int res = scanf(" %s", players[i].name);
Note: No need to do 22 * sizeof(char) here, since the C standard guarantees sizeof(char) == 1. Also, I used 22 because that's what OP's code uses to declare tname. However, it should be noted that scanf is less than ideal here, because it gives no way to limit the number of bytes written to the array, and thus no way to protect against buffer overflow. If you type a name longer than 21 characters (need to leave 1 byte for the null terminator), then you will overflow tname and your program will either crash or silently corrupt your data.
Turn players[i].name into a sized char array instead of just a char pointer. In other words, statically allocate memory to each players[i].name. This has the advantage of not needing to call free, as you would need to do with methods 1 and 2. OP didn't show the definition of struct player, but this should suffice for example purposes:
struct player {
char name[22];
// other stuff
};
And again, inside signPlayers, you would scanf directly into players[i].name instead of using tname.

Why will this not print?

Before you feel the need to mark this as a duplicate post, please don't. I have read all the threads on pointers, arrays, and functions I could find but almost all of them are far too advanced to be of any help to me.
I'm not getting an error, however my code will not print my array. It seems the issue here is using scanf. I don't think the values entered are actually being put into the array in main(). I've tried using pointers, but then I get the error "Thread 1: EXC_BAD_ACCESS (code=1, address=0x0)" whenever I try to use scanf to collect user inputted values to put into the array.
What I am working on is limited to declaring my array in the main() function, but all the operations are to be performed in promptData() function. Any help would be great, I'm at my wits end trying to figure this out on my own.
#import <stdio.h>
void promptData(double data[], int numElem);
int main(int argc, const char * argv[])
{
int size, i;
double array[size];
promptData(array, size);
for (i = 0; i < size; i++)
printf("%.2lf\n", array[i]);
return 0;
}
void promptData(double data[], int numElem)
{
int i;
printf("Enter integer values for size of array.\n");
scanf("%i", &numElem);
for (i = 0; i < numElem; i++)
{
printf("Enter array values.\n");
scanf("%lf", &data[i]);
}
}
Your program has undefined behaviour because variable size was not initialized and has indeterminate value.
You should at first in main ask the user to enter the size of the array then define the array itself and only after that fill it with values.
For example
int main(int argc, const char * argv[])
{
int size = 0;
printf( "Enter a positive integer value for the size of the array: ");
scanf( "%i", &size);
if ( size == 0 ) exit( 1 );
double array[size];
promptData(array, size);
//...
Also in C there is no such a directive as
#import <stdio.h>
Use instead
#include <stdio.h>
At least in ANSI C 89 and C 90, you can't give a variable as the size of an array. The size of array should be known at compile time. You should be doing something like double array[size];.
Even in C99, where you can have variable sized arrays; the variables should contain proper index values at the time you declare the array. In that case, you should read the number from stdin and then declare the array.
Also in C, all parameters are passed by value. This means every function takes a copy of the parameters in the function. If you want to modify a variable's value, you should pass a pointer to it, and then modify the pointer's dereferenced value, something like:
void change(int *x)
{
*x = 7;
}
void first(void)
{
int x = 5;
change(&x);
printf("%d\n", x);
}
Adding on to the other, correct, answer by Zenith, if you want a dynamically allocated array (like you want to be able to change its size based on user input), then your only option is to use one of the memory allocation functions like malloc().
Once you actually have the size in your main function, declare your array like this:
int *myArray = malloc(sizeof(int) * size));//note that malloc will return a NULL if it fails
//you should always check
if(myArray != null) {
//do stuff with myArray like you were. You can just use myArray[] as long as you
//make SURE that you don't go beyond 'size'
}
free(myArray);
//VERY important that every malloc() has a free() with it
Note: untested, but the idea is there.
Further, to answer your other question.
If you find yourself in a situation where you need to call a function and use things INSIDE that function to change stuff where you called it, you have only two choices in C.
You can either return the value and assign it to a variable in the calling function like this:
int result = myFunction(someVariable, anotherVariable);
//do stuff with result
Or, use pointers.
I'm not explaining pointers here, that's usually several lectures worth of information, and is one of the more difficult concepts to grasp for introductory programmers. All I can tell you is you need to learn them, but this format is not the right way to go about doing that.
You're passing size to promptData as a copy.
Thus changes to numElem inside promptData will not affect the size variable in your main. Hence size remains uninitialized, i.e. has an undefined value and therefore should not be used as a size for an array.
If you need to initialize an array with a size that's only known at run-time, you need to allocate memory for the array dynamically using malloc, for example:
double* array = malloc(size * sizeof(double));

Using malloc with a structure and strcpy

I'm attempting to make an array of the structure I made called StatusItem, which looks like this:
typedef struct
{
char* name;
char* index;
int optional;
} StatusItem;
Also, as I want this array to be of any size, I am using malloc. So the array is defined as such:
StatusItem* statusItem = NULL;
(its then passed to function which retrieves all the values as follows.)
statusItem = (StatusItem*)malloc(cJSON_GetArraySize(items));
...
for (i = 0 ; i < cJSON_GetArraySize(items) ; i++)
{
strcpy(statusItem[i].name,name->valuestring);
strcpy(statusItem[i].index,index->valuestring);
if(!parseInt(optional->valuestring, &statusItem[i].optional));
{
goto cleanup;
}
}
There's come code that involves the cJSON library in getting the string values of name, index and optional into the variables referenced above, and they are stored in the valuestring field of those variables.
I have checked that everything involving the cJSON library works fine, and returns the correct values, but the program is unable to access or store values in the statusItems array.
Any ideas? I'm almost positive that it involves some misuse of malloc on my part.
1) cJSON_GetArraySize(items) returns an element count - you need the size of the object factored in: malloc(cJSON_GetArraySize(items) * sizeof(StatusItem))
2) a StatusItem structure doesn't have memory for the actual string - only a pointer to a string. You can use strdup() to allocate and copy a string.
You probably want your code to look more like:
statusItem = (StatusItem*)malloc(cJSON_GetArraySize(items) * sizeof(StatusItem));
...
for (i = 0 ; i < cJSON_GetArraySize(items) ; i++)
{
statusItem[i].name = strdup(name->valuestring);
statusItem[i].index = strdup(index->valuestring);
if(!parseInt(optional->valuestring, &statusItem[i].optional));
{
goto cleanup;
}
}
Of course this means that you also have to free the duplicated strings explicitly when you free the array of StatusItem objects:
// to free the statusItem array, and the various strings it refers to:
for (i = 0 ; i < cJSON_GetArraySize(items) ; i++)
{
free(statusItem[i].name);
free(statusItem[i].index);
}
free(statusItem);
Two misuses spotted:
Don't cast the return value of malloc(), it's dangerous and superfluous.
You don't allocate any memory for the members of the structure - you're strcpy()ing to uninitialized pointers, so your program invokes undefined behavior.
Edit: actually three:
malloc(cJSON_GetArraySize(items));
doesn't allocate enough memory since it's not magic and it doesn't know you're reserving sizeof(StatusItem) bytes of memory, thus you have to multiply the allocation size by sizeof(StatusItem), or even better, by sizeof(*statusItem) for safety.
In addition, malloc takes a number of bytes, not elements. The value passed to it must be multiplied by the size of each element.
To avoid having to use strdup() which is a little 'messier' because it leaves the freeing of the memory up to the caller instead of taking care of everything itself, I modified my existing structure as follows:
typedef struct
{
char name[32];
char index[32];
int optional;
} StatusItem;
This allows 32 bytes for the name and index, which should be more than enough. Before, the structures fields were pointing to nothing, which was causing the error when trying to copy to that location. now, there is empty (or junk) memory waiting for the string to be placed in.
This allows for strcpy() to still be used, and allows for an overall cleaner implementation.

Input and output array don't match

I am writing a program for a class at school and I cannot get the program to print out what I type in.
The problem states that the first line needs to contain the number of questions on an 'exam' followed by a space then the answer key. I wanted to print the answer key to make sure that it was being entered in correctly and it never matches what I type in. The code is posted below.
This is the main file that starts being run and it calls a method from another file I have made the prototype file correctly so I'm pretty sure it's not that.
int main()
{
int i;
int noOfQuestions;
scanf("%d ", &noOfQuestions);
char * answerKeyPtr;
answerKeyPtr = fgetAnswers(noOfQuestions);
for(i = 0; i < noOfQuestions; i++){
printf("%c",answerKeyPtr[i]);
}
printf("\n");
return 0;
}
char *fgetAnswers(int noOfQuestions){
int i;
char * answerKeyPtr;
char AnswerKey[noOfQuestions];
answerKeyPtr = AnswerKey;
for(i = 0; i < noOfQuestions; i++){
scanf("%c",&AnswerKey[i]);
}
return answerKeyPtr;
}
What you have here is a memory problem.
You're storing data into the AnswerKey array, which is local to fgetAnswers(). The problem is you're returning a pointer to that local variable, and that variable's memory is not reliable as soon as your fgetAnswers() function finishes that memory should not be accessed. So when you try to print the data in main() you're accessing memory you shouldn't.
To solve it, create the AnswersKey array in main, and pass it as a parameter to the fgetAnswers() function.
The char array AnswerKey is allocated on the stack when fgetAnswers is called. When you return from fgetAnswers, data stored in the stack frame for that call is no longer valid. You'll need to pass in the array or alloc it so the input isn't stored in the stack.

C using malloc and duplicating array

I am supposed to follow the following criteria:
Implement function answer4 (pointer parameter and n):
Prepare an array of student_record using malloc() of n items.
Duplicate the student record from the parameter to the array n
times.
Return the array.
And I came with the code below, but it's obviously not correct. What's the correct way to implement this?
student_record *answer4(student_record* p, unsigned int n)
{
int i;
student_record* q = malloc(sizeof(student_record)*n);
for(i = 0; i < n ; i++){
q[i] = p[i];
}
free(q);
return q;
};
p = malloc(sizeof(student_record)*n);
This is problematic: you're overwriting the p input argument, so you can't reference the data you were handed after that line.
Which means that your inner loop reads initialized data.
This:
return a;
is problematic too - it would return a pointer to a local variable, and that's not good - that pointer becomes invalid as soon as the function returns.
What you need is something like:
student_record* ret = malloc(...);
for (int i=...) {
// copy p[i] to ret[i]
}
return ret;
1) You reassigned p, the array you were suppose to copy, by calling malloc().
2) You can't return the address of a local stack variable (a). Change a to a pointer, malloc it to the size of p, and copy p into. Malloc'd memory is heap memory, and so you can return such an address.
a[] is a local automatic array. Once you return from the function, it is erased from memory, so the calling function can't use the array you returned.
What you probably wanted to do is to malloc a new array (ie, not p), into which you should assign the duplicates and return its values w/o freeing the malloced memory.
Try to use better names, it might help in avoiding the obvious mix-up errors you have in your code.
For instance, start the function with:
student_record * answer4(const student_record *template, size_t n)
{
...
}
It also makes the code clearer. Note that I added const to make it clearer that the first argument is input-only, and made the type of the second one size_t which is good when dealing with "counts" and sizes of things.
The code in this question is evolving quite quickly but at the time of this answer it contains these two lines:
free(q);
return q;
This is guaranteed to be wrong - after the call to free its argument points to invalid memory and anything could happen subsequently upon using the value of q. i.e. you're returning an invalid pointer. Since you're returning q, don't free it yet! It becomes a "caller-owned" variable and it becomes the caller's responsibility to free it.
student_record* answer4(student_record* p, unsigned int n)
{
uint8_t *data, *pos;
size_t size = sizeof(student_record);
data = malloc(size*n);
pos = data;
for(unsigned int i = 0; i < n ; i++, pos=&pos[size])
memcpy(pos,p,size);
return (student_record *)data;
};
You may do like this.
This compiles and, I think, does what you want:
student_record *answer4(const student_record *const p, const unsigned int n)
{
unsigned int i;
student_record *const a = malloc(sizeof(student_record)*n);
for(i = 0; i < n; ++i)
{
a[i] = p[i];
}
return a;
};
Several points:
The existing array is identified as p. You want to copy from it. You probably do not want to free it (to free it is probably the caller's job).
The new array is a. You want to copy to it. The function cannot free it, because the caller will need it. Therefore, the caller must take the responsibility to free it, once the caller has done with it.
The array has n elements, indexed 0 through n-1. The usual way to express the upper bound on the index thus is i < n.
The consts I have added are not required, but well-written code will probably include them.
Altought, there are previous GOOD answers to this question, I couldn't avoid added my own. Since I got pascal programming in Collegue, I am used to do this, in C related programming languages:
void* AnyFunction(int AnyParameter)
{
void* Result = NULL;
DoSomethingWith(Result);
return Result;
}
This, helps me to easy debug, and avoid bugs like the one mention by #ysap, related to pointers.
Something important to remember, is that the question mention to return a SINGLE pointer, this a common caveat, because a pointer, can be used to address a single item, or a consecutive array !!!
This question suggests to use an array as A CONCEPT, with pointers, NOT USING ARRAY SYNTAX.
// returns a single pointer to an array:
student_record* answer4(student_record* student, unsigned int n)
{
// empty result variable for this function:
student_record* Result = NULL;
// the result will allocate a conceptual array, even if it is a single pointer:
student_record* Result = malloc(sizeof(student_record)*n);
// a copy of the destination result, will move for each item
student_record* dest = Result;
int i;
for(i = 0; i < n ; i++){
// copy contents, not address:
*dest = *student;
// move to next item of "Result"
dest++;
}
// the data referenced by "Result", was changed using "dest"
return Result;
} // student_record* answer4(...)
Check that, there is not subscript operator here, because of addressing with pointers.
Please, don't start a pascal v.s. c flame war, this is just a suggestion.

Resources