Is there any difference between returning a pointer to a struct, and returning a struct? Aside from the derefrenceing that is done would they both be working with the same struct? Is there any copying that gets done if it is just a regular return?

When you have a function in C (or C++), the function gets an area of memory on the stack to use for its local variables. If you create a variable like this:

MyStruct X;

inside your function, then the X structure will get created on the stack. If you return the variable like this:

return X;

then when the function ends, the code can't return the actual structure that's on the stack, since that stack space goes away (out of scope). So the code will actually create a copy of it and return that copy by copying over the members. However, the way around that is to allocate the structure on the heap, and instead pass around pointers to that structure. Then you're always working with the same copy. (In C++ you can accomplish this same thing using the new keyword.)

Does that help?

Jeff

Ok, I think I see, so when the code leaves the method it actually has to copy the struct from where it has been allocated to the method that called it right? So if you return a pointer it might be a little more efficient than copying the whole thing, but wouldn't it still have to copy the struct because the area on the stack is gone?

Correct, except for the part about the pointer. When you allocate something on the heap, the heap stays around and is there for all the functions to share. (In C, I think you use "malloc" to allocate it on the heap. I haven't done straight C in years though.) So what you do is this: In your function, you allocate the structure on the heap, and get back a pointer to that structure. At the end of your function, you return the pointer. The code that called the function gets the pointer to that same spot on the heap, and continues working on the structure. So that's actually a good bit more efficient. Also, if you're doing C++, by calling new, your object automatically gets allocated on the heap and you get back a pointer to it.

I am actually looking back at one of my old C programs that I wrote in a C/C++ data structures class. Apparently it was very efficient according to the teacher, but am looking back to determine why it was efficient.

So if you do not allocate on the heap and you do pass a pointer back to main, then the program will have to move the struct elsewhere?

No, when it's on the heap it stays right there and the main and other functions just access it through the pointer. The pointer gets passed around and anyone can use it.

Oh! Sorry, I guess I didn't explain that. When you call malloc in C or new in C++, it gets created on the heap and you get a pointer to its location in the heap.

But what if you don't use malloc? Does the variable then get copied to the heap?

What if you deliberately place the struct on the stack?

It has been a while since I have coded C, so I hope this makes sense?

Edited 2 Years Ago by overwraith

I think you're asking what happens in this case:

someObject* giveMeAnObject()
{
  someObject anInstance;
  return &anInstance;
}

So you get back a pointer to an object made inside the function, on the stack.

What happens is the memory is now available for other use and you're left with a pointer to a piece of memory that could contain anything, and might even segFault if the OS has decided you're not allowed to look at that memory anymore. This is as bad as it sounds.

Edited 2 Years Ago by Moschops

C doesn't have objects silly! But basically yes, if you choose to allocate your struct without the malloc method, on the stack (I'm not saying that's what I did in my code), and then return the address of that stack allocated struct, would you still be able to access it, would it move it's slef to the heap, or what.

From what you are saying, it would be popped off the stack and unavailable to the coder.

From what I learned here, allocating to the heap is a really good idea, and can be very benificial to efficiency.

Thanks everybody for bearing with me.

C doesn't have objects silly!

Yeah, sure it doesn't. I'm just imagining int, double, char, and all the structs I can think of :)

Yeah, sure it doesn't. I'm just imagining int, double, char, and all the structs I can think of :)

The cirriculum at my school says so, it might be mistaken, or I could have even interpreted it wrong. Dunno.

We're old school. If it takes up memory and has some kind of label, it's an object. Functions, variables, the lot. :)

This is actually a really good discussion, something I suspect a lot of beginning and even intermediate programmers might be wondering about. I think I'll write my next Tutorial about this topic. (I work for Daniweb as a volunteer.)

