Everything You Thought You Knew About Arrays is Wrong (in C Programming). This is Why.
ฝัง
- เผยแพร่เมื่อ 9 ก.ย. 2024
- An array is a list with a fixed length and type. An array is a special type of pointer variable. An array decays to a pointer when it’s passed to a function.
People say these things all the time. But is any of it true?
The fact of the matter is that in C, arrays don’t really exist. What you call an array is just a memory address and when you “index” into it, you are really just doing pointer arithmetic.
In this video, I dive down deeply to look at arrays at a low level to show both the way arrays are handled in computer memory and how your array definitions are translated into assembly language. By the end of this video, you may think about C arrays in a very different way!
If you haven’t already done so, I recommend that you watch my other video on arrays before watching this one:
• The ONE Thing Most C P...
MORE LESSONS
=============
To watch other videos about “low level programming”, go to the “Down To The Metal” playlist:
• Down To The Metal
Also, my series on “Programming C in Visual Studio”:
• Program C in Visual St...
Be sure to subscribe to the Code With Huw channel so that you get a notification when I upload more lessons in this series.
PROGRAMMING BOOKS
====================
If you want to learn programming in more depth (and also support this channel!) you might think of buying one of my books. I have written books on C programming, Using Pointers in C, Recursion and other programming topics. You can buy my books (paperback or Kindle) on Amazon.
** The Little Book Of Pointers **
Amazon (US) amzn.to/2LF2aVb
Amazon (UK) amzn.to/2FViSvS
** The Little Book Of C Programming **
Amazon (US) amzn.to/2RXwA6a
Amazon (UK) amzn.to/2JhlwOA
“CODE WITH HUW” ON TWITTER:
=================================
/ codewithhuw
“CODE WITH HUW” ON FACEBOOK:
=================================
/ codewithhuw
Good luck! And good programming!
"C isn't some low level Java, it's a high level assembly" is such a great way of putting it, really been enjoying your videos
Many thanks!
You know what's funny? When you hear it explained like this, you start wondering what the problem was in the first place. Thanks for letting us rediscover the simplicity that somehow got lost.
I think it's the simplicity that many people (accustomed to more complicated languages) find the hardest to thing to understand.
Many thanks for the comment.
Huw
The problem in the first place is the misconception that, in C, arrays are pointers.
Unfortunately, Huw decided to perpetuate this misconception (but using the term "address" instead of pointer).
An array in C is an object that represents a fixed number of consecutive objects of a certain type.
For example, int a[10] declares a to be an array object of 10 ints.
This means that "sizeof a" will evaluate to 10 * sizeof int = 40 (assuming ints are 32 bit).
However, when you use a in an expression, the name "a" is automatically converted to a pointer to the first element of the array.
But if you take the "address" of a, i.e. &a, you end up with a pointer to the array itself, i.e. a pointer to an object of the array type.
In other words:
#include
int y[] = { 0,1,2,3,4,5 };
int main()
{
printf("%d
", sizeof y);
printf("%d
", (char *)(y+1) - (char *)y);
printf("%d
", (char *)(&y+1) - (char *)&y);
return 0;
}
If you run this, you get:
24
4
24
You can´t explain this with just "arrays are pointers" or (even worse, because discarding the type info) "arrays are addresses".
@@ronald3836bro I'm pretty sure that C doesn't have objects... that's CPP.
@@omegawii Bro, I am pretty sure that the C standard knows better than you. You can look it up for yourself.
C standard, 6.2.5:
- An array type describes a contiguously allocated nonempty set of objects with a particular member object type, called the element type. The element type shall be complete whenever the array type is specified. Array types are characterized by their element type and by the number
of elements in the array. An array type is said to be derived from its element type, and if its element type is T, the array type is sometimes called “array of T”. The construction of an array type from an element type is called “array type derivation”.
C standard, 6.3.2.1:
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. If the array object has register storage class, the behavior is undefined.
@@omegawii All the languages have objects, but only some languages are oriented around them. Come to think of it OOP is a pretty misleading name for itself.
This video seems like a reasonable explanation of how arrays work in 'C', but this information cannot possibly be new or unexpected to any competent C programmer. If we imagine learning C as a 3 quarters college class, C101, C102, and C103, anyone earning a passing grade in C101 would understand how arrays work and not be confused by them; they are, after all, incredibly simple. It might have been better to title the video something like, "C Arrays for Beginners", or "How Arrays work in C".
If you look at the comment thread to my last video, you'll find that there is in fact a great deal of confusion. I was first alerted to this when reading some quite well-known C books which gave incorrect information on this. th-cam.com/video/H18yIPSsgLg/w-d-xo.htmlsi=gvSHNxuca_FhAp_7
@@LearnWithHuw First, thank you for taking the time to respond. Second, I have confirmed your assertion that this is a cause of confusion by seeing the volume of questions that turn up in my search results when I look for variations of "in C language is an array a pointer." I mistakenly assumed that everyone had iterated through an array using pointer arithmetic at some point, in which case the syntax you use makes it clear what's going on. Third, I watched the video you pointed me to, and I don't agree with that last part of your analysis (or better, the last part of your analysis as I understand it); specifically, your discussion of the quote that you display from K&R 1st edition vs. the quote from the 2nd edition. The first edition discusses what a REFERENCE to an array is; the second explains what the VALUE of an array is. A reference is, literally by definition, just another name for an existing object. It must be assigned when the reference variable is declared and cannot be assigned to null. It isn't a variable, and can't be set to a new value. I believe that references are implemented under the covers as immutable pointers (at least conceptually), but I wouldn't want to bet a kidney on it. But in any event, all the quote from the 1st edition is saying is that if you declare a new variable using the & and assign it to an array, that variable now stores the address of array[0]. The statement doesn't actually say or imply anything about what the value of an array is, or what data type an array is. I think the statement is straightforward and true, but I also think it would have been better to have both quotes in the same edition, since they describe quite different things.
In my experience, a full 80% of graduating seniors (BA CS) mixed up Reference and Value! This, despite the compiler giving clear warnings! Teachers and TAs don't care then, and I don't believe they care now.
Even fewer people realize that they can reference values in an array using an index variable, instead of actual pointer.
@@LearnWithHuw A minute and a half into the video I am now a new subscriber. As someone who's 1st programming language was BASIC, yes the original BASIC w/line numbers, then ForTran, COBAL, Pascal, FORTH, 6502 machine language & Assembly language this is most accurate description of C arrays. I took the plunge into after acquired the above under my belt. I've always cringed when C programmers would say C is a low-level language when in fact it's more of mid-level language.
@@TheWallReports Many thanks. Well, yes, I started with BASIC too (GW-BASIC). Awful. But an experience that every programmer should go through in order to appreciate other languages! 🙂
Ignore the pests. Everything you said is true, and anyone who didn't know these things was helped. Those who knew them already were not injured.
Ha! Thank you. That's made my day!
One can never the review the fundamentals enough. There is always something to learn or reinforce in a good presentation on subject. Holds true in Math , Physics and Programming. It maybe more relevant today as students are often studying the concepts of CS in languages like Java or Python and are not being drilled in the C language anymore. Valuable that this reference material is out there for those who seek it out.
In your view, does C have array types different from pointer types?
If yes, then arrays are not pointers!
Of course the answer is yes. Array types are not pointer types.
(Huw's initial video on this set out to show that arrays are not pointers, but addresses. Of course a pointer is really just an address (with type info).)
I studied and knew this since I studied assembly, but the way you represent is so captivating. I still gave the look into it.
That's very kind of you. Many thanks!
that "Danger"ous part reminded me of the days of C64 programming, where you would just poke data at memory addresses
These days, that sounds like the crazy world of microcontrollers! 🙂
Still what we're doing really, just with a seemingly infinite number of layers, abstractions, api's and obfuscations between us and the actual memory we need to read/write to. Man I hate "modern" programming!
When I still lacked a real grasp of how my C64 worked, I was playing around with "bouncing ball" programs in BASIC. Somehow my program tended to get corrupted all by itself, and I thought my C64 might be broken. But instead of returning my C64 to the shop I managed to figure out what was happening: the ball ended up offscreen due to an off-by-one error. The screen memory ran from 1024 to 2023, and the BASIC program started from memory location 2049. So poking the ball slightly south of the screen meant overwriting and thus corrupting the program.
Suddenly I understood how computers worked 🙂
I did look into this because I remember someone mentioning sizeof() and trying to make it out like arrays in C were objects of some sort, etc. And I remembered sizeof() being evaluated at compile time where it replaces the statement 'sizeof(y)' with a simple constant and that number is what is used rather than any sort of special metadata being referenced during runtime. Someone mentioned while I was looking this up that it remains true for regular C arrays but in C99 it changes for Variable Length Arrays (VLAs) as they are calculated during runtime instead which is of course, a different case entirely.
I'll note that down as a topic for a future video! 🙂 Thanks for the comment. Huw
@@LearnWithHuw In those ancient times as well as today, integers are not of the same length across the platforms. sizeof() helped and still helps navigating these vagaries.
Covering sizeof was good. I learn C back in 80s and moved on before C99 was widely adopted, and the compile time is way I learned it. It is good to review these things we take for granted as the appear the same, but not quite in later iterations'. Good video of the foundations of C .
@@johnkuehler2608 Many thanks.
Arrays in C certainly are objects of some sort (objects of the array type to be precise). If you don't believe it, look it up in the C standard.
Of couse sizeof is evaluated at compile time since C is statically typed. That does not mean that array types are not a thing in C, as Huw is somehow trying to argue (the logic escapes me). They certainly are. Otherwise, you could not define 2-dimensional arrays (which are really arrays of arrays -- can't do that with pointers).
I think my comment on this from the previous video was correct that I view an array as a pointer...which is just an address but I have to admit, I never thought that we could outright go to an address and write to it with the syntax you provided as if an array. Knowing that the array element syntax is just a compiler level syntax sugar, it makes sense thought. That being said you casted the address to an `int`, I suppose you could had casted it to a `short` or `char`, except that would write to a different part of that memory?
The `sizeof()` explanation was helpful, it cleared in my mind why an array created with `malloc()`(given the explanation that an array is just a pointer/address) will have a `sizeof()` return the size of the pointer, while a stack created array will have it's `sizeof()` return the size of the array. It's just compiler knowing in advance the size of the stack allocated one.
I am proud of myself for knowing this already. 😊
I was totally expecting you to say that 0x41a020 is an array of three 8 bit integers (four if you count the insignificant top byte) from a certain point of view.
Good video!
Many thanks.
Like these videos that go back to the fundamentals.
Many thanks.
This is so simple now and entertaining after reading your books and doing some practice! 💙
That's very kind. Many thanks.
Huw
Arrays in C are not addresses. As I pointed out on your previous video on the topic the C standard (which defines the programming language C, unlike the msvc which is just one of many implementations of it) an array is:
An array type describes a contiguously allocated nonempty set of objects with a
particular member object type, called the element type.36) Array types are
characterized by their element type and by the number of elements in the array. An
array type is said to be derived from its element type, and if its element type is T , the
array type is sometimes called ‘‘array of T ’’. The construction of an array type from
an element type is called ‘‘array type derivation’’.
I also showed that in some cases, some C compilers (such as gcc and clang) will put the array in a single vector register such as xmm0, in which case they won't have an address at all.
With your logic, everything that resides in memory is an address. Would you say a struct is an address, would you say that an int is an address?
Saying that an array is an address is analogous to saying that a house is its street address. Just imagine if someone dugg that house up and moved it to somewhere else, would the house have the same street address. No, but the house would still be the same.
To be perfectly clear, I do not say that an array is a pointer (you seemed to think that last time).
I'm aware of the C standard. It's very good for language lawyers and people who like to implement standards. But the section you quote is perfectly consistent with the statement 'an array is an address' - the two are not mutually exclusive.
Now, I can treat what is at the address in any way I want. Nothing stops me - I can cast to int*, char* or whatever. I can index outside the bounds of the array as I like. So does the array address or memory at the address change when I cast it? No, of course not. The thing that doesn't change is the address. That is the primary and enduring aspect of an array. I can change the stride and index at will - and apart from the risk of a machine exception - it works.
The section of the standard that you quote tries - and generally suceeds - in attempting to make the concept of an array precise so that a compiler implementer (e.g. me) has a good idea of what we're doing. It does not negate the concept of an array being an address - at least to my rather empirical mindset.
Concerning your point on 'xmm0': this is really a point concerning optimization. A good optimizer - and they are very good these days - can totally re-write your code so that it is unrecognisable and completely eliminate variables and even arrays. Huw is showing unoptimized code that can be understood using the debugger - not highly optimized production code.
@@Forrester-i4d and @tordjarv3802 Many thanks for that interesting discussion. It's given me some ideas for more videos on the nitty-gritty details both of casting and the effects of optimization!
Best wishes
Huw
@@Forrester-i4d even if we ignore the optimization situation, I still think that it is wrong to say that an array is an address. It has an address, but it is not an address. That the compiler uses the address to refer to the array is irrelevant, it is no different from referring to a particular house by using its street address, but the house is not the address. The address is a way to tell the CPU where the array is, not the array it self. In fact in the unoptimized case, every kind of data has an address, but would you say that a single precision floating point number is an address? For me it feels like the worlds "is" and "has" are confused in the video and that the 'thing' is confused with the reference to the thing. It is sort of like saying that the animal "cat" is literally the same thing as the letters 'c', 'a', and 't'. One is a living breathing organism and the other is a string of symbols. Anyway, I feel like I don't really seem to get the point across so I will give it a rest.
@@tordjarv3802 I enjoyed reading this, thanks for your input. You did get your point across. Roughly "Everything usually has an adress but that doesn't mean it is just an adress". But in real world use when you pass an array to a function it doesn't make a copy of it, it is not passed by value, it passes as pointer, so practically it is treated as a pointer that happens to point to an array. This fact is further pushed by the fact that you can't return an array, at best you'll return the pointer to an array. So especially for beginners (target audience of this video) it is rather helpful to say they're pointers because they'll have to treat it as such almost always anyway.
Thanks for this video.
It's fun (and a little nostalgic) to see that your coding style is pretty much how I learned C way back when.
Is it not permissible to combine variavble definition and initialization? Or are you still adhering to the old Pascal rule that
variable definitions must go in the top of the procedure, before any use of them?
I mean, you could have initialized the arraysize variable at the point of definition, but chose not to.
I am probably rather Wirthian in my programming style! 🙂
thank you for this! a few questions from someone who has never read k&r
1. how does C know to use the same memory address every time you declare the y array?
2. I thought that this was common across languages, that when you got down to where data was stored. it was either a linked set of addresses or a sequential defined area.
1. I explain this in the video somewhere. In Visual Studio, there is a linker option that causes the same addresses to be used. This is not the default and must be explicitly set.
2. In an array (in C), the elements are stored at sequential addresses. This is likely to be true in most other languages too - though it may not be obvious since higher level languages may have all kinds of constructs to hide the storage details from the programmer - that is, by interfacing between the code and the data using high-level objects, methods, properties etc. In C, an array is not a high-level construct. It's just an address (followed by sequential addresses) at which one or more data items can be stored.
@@LearnWithHuw thanks!
Answers to Huw's rhetorical questions:
o _An array is a list with a fixed length and type_ - True.
o _An array is a special type of pointer variable_ - False.
o _An array decays to a pointer when it’s passed to a function_ - True (and any other situation where an array is used where an RValue is required).
o _The fact of the matter is that in C, arrays don’t really exist_ - False.
Array *_types_* exist. You can see that by doing a typedef. Eg:
*typedef int A5[5]; // Type 'A5' is the type 'an array of 5 int's'.*
Array *_variables_* exist and can be defined: Eg:
*A5 a; // 'a' is a variable of type 'A5'.*
You can see that 'a' is a variable (LValue) because you can apply the '&' operator (which can only be applied to LValues) to it. Eg:
*A5** *p; // 'p' is a pointer to a variable of type 'A5'.*
*p = &a; // Apply the '&' operator to the array variable (LValue) 'a'.*
Note that 'p' points to a *whole array,* not an element of it, and so the results of pointer arithmetic on 'p' are undefined.
Thanks for sharing such high caliber knowledge! I struggled to understand the C language coming from the R/Python data science world. After randomly deciding to learn some assembly language (which was a lot easier than I thought it would be) most high-level programming language concepts are some how easier to understand/appreciate...especially C. Even though I don't use C for work it is my favorite programming language now.
Thank you. The power of C can be addictive. But it can be a challenge to learn if you are moving from some more "programmer-friendly" language.
Best wishes
Huw
I always pass arrays into functions as : &( array_name[ 0 ] ) because I think the difference between an array and pointer is just extra complexity there to screw me up when I decide to use sizeof and dont account for pointer decay .
With C if you ever wonder if a feature is some high level bit of functionality or just syntactic sugar around grungy pointer manipulation the later is likely correct. So dig into it like in this video and see how it works. It's been ages since I've coded in C/C++ but I still remember having to play some pointer tricks now and then.
Ha! Yes. C is certainly not a language for anyone who's afraid of pointers!
Thanks for the comment.
Huw
Thanks a lot --- as often, understanding what's really going on makes things much easier.
Many thanks. I'm glad it helped.
Woohoo, received my two little books today: Pointers and C programming. Excuse me, I’ll start devouring these books 📚
Take them slowly. One bite at a time! 😁 I hope you have fun!
Everytime somebody says, "we store a number in 4 Byte" I always hear in my head: 4 bit, which seems a reasonable small amount. Then I realize it's 4 Byte which always seems to me to be a massive amount of space to store an itty-bitty number
Love the video
I'm old enough to remember when ever single K counted. I think the first Turbo Pascal compiler and editor I used in the early 80s was a 35K executable, leaving lots of free space for my programs on a single 360K floppy disk. It sounds impossible now, but that's how it was back then.
Thanks for the comment.
Huw
I just checked. Turbo Pascal 3 was a .com file which (including the compiler and editor) came to a massive 39K. Every single bit must have been put to good use in that!
Thank you for the clear discussion of C array implementation. C is so powerful, but is unforgiving on letting you do some very stupid things. I subscribed and look forward to more of your videos.
Yes. There were a few comments on one of my videos in which people asked how C "protects" you from certain catastrophic errors. The answer is: it doesn't. You are on your own!
Thanks for your comment. Much appreciated.
Huw
I think people are confusing C with CPP.
Thank you for this interesing episode.
"An C array is just an address."
I cannot completely affirm this statement. As you later explain, the compiler "knows" some more properies of the C array at compile time. Beyond its start address the compiler particularly knows
- the data type which the array holds
- the amount of allocated memory for that array
(and indirectly by both information the number of elements which it can store without overwriting foreign memory).
If a C array was only an address, I could ask the compiler to set the third element of any array to 17. But as you showed in your example, this did not work. The compiler needs to calculate the offset relative to the start address by multiplying the index with the size of a single element. Also, it knows how many bytes must be affected by the assignment (this could be 4 bytes for an int or 8 bytes for a double on a certain platform, or 20 bytes for an array of a struct.) This information is not present if you have a raw address.
In your example you gave the compiler this extra information by reinterpreting this addess as an pointer to a certain type. By using this cast operator, it becomes your responsibility that it is either the same type you allocated the memory for or that at least the alignment rules match, compare the (strict) aliasing rules. This type punning may work on a certain CPU, but may fail on another.
So, the concept of a C array is more than its (start) address.
The funny thing that the intiial video tries to address the common misconception that arrays in C are just pointers. But he effectively ends up arguing that arrays are pointers ( = addresses). So he falls for the misconception.
@@ronald3836 He's rather clear on that, actually. Pointers aren't addresses. Pointers contain addresses. The address is the value.
@@jeffspaulding9834 No, pointers are addresses (plus type info). A pointer variable contains a pointer. The malloc() function returns a pointer.
Of course the term "pointer" is often used sloppily (even in K&R, as I noticed, but K&R also states that malloc() returns a pointer).
If "ptr" is a pointer variable, then "ptr" when used in an expression evaluates to the value of the variable, i.e. to the pointer it contains.
Anyway, an array is neither a pointer variable nor a pointer/address.
@@jeffspaulding9834 I believe the misconception that indeed does exist is that arrays "are" pointers (i.e. pointer values/addresses).
Huw seems to have become aware of the existence of a misconception but misunderstood this by confusing pointers with pointer variables. So he made a video to explain that an array is not a pointer variable.
But in that video he ends up arguing that an array is a pointer (i.e. address/pointer value). In other words, he fell for the very misconception he wanted to address (but misunderstood).
But even if the above reconstruction of mine is wrong: arrays in C are not addresses/pointer values. C has array types, which are definitely different from pointer types. So an array value is not a pointer value. However, an expression in C that "seems" to contain an array value in most cases evaluates to a pointer to the first element of the array (rather than to the value of the array, i.e. its contents).
Very informative
On the _Little Book of Pointers_ page 44 (ReturnStrings), I added the format string %s an argument to both printf calls; I got warning: format string is not a string literal. No biggie, just wanted you to know. I do enjoy your book a lot - lots of eureka moments on my side. Thank you
Thanks. I'm not exactly sure what code you've written here. Can you paste it as a comment? Thanks.
I just tried this and it doesn't cause any problem. Did you make any other changes?
printf("%s", string_function("Fred"));
@@LearnWithHuw no other changes except for the formatting string
@@LearnWithHuw
printf(“%s”,string_function(“Fred”));
printf(“%s”,string_function(“Gussie Fink-Nottle”));
Apologies sir, I replaced #include with #include because I get fatal error: ‘malloc.h’ file not found
What an excellent video. I think all the confusion around such fundamental concepts relates to the newer form of learning programming which emphasizes seemingly productive output at the cost of understanding.
People learning through quick tutorials, bootcamps etc.
Many thanks. Yes, I get the impression that many people learn lots about concepts and paradigms at the expense of the practicalities of hands-on programming. I'm glad you liked the video! 🙂
I finished a 6 month JavaScript bootcamp and already knew most of that stuff (except the bit about passing arrays to functions but I guess I just never encountered that in a high level language). I have friends with masters in SWE that would lose their marbles if they watched this video. It's not the bootcamps or tutorials. It's the lack of interest about how things work.
Turns out everything I thought I knew about arrays is right.
I've been looking forward to this, thanks Huw! 👍
More to come soon. 🙂
Amazing ❤😊 thanks for the great content
Thank you!
This memory address is good for mapping this on screen. They way explain didn't know can find like that.
The DANGER should've started when the "y" variable name wasn't used to assign values and used static addresses directly! Not only when the "y" declaration was removed 😅
To be fair, the Danger probably should have flashed throughout the video! 😁
The memory-view in hex is 'strange' for me, being used to programming the big-endian Motorola 68000 ; )
I started coding in Z80 (little-endian) on my Nascom 1, then Nascom 2,
followed by a tiny bit of 6502 (also little-endian) on a BBC Computer.
Then I did some 68010 on my Gauntlet PCB and 68000 on my Amiga 500 upto Amiga 4000 with FPU.
My first x86 (little-endian) coding was 32-bit [I didn't even bother with 16-bit], as my first PC was a 486 [just to play Doom].
I still code some x86-64, MMX, SSE, SSE2, AVX, AVX2 if I really need performance, but Microsoft Visual Studio makes it quite easy to include CPU Intrinsics to gain decent performance.
[I could make use of AVX512, but that would need an expensive upgrade].
I recently did a little more 6502 and some 6809 (big-endian) [disassembling Williams Defender].
All that makes it quite easy for me to 'switch-endian' with hex view :)
I did so much Z80 that I can code it in hex without looking up the instructions! :)
Thanks for the great explanation! Are these adresses of physical or virtual memory? I was under the impression that a process running above the OS can't access physical memory directly without special permissions, working only with the virtual memory space provided by the OS, preventing the process of "stepping" on another process memory space.
In Windows (and many other OSes), the addresses are in virtual memory. But that's not always the case with, for example, microcontrollers (for which C is widely used) where you may be addressing physical memory.
Thanks for the comment.
Best wishes
Huw
As far as the process is concerned, it's just memory. The point of virtual memory is that the application programmer does not have to worry about it (or about other programs that may be running on the same computer).
Me, looking at that thumbnail: What are car rays? Do cars give of radiation now!? o_O
What is it that makes you claim it’s not what I thought? What do you think I think?
I am not at liberty to reveal my mind-reading powers! 🙂
This video was a good try... but in the end it is quite disappointing. :-( I won't go so far as to say that Huw is wrong... exactly... but he definitely adds more smoke than clarity to a subject that already confuses many people. The 'memory address' thing (while an interesting side diversion on how compilers implement non-register variables) is a red herring. The thing that makes C arrays & pointers different from other more traditional languages of the era can be described completely using the abstract concepts of pointers & arrays without ever looking at underlying compiler implementation and physical memory addresses.
how do you make the array always have the same memory address in other compilers ?
I haven't tried that. If the option is available it should be somewhere in the linker (or maybe in the build/compile) options either for the entire workspace or for the current project.
That's one way to look at it, although I prefer to use the C standard definition of an array object.
You are of course right (and this video is nonsense).
So, let me check by asking a stupid question: if we reserve a block of memory with malloc(), then we can do what you did (from 5:57) with no fear of blowing up our program... to have complete correspondence, we just read out the numerical value of what malloc() returned to us and cast it to int * and it should run the same, right? (Too lazy to set it up right now!)
In principle that should work. In practice, I can't think of any reason why you would want to do it. Also, bear in mind that the actual address (when using malloc()) won't be available at compile-time so I don't think you'll be able to do what I've done in this video. Anyway, this is an interesting problem for a rainy day. Naturally, for real-world programs, the array syntax deals with most of the messy stuff for you.
Best wishes
Huw
@@LearnWithHuw Oh, I realize that there's likely no point in doing what I suggested, I was just wondering if it would work after watching your video. I mean, normally, you wouldn't do what you did either - you were making a point in an educational sense, but what you did does not improve readability either :)
Not being critical of you - I found your subject fascinating!
@@spiderjuice9874 Many thanks.
@spiderjuice9874, Most of C is pretty abstracted from the machine underneath (and so you can compile most programs on many different systems) including Pointers when they are used normally. However, the language does allow for *_some_* operations that depend on compiler and/or machine internals... although the language specification *describes when this is the case.*
One of these machine/compiler dependent areas is conversion between pointer types, or between pointers and integers. The result is *mostly undetermined* but certain rules do apply (quoting from K&R here, so newer C's may be a bit different):
- A pointer may be converted to any integral type *_large enough_* to hold it (the mapping is machine dependent but designed to be unsurprising to those whose understand the machine).
- An object of integral type may be explicitly converted to a pointer. The mapping always carries an integer converted from a pointer back to the same pointer, but is otherwise machine dependent.
Thus (assuming 'unsigned long' is large enough to hold a pointer) we can do:
int *p;
unsigned long ul;
p = (int *)malloc(3*sizeof(int)); // Allocate a three element array of 'int' on the heap.
ul = (unsigned long)p; // Machine dependent conversion of a pointer to an integer.
printf("%lu
",ul);
((int *)ul)[0] = 111; // Reverse conversing back to a pointer.
((int *)ul)[1] = 222; // Reverse conversing back to a pointer.
((int *)ul)[2] = 333; // Reverse conversing back to a pointer.
Note also that:
- A pointer to one type may be converted to a pointer to another type, however the resulting pointer may cause a memory exception upon use if it does not point to an object suitably aligned in memory.
Note: The 'malloc' function is *specifically designed* to return a pointer that is correctly aligned for all types that need alignment on the underlying machine.
However:
char c;
int i;
i = *(int *)&c; // May cause a memory trap error if 'int' and 'char' align differently on the specific machine!
Also:
- It is guaranteed that a pointer to an object to a given size can be converted to a pointer to an object of a smaller size and back again without change.
Ie, the following is guaranteed to work:
int i;
int *pi;
pi = (int *)(char *)&i; // Gets a pointer to 'i'.
but the following *might* not:
char c;
char pc;
pc = (char *)(int *)&c; // Might get a pointer to 'c'... but also MIGHT NOT!
This is because a fixed memory size can fit more copies smaller sized data in it than of larger sized data, so the address range of smaller objects is conceptually larger. This was more of an issue in older mainframe computers that had large memory word sizes and small address spaces than current computer designs. But it IS machine dependent.
:-)
what about C# it has oficial unsafe way to handle pointers, but everybody is afraid of it because they don't know C to begin with
I've been thinking about covering unsafe programming in C#. Thanks for the reminder! 🙂
Just to be clear: can you confirm that you disagree with nearly everything that is written in the top-rated StackOverflow answer to the question "In C, are arrays pointers or used as pointers?" ?
Hope you can tackle Rust in the future.
"arrays in C don't really exist" is just as "true" (and nonsensical) as "floats in C don't really exist -- they are just a couple of bytes".
Perhaps the best way to get to the crux of the matter: in C, array types exist, and they are not pointer types.
So you're really doubling down on this nonsense, huh?
Your 'debunking' of the sizeof point is the most hilarously ridiculous hand-waving I've seen in a good while. First, it doesn't make a blind bit of difference to the point whether sizeof is an operator or a function. What matters is its specified behaviour. The fact that you spend so long on this irrelevance (as if anyone was relying on it being a function for their argument), as well as the arrogant assumption that we were somehow unaware of sizeof's classification, is simply bizarre.
Second, if the 'concept' of what is passed when you give an array as an argument doesn't change in the call, then there would be no cause for the behaviour of sizeof to change, would there? Yet change it does, and the consequences of this are important for C learners to understand when passing arrays around.
I note you casually gloss over what *actually* happens when you attempt to use sizeof on an array function parameter, and worse you imply that people sometimes even do it in practical code (spoiler alert - they do not).
Note to any students here: the result will at best confuse you, and at worst mislead you (like our host here), because even though the parameter is declared as an array in the prototype, it is treated as a pointer in the function body. sizeof will give you the size of a pointer (likely 4 or 8) for that parameter, not the size of the same array type were it declared as a local variable. The actual size must be known by other means if not hard-coded (either another parameter, or a sentinel value in the array).
Which is a shame, because being able to call sizeof on an array is actually very useful (contrary to our host's attempt to downplay it) when doing things like calling memcpy, or to calculate the number of elements (sizeof (y)/sizeof (y[0])) for loop bounds, in order to avoid having to worry about whether you changed all your 6's to 7's in your code when you added that additional element to your initialiser.
Just be aware that it doesn't work on function parameters, regardless of what the function prototype might lead you to believe, because at that point you're really just working with a pointer.
This is just drivel, and is far more likely to confuse to the target audience of this video than what is actually said in the video. The author’s point is that sizeof happens at compile-time, not at runtime, so it’s appropriate to describe it more like a compile-time operator than a runtime function, which is helpful to a beginner trying to understand what’s going on.
And the author is correct that nothing “happens” to an array when a program calls a function and passes that array as a parameter. The array is ALWAYS nothing more than an address AT RUNTIME. However, the array variable was indeed much more than just an address at compile-time, which is why sizeof(declared_array_variable) returns something different than taking the sizeof that same address after its been passed to a function. Understanding that sizeof is a compile-time operator explains why you get these two different answers from sizeof. It’s because at compile-time there is a big difference between an array and a pointer, and sizeof is a compile-time operator. At runtime there is no difference- it is just an address, but sizeof is not a runtime operation.
My observation is that he labours too much on the "array is an address" thing. I'm not sure why he does that. Everything is addresses at the CPU. Fetches, stores, jumps etc. A pointer is a variable that contains an address. An array name is an address pointing to a block of data. An array name can be cast to a pointer and both will point to the same memory block. There's that whole thing about sizeof which isn't that difficult to grasp.
@@mensaswede4028 First, you are simply incorrect about the compile-time v runtime thing. sizeof has the exact same *behaviour* when the operand is a VLA, which by definition *has* to be computed at runtime.
But it's also weird that you're making my point for me. An array is not a pointer, absolutely, yet Huw is explicitly saying an array is nothing more than a pointer with his statement that "An array *is* an address" (a statement that can far more credibly be made about a pointer).
If I'm being charitable to Huw, I think he was originally trying to point to the fact that because the C standard doesn't require compilers to maintain information about the size of an array object at runtime beyond the scope it was declared in (and explicitly denies the availability of that information at the function boundary) that in practice implementations do not store it at runtime (VLAs notwithstanding) and that could then be linked to the curiosity that in C, arrays can undergo arithmetic as if they were pointers and pointers can be indexed as if they were arrays.
But leading off with some conceptual nonsense that in C 'an array *is* an address' is just setting people up for trouble down the line.
I wasn't even going to comment any further on this until he then piled on even more misinformation (e.g. on how sizeof on an array is used in practical code) in some strange attempt to save face.
But it is what it is and this is the internet, people can make their own judgements from here.
@@toby9999 Indeed, he's turning an implementation detail into an unnecessary conceptual claim, that actually obscures the useful information, in my opinion. I do get, though, that maybe more discussion on this point isn't going to help anyone now.
@@FraggleH Well I’ve changed my mind. You’re not just writing drivel, you actually don’t know what you’re talking about. But as you said, arguing on the internet is pointless and people gonna believe what they want.