I am fairly new to programming languages and wonder if it is possible to pass an argument without specific type to a function. For instance I have the following piece of code that defines a funcion add that will take a block of memory, check it if is filled via another function, and then adds an element to the list related to that block of memory.
This element can be an int, a float or a char. So I would like to write:
add(arrs1,20); //or also
add(arrs2,'b'); //or also
add(arrs3, 4.5);
Where arrs# are defined by struct arrs arrs#, and they refer to arrays of either floats, ints or chars but not mixed. How could I accomplish this?
int add(arrs list, NEW_ELEMENT){//adds NEW_ELEMENT at the end of an arrs
int check_if_resize;
check_if_resize=resize(list, list->size + 1);
list->ptr[list->used++] = NEW_ELEMENT;
return check_if_resize;
}
I appreciate your help.
C does, by design, not allow a single function to accept more than a single type for each argument. There are various ways to do something equivalent in C, though:
First and foremost, you can just write multiple different functions that do the same, but on different types. For instance, instead of add you could have three functions named add_int, add_char and add_float. I would recommend doing this in most cases, as it is by far the easiest and least error-prone.
Secondly, you might have noticed how printf can print both strings and numbers? So-called variadic functions, like printf, can take different types of arguments, but in the case of printf, you must still specify the type of arguments you want in the format string.
Finally, you can use void pointers if you need to work with the memory an object occupies, regardless of its type. Functions like memcpy and memset do this, for instance to copy the contents of one object directly to another. This is a bit harder to manage properly, as it is easy to make mistakes and end up corrupting memory, but its still doable (and sometimes even the best option).
But if you're a beginner in C, as you state you are, the first option is probably the easiest, especially when dealing with only a few different data types (in this case, three).
You can pass pretty much anything as a void * in a function that will add this content to a linked list by the use of memcpy (or even safe memmove).
As long as you have a pointer to the next node of your list, you don't have to worry about the type of the stored data.
Just be sure not to dereference a void *, but rather to cast it and use this casted variable (as a char if you want to work on this data byte by byte for instance)
Related
The Question:
I think I have figured out a way that, near as I can tell, allows you to write completely type-agnostic code that makes a copy of a variable of arbitrary type on the "stack" (in quotes because C standard does not actually require there to be a stack, so what I really mean is that it's copied with the auto storage class in local scope). Here it is:
/* Save/duplicate thingToCopy */
char copyPtr[sizeof(thingToCopy)];
memcpy(copyPtr, &thingToCopy, sizeof(thingToCopy));
/* modify the thingToCopy variable to do some work; do NOT do operations directly on the data in copyPtr, that's just a "storage bin". */
/* Restore old value of thingToCopy */
memcpy(&thingToCopy, copyPtr, sizeof(thingToCopy));
From my limited testing it works and near as I can tell it should work on all standards-compliant C implementations, but just in case I missed something, I'd like to know:
Is this completely in line with the C standard (I believe this should be good all the way from C89 through to the modern stuff), and if not, is it possible to fix and how?
What limitations on usage does this method force upon itself in order to stay standards-compliant?
For example, as I understand it, I am safe from alignment issues so long as I never use the char-array temp-copies directly - just as bins to save to and load from with memcpy. But I couldn't pass those addresses to other functions expecting pointers to the type I'm working with, without risking alignment issues (obviously syntactically I could do it perversely by first getting a void * from the char *, without even specifying the exact type I'm working with, but the point is that I think I would be triggering undefined behavior by doing so).
Is there is a more clean and/or performant* way to achieve the same thing?
*GCC 4.6.1 on my armel v7 test device, with -O3 optimization, produced identical code to regular code using normal assignments to temporary variables, but it could be that my test cases were just simple enough that it was able to figure it out, and that it would get confused if this technique were used more generally.
As a bonus passing interest, I'm curious if this would break in mostly-C-compatible languages (the ones I know of are C++, Objective-C, D, and maybe C#, though mentions of others are welcome too).
Rationale:
This is why I think the above works, in case you find it helpful to know where I'm coming from in order to explain any mistakes I may have made:
The C standard's "byte" (in the traditional sense of "smallest addressable unit of memory", not in the modernized "8 bits" meaning) is the char type - the sizeof operator produces numbers in units of char. So we can get exactly the smallest size of storage (that we can work with in C) needed for an arbitrary variable's type by using the sizeof operator on that variable.
The C standard guarantees that pretty all pointer types can be converted implicitly into a void * (but with a change of representation if their representation is different (but incidentally, the C standard guarantees that void * and char * have identical representations)).
The "name" of an array of a given type, and a pointer to that same type, can basically be treated identically as far as the syntax is concerned.
The sizeof operator is figured out at compile-time, so we can do char foo[sizeof(bar)] without depending on the effectively non-portable VLAs.
Therefore, we should be able to declare an array of "chars" that is the minimum size necessary to hold a given type.
Thus we should be able to pass the address of the variable to be copied, and name of the array, to memcpy (as I understand it, the array name is implicitly used as a char * to the first element of the array). Since any pointer can be implicitly converted to a void * (with change of representation is necessary), this works.
The memcpy should make a bitwise copy of the variable we are copying to the array. Regardless of what the type is, any padding bits involved, etc, the sizeof guarantees we'll grab all the bits that make up the type, including padding.
Since we can't explicitly use/declare the type of the variable we just copied, and because some architectures might have alignment requirements for various types that this hack might violate some of the time, we can't use this copy directly - we'd have to memcpy it back into the variable we got it from, or one of the same type, in order to make use of it. But once we copy it back, we have an exact copy of what we put there in the first place. Essentially, we are freeing the variable itself to be used as scratch space.
Motivation (or, "Dear God Why!?!"):
I like to write type-independent code when useful, and yet I also enjoy coding in C, and combining the two largely comes down to writing the generic code in function-like macros (you can then re-claim type-checking by making wrapper function definitions which call the function-like macro). Think of it like really crude templates in C.
As I've done this, I've run into situations where I needed an additional variable of scratch space, but, given the lack of a portable typeof() operator, I cannot declare any temporary variables of a matching type in such "generic macro" snippets of code. This is the closest thing to a truly portable solution that I've found.
Since we can do this trick multiple times (large enough char array that we can fit several copies, or several char arrays big enough to fit one), as long as we can keep our memcpy calls and copy pointer names straight, it's functionally like having an arbitrary number of temporary variables of the copied type, while being able to keep the generic code type-agnostic.
P.S. To slightly deflect the likely-inevitable rain of judgement, I'd like to say that I do recognize that this is seriously convoluted, and I would only reserve this in practice for very well-tested library code where it significantly added usefulness, not something I would regularly deploy.
Yes, it works. Yes, it is C89 standard. Yes, it is convoluted.
Minor improvement
A table of bytes char[] can start at any position in memory.
Depending on the content of your thingToCopy, and depending on CPU, this can result in sub-optimal copy performance.
Should speed matter (since it may not if this operation is rare), you may prefer to align your table, using int, long long or size_t units instead.
Major limitation
Your proposition only works if you know the size of thingToCopy.
This is a major issue : that means your compiler needs to know what thingToCopy is at compilation type (hence, it cannot be an incomplete type).
Hence, the following sentence is troubling :
Since we can't explicitly use/declare the type of the variable we just copied
No way. In order to compile char copyPtr[sizeof(thingToCopy)];, the compiler must know what thingToCopy is, hence it must have access to its type !
If you know it, you can simply do :
thingToCopy_t save;
save = thingToCopy;
/* do some stuff with thingToCopy */
thingToCopy = save;
which is clearer to read, and even better from an alignment perspective.
It would be bad to use your code on an object containing a pointer (except const pointer to const). Someone might modify the pointed-to data, or the pointer itself (e.g. realloc). This would leave your copy of the object in an unexpected or even invalid state.
Generic programming is one of the main driving forces behind C++. Others have tried to do generic programming in C using macros and casts. It's OK for small examples, but doesn't scale well. The compiler can't catch bugs for you when you use those techniques.
In case you have int foo(a_type first, ...), where the varargs are all const char * (or some other pointer type) and you can determine their number from first (or by a terminating NULL pointer), can you somehow interpret this structure as an array (const char **) with no need to malloc an actual array and copy the pointers into it? Shouldn't the arguments be properly aligned on the stack like if they where in an array? Would that be portable or can e.g. the order of the arguments be different?
Edit: Just to be clear, I didn't plan to use anything like that in production code. I just was curious.
You might be able to get away with it. For a while.
The problem is that in C you can get away with a lot of things in Undefined Behavior, but that does not make them correct.
One problem with your idea is that C does not require the computer to have anything called a "stack" or to put function arguments on it. They aren't required to be in any particular location or order.
No, it would not be portable. As far as the language standard is concerned, you must use the varargs functions and macros to access the variable arguments, and there is no way around that.
No; there's no guarantee that the variable argument list will be laid out contiguously, so you can't assume that your values will be laid as as though they were in an array. You must use the va_* macros to advance through the list.
I recently developed an interest in C programming so I got myself a book (K&R) and started studying.
Coming from a University course in Java (basics), pointers are a totally new chapter, and from what I read online it's a rather difficult concept to get your head around. Before getting to the pointer chapter I was under the impression that pointers are a major part of C and provide great benefits.
Upon reading the chapter and getting a basic idea of what pointers are and how they work, the benefits are not obvious to me.
For example (please correct me if I got this totally wrong) in the introduction of pointers in the K&R book it says that since we call by value, when passing a variable in a function call we pretty much pass a copy of the variable for the function to handle and therefore the function can't do anything to the original variable and we can overcome this with pointers.
In a later example that uses a char pointer, the book says that incrementing the char pointer is legal since the function has a private copy of the pointer. Aren't 'private copies' a reason to use pointers instead?
I guess I'm a bit confused on the whole use of pointers. If asked I can use pointers instead of using array subscripts for example, but I doubt this is the main use of pointers.
Linux and Open source programming was the main reason I got into C. I got the source code of a C project to study (Geany IDE) and I can see that pointers are used throughout the source code.
I also did a bit of searching in the forums and a found a couple of posts with similar questions. An answer was (I quote):
If you don't know when you should use pointers just don't use them.
It will become apparent when you need to use them, every situation is different.
Is it safe for me to avoid using pointers at the time being and only use them in specific situations (where the need for pointers will be apparent?)
One benefit of pointers is when you use them in function arguments, you don't need to copy large chunks of memory around, and you can also change the state by dereferencing the pointer.
For example, you may have a huge struct MyStruct, and you have a function a().
void a (struct MyStruct* b) {
// You didn't copy the whole `b` around, just passed a pointer.
}
Coming from Java, you'll have a slightly different perspective than what is presented in K&R (K&R doesn't assume that the reader knows any other modern programming language).
A pointer in C is like a slightly more capable version of a reference in Java. You can see this similarity through the Java exception named NullPointerException. One important aspect of pointers in C is that you can change what they point to by increment and decrement.
In C, you can store a bunch of things in memory in an array, and you know that they are sitting side by side each other in memory. If you have a pointer to one of them, you can make that pointer point to the "next" one by incrementing it. For example:
int a[5];
int *p = a; // make p point to a[0]
for (int i = 0; i < 5; i++) {
printf("element %d is %d\n", i, *p);
p++; // make `p` point to the next element
}
The above code uses the pointer p to point to each successive element in the array a in sequence, and prints them out.
(Note: The above code is an expository example only, and you wouldn't usually write a simple loop that way. It would be easier to access the elements of the array as a[i], and not use a pointer there.)
Your highlighted rule is very wise. It will keep you out of trouble but sooner or later you have to learn pointers.
So why do we want to use pointers?
Say I opened a textfile and read it into a giant string. I can't pass you the giant string by value because it's too big to fit on the stack (say 10mb). So I tell you where the string is and say "Go look over there at my string".
An array is a pointer ( well almost ).
int[] and int* are subtly different but interchangeable for the most part.
int[] i = new int[5]; // garbage data
int* j = new int[5] // more garbage data but does the same thing
std::cout << i[3] == i + 3 * sizeof(int); // different syntax for the same thing
A more advanced use of pointers that's extremely useful is the use of function pointers. In C and C++ functions aren't first class data types, but pointers are. So you can pass a pointer to a function that you want called and they can do so.
Hopefully that helps but more than likely will be confusing.
In a later example that uses a char pointer, the book says that incrementing the char pointer is legal since the function has a private copy of the pointer.
I'd say that this means that they are incrementing the pointer itself, which means changing the address (and therefore making it point to a different value). This could be useful if they were passed the first item of an array and want to continue in the array, but not change the values.
Remember that C (and the K&R book) very old, probably older than anything you've learned before (definitely ages older than Java). Pointers aren't an extra feature of C, they're a very basic part of how computers work.
Pointer theory isn't particularly hard to master, just that they're very powerful so an error will most likely crash your application, and compilers have a hard time trying to catch pointer-related errors. One of the big novelties about Java was to have 'almost' as much power as C without pointers.
So, in my opinion trying to write C avoiding pointers is like trying to ride a bike without one pedal. Yes, it's doable but you'll work twice as hard.
Ignore that answer please.
If you don't know how to use pointers, learn how to use them. Simple.
Pointers as you say allow you to pass more than one variable into a function via a pointer to it, as you have rightly observed. Another use of pointers is in referring to arrays of data, which you can step through using pointer arithmetic. Finally, pointers allow you allocate memory dynamically. Therefore advising you don't use pointers is going to severely limit what you can do.
Pointers are C's way to talk about memory addresses. For that reason, they are critical. As you have K&R, read that.
For one example of usage, see my answer to this. As I say in that answer, it isn't necessarily how you'd do it, given the question.
However, that technique is exactly how libraries like MPIR and GMP work. libgmp if you haven't met it powers mathematica, maple etc and is the library for arbitrary precision arithmetic. You'll find mpn_t is a typedef to a pointer; depending on the OS depends on what it points to. You'll also find a lot of pointer arithmetic in the slow, C, versions of this code.
Finally, I mentioned memory management. If you want to allocate an array of something you need malloc and free which deal with pointers to memory spaces; specifically malloc returns a pointer to some memory once allocated or NULL on failure.
One of my favourite uses of pointers yet is to make a C++ class member function act as a thread on windows using the win32 API i.e. have the class contain a thread. Unfortunately CreateThread on windows won't accept C++ class functions for obvious reasons - CreateThread is a C function that doesn't understand class instances; so you need to pass it a static function. Here's the trick:
DWORD WINAPI CLASSNAME::STATICFUNC(PVOID pvParam)
{
return ((CLASSNAME*)pvParam)->ThreadFunc();
}
(PVOID is a void *)
What happens is it returns ThreadFunc which gets executed "instead of" (actually STATICFUNC calls it) STATICFUNC and can indeed access all the private member variables of CLASSNAME. Ingenious, eh?
If that isn't enough to convince you pointers kinda are C, I don't know what is. Or maybe there's no point in C without pointers. Or...
Considering that you're coming from a Java background, here's the simplest way to get your head around what use pointers have.
Let's say you have a class like this in Java:
public class MyClass {
private int myValue;
public int getMyValue() { return myValue; }
public void setMyValue(int value) { myValue = value; }
}
Here's your main function, that creates one of your objects and calls a function on it.
public static void Main(String[] args) {
MyClass myInstance = new MyClass();
myInstance.setMyValue(1);
System.out.printLn(myInstance.getMyValue()); // prints 1
DoSomething(myInstance);
System.out.printLn(myInstance.getMyValue()); // prints 2
}
public static void DoSomething(MyClass instance) {
instance.setMyValue(2);
}
The myInstance variable you declared in Main is a reference. It's basically a handle that the JVM uses to keep tabs on your object instance. Now, let's do the same thing in C.
typedef struct _MyClass
{
int myValue;
} MyClass;
void DoSomething(MyClass *);
int main()
{
MyClass myInstance;
myInstance.myValue = 1;
printf("i", myInstance.myValue); // prints 1
MyClass *myPointer = &myInstance; // creates a pointer to the address of myInstance
DoSomething(myPointer);
printf("i", myInstance.myValue); // prints 2
return 0;
}
void DoSomething(MyClass *instance)
{
instance->myValue = 2;
}
Of course, pointers are much more flexible in C, but this is the gist of how they work in a Java sort of way.
If you are dealing with dynamically allocated memory, you have to use pointers to access the allocated space. Many programs deal with allocated memory, so many programs have to use pointers.
In this example:
int f1(int i);
f1(x);
the parameter i is passed by value, so the function f1 could not change the value of variable x of the caller.
But in this case:
int f2(int* i);
int x;
int* px = &x;
f2(px);
Here we still pass px parameter by value, but in the same time we pass x by refference!. So if the callee (f2) will change its int* i it will have no effect on px in the caller. However, by changing *i the callee will change the value of x in the caller.
Let me explain this more in terms of Java references (as pointed out by #Greg's answer)
In Java, there are reference types (i.e. reference to a class) and value types (i.e. int). Like C, Java is pass by value, only. If you pass a primitive type into a function, you actually pass the value of the value (after all, it is a "value type"), and therefore any modifications to that value inside that function are not reflected in the calling code. If you pass a reference type into a function, you can modify the value of that object, because when you pass the reference type, you pass the reference to that reference type by value.
Pointers make it possible to dynamically dispatch code given conditions or state of the program. A simple way of understanding this concept is to think of a tree structure where each node represents either a function call, variable, or a pointer to a sublevel node. Once you understand this you then use pointers to point to established memory locations that the program can reference at will to understand the intitial state and thus the first dereference and offset. Then each node will contain it's own pointers to further understand the state whereby a further dereference can take place, a function can be called, or a value grabbed.
Of course this is just one way of visualizing how pointers can be used since a pointer is nothing more than an address of a memory location. You can also use pointers for message passing, virtual function calls, garbage collection, etc. In fact I have used them to recreate c++ style virtual function calls. The result is that the c virtual function and c++ virtual functions run at the exact same speed. However the c implementation was much smaller in size (66% less in KB) and slightly more portable. But replicating features from other languages in C will not always be advantageous, obviously b/c other languages could be better equipped to gather information that the compiler can use for optimization of that data structure.
In summary there is a lot that can be done with pointers. The C language and pointers are devalued nowadays b/c most higher level languages come equipped with the more often used data structures / implementations that you would have had to build on your own with the use of pointers. But there are always times when a programmer may need to implement a complex routine and in this case knowing how to use pointers is a very good thing.
Dear all. I was wondering if there are examples of situations where you would purposefully pass an argument by value in C. Let me rephrase. When do you purposefully use C's pass-by-value for large objects? Or, when do you care that the object argument is fully copied in a local variable?
EDIT: Now that I think about it, if you can avoid pointers, then do. Nowadays, "deep" copying is possible for mostly everything in small apps, and shallow copying is more prone to pointer bugs. Maybe.
In C (sans const references), you pass by value for 3 reasons.
You don't want the source to be modified by the receiving function outside of its context. This is (was) the standard reason taught in school as it why to pass by value.
Passing by value is cheaper if the value fits within the architecture's register - or possibly registers if the compiler is very intelligent. Passing by value means no pointer creation and no dereference to get at the value being passed in. A small gain, but it does add up in certain circumstances.
Passing by value takes less typing. A weak reason to be sure, but there it is.
The const keyword negates most of reason 1, but reason 2 still has merit and is the main reason I pass by value.
Well, for one thing, if you want to change it.
Imagine the following contrived function:
int getNextCharAndCount (char *pCh);
Each time you call it, it returns the next most frequent character from a list by returning the count from the function and setting a character by way of the character pointer.
I'm having a hard time finding another use case which would require the pointer if you only ever wanted to use (but not change) the underlying character. That doesn't mean one doesn't exist of course :-)
In addition, I'm not sure what you're discussing is deep/shallow copy. That tends to apply to structures with pointers where a shallow copy just duplicates the top level while a deep copy makes copies of all levels.
What you're referring to is pass-by-value and pass-by-reference.
Passing by-reference is cheaper because you don't have to create a local copy of an object. If the function needs a local copy (for any purpose) - that could be a case.
I follow as a rule:
pass built-in types by value (int, char, double, float...)
pass classes and structs by (const) reference. There is no pointer handling involved whatsoever.
Never had any problems with this way of work.
If we're going to be pedantic about this, everyhing in C is pass-by-value. You may pass a pointer by value instead of passing the actual object by value, but it's still pass-by-value.
Anyway, why pass an entire object instead of a pointer to an object? Well, for one, your compiler may be able to optmize the call such that underneath the covers only an address is copied. Also/Alternatively, once you introduce pointers, your compiler may not be able to do as much optimization of your function because of aliasing. It's also less error prone to not have to remember to dereference. The caller can also be sure that what he passed in is not modified (const doesn't really guarantee this, it can be -dangerously- cast away)
I don't think your argument about chars holds water. Even though your char is conceptually 1 byte, each argument to a function call typically translates to a whole (word-sized) register and to the same amount of space on the stack for efficiency.
You can pass a whole struct on the stack as an argument if you really want to (and, I believe, return them as well). It's a way of avoiding both allocating memory and having to worry about pointer hygiene.
Depending on how the call stack is built the char and char* may take the same amount of space. It is generally better to have values aligned on word boundaries. The cost of accessing a 32 bit pointer on a word boundary may be significantly lower than accessing it on a non-word boundary.
Passing by value is safer if you don't want the value modified. Passing by reference can be dangerous. Consider passing by referennce
CONST int ONE = 1;
increment( *ONE );
print ONE;
Output is 2 if the constant was modified.
I will begin to use C for an Operating Systems course soon and I'm reading up on best practices on using C so that headaches are reduced later on.
This has always been one my first questions regarding arrays as they are easy to screw up.
Is it a common practice out there to bundle an array and its associated variable containing it's length in a struct?
I've never seen it in books and usually they always keep the two separate or use something like sizeof(array[]/array[1]) kind of deal.
But with wrapping the two into a struct, you'd be able to pass the struct both by value and by reference which you can't really do with arrays unless using pointers, in which case you have to again keep track of the array length.
I am beginning to use C so the above could be horribly wrong, I am still a student.
Cheers,
Kai.
Yes this is a great practice to have in C. It's completely logical to wrap related values into a containing structure.
I would go ever further. It would also serve your purpose to never modify these values directly. Instead write functions which act on these values as a pair inside your struct to change length and alter data. That way you can add invariant checks and also make it very easy to test.
Sure, you can do that. Not sure if I'd call it a best practice, but it's certainly a good idea to make C's rather rudimentary arrays a bit more manageable. If you need dynamic arrays, it's almost a requirement to group the various fields needed to do the bookkeeping together.
Sometimes you have two sizes in that case: one current, and one allocated. This is a tradeoff where you trade fewer allocations for some speed, paying with a bit of memory overhead.
Many times arrays are only used locally, and are of static size, which is why the sizeof operator is so handy to determine the number of elements. Your syntax is slightly off with that, by the way, here's how it usually looks:
int array[4711];
int i;
for(i = 0; i < sizeof array / sizeof *array; i++)
{
/* Do stuff with each element. */
}
Remember that sizeof is not a function, the parenthesis are not always needed.
EDIT: One real-world example of a wrapping exactly as that which you describe is the GArray type provided by glib. The user-visible part of the declaration is exactly what you describe:
typedef struct {
gchar *data;
guint len;
} GArray;
Programs are expected to use the provided API to access the array whenever possible, not poke these fields directly.
There are three ways.
For static array (not dynamically allocated and not passed as pointer) size is knows at compile time so you can used sizeof operator, like this: sizeof(array)/sizeof(array[0])
Use terminator (special value for last array element which cannot be used as regular array value), like null-terminated strings
Use separate value, either as a struct member or independent variable. It doesn't really matter because all the standard functions that work with arrays take separate size variable, however joining the array pointer and size into one struct will increase code readability. I suggest to use to have a cleaner interface for your own functions. Please note that if you pass your struct by value, called function will be able to change the array, but not the size variable, so passing struct pointer would be a better option.
For public API I'd go with the array and the size value separated. That's how it is handled in most (if not all) c library I know. How you handle it internally it's completely up to you. So using a structure plus some helper functions/macros that do the tricky parts for you is a good idea. It's always making me head-ache to re-think how to insert an item or to remove one, so it's a good source of problems. Solving that once and generic, helps you getting bugs from the beginning low. A nice implementation for dynamic and generic arrays is kvec.
I'd say it's a good practice. In fact, it's good enough that in C++ they've put it into the standard library and called it vector. Whenever you talk about arrays in a C++ forum, you'll get inundated with responses that say to use vector instead.
I don't see anything wrong with doing that but I think the reason that that is not usually done is because of the overhead incurred by such a structure. Most C is bare-metal code for performance reasons and as such, abstractions are often avoided.
I haven't seen it done in books much either, but I've been doing the same the same thing for a while now. It just seems to make sense to "package" those things together. I find it especially useful if you need to return an allocated array from a method for instance.
If you use static arrays you have access to the size of array using sizeof operator. If you'll put it into struct, you can pass it to function by value, reference and pointer. Passing argument by reference and by pointer is the same on assembly level (I'm almost sure of it).
But if you use dynamic arrays, you don't know the size of array at compile time. So you can store this value in struct, but you will also store only a pointer to array in structure:
struct Foo {
int *myarray;
int size;
};
So you can pass this structure by value, but what you realy do is passing pointer to int (pointer to array) and int (size of array).
In my opinion it won't help you much. The only thing that is in plus, is that you store the size and the array in one place and it is easy to get the size of the array. If you will use a lot of dynamic arrays you can do it this way. But if you will use few arrays, easier will be not to use structures.
I've never seen it done that way, but I haven't done OS level work in over a decade... :-) Seems like a reasonable approach at first glance. Only concern would be to make sure that the size somehow stays accurate... Calculating as needed doesn't have that concern.
considering you can calculate the length of the array (in bytes, that is, not # of elements) and the compiler will replace the sizeof() calls with the actual value (its not a function, calls to it are replaced by the compiler with the value it 'returns'), then the only reason you'd want to wrap it in a struct is for readability.
It isn't a common practice, if you did it, someone looking at your code would assume the length field was some special value, and not just the array size. That's a danger lurking in your proposal, so you'd have to be careful to comment it properly.
I think this part of your question is backwards:
"But with wrapping the two into a struct, you'd be able to pass the struct both by value and by reference which you can't really do with arrays unless using pointers, in which case you have to again keep track of the array length."
Using pointers when passing arrays around is the default behavior; that doesn't buy you anything in terms of passing the entire array by value. If you want to copy the entire array instead of having it decay to a pointer you NEED to wrap the array in a struct. See this answer for more information:
Pass array by value to recursive function possible?
And here is more on the special behavior of arrays:
http://denniskubes.com/2012/08/20/is-c-pass-by-value-or-reference/