I wrote this bit of code while I was in the class. If I ever did it over, there are a few things that I would change, but there is a lot of potential in the code. Basically this "batch" master record/transaction record processor uses this passing of pointers to heap allocated structs to preform it's work. Combines two files, the transaction file, and the old master file into the new master file. It really reads the file almost like the file it's slef is the list. One of the things that should probably be fixed is that it does not write to a binary file, it writes to a text file (that just happens to have a .dat extension, I know I was younger when I wrote it, I didn't know). The loop in main may also need some work.

/*
Author: Cameron Block
Assignment: 11.7, 11.8
File: FileMatching.c
Purpose: Develop a program that does various operations on a file. 
*/
#include <stdio.h>//standard input/output
#include <assert.h>
#include <stdlib.h>//malloc cmd for allocating structs
#include <string.h>//memset cmd for clearing memory

char *removeNewLine(char input[]);

typedef struct{//struct for old master file
int accountNum;
float currentBalance;
char name[32];
}MasterRecord;

typedef struct{//struct for transfer file
int accountNum;
float dollarAmmt;
}TransRecord;

MasterRecord *readMasterFileRecord();//pointers are really awesome
TransRecord *readTransactionRecord();
void writeNewMasterRecord(MasterRecord *record);
MasterRecord *createMasterRecord(TransRecord *input, char *name);//creates a new initialized master file record
char *strStrip(char input[]);

void main()
{
    //both transactions file, and master file must be in order
    MasterRecord *currentMaster=readMasterFileRecord();
    TransRecord *currentTransaction=readTransactionRecord();
    while(currentMaster!=NULL)
    {
        while(currentTransaction!=NULL)
        {
            if((currentTransaction->accountNum)==(currentMaster->accountNum))
            {
                currentMaster->currentBalance+=currentTransaction->dollarAmmt;
                //not entirely sure if it should be +=, or -=
            }
            else if(currentTransaction->accountNum > currentMaster->accountNum)
            {   
                writeNewMasterRecord(currentMaster);

                currentMaster=readMasterFileRecord();

                if(currentMaster== NULL){
                    currentMaster=createMasterRecord(currentTransaction, 
                        "Uninitialized Record");
                    printf("Unmatched Transaction Record for account number %d\n", currentMaster->accountNum);
                    //have to put currenbBal += dollarAmt here for this to work 
                    //for some reason, just discovered it through tryal and error
                    currentMaster->currentBalance+=currentTransaction->dollarAmmt;
                    continue;
                }

                if(currentMaster->accountNum == currentTransaction->accountNum)
                    continue;

                else if(currentTransaction->accountNum < currentMaster->accountNum){
                    currentMaster=createMasterRecord(currentTransaction, 
                        "Uninitialized Record");
                    printf("Unmatched Transaction Record for account number %d\n", currentMaster->accountNum);
                    continue;
                }
            }
            else
                break;

            currentTransaction=readTransactionRecord();
        }
        writeNewMasterRecord(currentMaster);
        currentMaster=readMasterFileRecord();
    }
    writeNewMasterRecord(NULL);//to close the file
    printf("The New Master File has been updated. . . \n");
    system("pause");
}

char *removeNewLine(char input[])
{//removes and replaces newline character with a space
    int i;
    for(i=0;i<strlen(input);i++)
        if(input[i]=='\n')
            input[i]=' ';
    return input;//for a chaining like effect
}

MasterRecord *readMasterFileRecord()
{//originally intended to put the pointers this function returned in an array, or list
    //note to self..., in C 
    //FILE is all caps, and NULL is all caps
    static FILE *ofPtr=NULL;
    static char nameBuffer[64];
    static int firstRun=1;//only set to 1 the first time called
    MasterRecord *oldStuff=(MasterRecord*)malloc(sizeof(MasterRecord));

    if(oldStuff==NULL)//should return null if fails malloc fails
        return oldStuff;

    memset(nameBuffer, '\0', 64);
    memset(oldStuff, '\0', sizeof(MasterRecord));

    //if these files dont exist theres no point in running the program
    if(firstRun)//if is first time called get a new pointer
        assert((ofPtr=fopen("oldmast.dat", "rb"))!=NULL);

    firstRun=0;//want this set to 0 after the first time through the function

    if(feof(ofPtr))//if is end of file return null
    {
        fclose(ofPtr);
        return NULL;
    }

    //read one struct from old master file
    //fscanf returns the number of arguments it successfully accomplished
    //if there is a blank line fscanf will return a number less than its 
    //number of arguments, which is 2. 
    if(fscanf(ofPtr, "%d %f", &oldStuff->accountNum, &oldStuff->currentBalance)<2)
        return NULL;
    //get the rest of the line and do some formatting to it, first and last name
    strcpy(&oldStuff->name, strStrip(removeNewLine(fgets(&nameBuffer, 64, ofPtr))));

    return oldStuff;
}

TransRecord *readTransactionRecord()
{//this function not done yet
    static FILE *tfPtr=NULL;
    static int firstRun=1;
    TransRecord *transStuff=(TransRecord*)malloc(sizeof(MasterRecord));

    if(transStuff==NULL)//malloc has failed
        return transStuff;//return NULL

    memset(transStuff, '\0', sizeof(TransRecord));

    if(firstRun)
        assert((tfPtr=fopen("trans.dat", "rb"))!=NULL);

    firstRun=0;

    if(feof(tfPtr))
    {
        fclose(tfPtr);
        return NULL;
    }

    if(fscanf(tfPtr, " %d %f", &transStuff->accountNum, &transStuff->dollarAmmt)<2)
        return NULL;

    return transStuff;
}

void writeNewMasterRecord(MasterRecord *record)
{
    static int firstRun = 1;
    static FILE *nfPtr=NULL;

    if(firstRun)
        assert((nfPtr=fopen("newmast.dat", "wb"))!=NULL);
    firstRun=0;

    if(record==NULL)
    {
        fclose(nfPtr);
        return;
    }

    fprintf(nfPtr, "%d %.2f %s\n", record->accountNum, record->currentBalance, record->name);
    fflush(nfPtr);
}

MasterRecord *createMasterRecord(TransRecord *input, char *name)//creates a new initialized master file record
{
    MasterRecord *newStuff=(MasterRecord*)malloc(sizeof(MasterRecord));
    memset(newStuff, '\0', sizeof(MasterRecord));
    //a little extra protection if someone decides to foreget to write a null character
    newStuff->accountNum=input->accountNum;
    //we do not set the current balance because this way the loop sets it for us. 
    //if we did end up putting it in here it would assign it twice
    strcpy(&newStuff->name, name);
    return newStuff;
}

char *strStrip(char input[])
{//removes trailing and preceding whitespace
    int i, j, first, last;
    char *trimmedstr;
    for(i=0;i<strlen(input);i++)//read from left to right
        if(input[i]!=' '){//find first instance of a not space
            first=i;//log it
            break;}

    for(i=strlen(input);i>0;i--)//read from right to left
        if(input[i]!=' '&&input[i]!='\0'){//find first instance of a not space
            last=i;//log it
            break;}

    //allocate string, subtract unwanted characters
    trimmedstr=calloc(strlen(input)-first-(strlen(input)-last)+1, sizeof(char));
    trimmedstr[strlen(input)-first-(strlen(input)-last)+1]='\0';

    for(i=first, j=0;i<=last;i++, j++)
        trimmedstr[j]=input[i];

    return trimmedstr;
}

//oldmast.dat

100 348.17 Alan Jones
300 27.19 Mary Smith
500 0.00 Sam Sharp
700 -14.22 Suzy Green

//trans.dat

100 27.14
300 62.11
400 100.56
400 123.50
400 123.50
900 82.17
900 82.17
900 82.17

//newmast.dat

100 375.31 Alan Jones
300 89.30 Mary Smith
400 347.56 Uninitialized Record
700 -14.22 Suzy Green
900 246.51 Uninitialized Record
This question has already been answered. Start a new discussion instead.