I think the best way to explain this is that the pointer has a type, and that's why it multiplies it. If you were using a void pointer _then_ you would need to get sizeof(person), but since the pointer is declared with a type, you don't need to do anything else.
So if you did want to increment the same address by one byte for some reason, would you make another pointer and declare it void or char then do +1 to get the address + 1 byte then?
@@undeadpresident void is the default pointer size for architecture (8 bytes for x86_64) if you want to increment by 1 byte you should make a uint8_t pointer
@@AlbatrossCommando Ah, so the uint8_t would keep it a consistent single byte regardless of architecture then. And looks like I have to use the inttypes header for it. I think I got it! Thanks
@@undeadpresident if you don't want to use any headers you can technically use a char pointer however while char is a single byte on most architectures that is not guaranteed and is implementation specific.
Dude, I sincerely did not understand a word of what you said, but I feel the need for learning C and low level programming. I just don't know why, but I have to do it, so I'll keep watching your channel until I learn. Thank you and keep it up.
Some issues I have with the example in this video: - There is no need to use a pointer to iterate over an array. Just use array indexing! - Assigning the address of an array to a pointer is incorrect. The array variable is a pointer to the first element of the array, so you should just assign the array variable to a pointer. This would also mitigate the warning that the compiler kept spitting out at you. - The segfault most likely was because you kept walking off the array during the last iteration of the for loop. You were trying to access member data through the pointer past the bounds of the array. With that said, you do a decent job explaining how to use a pointer to traverse an array. 👍
@@6754bettkitty As someone who is used to high level languages I was confused because of him asigning the pointer to an array, it makes more sense the way you correct it 😅
I haven't used C much and this is exactly how I expected it to be, just incrementing the offset with SIZEOF structure and was later confused about why. Really pleased with your explanation.
One thing that I can add to the "why use pointer?" Question is that pointers make you able to toss around arrays in and out of functions (read: as arguments and as return value). I know it has been said (briefly) in the beginning but I just want to stress this out as it might be what most newbies like me find out very useful.
Nice video. Pointer magic has gotten better or worse (your choice ;-) in C over the years. I was an ASM programmer when I moved to C in the 80s, so pointers were normal for me. Back then I was talking to a lot of Mainframe Cobol programs and would marshal data back and forth between. There you had a lot of alignment issues you had to deal with and I had a fair bit of code to pad and un-pad and convert data structures. One of the great things about C is you have lots of rope. You just need to learn how to control it so as not to hurt yourself.
@UReasonIt - Me too (late eighties and nineties). I used C/C++ to convert COBOL databases into all sorts of formats, and I enjoyed it! It was a fun challenge, and my boss didn’t understand anything about data conversion, so everything I did was magic to him! Reel-to-reel tapes and EBCDIC! Ah, memories!
Programming C for a while, but 0:33 was the best explanation that I ever heard for using pointers! Thanks 😎 I use also always the python console for calculations or conversions 😂
Well Pointers are used also to point to smaller.than int, and you can pass structs by value as parameters. While LLL is correct *in practice* I wouldn't use that statement as a definition 👍
You could, but add is typically faster than multiply. If your structures are sized nicely it might end up as just a shift. Even better, the compiler might be smart enough to simplify it back to an addition.
Thanks for this wonderful tutorial! I like the highlight of incrementing pointer (let's say it's called pointer) by sizeof(*pointer) becomes incrementing the pointer by sizeof(*pointer) * sizeof(*pointer) bytes, instead of by sizeof(*pointer) bytes or one element away. But let's not advertise for hungarian notation... Both the programmer and the compiler can keep track of the type themselves. And it is worth to mention that the pointer to the first element of the array should be assigned as struct Person *person = people;, as warned by the compiler for incompatible pointer types, since it actually mean different thing in terms typing (people itself is already struct Person * when it decays to pointer; and adding an extra address of operator (&) in front makes it becomes struct Person (*)[100]: a pointer to "array of 100 struct Person elements" instead of a pointer to struct Person, what we want to access for each element in the array) but it just points to the same location for the array head case. If we do &person + 1, it numerically returns &person[100] as a pointer to another struct Person array of 100 elements, which is out of bound, and hence causes UB. If we have to be really extra explicit, we can write it this way: struct Person *person = &person[0];
~ 1:55 - Not necessarily - we could use index into array people, just like any other array. _Allocating_ memory from the heap for 100 instances of this structure would be a better example.
If a person have programmed in Assembler only (due to starting programming in 1980's) when everything is just memory locations and offsets, C just handles it for you with safe guards, though a (void*) can override mismatch pointer types if you know it's still compatible.
4:00 i read a strlen implementation that increments an integer pointer by 1 which made me very confused. thank you for your explanation, this was very helpful
7:23 I think it's wrong because at i=99, the pointer would point outside the people array after incrementing the pointer, and then trying to set it's content should crash.
Wouldn't you have to divide the condition by 4 since it's incrementing it by 4, if you want to only reset every 4th element in an array? So in the end it would end up being way over 300.
C don't like you to use absolute raw addresses, but sometimes you have a flag saved in flash, you can make C read it by casting it as a pointer: (uint32_t*)0xDE00 but you want the number that is inside that address, so the correct way is: uint32_t temp = *(uint32_t*)0xDE00, pointer to the point-casted address.
Are people confused about pointer arithmetic? When I was learning pointers, I was confused about a lot, but pointer arithmetic was the one thing i found straightforward and intuitive.
Basic was my first language, but I couldn't ever get anything complicated to work (to be fair, I was 6). I played around with debug in DOS, and learned some assembly. C was my first real programming language, and I never had any problems with pointer arithmatic, but it seems that almost everybody else does. It was explained to me about the same way that you explained it, and that's also the way that I explain it, so I'll be sending your video to anybody having trouble with pointer arithmatic, now. Thanks!
I doubt I'd understand it if I didn't also learn assembly first. When I took a programming class in college we used Python and the concept of "shallow copy" (i.e. a pointer to the original variable's memory location) vs "deep copy" (if you hexdumped your RAM you'd see the same data twice) made little sense to me. When you're just told "that's how it works" but you don't understand why it's hard to understand it.
First real error in this video at about 0:38 when you say you cannot pass around structures into function calls. The point is, you indeed CAN pass structures into function calls, but most likely this is not what you want. C is passing everything (except of arrays) 'by value' ... this technically means, a copy of the item is passed into the function. If you create a function accepting a 'struct Person person' as an argument, then the whole structure is copied onto the stack as an argument. The function then reads and eventually makes changes in the copy (this is why you likely do not want this) ... and when the function returns, all changes are lost (because they were only done on the copy). If you use a 'struct Person *p_person) as argument, you pass the pointer to the object ... changes will now be done on the original (accessed via the p_person->member access). Not only is passing pointer much quicker (because much less data is copied onto the stack) it also allows the function to modify the original data. The second (only cosmetic) error is when you do the p_person += 3 increment within the loop to 100 ... remember this loop is executed 100 times ... incrementing your pointer 100 times by 3 members ... the pointer ends up at member 300 ... which does not exist ... and you are already accessing memory outside the bounds of your array. (invoking undefined behaviour). Mastering C is to a good extend avoiding all such situations ... thats why I had not left this part in the video without pointing this out.
Happy to see someone caught this as well. His biggest error is not understanding that ‘struct Person p_Person’ is a pointer to a *single* struct of type Person yet he pointed it to an array of struct of type Person, these are NOT the same thing! The compiler even complained as such, but he blamed it on the line that contained ‘sizeof’, which had nothing to do with it as that is a runtime error. What he should have done is ‘struct Person *p_Person = &people[0];’ and then you can use simple p_Person++ in the loop and the compiler will know to increment by the right number of bytes to point to the next Person in the array.
Assume A is array of some type .. To access some element of that array write A[i] =... This is equivalent to *(A+i) = ... This is also equivalent to *(i+A)=... Even more, you can write i[A]=... and it should work.
My GCC gave me a warning, which I could only solve, when I changed the line 13 to this nasty thing after reading the docs how to initialize the struct: " struct Person (*p_Person)[100] = &people;". Then the warning was gone, but then I got a segmentation fault with the arrow thing. I could line 18 that way: " (*p_Person)[i].age = 0;". I wonder why your stuff compiles and works fine. Nonetheless, I am happy with the way you teach. I hope to not encounter worse problems, while following your video. Merry Christmas. 🙂
If you want to move inside the struct (go to the next char in name for an example) simply cast the pointer into a char* then any arithmetic operation will jump a single byte and the += sizeof(struct person) will work when iterating along an array
I use pointers in C all the times, I browse UA-cam to learn how programmers think and learn how they think from them. By doing that you gain more understanding and knowledge and thus ability to put this knowledge into practical use. I like your way of thinking. I believe that programming with C without using pointers is incomplete.
Was relearning cpp and stumbled across this vid. Actually, knowing that arrays and “index” are just 2 pointers getting added together to get the deref value ties this together.
-> in functions that take struct pointers, as C knows the offset for all struct members, it only need an base address input to call the function. the offsets is then hard-coded by C in to the machine code it generates for this function, as all MCU has some type of opcode with X offset like: STA $3002, X
I usually use an alternative method to do that: struct Person people[100]; struct Person *p = NULL; for(int i = 0; i < 100; i++) { p = &people[i]; p->age = 0; p->name = "someone"; }
Oh my i was firstly goin to say "just add one to it" then i thought "whait that would be wrong..." and then I am back when I started... Great tutorial!
It all makes sense, you are not trying to access the next byte, you are accessing the next element in the array, p_Person stores the address of the first people element, by incrementing it by 1, you are now accessing the next element in the array, meanwhile the way you access the struct itself is again de referencing that specific Person element by doing p_Person->age.
Technically we use pointers in most programming languages: Java, Python, you name it. That's why they always tell you to never create a copy of an object with the equals operator. Because all you are doing is just creating another variable with the same pointer reference as the original object The difference is that in lower level languages like C and C++ you need to define your pointers manually
In the following declaration: int* arr[] Is arr a pointer to an array or an array of pointers? How can we change the declaration to clarify the intended data structure?
lol, a classic mistake. He made the pointer skip every 3 elements (0, 4, 7, etc), but he didn't adjust the for loop so it started to point to out of bounds memory. Its easy to miss bugs like that during a presentation/tutorial
@@Protofall He also incremented the pointer before setting the name, so the last loop would be outside the array. Would also fail to set the name string during the first time in the loop.
Didn't know the compiler did this but I also never did += sizeof() stuff because I genuinely thought that when you do *struct = &struct what you have is a pointer to a pointer array which secretly point to the struct array, I would never think that the compiler would just multiply the value because that for me looked unintuitive and like a bigger secret thing for the compiler to do that the user wouldn't even think about and so did I, really enjoyed the explanation in the video.
I don't really understand why don't we just use people[i] in loop? I have been learning c++ for a while, so maybe there are some differces with c++, so is there a real need to use pointers in c++?
You should probably have mentioned that the reason the compiler know how far to jump is because you beclare p_people as a struct Person pointer. If it was a uint8_t pointer you would have had to tell it to jump sizeof(struct Person).
@Dave Bieleveld You wouldn't. The point that the OP was making is that when you add one to a pointer, it's not like an integer where at literally adds one to its value; it adds the number of bytes to get to the next element. For example, let's pretend we had a pointer to int at address 0x1000 of a computer. int *ip; ip = (int *) 0x1000; I have to cast it because C doesn't allow you to convert random integers into pointers without saying so. Anyway, because int is 4 bytes, the next int would be at address 0x1004, and so the compiler will add 4 to the pointer when I increment it like this. ip++; Now it's pointing to the integer at address 0x1004. But if I do the same thing with a pointer to char, the same wouldn't happen. A char is one byte, so if you incremented a char pointer instead, it would only go to address 0x1001. This is because it only takes one byte to get to the next char. Pointer arithmetic can be convenient for iterating through arrays, because if you just keep incrementing the pointer, you'll go through each element of the array, even though you only have the address of the first element of the array. You're just going to the next place where an integer would be in memory. This is actually how arrays are formally defined. The following are equivalent: ip[1]; *(ip + 1); IDK if you already knew all this; I'm just explaining as clearly as possible because there's no reason not to.
My c/c++ teacher had this very neat mnemonics, square bracket is just an asterisk in disguise. ie.: *(pA+0) === pA[0] Say you have the person array from the video, and you want to assign a pointer to the second element to pA, well clearly you mean: person[1] not person[sizeof(Person)] right? well since person[1] is a bracketed expression and brackets are just an asterisk in disguise it must be equivalent to: person[1] === *(person + 1) //And it is Asterisk is a de-reference operator, meaning, it gives us a struct rather than the address, if we want an address to populate our pA pointer, we need to use the & operator: &*(person + 1) But now we have a reference operator and a de-reference operator one after the other, do they cancel out? of course they do &*(person + 1) === (person + 1) And now we are back a the videos example pA = (person +1) or if we want to increment over pA and have initialized it as pA = person (LLL uses &person which despite being kind of wrong (as you can tell by the compiler warnings) still works because pointer to array and pointer to the first element of the array happens to be the same) we can use the pA = pA+1 or the equivalent pA += 1 or the iconic pA++ TLDR: (pA = pA+1) === (pA = &*(pA+1)) === (pA = &pA[1]) For those wondering at ua-cam.com/video/q24-QTbKQS8/v-deo.html it crashed because he is now reading every 4th person in 100 sized array 100 times. Ie he is looking at 1st, 5th, 9th, ..., 97th, 101st, 105th, ..., and when i would be 99th (last valid i of loop) the the pointer now points to a memory location that would correspond to a 397th Person in the array, waaaay past the existing 100 sized array.
Okay, that does make sense, although it's somewhat counterintuitive. BTW the program crashed at the end probably because with the for loop you've tried to access values outside of the "people" array after the i counter got to 19 or so.
Common trap: you declare a pointer uint32_t* or char*, that has nothing to do with pointer address size, that is always fixed to 16 or 32bit based on the mcu core, what size-type does is how C handles ++ and +=1 etc, so that would be +4bytes if you uint32_t* declared it.
Yep, and this is *exactly* why the compiler complained, it had nothing to do with the line he pointed to that used ‘sizeof’. ‘struct Person *p_Person’ is a pointer to a single struct of type Person. The compiler says ‘hold up!’ you pointed me to the beginning of an array of type ‘struct Person’, those are not the same thing! What he should have done is ‘struct Person *p_Person = &people[0];’
So I was confused on pointer until I try it and found the conclusion: & is to show the address of value and * is just pointer. * the value to copy address . And when I make some change about the value in address and call the pointer, the pointer shows the value in address . In short , it’s const .
It's more simple to use the bracket syntax for this kind of thing. pointer arithmetic is useful when doing memcpy or getting a pointer in a specific part of the memory.
This is a really cool video showing why we don't need to account for the size of structs since gcc will already do it for us, but what if we literally only wanted to grab the next byte? We couldn't say +1 because as you pointed out the compiler would grab the next "person" so to speak, but what if I literally wanted the next byte?
The pointer is pointing at a multi-element struct. If you want to access the 2nd byte of the 'name' array of chars (2nd letter of 'name') then simply say what you want: p_person->name[1]... Don't listen to bnibbe3... It's a mistake to write code that "knows" name is the first element of the structure. Someone will come along and insert "title" (ie: "Mr." or "Ms.") ahead of name and your code is broken...
First of all, thank you for teaching me. Isn't "p_Person += 1";" supposed to be the last line in the for loop? So that the names get assigned on pace with the ages I guess.
Compiler gave an error because, in line 11 - struct Person people is already pointer, therefore in line 13 you should not pass reference to (&people), but just people.
at 7:40 it crashed because you were iterating through it 100 times while trying to access every fourth "person" and therefore it is trying to access memory outside of "people"
What if you needed to change the size of the array without loosing any of the original data? You would create a new larger array, fill it with the original data, and then reassign the pointer so it now points to the larger array which contains all of the original data and has space for new data. Then delete the original array to free up memory.
warning: initialization of 'struct Person *' from incompatible pointer type 'struct Person (*)[100]'. (GCC 9.5.0); warning: assignment type mismatch: pointer to struct Person {array[64] of char name} "=" pointer to array[100] of struct Person {array[64] of char name, int age} (Sun Studio 12.6). There is no need to do struct Person *p_Person = &people; that just confuses further (and isn't clean code, as both compilers point out). You were talking about the compiler having your back and taking care of things for you, so remain consistent: struct *p_Person = people; works perfectly and is idiomatic to programming in a high level language. A compiler will know that you want the address of people because you told it that your p_Person is a pointer to a struct.
I would also suggest be a bit more pedantic and point to &people[0] so folks just learning don’t need to know the language idiosyncrasies, such as pointer decay
i understand what u explained but why u fixed p_Person->name[0] to 0 so all the struct will be filled with zeros and also all ages which will be filled will be 0 !! so whats the purpose of that and how to fill with different values ? my last question is if you iterate a struct of 100 bytes so the couple of (age,name) is it 50 ? otherwise thank you for your explanation
C noob here. I don't understand the point of the for loop if you're not using "i" at all, just counting to make sure you do the work on p_Person 100 times? Also, I don't get why you set ->name to 0 AFTER you've increased the pointer... ? Clarifications welcome. Thanks!
I accept the premise that the C/C++ compiler already knows you are coding a struct and calculates the memory position of the array people. if you change the value of p_Person anywhere in the code, does that mean p_Person points to that Person in the sequence? Example: p_Person = 3 ; p_Person -> name[0] = 0 ; p_Person -> age = 0 ; Does this mean that p_Person points to Person[3] in other language's notation? In essence, which array element in Person does p_Person point to? Many thanks.
No, it doesn't work that way. _p_Person = 3_ would redirect the pointer to memory address 3 (the third byte in virtual memory address space) and no longer point to your data structure at all. Any attempts to write to it would either trample any existing data there or page fault if memory address 3 is not currently allocated.
This is by far the most complicated way to explain pointers and pointer arithmetic. I only understood this because I have years of experience in low level c++. The disassembly would have lost any beginner
7:40 it crashed because you didn't assign pointer correctly. Remember this, NAME OF ARRAY IS ADRESS OF FIRST ELEMENT OF ARRAY. So, to simplify with array of integers... int A[5] = {1, 2, 3, 4, 5}; int *p = &A[0]; or int *p = A; or if you just want to declare and then assign value in the next line: int *p; p = A; or p = &A[0]
thank you for the explanation - great job. - maybe out of context...but would it make sense or be correct to switch line 19 and 20 and simple in the new line 19 write p_Person->name = 0; Or would that not work at all ?
Well yeah, when you initialized the struct in main, you took a contiguous chunk in memory, so when you incremented that pointer it's just gonna point to the next space in memory because they're right next to each other. Right? or am I understanding it incorrectly(or overthinking it)?
But then you need to do 10x as much code to make this safe. I've very rarely used real C in the last several years even on embedded systems because C++ and vectors work so much better. I suppose maybe if you're on extremely constrained devices this is still useful, but at that point you have to be shipping millions of units to offset the salary for more dev time and not simply use a more powerful MCU. You also didn't explain the syntax of the thumbnail which is exactly what I came here hoping to confirm.
if we identified the p_Person as a void pointer,so Then wouldn't the first spelling we wrote be correct?Surely We need to specify that our void pointers is Struct while we using void pointers
Wouldn't this be compiler dependent? Unless it's in the official C rules? Also, hypothetically, how would you add 1 (a singular byte) to the address? p_Person += (1/sizeof(Person)) ?
@@MatiasGRodriguez that is wrong, you can't use the += operator with a cast like that. You would have to do something like: p_Person = (struct Person *)((char *)p_Person + 1)
@Lenny McLennington Good grief. Thanks for reminding me why I prefer assembly over C. Don't get me wrong, when you're trying to write a mathematical equation C is obviously superior to assembly but the type-wrangling you have to do is really obnoxious for something that should be very simple.
For a language that is so unhelpful i would prefer the syntax of `p_Person += sizeof(struct Person)`. It feels odd to have the c compiler be helpful at all.
100 in hex 0x64, and i know you wrote for loop comparison as i < 100 only, but compiler compared it against 99 and uses jump when less or equal. How did that happen, and isn't there a assembly equivalent of just jump when less?
3months late but I saw that it was comparing to 99 as well. 1 instruction you might be talking about is JL(Jump less) for arithmetic operations and JB(Jump below) for logical operations
Did this change at some point in the C standard or is there a lot code working on void* without casting them to the proper type? I can definitely remember seeing and incrementing by sizeof myself in older C code. By older I mean pre C99.
Bugs: name member is written after pointer is modified so first element in the array doesn't have it's name modified, then in the last iteration we write one-past-end-of-array. Iteration count should be 25 when we do += 4 for the pointer, again writing past end of array.
Idk why people will always use pointer declarations of the form "T *Name = &x;" instead of "T* Name = &x;" When I was learning C for the first time I was always so confused about the use of an asterisk in both de-referencing a pointer and declaring a pointer type. It's always easier (and still correct) for me to understand when I group the asterisk with the actual type, since it then translates logically to "This variable of a 'Type T pointer' is named Name and is equal to the address of x" instead of "This variable of a 'Type T' is named *Name (and since there's an asterisk it's a pointer) and is equal to the address of x". Just kind of a runaway thought, but I would recommend anyone learning C to just group your type and asterisk together so you can see that you are actually declaring a pointer, and it's not just a variable with an asterisk at the beginning of it's name. It then also makes more sense when de-referencing, as you are looking at the value, not the pointer that you declared.
I think the best way to explain this is that the pointer has a type, and that's why it multiplies it. If you were using a void pointer _then_ you would need to get sizeof(person), but since the pointer is declared with a type, you don't need to do anything else.
Exactly... you're right
So if you did want to increment the same address by one byte for some reason, would you make another pointer and declare it void or char then do +1 to get the address + 1 byte then?
@@undeadpresident void is the default pointer size for architecture (8 bytes for x86_64) if you want to increment by 1 byte you should make a uint8_t pointer
@@AlbatrossCommando Ah, so the uint8_t would keep it a consistent single byte regardless of architecture then. And looks like I have to use the inttypes header for it. I think I got it! Thanks
@@undeadpresident if you don't want to use any headers you can technically use a char pointer however while char is a single byte on most architectures that is not guaranteed and is implementation specific.
Dude, I sincerely did not understand a word of what you said, but I feel the need for learning C and low level programming. I just don't know why, but I have to do it, so I'll keep watching your channel until I learn. Thank you and keep it up.
I think Cherno video on pointers can help you.
How's it going so far?
exact same thing here bro... i just like it so much idk why, and so i'm learning C using online resources
How go the studies?
Some issues I have with the example in this video:
- There is no need to use a pointer to iterate over an array. Just use array indexing!
- Assigning the address of an array to a pointer is incorrect. The array variable is a pointer to the first element of the array, so you should just assign the array variable to a pointer. This would also mitigate the warning that the compiler kept spitting out at you.
- The segfault most likely was because you kept walking off the array during the last iteration of the for loop. You were trying to access member data through the pointer past the bounds of the array.
With that said, you do a decent job explaining how to use a pointer to traverse an array. 👍
YUP! Indeed, remove the ampersand character.
build/test-pointer.cpp: In function ‘int main(int, char**)’:
build/test-pointer.cpp:16:31: error: cannot convert ‘Person (*)[nPeople]’ to ‘Person*’ in initialization
16 | struct Person *p_Person = &people;
| ^~~~~~~
| |
| Person (*)[nPeople]
@@6754bettkitty As someone who is used to high level languages I was confused because of him asigning the pointer to an array, it makes more sense the way you correct it 😅
My uni instructors banned us from using array subscripting during our lab on pointers forcing us to learn how to access arrays with them lol
I haven't used C much and this is exactly how I expected it to be, just incrementing the offset with SIZEOF structure and was later confused about why.
Really pleased with your explanation.
M
One thing that I can add to the "why use pointer?" Question is that pointers make you able to toss around arrays in and out of functions (read: as arguments and as return value). I know it has been said (briefly) in the beginning but I just want to stress this out as it might be what most newbies like me find out very useful.
Ty bro
Nice video. Pointer magic has gotten better or worse (your choice ;-) in C over the years. I was an ASM programmer when I moved to C in the 80s, so pointers were normal for me. Back then I was talking to a lot of Mainframe Cobol programs and would marshal data back and forth between. There you had a lot of alignment issues you had to deal with and I had a fair bit of code to pad and un-pad and convert data structures. One of the great things about C is you have lots of rope. You just need to learn how to control it so as not to hurt yourself.
@UReasonIt - Me too (late eighties and nineties). I used C/C++ to convert COBOL databases into all sorts of formats, and I enjoyed it! It was a fun challenge, and my boss didn’t understand anything about data conversion, so everything I did was magic to him!
Reel-to-reel tapes and EBCDIC! Ah, memories!
What made this click for me was comparing the pointer to a number which can be iterated to access neighboring values. Thank you very much!
Programming C for a while, but 0:33 was the best explanation that I ever heard for using pointers! Thanks 😎
I use also always the python console for calculations or conversions 😂
Well
Pointers are used also to point to smaller.than int, and you can pass structs by value as parameters. While LLL is correct *in practice* I wouldn't use that statement as a definition 👍
Yeah looking back at this I realized I oversimplified a bit. Writing back things like ints, for example, is also common.
python interpreter supports big int, i'm sold
You can also do, at the begining of the for loop
p_Person = &people[i];
Much more comfortable (even though your method isn't confusing either).
You could, but add is typically faster than multiply. If your structures are sized nicely it might end up as just a shift. Even better, the compiler might be smart enough to simplify it back to an addition.
Thanks for this wonderful tutorial! I like the highlight of incrementing pointer (let's say it's called pointer) by sizeof(*pointer) becomes incrementing the pointer by sizeof(*pointer) * sizeof(*pointer) bytes, instead of by sizeof(*pointer) bytes or one element away.
But let's not advertise for hungarian notation... Both the programmer and the compiler can keep track of the type themselves.
And it is worth to mention that the pointer to the first element of the array should be assigned as struct Person *person = people;, as warned by the compiler for incompatible pointer types, since it actually mean different thing in terms typing (people itself is already struct Person * when it decays to pointer; and adding an extra address of operator (&) in front makes it becomes struct Person (*)[100]: a pointer to "array of 100 struct Person elements" instead of a pointer to struct Person, what we want to access for each element in the array) but it just points to the same location for the array head case.
If we do &person + 1, it numerically returns &person[100] as a pointer to another struct Person array of 100 elements, which is out of bound, and hence causes UB.
If we have to be really extra explicit, we can write it this way: struct Person *person = &person[0];
Pretty cool; for beginners, I believe line 13 might be confusing. I would have used "= people" or "= &people[0]"
Ah good catch. Yeah much simpler.
It also gets rids of the compile time warning
And it's also correct, &people is the pointer of the pointer.
@@tedpixie true it's the pointer of the array that itself points to something already
Yeah the fact that `people` = `&people` = `&people[0]` was one of the most confusing things for me learning C
~ 1:55 - Not necessarily - we could use index into array people, just like any other array. _Allocating_ memory from the heap for 100 instances of this structure would be a better example.
If a person have programmed in Assembler only (due to starting programming in 1980's) when everything is just memory locations and offsets,
C just handles it for you with safe guards, though a (void*) can override mismatch pointer types if you know it's still compatible.
4:00 i read a strlen implementation that increments an integer pointer by 1 which made me very confused. thank you for your explanation, this was very helpful
"So today we're going to talk about pointers in C."
**Quora has nervous breakdown**
7:23 I think it's wrong because at i=99, the pointer would point outside the people array after incrementing the pointer, and then trying to set it's content should crash.
That is true, well spotted! Its not necessarily a crash, but it will be undefined behaviour.
Also name of the first element of the table will not be initialized.
Wouldn't you have to divide the condition by 4 since it's incrementing it by 4, if you want to only reset every 4th element in an array?
So in the end it would end up being way over 300.
C don't like you to use absolute raw addresses, but sometimes you have a flag saved in flash, you can make C read it by casting it as a pointer: (uint32_t*)0xDE00
but you want the number that is inside that address, so the correct way is: uint32_t temp = *(uint32_t*)0xDE00, pointer to the point-casted address.
Are people confused about pointer arithmetic? When I was learning pointers, I was confused about a lot, but pointer arithmetic was the one thing i found straightforward and intuitive.
Basic was my first language, but I couldn't ever get anything complicated to work (to be fair, I was 6). I played around with debug in DOS, and learned some assembly. C was my first real programming language, and I never had any problems with pointer arithmatic, but it seems that almost everybody else does. It was explained to me about the same way that you explained it, and that's also the way that I explain it, so I'll be sending your video to anybody having trouble with pointer arithmatic, now. Thanks!
I doubt I'd understand it if I didn't also learn assembly first. When I took a programming class in college we used Python and the concept of "shallow copy" (i.e. a pointer to the original variable's memory location) vs "deep copy" (if you hexdumped your RAM you'd see the same data twice) made little sense to me. When you're just told "that's how it works" but you don't understand why it's hard to understand it.
You make everything seem so easy! Thank you so much for your videos. You definitely make my life much easier.
Glad you like them!
First real error in this video at about 0:38 when you say you cannot pass around structures into function calls.
The point is, you indeed CAN pass structures into function calls, but most likely this is not what you want.
C is passing everything (except of arrays) 'by value' ... this technically means, a copy of the item is passed into the function.
If you create a function accepting a 'struct Person person' as an argument, then the whole structure is copied onto the stack as an argument. The function then reads and eventually makes changes in the copy (this is why you likely do not want this) ... and when the function returns, all changes are lost (because they were only done on the copy).
If you use a 'struct Person *p_person) as argument, you pass the pointer to the object ... changes will now be done on the original (accessed via the p_person->member access).
Not only is passing pointer much quicker (because much less data is copied onto the stack) it also allows the function to modify the original data.
The second (only cosmetic) error is when you do the p_person += 3 increment within the loop to 100 ... remember this loop is executed 100 times ... incrementing your pointer 100 times by 3 members ... the pointer ends up at member 300 ... which does not exist ... and you are already accessing memory outside the bounds of your array. (invoking undefined behaviour).
Mastering C is to a good extend avoiding all such situations ... thats why I had not left this part in the video without pointing this out.
Happy to see someone caught this as well.
His biggest error is not understanding that ‘struct Person p_Person’ is a pointer to a *single* struct of type Person yet he pointed it to an array of struct of type Person, these are NOT the same thing! The compiler even complained as such, but he blamed it on the line that contained ‘sizeof’, which had nothing to do with it as that is a runtime error.
What he should have done is ‘struct Person *p_Person = &people[0];’ and then you can use simple p_Person++ in the loop and the compiler will know to increment by the right number of bytes to point to the next Person in the array.
Assume A is array of some type ..
To access some element of that array write A[i] =...
This is equivalent to *(A+i) = ...
This is also equivalent to *(i+A)=...
Even more, you can write i[A]=... and it should work.
My GCC gave me a warning, which I could only solve, when I changed the line 13 to this nasty thing after reading the docs how to initialize the struct: " struct Person (*p_Person)[100] = &people;". Then the warning was gone, but then I got a segmentation fault with the arrow thing. I could line 18 that way: " (*p_Person)[i].age = 0;". I wonder why your stuff compiles and works fine. Nonetheless, I am happy with the way you teach. I hope to not encounter worse problems, while following your video. Merry Christmas. 🙂
Love the way you ended the video lmao "Still crash not sure why" this define C so well.
If you want to move inside the struct (go to the next char in name for an example) simply cast the pointer into a char* then any arithmetic operation will jump a single byte and the += sizeof(struct person) will work when iterating along an array
Thank you! Starting my C class this week so I think I will be watching quite a few LLL videos
I use pointers in C all the times, I browse UA-cam to learn how programmers think and learn how they think from them. By doing that you gain more understanding and knowledge and thus ability to put this knowledge into practical use. I like your way of thinking. I believe that programming with C without using pointers is incomplete.
Yes, pointers should be the standart like in other language. Mostly a copy of an object is not needed.
Was relearning cpp and stumbled across this vid. Actually, knowing that arrays and “index” are just 2 pointers getting added together to get the deref value ties this together.
It help me a lot when clearing up the concepts for my final.
-> in functions that take struct pointers, as C knows the offset for all struct members, it only need an base address input to call the function.
the offsets is then hard-coded by C in to the machine code it generates for this function, as all MCU has some type of opcode with X offset like: STA $3002, X
Thanks for the refresher. I haven't looked at C in almost 10 years.
thankyou I was really struggling trying to use structs like objects, now I know how to use pointers :)
I usually use an alternative method to do that:
struct Person people[100];
struct Person *p = NULL;
for(int i = 0; i < 100; i++)
{
p = &people[i];
p->age = 0;
p->name = "someone";
}
good explanation, thanks!
Oh my i was firstly goin to say "just add one to it" then i thought "whait that would be wrong..." and then I am back when I started... Great tutorial!
My mind is blown. Thank you so much!
It all makes sense, you are not trying to access the next byte, you are accessing the next element in the array, p_Person stores the address of the first people element, by incrementing it by 1, you are now accessing the next element in the array, meanwhile the way you access the struct itself is again de referencing that specific Person element by doing p_Person->age.
thank you, you're a good teacher
Technically we use pointers in most programming languages: Java, Python, you name it. That's why they always tell you to never create a copy of an object with the equals operator. Because all you are doing is just creating another variable with the same pointer reference as the original object
The difference is that in lower level languages like C and C++ you need to define your pointers manually
In the following declaration:
int* arr[]
Is arr a pointer to an array or an array of pointers?
How can we change the declaration to clarify the intended data structure?
@@nomadd3v wrong
Pointer array -> pointer -> integer value
Thank You very much for making this video!!! - I was lacking that example. Sub added after I watched that.
The funniest part about C is the fact that after this whole, well done explanation, at the end he still says, "Still crashed, not sure why..."
lol, a classic mistake. He made the pointer skip every 3 elements (0, 4, 7, etc), but he didn't adjust the for loop so it started to point to out of bounds memory. Its easy to miss bugs like that during a presentation/tutorial
@@Protofall He also incremented the pointer before setting the name, so the last loop would be outside the array. Would also fail to set the name string during the first time in the loop.
That's C for you.
Can somebody explain or give me some readings on this 0:33 ? When are pointers not used for function argument calls?
Great explanation! Subscribed! I even understood your explanation/demonstration in assembly!!
Didn't know the compiler did this but I also never did += sizeof() stuff because I genuinely thought that when you do *struct = &struct what you have is a pointer to a pointer array which secretly point to the struct array, I would never think that the compiler would just multiply the value because that for me looked unintuitive and like a bigger secret thing for the compiler to do that the user wouldn't even think about and so did I, really enjoyed the explanation in the video.
I don't really understand why don't we just use people[i] in loop? I have been learning c++ for a while, so maybe there are some differces with c++, so is there a real need to use pointers in c++?
You should probably have mentioned that the reason the compiler know how far to jump is because you beclare p_people as a struct Person pointer. If it was a uint8_t pointer you would have had to tell it to jump sizeof(struct Person).
@Dave Bieleveld You wouldn't. The point that the OP was making is that when you add one to a pointer, it's not like an integer where at literally adds one to its value; it adds the number of bytes to get to the next element.
For example, let's pretend we had a pointer to int at address 0x1000 of a computer.
int *ip;
ip = (int *) 0x1000;
I have to cast it because C doesn't allow you to convert random integers into pointers without saying so. Anyway, because int is 4 bytes, the next int would be at address 0x1004, and so the compiler will add 4 to the pointer when I increment it like this.
ip++;
Now it's pointing to the integer at address 0x1004.
But if I do the same thing with a pointer to char, the same wouldn't happen. A char is one byte, so if you incremented a char pointer instead, it would only go to address 0x1001. This is because it only takes one byte to get to the next char.
Pointer arithmetic can be convenient for iterating through arrays, because if you just keep incrementing the pointer, you'll go through each element of the array, even though you only have the address of the first element of the array. You're just going to the next place where an integer would be in memory. This is actually how arrays are formally defined. The following are equivalent:
ip[1];
*(ip + 1);
IDK if you already knew all this; I'm just explaining as clearly as possible because there's no reason not to.
@Dave Bieleveld he said you don't
Thank you so much. I'm having great trouble picking up assembly language for the schoolwork. Much appreciated 🙏🏻
My c/c++ teacher had this very neat mnemonics, square bracket is just an asterisk in disguise.
ie.:
*(pA+0) === pA[0]
Say you have the person array from the video, and you want to assign a pointer to the second element to pA, well clearly you mean:
person[1]
not person[sizeof(Person)] right?
well since person[1] is a bracketed expression and brackets are just an asterisk in disguise it must be equivalent to:
person[1] === *(person + 1) //And it is
Asterisk is a de-reference operator, meaning, it gives us a struct rather than the address, if we want an address to populate our pA pointer, we need to use the & operator:
&*(person + 1)
But now we have a reference operator and a de-reference operator one after the other, do they cancel out? of course they do
&*(person + 1) === (person + 1)
And now we are back a the videos example
pA = (person +1)
or if we want to increment over pA and have initialized it as pA = person (LLL uses &person which despite being kind of wrong (as you can tell by the compiler warnings) still works because pointer to array and pointer to the first element of the array happens to be the same)
we can use the
pA = pA+1 or the equivalent pA += 1 or the iconic pA++
TLDR:
(pA = pA+1) === (pA = &*(pA+1)) === (pA = &pA[1])
For those wondering at ua-cam.com/video/q24-QTbKQS8/v-deo.html it crashed because he is now reading every 4th person in 100 sized array 100 times. Ie he is looking at 1st, 5th, 9th, ..., 97th, 101st, 105th, ..., and when i would be 99th (last valid i of loop) the the pointer now points to a memory location that would correspond to a 397th Person in the array, waaaay past the existing 100 sized array.
one thing that confuses beginners is the fact that ptr = &ptr[0]
Okay, that does make sense, although it's somewhat counterintuitive. BTW the program crashed at the end probably because with the for loop you've tried to access values outside of the "people" array after the i counter got to 19 or so.
Thanks for the knowledge!
Common trap: you declare a pointer uint32_t* or char*, that has nothing to do with pointer address size, that is always fixed to 16 or 32bit based on the mcu core,
what size-type does is how C handles ++ and +=1 etc, so that would be +4bytes if you uint32_t* declared it.
that's what he said during the video tho
Also 64bit if compiled 64bit.
2:30 *p_person points towards the first index of struct variable people array
Yep, and this is *exactly* why the compiler complained, it had nothing to do with the line he pointed to that used ‘sizeof’. ‘struct Person *p_Person’ is a pointer to a single struct of type Person. The compiler says ‘hold up!’ you pointed me to the beginning of an array of type ‘struct Person’, those are not the same thing!
What he should have done is ‘struct Person *p_Person = &people[0];’
So I was confused on pointer until I try it and found the conclusion:
& is to show the address of value and * is just pointer. * the value to copy address . And when I make some change about the value in address and call the pointer, the pointer shows the value in address . In short , it’s const .
I don't know C. I develop stuff on the web. I really enjoyed how you explained everything and it makes sense. Now I am into C
This just clicked for me! Thanks!!!
Wow! it's Amazing I did it well ! Perfect work !
Very very good explanation
its worked GREAT JOB.
It's more simple to use the bracket syntax for this kind of thing. pointer arithmetic is useful when doing memcpy or getting a pointer in a specific part of the memory.
This is a really cool video showing why we don't need to account for the size of structs since gcc will already do it for us, but what if we literally only wanted to grab the next byte? We couldn't say +1 because as you pointed out the compiler would grab the next "person" so to speak, but what if I literally wanted the next byte?
The pointer is pointing at a multi-element struct.
If you want to access the 2nd byte of the 'name' array of chars (2nd letter of 'name')
then simply say what you want: p_person->name[1]...
Don't listen to bnibbe3...
It's a mistake to write code that "knows" name is the first element of the structure.
Someone will come along and insert "title" (ie: "Mr." or "Ms.") ahead of name and your code is broken...
First of all, thank you for teaching me. Isn't "p_Person += 1";" supposed to be the last line in the for loop? So that the names get assigned on pace with the ages I guess.
i love you thank you so much.
No problem 😊
Compiler gave an error because, in line 11 - struct Person people is already pointer, therefore in line 13 you should not pass reference to (&people), but just people.
Awesome video!
at 7:40 it crashed because you were iterating through it 100 times while trying to access every fourth "person" and therefore it is trying to access memory outside of "people"
Nice explanation 😊
(with sarcasm in voice: ) right... now i know why i should use pointers ... but 1sec : is writing '
for (int i =0 ; i
What if you needed to change the size of the array without loosing any of the original data? You would create a new larger array, fill it with the original data, and then reassign the pointer so it now points to the larger array which contains all of the original data and has space for new data. Then delete the original array to free up memory.
it crashed because you made p_Person += 4 and you still iterated 100 times. i would write p_Person[i].name[0] = 0; etc...
warning: initialization of 'struct Person *' from incompatible pointer type 'struct Person (*)[100]'. (GCC 9.5.0);
warning: assignment type mismatch:
pointer to struct Person {array[64] of char name} "=" pointer to array[100] of struct Person {array[64] of char name, int age} (Sun Studio 12.6).
There is no need to do struct Person *p_Person = &people; that just confuses further (and isn't clean code, as both compilers point out). You were talking about the compiler having your back and taking care of things for you, so remain consistent: struct *p_Person = people; works perfectly and is idiomatic to programming in a high level language. A compiler will know that you want the address of people because you told it that your p_Person is a pointer to a struct.
I would also suggest be a bit more pedantic and point to &people[0] so folks just learning don’t need to know the language idiosyncrasies, such as pointer decay
So, how to understand pointer math in C? Learn Assembly, good... good... i'm on the right path, and seem to have found a good channel.
Pretty much
i understand what u explained but why u fixed p_Person->name[0] to 0 so all the struct will be filled with zeros and also all ages which will be filled will be 0 !! so whats the purpose of that and how to fill with different values ?
my last question is if you iterate a struct of 100 bytes so the couple of (age,name) is it 50 ?
otherwise thank you for your explanation
C noob here. I don't understand the point of the for loop if you're not using "i" at all, just counting to make sure you do the work on p_Person 100 times? Also, I don't get why you set ->name to 0 AFTER you've increased the pointer... ? Clarifications welcome. Thanks!
5:48 Is that an example of proof that the scale factor of a struct corresponds to the size of a struct person?
I am actually interested at how do you check what you do on C, in the assembly editor.??? intrigued actually :)🤔🤭
objdump, a CLI tool
I accept the premise that the C/C++ compiler already knows you are coding a struct and calculates the memory position of the array people. if you change the value of p_Person anywhere in the code, does that mean p_Person points to that Person in the sequence? Example:
p_Person = 3 ;
p_Person -> name[0] = 0 ;
p_Person -> age = 0 ;
Does this mean that p_Person points to Person[3] in other language's notation? In essence, which array element in Person does p_Person point to?
Many thanks.
No, it doesn't work that way. _p_Person = 3_ would redirect the pointer to memory address 3 (the third byte in virtual memory address space) and no longer point to your data structure at all. Any attempts to write to it would either trample any existing data there or page fault if memory address 3 is not currently allocated.
@@BlueEyesDY, thanks for the clarification. It helps my understanding.
This is by far the most complicated way to explain pointers and pointer arithmetic. I only understood this because I have years of experience in low level c++. The disassembly would have lost any beginner
Do you have a video where you talk about void pointers and pointers to pointers?
Need more RISC-V pls
Jajjajajajajaja went to the assembly to explain the inner workings
*Starts to read it like it's the alphabet*
That was impressive
for (int i=0; i
Hello, great video. Are you wokring in embedded programming? if yes would be possible to make video by you about design patterns etc.? thank you a lot
7:40 it crashed because you didn't assign pointer correctly. Remember this, NAME OF ARRAY IS ADRESS OF FIRST ELEMENT OF ARRAY.
So, to simplify with array of integers... int A[5] = {1, 2, 3, 4, 5}; int *p = &A[0]; or int *p = A; or if you just want to declare and then assign value in the next line: int *p; p = A; or p = &A[0]
yes! confusing stuff man thank you
thank you for the explanation - great job. - maybe out of context...but would it make sense or be correct to switch line 19 and 20 and simple in the new line 19 write p_Person->name = 0; Or would that not work at all ?
Thank you, amazing
Welcome back LLL
Well yeah, when you initialized the struct in main, you took a contiguous chunk in memory, so when you incremented that pointer it's just gonna point to the next space in memory because they're right next to each other. Right? or am I understanding it incorrectly(or overthinking it)?
yes, you're right
This video is pretty cool
But then you need to do 10x as much code to make this safe. I've very rarely used real C in the last several years even on embedded systems because C++ and vectors work so much better. I suppose maybe if you're on extremely constrained devices this is still useful, but at that point you have to be shipping millions of units to offset the salary for more dev time and not simply use a more powerful MCU.
You also didn't explain the syntax of the thumbnail which is exactly what I came here hoping to confirm.
if we identified the p_Person as a void pointer,so Then wouldn't the first spelling we wrote be correct?Surely We need to specify that our void pointers is Struct while we using void pointers
Wouldn't this be compiler dependent? Unless it's in the official C rules?
Also, hypothetically, how would you add 1 (a singular byte) to the address?
p_Person += (1/sizeof(Person)) ?
To increment by one byte. First cast the p_Person pointer to a pointer of one-byte type, such as uint8_t:
((uint8_t*)p_Person) += 1
It's part of the C programming language
@@MatiasGRodriguez that is wrong, you can't use the += operator with a cast like that. You would have to do something like:
p_Person = (struct Person *)((char *)p_Person + 1)
@Lenny McLennington Good grief. Thanks for reminding me why I prefer assembly over C. Don't get me wrong, when you're trying to write a mathematical equation C is obviously superior to assembly but the type-wrangling you have to do is really obnoxious for something that should be very simple.
@@williamdrum9899 you're right.
Implicitivity is the bane of all programming
For a language that is so unhelpful i would prefer the syntax of `p_Person += sizeof(struct Person)`. It feels odd to have the c compiler be helpful at all.
100 in hex 0x64, and i know you wrote for loop comparison as i < 100 only, but compiler compared it against 99 and uses jump when less or equal. How did that happen, and isn't there a assembly equivalent of just jump when less?
3months late but I saw that it was comparing to 99 as well. 1 instruction you might be talking about is JL(Jump less) for arithmetic operations and JB(Jump below) for logical operations
hello, i know nothing about C and this might be the reason i'm finding funny that adding 1 to a pointer is WILD lol
Did this change at some point in the C standard or is there a lot code working on void* without casting them to the proper type? I can definitely remember seeing and incrementing by sizeof myself in older C code. By older I mean pre C99.
Assembly is the easiest way to understand pointers 👏👏👏
Especially 6502 Assembly. You'll learn real quick to prefix your numbers with #
Proper title: "This is 100% The Easiest Way to SEGFAULT"
😂
Bugs: name member is written after pointer is modified so first element in the array doesn't have it's name modified, then in the last iteration we write one-past-end-of-array. Iteration count should be 25 when we do += 4 for the pointer, again writing past end of array.
The last bug would be avoided automatically if we just accessed people[i] ; i += 4, no problem of pointer and index going "out of sync"
Idk why people will always use pointer declarations of the form
"T *Name = &x;"
instead of
"T* Name = &x;"
When I was learning C for the first time I was always so confused about the use of an asterisk in both de-referencing a pointer and declaring a pointer type. It's always easier (and still correct) for me to understand when I group the asterisk with the actual type, since it then translates logically to "This variable of a 'Type T pointer' is named Name and is equal to the address of x" instead of "This variable of a 'Type T' is named *Name (and since there's an asterisk it's a pointer) and is equal to the address of x". Just kind of a runaway thought, but I would recommend anyone learning C to just group your type and asterisk together so you can see that you are actually declaring a pointer, and it's not just a variable with an asterisk at the beginning of it's name. It then also makes more sense when de-referencing, as you are looking at the value, not the pointer that you declared.
i think its the convention u are trying to separate the type with pointer or variable