14 ms·
How to find size of an array in C without sizeof
- jheriko 10y agothere is a classic mistake here... the idea that pointer arithmetic does not rely on sizeof. that's the entire mystery opened and closed afaik. sure you can use some obscure notation if you like, but why not just use sizeof?
- Etheryte 10y agoGiven how many bugs & errors stem from simple fails in range checks etc, I would much rather go with the tried and true way rather than use something "clever". Quoting http://stackoverflow.com/a/16019052/1470607 http://stackoverflow.com/a/16019052/1470607 Note that this trick will only work in places where `sizeof` would have worked anyway.
- Animats 10y agoYes. This only works for arrays on the stack, at best. It assumes that arrays are placed on the stack in the order of declaration, which is not a requirement of the C standard and may differ between compilers. Unless you're writing a buffer overflow exploit, in which case you need to know exactly what's on the stack and where, this isn't a good way to program. Update: misread the article; thought he was differencing with the beginning of the next array.
- ycmbntrthrwaway 10y ago> It assumes that arrays are placed on the stack in the order of declaration I am not sure it is the case here. The code uses only one array, how can it assume the order of arrays?
- dllthomas 10y agoI don't see how the code assumes anything about the placement of the array. Indeed, it works just fine for static arrays: $ cat test.c #include <stdio.h> int arr[5]; int main(int argc, char *argv[]) { printf("%lu, %ld\n", sizeof(arr) / sizeof(*arr), (&arr)[1] - arr); } $ gcc test.c && ./a.out 5, 5 Not saying it's "a good way to program" - it's needlessly obfuscated compared to the standard sizeof alternative. But it doesn't rely on anything tricky.
- hnfairy 10y agoDespite the argument at the end, this is undefined behavior in the latest C specification. The code dereferences a pointer one past the last element. C11 6.5.6/8: If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated
- dom0 10y agoThe snipper only calculates the pointer, and does not dereference it. Should be fine.
- Buge 10y agoIt's a complicated situation. There's a pointer to an array, and that pointer is dereferenced, resulting in an array (that then decays to a pointer). But that second array/pointer is not dereferenced. I'm not sure if it's legal.
- cbsmith 10y agoWhere is it dereferencing the array? *(&arr + 1) - arr That translates to taking the address one point past the array and subtracting the address of the array from it. It doesn't actually dereference the location past the end of the array. While: (&arr)[1] - arr might appear to be doing something different, it actually isn't.
- Buge 10y ago&arr is a pointer to an array (it points to the existing array). &arr + 1 is a pointer to an array that begins just after the existing array. * is the dereference operator, so it seems to me that *(&arr + 1) dereferences the pointer to the array, resulting in an array (or a reference to an array), which then decays to a pointer.
- cbsmith 10y agoAh, but since it is dereferencing an array... you haven't actually dereferenced to the memory location yet. If you had, you wouldn't be possible to subtract a pointer from it.
- gruez 10y agoHow is this better than the sizeof method? This looks like a clever way to access sizeof information without explicitly using the sizeof operator.
- ycmbntrthrwaway 10y agoThe result you get with this trick is signed, while the result you get with sizeof is unsigned. Edit: Just to clarify, what you get is ptrdiff_t instead of size_t. So if array size is greater than PTRDIFF_MAX, you get undefined behavior [1]. [1] http://en.cppreference.com/w/c/types/ptrdiff_t http://en.cppreference.com/w/c/types/ptrdiff_t
- flamedoge 10y agoHow likely do you run into array bigger than 2gb?
- minipci1321 10y agoProbably not very likely, but keep in mind that this method could also be used without actually allocating the array -- akin to the 'offsetof()' macro. (Which is undefined behavior.)
- dragandj 10y agoToday, with ML, big data and similar applications, that might be often.
- quotemstr 10y agoThe "how likely is it, really?" response to questions of technical correctness has always bothered me. It takes a mindset completely alien to mine to say "Here's a race condition. Sure, it's undefined behavior, but the race is narrow, so it's rare" or to say "Sure, memory allocation can theoretically fail, but in practice almost never does" or to say "fsync is too slow and most computers have batteries these days". Software is unreliable enough as it is due to problems beneath our notice. It seems reckless to avoid fixing problems that we do notice. Sure, you could argue that rare problems are rare and that users probably won't notice them --- this attitude is penny-wise and pound-foolish, because you can't meaningfully reason about a system that's only probably correct.
- mjburgess 10y agoThe problem you're latching on to I think is how the context for caculating a probability can vary. If it were really as likely as, say, the sun exploding that X happened then it would be of no use to expend time on X. BUT very often people speaking about the probability of events given suspicious constraints. While a memory allocation might not fail in most situations it will fail often in some situations. And a one-in-a-million chance is almost guaranteed when there are millions of uses.
- Maro 10y agoI haven't written C in a while, but I think this is pretty stupid. sizeof() is a compile-time thing in C, so it's substituted with a number by the time you get an executable. See: http://stackoverflow.com/questions/671790/how-does-sizeofarray-work http://stackoverflow.com/questions/671790/how-does-sizeofarr... I think this is effectively doing the same thing, but in a non-standard way; ie. I think `int n = (&arr)[1] - arr;` is substituted with the actual the number by the compiler the same way sizeof() would be, only noone will know wtf is going on. Disclaimer: I didn't look at the generated code to confirm; I guess it could even be compiler/runtime dependent.
- dllthomas 10y agoI don't think anyone is proposing that people use this. I read it as an exercise to stretch our understanding of other bits of the language.
- millstone 10y agoI'm surprised at all of the comments calling this stupid or pointless. The point is not that you should this trick in lieu of sizeof; the point is to shed light on a subtly of C arrays.
- btrask 10y agoI suspect this article made a lot of people feel stupid, or in other words, it taught us something. Sometimes the ego gets out of check. I think the article is well-presented and educational.
- grobbles 10y agoI suspect this article made a lot of people feel stupid Anyone who doesn't understand pointer arithmetic in C has no business being involved with C. I'm not trying to be negative about this post, but the notion that people are feeling "stupid" about this is hysterical. HN has absolutely trended toward utterly beginner type C information being some novelty on here. It's a bit bizarre, and the general skill level of the site has catastrophically declined.
- gragas 10y ago>I think this article made a lot of people feel stupid I don't think so. Anyone with a solid understanding of C understands pointer arithmetic. I think the article isn't obvious only to those who have a weak understanding of the language.
- jmspring 10y agoThere are many that have a weak understanding of the language. I got asked some years back why I defaulted to C in some interview questions -- I grew up with the language, understand the nuances and many of the implementations. It's now possible to make your way through a university education in CS without ever touching or understanding C. This is a problem.
- gragas 10y ago
- halayli 10y agothis is undefined behavior. &arr + 1 can overflow. There's no guarantee &arr isn't near memory end boundary. &arr + 1 is converted at compile time to rbp - X where X is an integer determined by the compiler similarly to how sizeof works. Basically ptr + integer requires the compiler to determine the sizeof ptr's type.
- cperciva 10y agothis is undefined behavior. &arr + 1 can overflow No. From 6.5.6 Additive operators: 7 For the purposes of these operators, a pointer to an object that is not an element of an array behaves the same as a pointer to the first element of an array of length one with the type of the object as its element type. 8 [...] if the expression P points to the last element of an array object, the expression (P)+1 points one past the last element of the array object [...] If both the pointer operand and the result point to elements of the same array object, or one past the last element of the array object, the evaluation shall not produce an overflow; otherwise, the behavior is undefined. If the result points one past the last element of the array object, it shall not be used as the operand of a unary * operator that is evaluated. So &arr + 2 can overflow, and &arr + 1 cannot be dereferenced, but &arr + 1 shall not overflow and is not undefined behaviour.
- halayli 10y agoBut arr != &arr even though they have the same value. #8 applies to arr (P), but in the post OP is using &arr which is a ptr to array[x] and doesn't apply to it.
- wruza 10y agoCan't we declare pointer of type &arr, assign it there and be sure that it points to equivalent of array[1] of &arr? If yes, then is it logically possible to have UB on that?
- sirclueless 10y agoYou can define a pointer of type `int (*)[5]` and assign `(&arr)[1]` to it. That's fine, it's a pointer to the 5-element array just after the one we're sure is valid. Dereferencing the pointer is UB, but you can create the pointer, assign it to a variable, etc.
- disposablezero 10y agoMany implementations historically also allocated enough memory to include one extra element at the end of the array.
- tedunangst 10y agoI find this improbable.
- angry_octet 10y agoAgreed, compiler implementors rarely decide to use more memory than is required. There may be a stack canary, but this is between stack allocated variables and control flow structures, not for every array.
- stirner 10y agoThe printf commands say "the address of..." but proceed to print out the value, not address.
- hmottestad 10y agoLooks fine to me. An address is just a number, this one being hex encoded.
- stirner 10y agoOkay. In my experience "the address of x" is taken to be synonymous with "&x", but I suppose that's a pedantic difference.
- hmottestad 10y agoarr is an array. So printing arr[0] would print the contents within the first position of arr, and &arr[0] would print its address. However if you simply print arr, then that's not the contents of the array, so it will print the address. &arr[0] should print the same as arr.
- minipci1321 10y agoFor the completeness sake, the size of an array can also be computed via linker symbols, see for example: http://stackoverflow.com/questions/29901788/finding-the-last-variable-in-attribute-section http://stackoverflow.com/questions/29901788/finding-the-last.... Same constraints apply (pointer arith). I am not sure why this method, applied to ordinary arrays, would be preferred to sizeof (), but since we're shedding light here... EDIT: pointer arith constraints only apply if we compute the difference (end - beg) in the C code. We could also do that in the linker script itself, and I don't recall whether or not C semantics of ptrdiff_t would be preserved in that case. Such preservation doesn't seem very probable to me, so potentially this method might allow to avoid overflows (or to move them much higher) -- to be checked in the 'ld' doc!
- tlb 10y agoDo all linkers guarantee not to round this up to a word size?
- Stratoscope 10y agoWhether you use this method of getting the number of elements in an array or the more traditional sizeof method, please encapsulate the logic in a macro. Instead of writing either of these: size_t length = sizeof array / sizeof array[0]; size_t length = (&array)[1] - array; Define this macro instead: #define countof( array ) ( sizeof(array) / sizeof((array)[0]) ) Or if you must: #define countof( array ) ( (&(array))[1] - (array) ) And then you can just say: size_t length = countof(array); Edit: I used to call this macro 'elementsof', but it seems that 'countof' is a more common name for it and is a bit more clear too - so I'm going to run with that name in the future.
- gpderetta 10y agoI doubt the author meant for this trick to be actually used, they were just showing how pointers to arrays are typed correctly in a clever way.
- Stratoscope 10y agoIndeed, one could hope that is the case! :-) But my point with suggesting the macro applies equally to the more traditional sizeof division. I have seen code that divides the two sizeofs every time an array length is needed. I think it's better to put that calculation in a macro so you only do it in one place.
- markrages 10y agoYou are dividing one constant by another -- surely that would be handled at compile time?
- Stratoscope 10y agoYou are correct, the compiled code will be the same whether you use a macro or not. In fact, this is true for any C macro. A macro is merely a source code text substitution done by the preprocessor. Using a macro is exactly the same as writing out the equivalent macro expansion everywhere you use it. My suggestion to use a macro is not because of any difference in the compiled code, but to improve the readability of the source code.
- pmiller2 10y agoWas anyone else's first thought "Hmm... cool," followed by "I hope nobody asks me this on an interview?"
- exabrial 10y agoIf you are asked this in an interview, it's not longer an interview... I would simply reply "what circumstances would dictate the necessity of such rather than producing clean code for my coworkers?"
- pmiller2 10y agoHence why I hope noone asks me it. :)
- p1esk 10y agoI actually thought: "cool... I hope someone asks me this on an interview!"
- icedchai 10y agoInteresting. I've been working with C for almost 30 years (first taught it to myself when I was 14) and never thought about the actual type of array.
- cbsmith 10y agoWhich kind of explains so much about the problem with C. ;-)
- jjnoakes 10y agoI think it more explains that you can do a lot without fully understanding what it is you are working with. Which can be good or bad.
- cbsmith 10y ago> I think it more explains that you can do a lot without fully understanding what it is you are working with. That's the same thing I'm saying. :-)
- cbsmith 10y agoI think the important irony that perhaps wasn't plainly obvious in my statement is that C is often cited by programmers as a preferred tool (particularly over Java or C++) because they "know exactly what is going on with each line of code". ;-)
- psyc 10y agoYou're not alone. I've been programming in either C or C++ for 25 years, and it wouldn't have occurred to me that you can have a "pointer to array of size N" that includes the size. Though I probably could have been led there with a little Socratic questioning.
- jjnoakes 10y agoIf you start thinking about two dimensional arrays, you'll probably get close quickly.
- Chinjut 10y agoC is such a boondoggle of a language... We're condemned to forever explore its every weird nook and cranny for historical reasons, rather than because it is the cleanest, best approach to things possible.
- minipci1321 10y agoC for sure has its weird sides, but does appear much more logical and consistent when observed "from the below", from how-the-hardware-runs perspective. For example, the shift operators have higher precedence than bitwise masking (and/or/xor) since this way the expressions setting/clearing ranges of bits won't require parentheses (so increased readability) and the masking constants in them will be the narrowest. Loading a wide immediate value into a register sometimes takes several instructions, so such precedence also brings in the least cost as well (nowadays compilers take care of that to some extent). But people frequently mess up this aspect, use lots of parens (and ending up with wide masks) saying this rule is not intuitive. It is.
- armitron 10y agoYou could attempt to rationalize some of its (terrible) design decisions after-the-fact by finding convenient examples, but compared to the clarity and surety of straight-up assembly, C is a dystopian nightmare of enormous unseen complexity and undefined behavior.
- slobdell 10y agoThat pun in the first sentence alone made the article worth it.
- deleted 10y ago[deleted]
- utopcell 10y agonice exposition to c array types. in c++, a compile-time equivalent to sizeof would be: template<typename T, size_t N> size_t sz(T(&)[N]) { return N; }
- russkrayer 10y agoThanks for posting this question. The responses are very interesting.
- arjun024 10y agoAuthor of the article here. There's no intention here to encourage people to use this in code (in fact the opposite). This article is more of a "Did you know cool shit like this exist?".
- mynameisbahaa 10y agoPlease fix your site's header :)
- arjun024 10y agoI'll appreciate if you could provide a screenshot :)
- mynameisbahaa 10y ago(1)- Taken using chrome extension:http://imgur.com/a/PYMRD http://imgur.com/a/PYMRD (2)- Print screen of chrome version 54.0.2840: http://imgur.com/a/dDwK9 http://imgur.com/a/dDwK9 (3)- Print screen of internet explorer version 11 :http://imgur.com/a/uxAv5 http://imgur.com/a/uxAv5 All running on 32bit windows 8.1
- nishs 10y agoAuthor might have fixed it already, but it looks alright to me on Chrome 55.0.2883.95 / macOS 10.12. http://imgur.com/a/fhDr1 http://imgur.com/a/fhDr1
- mynameisbahaa 10y agoI dug deeper and the problem was at my end. The computer I am using has a parental control software installed and configured to block certain websites including twitter which caused the author's site not to load all the needed assets and screwed up the page header. sorry for the inconvenience but I would have been able to figure it out quicker than this if people who down-voted my comment took the time to tell that the site is working fine for them!
- Nimitz14 10y agoWhy do we dereference the array pointer? Wouldn't that give us the value at the address when we just want the address? Also wouldn't the subtraction just give us a number of bytes and thus we'd still need to divide by sizeof(int))?
- clarry 10y agoPointer arithmetic works element-wise, not byte-wise. So if p is a pointer, then p+1 refers to the next element after p, regardless of the size of the pointee. And so (p+1) - p is 1, again regardless of the size of the pointee. In this case, &arr is a pointer to array, and &arr + 1 would point to the next array following the first one. But we wanted to calculate the number of elements in the array, not the fact that we have one array. So we dereference the pointer, thus getting an array type, which in turns "decays" to a pointer to the first element of the array, which has the right type for counting the elements using pointer arithmetic.
- Nimitz14 10y agoThank you.
- angry_octet 10y agoWhile this is as interesting as any c arcana, I truly hope that people are not passing around pointers to arrays and then using sizeof(array)/sizeof(elem) to figure out how big they are, like they are stuck in a first year programming assignment that denies them the use of malloc, so they use C99 VLAs everywhere.
- angeladur 10y agoI would do this only when I am obfuscating code.