ProgrammingThis forum is for all programming questions.
The question does not have to be directly related to Linux and any language is fair game.
Notices
Welcome to LinuxQuestions.org, a friendly and active Linux Community.
You are currently viewing LQ as a guest. By joining our community you will have the ability to post topics, receive our newsletter, use the advanced search, subscribe to threads and access many other special features. Registration is quick, simple and absolutely free. Join our community today!
Note that registered members see fewer ads, and ContentLink is completely disabled once you log in.
If you have any problems with the registration process or your account login, please contact us. If you need to reset your password, click here.
Having a problem logging in? Please visit this page to clear all LQ-related cookies.
Get a virtual cloud desktop with the Linux distro that you want in less than five minutes with Shells! With over 10 pre-installed distros to choose from, the worry-free installation life is here! Whether you are a digital nomad or just looking for flexibility, Shells can put your Linux machine on the device that you want to use.
Exclusive for LQ members, get up to 45% off per month. Click here for more info.
I've never used a full-scale IDE but I find the geany editor ideal for writing code.
I never found IDEs helpful. They just get in the way, I am using VI/Neovim (both Vi and neovim) myself at the moment. I plan to either use ED or write my own, I love syntax highlighting and really having my code in view; but ED is just a nice way to not have anything in my way.
I never found IDEs helpful. They just get in the way, I am using VI/Neovim (both Vi and neovim) myself at the moment. I plan to either use ED or write my own, I love syntax highlighting and really having my code in view; but ED is just a nice way to not have anything in my way.
Distribution: Currently: OpenMandriva. Previously: openSUSE, PCLinuxOS, CentOS, among others over the years.
Posts: 3,881
Original Poster
Rep:
@RT, FOSSilized_Daemon's first post #413 was very much on-topic and was directly responding to my OP (as they quoted, although they didn't need to quote the whole post). FOSSilized_Daemon's second post #415 was in response to Contrapak's question, which we can hardly blame FOSSilized_Daemon for in all "fairness". I was not saying otherwise. I did not mark either of FOSSilized_Daemon's posts as "helpful" (not saying they weren't) - my guess would be that was Contrapak that did, but only FOSSilized_Daemon really knows the answer to that (and I'm not asking them for it either). But since Contrapak has derailed this thread enough as it is, I don't wish to argue the point with you all the same. But that said, I still maintain what I said, and I believe I do have a right to say that given I am the one that started this thread to begin with. But moving on...
I've been re-reading the chapter about "Pointers", and while they do make more sense to me now; I do still have a question that isn't quite clear to me. Now it say's the following;
Quote:
Originally Posted by C book
One thing worth remembering when dealing with pointers that are sent to
functions as arguments: The value of the pointer is copied into the formal parameter
when the function is called. Therefore, any change made to the formal parameter by the
function does not affect the pointer that was passed to the function. But here’s the catch: Although the pointer cannot be changed by the function, the data elements that the
pointer references can be changed!
What I've highlighted in bold above isn't clear to me. I DO understand the second part of that same sentence, which if I'm right means that; you CAN change the "value" of whatever the pointer is pointing to from within a function that you have passed the pointer to as an "argument". But my question is; does what I've highlighted mean that, while you CAN change the value of what's being pointed to by the pointer in question; you CANNOT change what the pointer is pointing to ? Correct or Incorrect?
I have written some code based on the example in the book below where it say's what I've quoted above, and while my test code does seem to clarify my "theory" above, I'm still not quite sure I get the point of what I've highlighted in what I've quoted above.
Here's the test code I wrote to test my theory;
Code:
#include <stdio.h>
void test (int *int_pointer);
int main(void) {
int i = 50, *p = &i;
printf("Value of \"i\" before the function call: %i\n", i);
test(p);
printf("\nValue of \"i\" after the function call: %i\n", i);
return 0;
}
void test (int *int_pointer) {
int testvar = 0;
*int_pointer = 100;
int_pointer = &testvar;
*int_pointer = 200;
printf("\nValue of \"testvar\" inside function \"test\": %i\n", *int_pointer);
}
Here's the result of it when run;
Code:
[james@jamespc devel]$ ./ptr_test_func
Value of "i" before the function call: 50
Value of "testvar" inside function "test": 200
Value of "i" after the function call: 100
It is talking about the pointer "p" used in "test(p)".
Changes to "int_pointer" (note: not what is pointed to by "int_pointer") will change the "p" pointer in main.
To make that clearer you needed to print the value of "int_pointer" in the test function (remove the indirection, and instead of using %i in the format, use %p). Then in the main print the "p" variable.
This is the same thing that happens with
Code:
void test(int val)
{
val = 256;
printf("in test: val = %d\n",val);
}
int main(void)
{
int main_val = -500;
printf("main_val = %d\n",main_val);
test(main_val);
printf("after calling test; main_val = %d\n",main_val);
return(0);
}
The value of main_val doesn't change just because the parameter passed was changed. This is the "pass by value" of C parameters. The only change is that the value passed is the pointer to the value, not the value itself.
But the limitation on the parameter remain.
What makes it look confusing is when you DO want to change the pointer... You then have to pass a pointer to the pointer...
So you get a different test program
Code:
#include <stdio.h>
int value = 256;
int *a = &value;
int other_value = 0;
void test(int **ptr_ptr)
{
printf("in test: ptr_ptr = %p\n",ptr_ptr);
*ptr_ptr = &other_value; // note the indirection
}
int main(void)
{
printf("main value = %d, and a= %p\n",value, a);
test(&a);
printf("after calling test; a = %p\n",a);
return(0);
}
Now you can see the pointer a changed... but that is due to the value being passed was a pointer to the pointer....
BTW, I put the test function first simply to avoid having to put a forward declaration. It is a style variation I use because I have found that if I change the function, I sometimes forget to change the forward declaration, thus creating extra errors when the parameters change.
Distribution: Currently: OpenMandriva. Previously: openSUSE, PCLinuxOS, CentOS, among others over the years.
Posts: 3,881
Original Poster
Rep:
Quote:
Originally Posted by jpollard
It is talking about the pointer "p" used in "test(p)".
Changes to "int_pointer" (note: not what is pointed to by "int_pointer") will change the "p" pointer in main.
...
The value of main_val doesn't change just because the parameter passed was changed. This is the "pass by value" of C parameters. The only change is that the value passed is the pointer to the value, not the value itself.
...
Thanks again for your help jpollard!
While I'm little clearer now; I'm still not sure I'm totally clear on what you mean. I can see what your example programs are doing, but I'm still not sure I'm clear about what actually changes and what doesn't.
To use your first example program as an example; do you mean that the "main_val" variable being passed to the "test" function is still pointing to "var", but this CAN be changed; but the actual "value" of "main_val" CANNOT be by the "test" function? Because I'm still not clear.
Changes to "int_pointer" (note: not what is pointed to by "int_pointer") will change the "p" pointer in main.
I think that should have read "will not". The function gets an independent copy of the pointer, though they will both initially have the same value (i.e. point to the same memory location).
Code:
#include <stdlib.h>
#include <stdio.h>
static int x = 1000 ;
static int y = 2000 ;
void change_value( int *my_pointer )
{
*my_pointer = 10 ;
printf("my_pointer before change= %p\n", my_pointer) ;
my_pointer = & y ;
printf("my_pointer after change= %p\n", my_pointer) ;
return ;
}
int main( )
{
int *p = & x ;
printf("before p = %p, *p = %d\n", p, *p ) ;
change_value( p ) ;
printf("after p = %p, *p = %d\n", p, *p ) ;
return EXIT_SUCCESS ;
}
output:
Code:
before p = 0x404030, *p = 1000
my_pointer before change= 0x404030
my_pointer after change= 0x404034
after p = 0x404030, *p = 10
The change to my_pointer does not change p. The value of x, a.k.a *p, a.k.a *my_pointer (before we changed it) is changed however.
Last edited by GazL; 06-30-2019 at 06:46 AM.
Reason: cleanup whitespace
To use your first example program as an example; do you mean that the "main_val" variable being passed to the "test" function is still pointing to "var", but this CAN be changed; but the actual "value" of "main_val" CANNOT be by the "test" function? Because I'm still not clear.
When the function "test" is called, it is a COPY of the value of "main_val" that is the parameter.
Changing the parameter value in the test function does nothing to the value of "main_val".
This is where a bit of assembly COULD make things clearer, but it needs a decent machine architecture to keep things simple. Unfortunately, we don't have one.
The parameters are copies of values pushed on the stack, any changes within the function called can only change the value on the stack. When the function returns, the value(s) on the stack are discarded.
The only way to make changes to the original value is if it is an ADDRESS of where the original value is what gets placed on the stack. Then the function can use indirection to change the original value as what is on the stack is a pointer. The function can usually change the pointer (unless it is defined as const) but now the pointer points elsewhere; and when the function returns the pointer value on the stack is discarded.
Distribution: Currently: OpenMandriva. Previously: openSUSE, PCLinuxOS, CentOS, among others over the years.
Posts: 3,881
Original Poster
Rep:
Thanks again GazL and jpollard!
I think I get what ya's are saying now, it's just the way it's phrased in the C book is really confusing. Thanks also for the example programs, as with that and what you guys have said it makes a lot more sense now. I think I was getting confused between the value of the actual variable and the actual pointer in hindsight.
You two have been really helpful to me, and I'm really grateful for the help both of you have given me. I wish I could thank you both in person. Thanks a lot again, and sorry about the confusion - it'll hopefully all make sense one day (not that what you guys have said so far doesn't). A BIG thanks to both of you again!
Quote:
Originally Posted by jpollard
...
The parameters are copies of values pushed on the stack, any changes within the function called can only change the value on the stack. When the function returns, the value(s) on the stack are discarded.
The only way to make changes to the original value is if it is an ADDRESS of where the original value is what gets placed on the stack. Then the function can use indirection to change the original value as what is on the stack is a pointer. The function can usually change the pointer (unless it is defined as const) but now the pointer points elsewhere; and when the function returns the pointer value on the stack is discarded.
I have read a little bit about the "stack", but nowhere near enough to be any expert on it though. But I would like to get a better understanding of exactly what the "stack" is and how exactly it works. But I might wait until I've got a better handle on C before tackling Assembly tho.
Thanks again GazL and jpollard!
I have read a little bit about the "stack", but nowhere near enough to be any expert on it though. But I would like to get a better understanding of exactly what the "stack" is and how exactly it works. But I might wait until I've got a better handle on C before tackling Assembly tho.
It isn't that it is difficult, it just has some ticky details - and the Intel architecture is one of the worst for ticky details before getting to comparisons with high level languages.
Personally, I like the PDP-11 as being the simplest yet sufficiently complete for explanations.
It is a 16 bit architecture and has only 8 registers (register 7 is the program counter, register 6 is the stack pointer, the others - 0,1,2,3,4,5 are general purpose 16 bit registers that CAN be used for implementing stacks... for 32 bit integers you have to combine [0,1], [2,3] and [4,5]. But the basic calling sequence is pretty much the same for all languages. The # below is for constant values. A number with "." is generally a decimal, without it then it is octal...
Code:
MOV value,-(SP) ; meaning push the contents of address value on the stack
JSP PC,address ; meaning save the program counter register on the stack and jump to the address
ADD #2,SP ; meaning discard the top element on the stack (the value pushed).
Now the subroutine (address) above has a bit more to do:
Code:
address: MOV R1,-(SP) ; save registers. R0 is always considered the return value,
MOV R2,-(SP) ; but it too could be saved
MOV R3,-(SP)
MOV R4,-(SP)
MOV R5,-(SP) ; this one is special by convention.
MOV SP,R5 ; this saves the current stack pointer in R5. this allows the function
; to use R5 with an offset to locate the parameter, or other offsets
; for local variables
MOV 12(R5),R0 ; this copies the value of the parameter to R0
ADD #5,R0 ; so now add 5 to it.
; assuming that is all...
MOV R5,SP ; restore the stack (and discard local variables.
MOV (SP)+,R5 ; restore the old r5
MOV (SP)+,R4
MOV (SP)+,R3
MOV (SP)+,R2
MOV (SP)+,R1
RTS PC ; which restores the PC, (a MOV (SP)+,PC would work too, but is slower)
Now the usage of R5 is that of a "frame pointer" that is a reference to WHERE on the stack the parameters, saved registers, and any local variables may be. You might also find references to "activation record" which is the area of the stack used for that information.
And this function is equivalent to
Code:
int address(int value)
{
return(value+5);
}
Because it is so short and doesn't use registers 1,2,3,4 - they don't even have to be saved or restored. Only the frame pointer (r5 here) is used. And if the registers aren't saved, it could just as easily use the stack pointer directly. It gets more useful when there are local variables as it provides a "fixed" reference for where they are on the stack.
Distribution: Currently: OpenMandriva. Previously: openSUSE, PCLinuxOS, CentOS, among others over the years.
Posts: 3,881
Original Poster
Rep:
I won't pretend I "completely" understand all of that, but from what I can tell, it seems that's assembly code. Hopefully I'm right there. I must say though, that explanation is quite good in my non-expert opinion. The "SP" means the "stack pointer" ?
I'll try to remember your post above jpollard when I can hopefully be in a better position to learn assembly and more about "stacks". But no, I won't ignore your post
But while I get an integer takes up a certain amount of "bits", and the processor deals in "bits"; I can't say I'm entirely clear on exactly what's meant by a 32 bit integer beyond the obvious. Let alone am any expert on "bits" and "bytes" in general, but hopefully one day it becomes more clear. Maybe when I know more about math it might be more clear.
I won't pretend I "completely" understand all of that, but from what I can tell, it seems that's assembly code. Hopefully I'm right there. I must say though, that explanation is quite good in my non-expert opinion. The "SP" means the "stack pointer" ?
yes. For some reason my old teacher hat got kicked... :-)
Quote:
I'll try to remember your post above jpollard when I can hopefully be in a better position to learn assembly and more about "stacks". But no, I won't ignore your post
I found assembly rather easy. My second language learned after FORTRAN - and the Fortran compiler gave me help as I could get it to print the assembly of the fortran program, annotated by the fortran itself. So it was mostly self taught.
The PDP-11 was the simplest general purpose stack machine I've ever used.
Quote:
But while I get an integer takes up a certain amount of "bits", and the processor deals in "bits"; I can't say I'm entirely clear on exactly what's meant by a 32 bit integer beyond the obvious. Let alone am any expert on "bits" and "bytes" in general, but hopefully one day it becomes more clear. Maybe when I know more about math it might be more clear.
Well, like earlier Intel processors, the registers were limited in size to 16 bits. But certain operations COULD do 32 bit arithmetic, but only if two registers were used. So r0,r1 combined provide a 32 bit integer.
It could also be done manually, by adding the low order 16 bit values togetether, adding a carry to the high order 16 bits, and add the high order 16 bit values together. But that takes more instructions, so the vendor added the hardware 32 bit add/subtract/multiply/divide as an option, then added 32 bit and 64 bit floating point. But the base machine remained a 16 bit processor, with 16 bit addressing. And like the 8086 with its expanded memory - the PDP-11 had an MMU that added either 2 or 6 bits to the physical address. The 2 bit addition allowed up to 262K (- 4K for memory mapped I/O registers), the 6 bits allowed up to 4MB (again, minus the 4K for I/O registers).
LinuxQuestions.org is looking for people interested in writing
Editorials, Articles, Reviews, and more. If you'd like to contribute
content, let us know.