Accessing char attribute of a struct - c

in my code I have a function that can initialize new instance of a struct, which has a char attribute, and the same function returns a pointer to an instance.
But when I try to read the char attribute from different scopes, I get also different characters.
Below you can find my code and also the results.
Thanks in advance!
Main function
Here the character is printed correctly.
int main(int argc, char const *argv[]) {
char line[] = "2-11 f: fjdfffmffffrff";
char *pnt = line;
Entry *entry = getEntry(&pnt);
printf("(main) min %d\n", entry->min);
printf("(main) max %d\n", entry->max);
printf("(main) character %c\n", entry->character); <-----
printf("(main) password %s\n", entry->password);
printEntry(entry);
return 0;
}
Function
While here the printed character is some random char.
void printEntry(Entry * entry){
printf("(function) min %d\n", entry->min);
printf("(function) max %d\n", entry->max);
printf("(function) character %c\n", entry->character); <--
printf("(function) password %s\n", entry->password);
}
STDOUT
Here are the results.
(main) min 2
(main) max 11
(main) character f
(main) password fjdfffmffffrff
(function) min 2
(function) max 11
(function) character L
(function) password fjdfffmffffrff
Edit
Entry struct
typedef struct Entry {
int min, max;
char character;
char *password;
} Entry;
char **separateBySpace(char **stringPtr) {
char **ptrArray = (char **) malloc(ARR_PTR_LEN * sizeof(char *));
char delim[] = " ";
char *ptr = strtok(*stringPtr, delim);
ptrArray[0] = ptr;
int x = 1;
while (ptr != NULL) {
if (x >= ARR_PTR_LEN) {
break;
}
ptr = strtok(NULL, delim);
ptrArray[x] = ptr;
x++;
}
return ptrArray;
}
Entry *getEntry(char **stringPtr) {
char **pntArray = separateBySpace(stringPtr);
char *rules = pntArray[0];
char *character = pntArray[1];
char *password = pntArray[2];
int *array = getRange(rules);
Entry entry = {.min = *(array), .max= *(array + 1), .character = *(character), .password= password};
Entry *pntEntry = malloc(sizeof(struct Entry));
pntEntry = &entry;
return pntEntry;
}

