1

Hello i'm selfstudying C and i'm a bit confused about the following code since i don't know if i'm understanding the code properly. I would be very thankful if someone could read my explanation and correct me if i'm wrong.

The code is from a header file. The function of the program should be uninteresting at this point, since my comprehension problem is about the pointers and the values the functions give back. So first of all i'm declaring 3 arrays of char and an integer in my employee struct.

struct employee
{
    char firstname[11];
    char lastname[11];
    char number[11];
    int salary;
}


5 functions are declared in the header file. The first function takes 4 values (3 pointers and one int) and gives back a pointer to a struct. The second function gets a pointer to the "struct employee" and gives back a pointer to an element of the array "char firstname" in the struct employee. The functions 3 and 4 are doing the same for the other both arrays.
The function 5 gets a pointer to the struct employee but gives back an int and not a pointer. So it is just using the declared variable in the struct.

struct employee* createEmployee(char*, char*, char*, int); //1
char* firstname (struct Employee*);                        //2
char* lastname (struct Employee*);                         //3
char* number (struct Employee*);                           //4
int salary (struct Employee*);                             //5
2
  • 1
    Without function bodies it is hard to say what they do, but signatures shows what you explained. And what is the question? Commented Dec 12, 2010 at 20:24
  • My question is if my explanation is correct. I'm not sure if i understand how the pointers work in this case. Commented Dec 12, 2010 at 20:27

5 Answers 5

2

Your understanding is pretty much correct. To be a little more accurate and/or less abusive of the English language, functions 2-4 return a pointer to an element of the corresponding array.

The idea is that the contents of each array represent some kind of text, with each element corresponding a character of text, up until the first appearance of a zero value (used to mark the end of the "string"). Keep in mind that there is no provision for Unicode here, or even for specifying an encoding: we assume ASCII, and for any bytes not in the range 0..127, all bets are off. A better name for the type char would be byte; but we didn't really know any better back then. (You should also be aware that char is a separate type from both signed char and unsigned char, and that char may or may not be signed.)

This way, the names and number can be any length up to ten (an 11th byte is reserved in order to have room for the "null terminator" with the zero value), and the returned pointer can be used to inspect - and modify; but that might not be a good idea - the data in the arrays that are part of the Employee structure.

The int returned as the Employee 's salary is a copy of the one in the struct. So although we can see the whole value, this does not let us modify the value in the struct. Of course, as long as we have the struct definition and an Employee instance, there is no protection; we can access the member directly instead of going through the function.

In C, we get "encapsulation" and "data hiding" by not providing these definitions. Instead, we would just put struct Employee; and the function declarations in the header, and the struct definition in the implementation file. Now calling code doesn't know anything about what an Employee is; only about what it can do.

Sign up to request clarification or add additional context in comments.

Comments

2

Almost right, except when you read the functions, the input "char*" should be read as a string is passed in though technically it is a pointer. for integers the actual value is passed, so you are right there.

so,

struct employee* createEmployee(char*, char*, char*, int);

would mean that the procedure creates a employee with the four inputs passed (the first three are strings (Char*) which probably are first name, last name and number/id in that order and the last one is the salary.)

2 Comments

Pointers to char are pointers to char. Anything else is just convention. There is no real string type in C.
Agreed, There is no string type in C. but that is the intention of the program. pass in first name, last name as strings. the way you'd do that is by passing the pointer.
1

What you cannot be sure by just reading the function signature whether the values/pointers returned are reference to same values/pointers from the sturct, or the function(s) is creating a copy and returning pointer to the new value. This is why its a good idea to also write the documentation as comments with the function signature in the header file.

2 Comments

While true, it would be strange and un-idiomatic to write C code this way. If you don't want the caller to modify your data via a returned pointer, you should say so explicitly, using language features - i.e. by using the const keyword appropriately.
Agreed, in my defence, C is my secondary language :P
1

Your explanations make sense, except #2-4: since they return char*, they cannot return an element of a char array (the correct type for the latter would be simply char). Each of the three functions returns a pointer to char. One presumes that they return the pointer to the first element of the corresponding array, which is basically how arrays are passed around in C.

4 Comments

Did you miss the words "a pointer" immediately before the emboldened "of an element" in the question?
@Karl I did not (it wasn't there). The OP has edited the question in response to my answer.
SO is full of ninjas, I tell you. ;)
Anyway, while it's true that this is "basically how arrays are passed around in C", it would be irresponsible to explain this without mentioning all the pitfalls - a little knowledge is a dangerous thing. Ordo, please read through c-faq.com/aryptr/index.html (and the linked sections; that page is just a table of contents).
1

There is a problem in 1. What you're doing I assume is:

struct employee* createEmployee(const char* f, const char* l, const char* n,
const int sal)
{
     struct employee* e;
     strcpy(e->firstname, f);
     strcpy(e->lastname,  l);
     /* ... */
     return e;
}

The problem here is the arrays coming in. It is perfectly feasible for a char array of any size to be passed in; anything over the length of 11 would cause you to start overwriting data at &(e->firstname[0])+11;. Over what? Exactly. You've no idea (and nor have I, it'd be determined at run-time). This could cause some serious problems.

One way around that is to use functions from stdlib.h and string.h i.e. strlen() to test the length of the data being passed in to ensure it fits your field size.

A better method might be to write:

int createEmployee(struct employee* e, const char* f, const char* l, const char* n, 
const int sal)
{
    int error = 0;
    if ( strlen(f) < 11 )
    {    
        strncpy(e->firstname, f);
    }
    else
    {
        error++;
    }
    /* ... */

    return error;
}

See what I've done? Yes it will work - anything passed in as a pointer can be modified. It's not pass-by-reference, quite. Edit: as aix says, pointers are "how arrays are passed around in C".

Another potential method is strncpy() which will truncate the source string according to the last argument, so strncpy(e->firstname, f, 11); would be safe.

You might well try dynamic memory allocation for the field sizes based on requirement, too. I'm guessing you'll be learning that later/as another challenge.

Also, another suggestion whilst we're at it is to define the pointer to a struct using typedef. It makes things a little more readable although the way you've done it is definitely clearer for someone learning.

4 Comments

o_O There are several problems here: There's no indication that the OP has actually written, or even looked at any implementation code (I think he's reading examples); strcpy is as much a function from the standard library as strlen is; error reporting is not a problem because we're returning a pointer, so we can just return NULL if there's an error (and who cares about the error count anyway?); "out parameters" are ugly in general; and your first example is broken since the Employee is never actually allocated anywhere (and when you pass in a pointer, the caller must allocate one).
@Karl I didn't want to throw too much in in one go, but if you add 1, then 2, then 4 to the error count you effectively use bits in a binary string to represent which error has occured.
Yes, out parameters are ugly, agreed. I still think it's better than creating the struct in the function because the next logical step is to alloc in the function for dynamic arrays... which might result in ignoring free's later. Yes, I haven't created an instance of the employee struct anywhere in the function, but no, the function itself is not broken, like you say, the caller has to allocate it.
In fact, no, I don't think out parameters are ugly. I think being inconsistent is ugly. Return values are also a great way to indicate whether a function worked or not.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.