Passing an "array" in C
-
Although I use C# most of the time, I try to use C for small exercises -- to keep my C sharp! :~ Anyway... I don't recall having to pass arrays back when I was using C full-time (in the 90s), so I don't know which technique is preferred. As I see it, there are two basic techniques as precedents in C: 0) Passing the length of the array with the array -- e.g.
main ( int argc , char *argv[] )
1) Having a "special value" at the end of the array to indicate the end -- e.g. NULL-terminated strings. So, if I want to pass an array of structs (four ints), which technique is preferred by the C/C++ community? I'm actually considering door number... 2) Using a linked list instead. It just seems cleaner, though memory usage is actually increased by 25% in this case. Again, this is just an exercise, but practicing poor technique can be worse than not practicing at all. So, what say you? If you have to pick door 0 or door 1, which do you prefer? -
Although I use C# most of the time, I try to use C for small exercises -- to keep my C sharp! :~ Anyway... I don't recall having to pass arrays back when I was using C full-time (in the 90s), so I don't know which technique is preferred. As I see it, there are two basic techniques as precedents in C: 0) Passing the length of the array with the array -- e.g.
main ( int argc , char *argv[] )
1) Having a "special value" at the end of the array to indicate the end -- e.g. NULL-terminated strings. So, if I want to pass an array of structs (four ints), which technique is preferred by the C/C++ community? I'm actually considering door number... 2) Using a linked list instead. It just seems cleaner, though memory usage is actually increased by 25% in this case. Again, this is just an exercise, but practicing poor technique can be worse than not practicing at all. So, what say you? If you have to pick door 0 or door 1, which do you prefer?Which method you use depends on what your data looks like. If you have an array of int, then there may not be a special value that can be the sentinel for the end of the array. On the other hand if you have an array of pointers to something, then maybe a NULL pointer is a good choice as a sentinel. Not always, though. If you had an sparse array of pointers, it would be conventional for the empty elements to be NULL, so you could not use that as the sentinel. Advantages to passing the length are that you can work your way forwards or backwards through the array without having to count how many first, and you know the bounds, so you shouldn't have any Undefined Behaviour from trying to access outside the array.
-
Which method you use depends on what your data looks like. If you have an array of int, then there may not be a special value that can be the sentinel for the end of the array. On the other hand if you have an array of pointers to something, then maybe a NULL pointer is a good choice as a sentinel. Not always, though. If you had an sparse array of pointers, it would be conventional for the empty elements to be NULL, so you could not use that as the sentinel. Advantages to passing the length are that you can work your way forwards or backwards through the array without having to count how many first, and you know the bounds, so you shouldn't have any Undefined Behaviour from trying to access outside the array.
Thanks. With these particular structs, there is a field in which a 0 (or anything less than 1) could easily be used to indicate that the item is the terminator. And in this particular exercise, I need to visit each item once per call, so knowing the length would only keep me from going off the end, not aid in navigation. I'll give door 1 a try. I think that will require only that I allocate one more item and make a small change to the
for
loop I would otherwise use. -
Although I use C# most of the time, I try to use C for small exercises -- to keep my C sharp! :~ Anyway... I don't recall having to pass arrays back when I was using C full-time (in the 90s), so I don't know which technique is preferred. As I see it, there are two basic techniques as precedents in C: 0) Passing the length of the array with the array -- e.g.
main ( int argc , char *argv[] )
1) Having a "special value" at the end of the array to indicate the end -- e.g. NULL-terminated strings. So, if I want to pass an array of structs (four ints), which technique is preferred by the C/C++ community? I'm actually considering door number... 2) Using a linked list instead. It just seems cleaner, though memory usage is actually increased by 25% in this case. Again, this is just an exercise, but practicing poor technique can be worse than not practicing at all. So, what say you? If you have to pick door 0 or door 1, which do you prefer?you can always do a 'container':
typedef struct intArray_t
{
int *pData;
int len;
} intArray;intArray newIntArray(int len)
{
intArray ia;
ia.pData = (int*)malloc(len * sizeof(int));
ia.len = len;
return ia;
}intArray myData = newIntArray(10);
etc. then you pass intArrays around
-
you can always do a 'container':
typedef struct intArray_t
{
int *pData;
int len;
} intArray;intArray newIntArray(int len)
{
intArray ia;
ia.pData = (int*)malloc(len * sizeof(int));
ia.len = len;
return ia;
}intArray myData = newIntArray(10);
etc. then you pass intArrays around
Oh, of course. :doh:
-
you can always do a 'container':
typedef struct intArray_t
{
int *pData;
int len;
} intArray;intArray newIntArray(int len)
{
intArray ia;
ia.pData = (int*)malloc(len * sizeof(int));
ia.len = len;
return ia;
}intArray myData = newIntArray(10);
etc. then you pass intArrays around
And now I've learned that the version of MinGW GCC I have will allow
typeof
and the declaration of variables infor
statements (for ( int i ...
) provided I invoke-std=gnu99
. :jig: What a fun exercise for a rainy Friday afternoon. -
Although I use C# most of the time, I try to use C for small exercises -- to keep my C sharp! :~ Anyway... I don't recall having to pass arrays back when I was using C full-time (in the 90s), so I don't know which technique is preferred. As I see it, there are two basic techniques as precedents in C: 0) Passing the length of the array with the array -- e.g.
main ( int argc , char *argv[] )
1) Having a "special value" at the end of the array to indicate the end -- e.g. NULL-terminated strings. So, if I want to pass an array of structs (four ints), which technique is preferred by the C/C++ community? I'm actually considering door number... 2) Using a linked list instead. It just seems cleaner, though memory usage is actually increased by 25% in this case. Again, this is just an exercise, but practicing poor technique can be worse than not practicing at all. So, what say you? If you have to pick door 0 or door 1, which do you prefer?Method
0
is the one I observe most of the time (and use myself more frequently). Even Windows API use it. Sentinel method has its usages (k5054 already pointed out its drawbacks). I won't use method2
, I mean I won't choose a linked list instead of an array just in order to avoid the extra parameter in my function. -
Method
0
is the one I observe most of the time (and use myself more frequently). Even Windows API use it. Sentinel method has its usages (k5054 already pointed out its drawbacks). I won't use method2
, I mean I won't choose a linked list instead of an array just in order to avoid the extra parameter in my function.Thanks. The sentinel method worked well in this case, but then Chris Losinger suggested method 3 -- using a container*. So I'm using that now. This technique has the added benefit that I can store the number of items allocated as well as the number of items in use -- sort of like how a List works in .net. I can add items up to the limit (I have no need to expand the array; I know how many items I need up front). * Didn't we used to call that a Control Block? A simple way to avoid having bullions and bullions of function parameters?
-
Thanks. The sentinel method worked well in this case, but then Chris Losinger suggested method 3 -- using a container*. So I'm using that now. This technique has the added benefit that I can store the number of items allocated as well as the number of items in use -- sort of like how a List works in .net. I can add items up to the limit (I have no need to expand the array; I know how many items I need up front). * Didn't we used to call that a Control Block? A simple way to avoid having bullions and bullions of function parameters?
Quote:
Didn't we used to call that a Control Block? A simple way to avoid having bullions and bullions of function parameters?
Of course I am aware of the general technique (pass a struct instead of tons of parameters, again Windows API docet), however it is the very first time I hear the term 'Control Block' used with such a meaning. By the way, you are welcome.
-
Quote:
Didn't we used to call that a Control Block? A simple way to avoid having bullions and bullions of function parameters?
Of course I am aware of the general technique (pass a struct instead of tons of parameters, again Windows API docet), however it is the very first time I hear the term 'Control Block' used with such a meaning. By the way, you are welcome.
PIEBALD whips out his trusty "MS-DOS Programmer's Reference" (1993, "covers through version 6"!)... and it just says "structure", e.g.
RWBLOCK structure
. And VMS uses "descriptors" which are similar. " The Descriptor Classic C programming uses pointers to various structures, including null-terminated strings; ASCIZ strings. These are used within the OpenVMS standard C library, though most OpenVMS interfaces use descriptors. An OpenVMS construct that will be entirely new to even experienced C programmers is the string descriptor. This is typically a small data structure, containing the data length, data type, descriptor class, and data address for a chunk of data. " -- http://labs.hoffmanlabs.com/node/273[^] But I'm sure we used the term "Control Block" where I worked. Maybe it's from the UNIX culture? :shrug: -
PIEBALD whips out his trusty "MS-DOS Programmer's Reference" (1993, "covers through version 6"!)... and it just says "structure", e.g.
RWBLOCK structure
. And VMS uses "descriptors" which are similar. " The Descriptor Classic C programming uses pointers to various structures, including null-terminated strings; ASCIZ strings. These are used within the OpenVMS standard C library, though most OpenVMS interfaces use descriptors. An OpenVMS construct that will be entirely new to even experienced C programmers is the string descriptor. This is typically a small data structure, containing the data length, data type, descriptor class, and data address for a chunk of data. " -- http://labs.hoffmanlabs.com/node/273[^] But I'm sure we used the term "Control Block" where I worked. Maybe it's from the UNIX culture? :shrug: -
you can always do a 'container':
typedef struct intArray_t
{
int *pData;
int len;
} intArray;intArray newIntArray(int len)
{
intArray ia;
ia.pData = (int*)malloc(len * sizeof(int));
ia.len = len;
return ia;
}intArray myData = newIntArray(10);
etc. then you pass intArrays around
Wait a minute... isn't that code example missing some indirection?
-
Wait a minute... isn't that code example missing some indirection?
it's possible. where?
-
it's possible. where?
Or maybe that's not C? Can you return a local variable like that in C? Even returning a pointer to a local variable is verboten isn't it? Or is my C-fu still that rusty?
-
Or maybe that's not C? Can you return a local variable like that in C? Even returning a pointer to a local variable is verboten isn't it? Or is my C-fu still that rusty?
sure, you can return locals - even if they're structs. a pointer to a local would be a bad idea, because the local goes out of scope. but when you return a local, it makes a copy for the caller.
-
sure, you can return locals - even if they're structs. a pointer to a local would be a bad idea, because the local goes out of scope. but when you return a local, it makes a copy for the caller.
Huh. I guess I thought C was dumber than that, one semester of C wasn't enough. That explains that small memory leak I wrote in 1994... I wonder whether or not that program is still in use... :~
-
Which method you use depends on what your data looks like. If you have an array of int, then there may not be a special value that can be the sentinel for the end of the array. On the other hand if you have an array of pointers to something, then maybe a NULL pointer is a good choice as a sentinel. Not always, though. If you had an sparse array of pointers, it would be conventional for the empty elements to be NULL, so you could not use that as the sentinel. Advantages to passing the length are that you can work your way forwards or backwards through the array without having to count how many first, and you know the bounds, so you shouldn't have any Undefined Behaviour from trying to access outside the array.
Unfortunately in C language there has not any concept of bounds checking.If we are inserting an array elements which exceeds size of an array automatically it is treated as garbage value. Example: #include #include main() { int a[10]; a[3]=4; a[11]=3;//does not give segmentation fault a[25]=4;//does not give segmentation fault a[20000]=3; //gives segmentation fault getch(); }
-
sure, you can return locals - even if they're structs. a pointer to a local would be a bad idea, because the local goes out of scope. but when you return a local, it makes a copy for the caller.
I pulled out my old VAX C book for reference, and all I see is passing structs in as parameters, but it seems to say it's limited to 1020 bytes. If that's true, it must apply to return values as well -- they're still on the stack, yes?
-
I pulled out my old VAX C book for reference, and all I see is passing structs in as parameters, but it seems to say it's limited to 1020 bytes. If that's true, it must apply to return values as well -- they're still on the stack, yes?
that's probably the VMS stack size. i believe the struct returning behavior is compiler dependent (OS dependent?). some will return the struct in a register, if the struct is small enough, or on the stack. some compilers are smart enough to use the caller's struct directly so as to avoid making a copy. that struct i showed is sizeof(int *) + sizeof(int). so, 8 or 12 bytes. nothing to worry about.
-
I pulled out my old VAX C book for reference, and all I see is passing structs in as parameters, but it seems to say it's limited to 1020 bytes. If that's true, it must apply to return values as well -- they're still on the stack, yes?
I believe that's implementation defined. With my linux box I can use ulimit to set the size of the stack. By default it seems to be 8MB, but I can modify that upwards more or less as needed (obviously within memory limits of the system). I seem to recall that early C implementations were limited to returning basic types (e.g. int, double, char *, etc). gcc-5.2 still has a warning flag for aggregate returns - which suggests that other C compilers might still adhere to that.