I know that in C we cannot return an array from a function, but a pointer to an array. But I want to know what is the special thing about structs that makes them return-able by functions even though they may contain arrays.
Why is the struct wrapping makes the following program valid?
#include <stdio.h>
struct data {
char buf[256];
};
struct data Foo(const char *buf);
int main(void)
{
struct data obj;
obj = Foo("This is a sentence.");
printf("%s\n", obj.buf);
return 0;
}
struct data Foo(const char *buf)
{
struct data X;
strcpy(X.buf, buf);
return X;
}
A better way of asking the same question would be "what is special about arrays", for it is the arrays that have special handling attached to them, not structs.
The behavior of passing and returning arrays by pointer traces back to the original implementation of C. Arrays "decay" to pointers, causing a good deal of confusion, especially among people new to the language. Structs, on the other hand, behave just like built-in types, such as ints, doubles, etc. This includes any arrays embedded in the struct, except for flexible array members, which are not copied.
First of all, to quote C11, chapter §6.8.6.4, return statement, (emphasis mine)
If a return statement with an expression is executed, the value of the expression is
returned to the caller as the value of the function call expression.
Returning a structure variable is possible (and correct), because, the structure value is returned. This is similar to returning any primitive data type (returning int, for example).
On the other hand, if you return an array, by using the return <array_name>, it essentially returns the address of the first element of the arrayNOTE, which becomes invalid in the caller if the array was local to the called functions. So, returning array in that way is not possible.
So, TL;DR, there is nothing special with structs, the speciality is in arrays.
NOTE:
Quoting C11 again, chapter §6.3.2.1, (my emphasis)
Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. [...]
There isn't anything special about struct types; it's that there's something special about array types that prevents them from being returned from a function directly.
A struct expression is treated like an expression of any other non-array type; it evaluates to the value of the struct. So you can do things like
struct foo { ... };
struct foo func( void )
{
struct foo someFoo;
...
return someFoo;
}
The expression someFoo evaluates to the value of the struct foo object; the contents of the object are returned from the function (even if those contents contain arrays).
An array expression is treated differently; if it's not the operand of the sizeof or unary & operators, or if it isn't a string literal being used to initialize another array in a declaration, the expression is converted ("decays") from type "array of T" to "pointer to T", and the value of the expression is the address of the first element.
So you cannot return an array by value from a function, because any reference to an array expression is automatically converted to a pointer value.
Structs have data members public by default so it is possible in case of struct to access data in main but not in case of class. So , the struct wrapping is valid.
Related
I'm new to C. I haven't been learning it for very long and one problem that I have encountered already is that I come across this error when trying to set the values inside of the new_src array:
warning: incompatible pointer to integer conversion assigning to 'char' from 'char *'; dereference with * [-Wint-conversion]
My code is:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * add_0_to_front(char array[]){
static char destination[10] = {'0'};
strcat(destination, array);
return destination;
}
int main(){
char src[7] = "1111111";
char new_src[8];
*new_src = add_0_to_front(src);
printf("%s\n", new_stack);
return 0;
}
The purpose of the program is to place a '0' at the beginning of the src array. Essentially to make the binary value in src a length of 8.
Thanks. 🙏
There is no way to use an array as a single object in C. The value of an array (that is, the entire sequence of values of its elements) cannot be assigned to another array or used in an expression. The reasons for this lie in the history of computers and the development of C.
When you do try to use an array in an expression, whether it is on the left side or the right side of an assignment, it is automatically converted to a pointer to its first element, except when it is the operand of sizeof, is the operand of unary & or is a string literal used to initialize an array. C programmers are accustomed to this conversion and use arrays as pointers.
To accomplish your goal, a common solution is to design the routine to work on the array directly. Pass the array to the routine (by passing a pointer to its first element), and then write code in the routine to move all elements in the array back one position and store a '0' at the front.
Keep in mind you need to allow room in the array for a terminating null character if you will use it as a string with C library functions.
Another solution would be for the routine to use its own array and return a pointer to the first element of that array, and then the caller can copy data from the routine’s array to the caller’s array by using memcpy, as in memcpy(new_src, add_0_to_front(src), sizeof new_src);. One issue with that is managing the memory the routine uses for its own array. Using a static object inside the routine is bad form because it means only one result from the routine can be “live” at the same time; callers have to take care to be sure the data from the last call is no longer needed before the routine is called again. An alternative is to use malloc to allocate memory, but then the memory should be freed by calling free when it is no longer needed (unless it is no longer needed because the program is exiting now).
It is possible to embed an array in a struct and then use that as a single object. For example, struct EightChar { char array[8]; }; declares a struct EightChar that contains an array of eight char, and that can be returned from a routine and assigned as an object.
So, the exact cause of the error message is the line
*new_src = add_0_to_front(src); // char = char *
add_0_to_front is declared to return a value of type char *, but the expression *new_src is exactly equivalent to new_src[0], which has type char. You're trying to assign a char * value to a char object, which isn't allowed.
But there's a deeper problem here.
Arrays in C are weird, and are not treated like any other aggregate type. You cannot use = to assign whole arrays; you can only assign to individual elements:
6.5.16 Assignment operators
...
Constraints
2 An assignment operator shall have a modifiable lvalue as its left operand.
where a modifiable lvalue is
6.3.2.1 Lvalues, arrays, and function designators
1 An lvalue is an expression (with an object type other than void) that potentially
designates an object;64) if an lvalue does not designate an object when it is evaluated, the
behavior is undefined. When an object is said to have a particular type, the type is
specified by the lvalue used to designate the object. A modifiable lvalue is an lvalue that
does not have array type, does not have an incomplete type, does not have a const-qualified type, and if it is a structure or union, does not have any member (including,
recursively, any member or element of all contained aggregates or unions) with a const-qualified type.
64) The name ‘‘lvalue’’ comes originally from the assignment expression E1 = E2, in which the left
operand E1 is required to be a (modifiable) lvalue. It is perhaps better considered as representing an
object ‘‘locator value’’. What is sometimes called ‘‘rvalue’’ is in this International Standard described
as the ‘‘value of an expression’’.
An obvious example of an lvalue is an identifier of an object. As a further example, if E is a unary
expression that is a pointer to an object, *E is an lvalue that designates the object to which E points.
C 2011 Online Draft
You can't return destination and assign it to new_src using the = operator. What you can do is pass new_src as a parameter to add_0_to_front and modify it within the function.
But there's another problem - under most circumstances, an expression of type "N-element array of T" will be converted, or "decay", to an expression of type "pointer to T" and the value of the expression will be the address of the first element1:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. If the array object has
register storage class, the behavior is undefined.
IOW, array expressions lose their "array-ness" when you pass them as arguments to a function. Critically, pointers don't have any metadata that tell you whether they point to a single object or to the first element of an array, so there's no way to determine the array size based on the pointer itself - if you pass an array to a function, you must also pass its size as a separate parameter.
Here's one way of doing what you want:
#include <stdio.h>
#include <string.h>
#include <assert.h>
/**
* Add a 0 to the beginning of dst, then append the contents
* of src to it.
*/
void add_0_to_front( char *dst, size_t dst_size, const char *src )
{
/**
* Make sure dst isn't an invalid pointer *and* that there's
* at least enough room to write the string "0" to it.
*/
assert( dst != NULL && dst_size >= 2 );
strcpy( dst, "0" );
strncat( dst, src, dst_size - 2 );
}
int main( void )
{
/**
* Array size is determined by the number of elements in the
* initializer - in this case, the array size is 8 (7 characters
* plus the string terminator)
*/
char src[] = "1111111";
/**
* Array size is determined by taking the size of the src array (8)
* and adding 1.
*/
char dst[sizeof src + 1];
add_0_to_front( dst, sizeof dst, src );
printf( "src = %s, dst = %s\n", src, dst );
return 0;
}
If you're interested in the historical reasons for this, check out this paper by Dennis Ritchie explaining the early development of C.
You can try out below code. Return type of a add_0_to_front function is char* and need to collect back it in char* in main . In above code you are trying to collect it in new_src[0] or *new_src that is the reason you are getting warning or incorrect results
#include <stdio.h>
#include <stdlib.h>`enter code here`
#include <string.h>
char * add_0_to_front(char array[]){
static char destination[10] = {'0'};
strcat(destination, array);
return destination;
}
int main(){
char src[7] = "1111111";
char* new_src = add_0_to_front(src);
printf("%s\n", new_src);
return 0;
}
For example,
int x[10];
int i = 0;
x = &i; //error occurs!
According to C - A Reference Manual, an array name cannot be an lvalue. Thus, x cannot be an lvalue. But, what is the reason the array name cannot be an lvalue? For example, why does an error occur in the third line?
Your reference is incorrect. An array can be an lvalue (but not a modifiable lvalue), and an "array name" (identifier) is always an lvalue.
Take your example:
int x[10];
int i = 0;
x = &i; //error occurs!
Apply C11 6.5.1, paragraph 2:
An identifier is a primary expression, provided it has been declared
as designating an object (in which case it is an lvalue) ...
We see that x is a primary expression and is an lvalue, because it has previously been declared as designating an array object.
However, the C language rules state that an array expression in various contexts, including the left-hand-side of an assignment expression, are converted to a pointer which points at the first element of the array and is not an lvalue, even if the array was. Specifically:
Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. If the array object has
register storage class, the behavior is undefined.
(C11 6.3.2.1 paragraph 3).
The pointer which is the result of the conversion specified above is not an lvalue because an lvalue designates an object, and there is no suitable object holding the pointer value; the array object holds the elements of the array, not a pointer to those elements.
The example you use in your question implies that you understand that an array expression decays (is converted to) a pointer value, but I think you are failing to recognize that after the conversion, the pointer value and the array are two different things. The pointer is not an lvalue; the array might be (and in your example, it is). Whether or not arrays are lvalues in fact has no bearing on your example; it is the pointer value that you are trying to assign to.
If you were to ask instead: Why do arrays decay to pointers when they are on the left-hand-side of an assignment operator? - then I suspect that there is no particularly good answer. C just doesn't allow assignment to arrays, historically.
Array names are non-modifiable lvalues in C.:)
Arrays are named extents of memory where their elements are placed. So you may not substitute one extent of memory for another extent of memory. Each extent of memory initially allocated for an array declaration has its own unique name. Each array name is bounded with its own extent of memory.
An array is an lvalue, however it is a non-modifiable lvalue.
It most likely has to do with compatibility of types. For example, you can do this:
struct ss {
char c[10];
};
...
struct ss s1 = { { "hello" } };
struct ss s2 = s1;
But not this:
char s1[10] = "hello";
char s2[10] = s1;
It's true that array names yield pointer values in many contexts. But so does the & operator, and you don't expect that to be assignable.
int i = 42;
int *j = malloc(sizeof *j);
&i = j; /* obviously wrong */
int a[] = {1,2,3};
&a[0] = j; /* also obviously wrong */
a = j; /* same as the previous line! */
So when learning the relationship between arrays and pointers, remember that a is usually the same as &a[0] and then you won't think lvalue-ness is an exception to the rule - it follows the rule perfectly.
How does char s[] act as a pointer while it looks like an array declaration?
#include<stdio.h>
void test(char s[]);
int main()
{
char s[10]="test";
char a[]=s;
test(s);
char p[]=s;
return 0;
}
void test(char s[])
{
printf(s);
}
In the context of a function parameter declaration (and only in that context), T a[N] and T a[] are the same as T *a; they declare a as a pointer to T, not an array of T.
Chapter and verse:
6.7.6.3 Function declarators (including prototypes)
...
7 A declaration of a parameter as ‘‘array of type’’ shall be adjusted to ‘‘qualified pointer to
type’’, where the type qualifiers (if any) are those specified within the [ and ] of the
array type derivation. If the keyword static also appears within the [ and ] of the
array type derivation, then for each call to the function, the value of the corresponding
actual argument shall provide access to the first element of an array with at least as many
elements as specified by the size expression.
Anywhere else, T a[]; declares a as an array with an as-yet-unspecified size. At this point the declaration is incomplete, and a cannot be used anywhere until a size has been specified, either by specifying it explicitly:
T a[N];
or using an initializer:
T a[] = { /* comma-separated list of initial values */ }
Chapter and verse, again:
6.7.6.2 Array declarators
...
3 If, in the declaration ‘‘T D1’’, D1 has one of the forms: D[ type-qualifier-listopt assignment-expressionopt ]
D[ static type-qualifier-listopt assignment-expression ]
D[ type-qualifier-list static assignment-expression ]
D[ type-qualifier-listopt * ]
and the type specified for ident in the declaration ‘‘T D’’ is ‘‘derived-declarator-type-list
T’’, then the type specified for ident is ‘‘derived-declarator-type-list array of T’’.142)
(See 6.7.6.3 for the meaning of the optional type qualifiers and the keyword static.)
4 If the size is not present, the array type is an incomplete type. If the size is * instead of
being an expression, the array type is a variable length array type of unspecified size,
which can only be used in declarations or type names with function prototype scope;143)
such arrays are nonetheless complete types. If the size is an integer constant expression
and the element type has a known constant size, the array type is not a variable length
array type; otherwise, the array type is a variable length array type. (Variable length
arrays are a conditional feature that implementations need not support; see 6.10.8.3.)
142) When several ‘‘array of’’ specifications are adjacent, a multidimensional array is declared.
143) Thus, * can be used only in function declarations that are not definitions (see 6.7.6.3).
...
6.7.9 Initialization
...
22 If an array of unknown size is initialized, its size is determined by the largest indexed
element with an explicit initializer. The array type is completed at the end of its
initializer list.
So, why are arrays as function parameters treated differently than arrays as regular objects? This is why:
6.3.2.1 Lvalues, arrays, and function designators
...
3 Except when it is the operand of the sizeof operator, the _Alignof operator, or the
unary & operator, or is a string literal used to initialize an array, an expression that has
type ‘‘array of type’’ is converted to an expression with type ‘‘pointer to type’’ that points
to the initial element of the array object and is not an lvalue. If the array object has
register storage class, the behavior is undefined.
Under most circumstances, an expression of type "N-element array of T" is converted ("decays") to an expression of type "pointer to T". If you pass an array expression as an argument to a function, like so:
int foo[10];
...
bar( foo );
what the function bar actually receives is a pointer to int, not a 10-element array of int, so the prototype for bar can be written
void bar( int *f );
"But why..." I hear you starting to ask. I'm getting to it, really.
C was derived from an earlier language called B (go figure), and in B the following things were true:
Pointer objects were declared using empty bracket notation - auto p[];. This syntax was kept for pointer function parameter declarations in C.
Array declarations set aside memory not only for the array elements, but also for an explicit pointer to the first element, which was bound to the array variable (i.e., auto v[10] would set aside 11 memory cells, 10 for the array contents, and the remaining one to store an offset to the first element).
The array subscript operation a[i] was defined as *(a + i) -- you'd offset i elements from the base address stored in the variable a and dereference the result (B was a "typeless" language, so all scalar objects were the same size).
For various reasons, Ritchie got rid of the explicit pointer to the first element of the array, but kept the definition of the subscript operation. So in C, when the compiler sees an array expression that isn't the operand of the sizeof or unary & operators, it replaces that expression with a pointer expression that evaluates to the address of the first element of the array; that way the *(a + i) operation still works the way it did in B, without actually setting aside any storage for that pointer value. However, it means arrays lose their "array-ness" in most circumstances, which will bite you in the ass if you aren't careful.
You can't assign to arrays, only copy to them or initialize them with a valid initializer (and another array is not a valid initializer).
And when you declare a function like
void test(char s[]);
it's actually the same as declaring it
void test(char *s);
What happens when you call a function taking an "array" is that the array decays to a pointer to the first element, and it's that pointer that is passed to the function.
So the call
test(s);
is the same as
test(&s[0]);
Regarding the function declaration, declaring a function taking an array of arrays is not the same as declaring a function taking a pointer to a pointer. See e.g. this old answer of mine as an explanation of why.
So if you want a function taking an array of arrays, like
void func2(char a[][X]);
it's not the same as
void func2(char **a);
Instead it's the same as
void func2(char (*a)[X]);
For more "dimensions" it doesn't change anything, e.g.
void func3(char a[][X][Y]);
is the same as
void func3(char (*a)[X][Y]);
char a[] is an array and not a pointer, the inittialization is invalid.
s is an array, too, but in the context of an expression it evaluates to a pointer to its first element.
char a[] is only valid if you have a following initializer list. It has to be an array of characters. As a special case for strings, C allows you to type an array of characters as "str", rather than {'s','t','r','\0'}. Either initializer is fine.
In the code char a[]=s;, s is an array type and not a valid initializer, so the code will not compile.
void test(char s[]) is another special case, because arrays passed as parameters always get replaced by the compiler with a pointer to the first element. Don't confuse this with array initialization, even though the syntax is similar.
suppose i have something like this
typedef char string[21]
struct planet_s
{
string name,orbits;
float distance;
string discoverer;
int yeardiscovered;
}one_planet;
next i need to initialize some information about the planet therefore my text book says
strcpy(one_planet.name, "Earth"); ?confused with these
strcpy(one_planet.orbits, "Sun"); ?
one_planet.distance = 150;
one_planet.mass = 6.00e+24;
strcpy(one_planet.discoverer, "me"); ?
one_planet.yeardiscovered = 1000;
my Confusion arises in the strcpy,let me put things in point form
Strcpy needs a pointer to a string as its first argument
does one_planet.name resolve into a pointer to the strcuts name array(does the dot operator resolve into an address)?
how come one_planet.distance =150 does not resolve into an address since we are assign it its value straight away? this is what i been taught, one_planet.distance directly access the struct element and assigns it. my confusion is with the strcpy, since it needs an address to store a string?
hope you understand where my confusion comes form thanks.
Except when it is the operand of the sizeof or unary & operator, or when it is a string literal being used to initialize another array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element in the array.
In the line
strcpy(one_planet.name, "Earth");
the expression one_planet.name has type "21-element array of char"; since the expression is not the operand of the sizeof or unary & operators, it is converted to an expression of type "pointer to char", and the address of the first element of the array is passed to strcpy.
The . operator doesn't make a difference in this case; what matters is the type of the member, regardless of how that member is accessed.
The dot operator has nothing to do with this.
Compare your code to this:
int main(void)
{
char string[21];
strcpy(string, "Earth");
return 0;
}
the above is 100% natural and fine C code, and it relies on the fact that in some contexts, the name of an array (string) evaluates to just a pointer to the first argument.
In other words, string above is evaluated to the exact same address value as the expression &string[0].
The fact that your string is embedded inside a struct has nothing to do with this.
No it's not the "dot operator" . that creates a pointer, it's because e.g. the orbits member is an array. All arrays decays to pointers when passed as arguments.
For eg. I have an array of structs 'a' as below:
struct mystruct{
int b
int num;
};
struct bigger_struct {
struct my_struct a[10];
}
struct bigger_struct *some_var;
i know that the name of an array when used as a value implicitly refers to the address of the first element of the array.(Which is how the array subscript operator works at-least)
Can i know do the other way around i.e
if i do:
some_var->a->b, it should be equivalent to some_var->a[0]->b, am i right? I have tested this and it seems to work , but is this semantically 100% correct?
Is some_var->a->b equivalent to some_var->a[0]->b?
No, it is equivalent to some_var->a[0].b.
The exact specification of the array-to-pointer conversion is actually quite straightforward:
Except when it is the operand of the sizeof operator or the unary & operator, or is a
string literal used to initialize an array, an expression that has type array of type is converted to an expression with type pointer to type that points to the initial element of the array object and is not an lvalue (C99 6.3.2.1).
some_var->a has the type my_struct[10], which is an array type, and since it is not the operand of the sizeof or unary & operator and is not a string literal, it is converted to a pointer to the initial element of the array.
Assuming that you have allocated memory for some_var, its safe to do some_var->a->b (as arrays decay into pointer).
Yes, _var->a[0]->b and _var->a->b are equivalent
Because a[0] and a is representing the base address of the structure.