Function getEntry returns the address of a non-static local variable. Using this address in main is undefined behavior. Probably the call to printEntry partially overwrites the data, that's why you see different output.
You try to dynamically allocate memory for the returned data with
Entry *pntEntry = malloc(sizeof(struct Entry));
but you throw away the address to this memory and assign the address of your local variable with
pntEntry = &entry;
You probably want to copy the structure instead of the pointer. This would be
*pntEntry = entry;
Not related to your problem:
Your program should free all allocated memory when it is no longer used.
With the code shown in the question it is not necessary to pass the address of the pointer to the input string to getEntry and separateBySpace as a type char** because you don't want to modify the pointer. Passing a char* would be sufficient.
In separateBySpace you return an array of pointers that point to characters of the input string which gets modified by strtok. Later in getEntry you assign the pointer password to a pointer in your Entry structure. This means you should not change the string variable that was passed as an argument to getEntry, otherwise the password´ string referenced in the returned Entry` structure will change.
Edit (to answer a comment):
I think in getEntry you can free the pointer array allocated in separateBySpace because all values have been used or copied to the Entry structure.
At the end of the program you should free the memory pointed to by entry that was allocated in getEntry.
You must not free the memory password because this points to a character in your local variable line. Freeing entry->password before freeing entry would be necessary if you would allocate memory for a copy of the password, for example using strdup. This would also fix the possible problem that entry->password points to an element of line.

Related

Appending to string as input argument

I am having trouble generating a string inside a C routine.
Goal
Have function generate a custom string and return the value
e.g. 'void getName( char ** name )'
Attempt
int main(void) {
char *name;
getName(&name);
}
void getName(char **name) {
*name = "#"; // Load with prefix
//?strcpy(*name[1], "123"); // Goal: "#123"
}
How can I have getName() generate #123 as shown here?
1st problem: use malloc to allocate memory.
char *name = malloc(sizeof("#123")+1);
Even if you will run it after allocating memory, it will give runtime error; as you are doing:
*name = "#";
The problem is first you allocate space for 5 chars and point your pointer to the beginning of that memory. Then in the second line you point your pointer to a string literal causing a memory leak.
The pointer no longer points to the allocated memory.
You will like to do this:
int main(void) {
char *name = malloc(sizeof("#123")+1);
getName(&name);
printf("%s", name);
free(name);
name = NULL;
}
void getName(char **name) {
strcpy((*name), "#");
strcat(*name,"123");
}

Allocating memory to a struct member pointer in C

I have a structure with a member that I need to pass to a function by reference. In that function, I'd like to allocate memory & assign a value. I'm having issues somewhere along the line - it seems that after the code returns from allocateMemory, the memory that I had allocated & the values that I assigned go out of scope (this may not be exactly what is happening, but it appears to be the case).
#include <stdio.h>
#include <stdlib.h>
typedef struct myStruct_t
{
char *myString;
} myStruct;
void allocateMemory(void *str);
int main(void) {
myStruct tmp = {
.myString = NULL
};
myStruct *p = &tmp;
allocateMemory(p->myString);
//allocateMemory(&(p->myString)); //also tried this
printf("%s", p->myString);
return 0;
}
void allocateMemory(void *str)
{
str = malloc(8);
((char *)str)[0] = 'a';
((char *)str)[1] = 0;
}
If I print the value of str inside of allocateMemory, the 'a' is successfully printed, but if I attempt to print p->myString in main, my string is empty.
Can anyone tell me what I'm doing wrong?
You need to pass address of the structure member and then you can change (aka allocate memory) to it. In your version of the function, you are not taking a pointer not reference of a pointer, so you can change the content of memory referenced by the pointer but not the pointer itself.
So change your function to
void allocateMemory(char **ret_str)
{
char *str = malloc(8);
str[0] = 'a';
str[1] = 0;
*ret_str = str;
}
And then call it as
allocateMemory(&p->myString)
An alternative way of writing the same function Rohan did, eliminating the need to define any new variables:
void allocateMemory(char **str, size_t size) {
*str = malloc(size);
(*str)[0] = 'a';
(*str)[1] = '\0';
}
Note that I pass a size parameter to justify using malloc() in the first place.

C - proper syntax for pointer

I call a function global var as follow:
char *Pointer;
I then pass it into function:
char *MyChar = DoSomething (&Pointer);
which is defined as:
char *DoSomething (char *Destination)
{
free (*Destination);
//re-allocate memory
Destination = malloc (some number);
//then do something...
//finally
return Destination;
}
it only works if I use (*Destination) instead of (Destination). can someone tell me if that is correct? I still do not understand why it does not take (Destination).
It is correct, Destination is already declared as a pointer, so you pass the address of Destination in DoSomething(&Destination), that is like a pointer to pointer, then you need to dereference Destination inside DoSomething() function, for which the indirection operator * works.
But the right way, is not to pass the address of the pointer, but the pointer instead like in
DoSomething(Destination);
now, since you want to malloc Destination inside the function, you should the do this
char * DoSomething( char **Destination )
{
// free( Destination ); why?
//re-allocate memory
*Destination = malloc( some number );
//then do something...
//finally
return *Destination;
}
this is a demonstration of how you can use pointers
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
char *copyString(const char *const source)
{
char *result;
int length;
length = strlen(source);
result = malloc(length + 1);
if (result == NULL)
return NULL;
strcpy(result, source);
printf("The address of result is : %p\n", result);
printf("The content of result is : %s\n", result);
printf("The first character of result is # %p\n", &result[0]);
return result;
}
int main()
{
char *string = copyString("This is an example");
printf("\n");
printf("The address of string is : %p\n", string);
printf("The content of string is : %s\n", string);
printf("The first character of string is # %p\n", &string[0]);
/* we used string for the previous demonstration, now we can free it */
free(string);
return 0;
}
if you execute the previous program, you will see that the pointers both point to the same memory, and the contents of the memory are the same, so calling free in main() will realease the memory.
Here is a correct approach
char *Pointer;
//,,, maybe allocating memory and assigning its address to Pointer
//... though it is not necessary because it is a global variable and
//... will be initialized by zero. So you may apply function free to the pointer.
char *MyChar = DoSomething( Pointer );
char * DoSomething( char *Destination )
{
free( Destination );
//re-allocate memory
Destination = malloc( some number );
//then do something...
//finally
return Destination;
}
As for your code then
Type of the argument does not correspond to type of the parameter in function call
char *MyChar = DoSomething (&Pointer);
the type of the parameter is char * ( char *Destination ) while the type of argument is
char ** ( &Pointer )
As Destination is a pointer then instead of
free (*Destination);
you have to write
free( Destination );
It's because you are passing in an address of the pointer char *Pointer with the line
char *MyChar = DoSomething (&Pointer);
Since you are passing in the address of the pointer in your function DoSomething it sees the functional scope variable Destination as a pointer to an address that is the address of the pointer Pointer.
So rather than passing in the address of Pointer with
char *MyChar = DoSomething(&Pointer);
you need to pass in the pointer itself like so:
char *MyChar = DoSomething(Pointer);
which will allow you to use
free(Destination);
Notice the lack of & indicating the address of Pointer.

C -- Structs and Pointers Basic Questions

So I'm trying to learn C right now, and I have some basic struct questions I'd like to clear up:
Basically, everything centers around this snippet of code:
#include <stdio.h>
#include <stdlib.h>
#define MAX_NAME_LEN 127
typedef struct {
char name[MAX_NAME_LEN + 1];
unsigned long sid;
} Student;
/* return the name of student s */
const char* getName (const Student* s) { // the parameter 's' is a pointer to a Student struct
return s->name; // returns the 'name' member of a Student struct
}
/* set the name of student s
If name is too long, cut off characters after the maximum number of characters allowed.
*/
void setName(Student* s, const char* name) { // 's' is a pointer to a Student struct | 'name' is a pointer to the first element of a char array (repres. a string)
char temp;
int i;
for (i = 0, temp = &name; temp != '\0'; temp++, i++) {
*((s->name) + i) = temp;
}
/* return the SID of student s */
unsigned long getStudentID(const Student* s) { // 's' is a pointer to a Student struct
return s->sid;
}
/* set the SID of student s */
void setStudentID(Student* s, unsigned long sid) { // 's' is a pointer to a Student struct | 'sid' is a 'long' representing the desired SID
s->sid = sid;
}
I've commented up the code in an attempt to solidify my understanding of pointers; I hope they're all accurate.
Also, I have another method,
Student* makeAndrew(void) {
Student s;
setName(&s, "Andrew");
setStudentID(&s, 12345678);
return &s;
}
which I'm sure is wrong in some way... I also think my setName is implemented incorrectly.
Any pointers? (no pun intended)
This is very wrong. If you insist on not using strcpy do something like this (not tested)
int iStringLength = strlen(name);
for (i = 0; i < iStringLength; i++) {
s->name[i] = name[i];
}
but make sure that the length is not longer than your array size.
This is also wrong
Student* makeAndrew(void) {
Student s;
setName(&s, "Andrew");
setStudentID(&s, 12345678);
return &s;
}
because the s object is destroyed when the function exits - it is local to the function scope and yet you return a pointer to it. So if you try to access the struct using this pointer it will not be valid as the instance no longer exists. If you want to do this you should dynamically allocate it using malloc . Alternatively do not return a pointer at all and use the alternative option of #Andrew .
In your "another method" you are locally declaring Student s, which will dynamically allocate space (usually on the stack) and you are returning that address on completion.
However, that stack-space will be released on the return, so there is no guarantee that the data is uncorrupted - in fact the likelyhood is that it will be!
Declare Student s in the call to your method, and pass the pointer to makeAndrew:
void makeAndrew(Student *s) {
setName( s, "Andrew");
setStudentID( s, 12345678);
}
...
Student s;
makeAndrew( &s );
...
Your function makeAndrew returns pointer to a local variable. It is only valid before the scope ends, so as soon as the function finishes, it will change when the memory gets overwritten - i. e. almost instantly. You would have to allocate it dynamically (using Student *s = new Student;, or if you really want to stick to pure C, Student *s = malloc (sizeof Student );, and then free it outside the function after it is not needed to avoid memory leak.
Or do it as Andrew suggested, it's less error-prone.
I would change the makeAndrew() function to just return a struct, not a pointer to a struct to correct the error with respect to returning a pointer to a temporary variable:
Student makeAndrew(void)
{
Student s;
setName(&s, "Andrew");
setStudentID(&s, 12345678);
return s;
}
Student aStudent = makeAndrew();
Your setName does have an error with respect to temp, which should be a char *, since you are incrementing it in your loop to point to another character in the input c-string. I think it was missing the null termination as well. And as you mention in your comment, there should be a check for overflow of the name char array in Student:
void setName(Student* s, const char* name) { // 's' is a pointer to a Student struct |
// 'name' is a pointer to the first element of a char array (repres. a string)
const char *temp;
int i;
for (i = 0, temp = name; *temp != '\0' && i <= MAX_NAME_LEN; temp++, i++)
{
*((s->name) + i) = *temp;
}
s->name[i] = '\0';
}
You can use strncpy to simplify setName:
void setName2(Student *s,const char *name)
{
#include <string.h>
strncpy(s->name, name,MAX_NAME_LEN);
s->name[MAX_NAME_LEN] = '\0';
}

Assigning memory to double pointer?

I am having trouble understanding how to assign memory
to a double pointer.
I want to read an array of strings and store it.
char **ptr;
fp = fopen("file.txt","r");
ptr = (char**)malloc(sizeof(char*)*50);
for(int i=0; i<20; i++)
{
ptr[i] = (char*)malloc(sizeof(char)*50);
fgets(ptr[i],50,fp);
}
instead of this I just assign a large block of memory and
store the string
char **ptr;
ptr = (char**)malloc(sizeof(char)*50*50);
would that be wrong? And if so why is it?
Your second example is wrong because each memory location conceptually would not hold a char* but rather a char. If you slightly change your thinking, it can help with this:
char *x; // Memory locations pointed to by x contain 'char'
char **y; // Memory locations pointed to by y contain 'char*'
x = (char*)malloc(sizeof(char) * 100); // 100 'char'
y = (char**)malloc(sizeof(char*) * 100); // 100 'char*'
// below is incorrect:
y = (char**)malloc(sizeof(char) * 50 * 50);
// 2500 'char' not 50 'char*' pointing to 50 'char'
Because of that, your first loop would be how you do in C an array of character arrays/pointers. Using a fixed block of memory for an array of character arrays is ok, but you would use a single char* rather than a char**, since you would not have any pointers in the memory, just chars.
char *x = calloc(50 * 50, sizeof(char));
for (ii = 0; ii < 50; ++ii) {
// Note that each string is just an OFFSET into the memory block
// You must be sensitive to this when using these 'strings'
char *str = &x[ii * 50];
}
char **ptr;
fp = fopen("file.txt","r");
ptr = (char**)malloc(sizeof(char*)*50);
for(int i=0; i<50; i++)
{
ptr[i] = (char*)malloc(sizeof(char)*50);
fgets(ptr[i],50,fp);
}
fclose(fp);
may be your typo mistake but your loop should be of 50 instead of 20 if you are looking for 50 x 50 matrix. Also after allocation of memory mentioned above you can access the buffer as ptr[i][j] i.e in the 2D format.
A double pointer is just a pointer to another pointer. So you can allocate it like this:
char *realptr=(char*)malloc(1234);
char **ptr=&realptr;
You have to keep in mind where your pointer is stored at (in this example the double pointer points to a pointer variable on the stack so it's invalid after the function returns).
i will give one example, which might clear of the doubt,
char **str; // here its kind a equivalent to char *argv[]
str = (char **)malloc(sizeof(char *)*2) // here 2 indicates 2 (char*)
str[0]=(char *)malloc(sizeof(char)*10) // here 10 indicates 10 (char)
str[1]=(char *)malloc(sizeof(char)*10) // <same as above>
strcpy(str[0],"abcdefghij"); // 10 length character
strcpy(str[1],"xyzlmnopqr"); // 10 length character
cout<<str[0]<<endl; // to print the string in case of c++
cout<<str[1]<<endl; // to print the string in case of c++
or
printf("%s",str[0]);
printf("%s",str[1]);
//finally most important thing, dont't forget to free the allocated mem
free(str[0]);
free(str[1]);
free(str);
other simpler way to memorize
Case -1 :
step-1 : char *p;
step -2 :
please read it like below
char (*p); ==> p is a pointer to a char
now you just need to do malloc for the type (step-2) without braces
i.e., p = malloc(sizeof(char) * some_len);
Case -2 :
step-1 : char **p;
step -2 :
please read it like below
char* (* p); ==> p is a pointer to a char *
now you just need to do malloc for the type (step-2) without braces
i.e., p = malloc(sizeof(char *) * some_len);
Case -3 :
No one uses this but just for sake of explanation
char ***p;
read it as,
char** (*p); ==> p is a pointer to a char** (and for this check case-2 above)
p = malloc(sizeof(char**) * some_len);
Adding to Pent's answer, as he correctly pointed out, you will not be able to use this double pointer once the function returns, because it will point to a memory location on the function's activation record on stack which is now obsolete (once the function has returned). If you want to use this double pointer after the function has returned, you may do this:
char * realptr = (char *) malloc(1234);
char ** ptr = (char **) malloc(sizeof(char *));
*ptr = realptr;
return ptr;
The return type of the function must obviously be char ** for this.
well, this is how I do it:
#include <stdlib.h>
int main(void)
{
int i = -1; // just a counter
int j = 5; // how many strings
char *s[j];
while(++i < j)
s[i] = malloc(sizeof(char*)); // allocating avery string separately
return (0);
}
this also works:
char **allocate(int lines)
{
int i = -1;
char **s = malloc(sizeof(char *) * lines); // allocating lines
while (++i < lines)
{
s[i] = malloc(sizeof(char*)); // alicating line
scanf("%s", s[i]);
}
return (s);
}
int main(int ac, char *av[])
{
int lines = 5; // how many lines
char **s = allocate(lines);
return (0);
}
Double pointer is, simply put, a pointer to a pointer,
In many cases it is used as an array of other types.
For example, if you want to create an array of strings you can simply do:
char** stringArray = calloc(10, 40);
this will create an array of size 10, each element will be a string of length 40.
thus you can access this by stringArray[5] and get a string in the 6th position.
this is one usage, the others are as mentioned above, a pointer to a pointer, and can be allocated simply by:
char* str = (char*)malloc(40);
char** pointerToPointer = &str //Get the address of the str pointer, valid only in the current closure.
read more here:
good array tutorial

Resources