Factsheet
Since I’ve started exploring C, I’ve realized that many programming languages rely on libraries built using C “bindings.” I know C is fast and simple, so why don’t people just stick to using and improving C instead of creating new languages every couple of years?
I am currently working on an assignment in C where we are required to make a stack by simply using pointers.
I know the line: int *ptr = &val; declares ptr to be a "pointer to"(which is my interpretation of what asterisk * means in C) the "address of" the integer variable val.
When I want to create a double pointer, or a pointer to a pointer, I do so like:
int **ptr_ptr = &ptr; By setting ptr_ptr to a "pointer to" the address of pointer ptr.
When we use the asterisk anywhere other than in a declaration, it is usually referred to as dereferencing that pointer (I think), and grabbing the value that the pointer actually points to. This goes against my intuition that an asterisk means "pointer to".
Could anybody explain the proper meaning of the asterisk in C? Is it just that it means different things depending on how it is used (i.e. in a declaration versus anywhere else)?
Thanks!
In C, arguments are passed by values. For example if you have an integer varaible in main
int main( void )
{
int x = 10;
//...
and the following function
void f( int x )
{
x = 20;
printf( "x = %d\n", x );
}
then, if you call the function in main like this
f( x );
then the parameter gets the value of variable x in main. However the parameter itself occupies a different extent in memory than the argument. So any changes of the parameter in the function do not influence to the original variable in main because these changes occur in a different memory extent.
So how to change the varible in main in the function?
You need to pass a reference to the variable using pointers.
In this case the function declaration will look like
void f( int *px );
and the function definition will be
void f( int *px )
{
*px = 20;
printf( "*px = %d\n", *px );
}
In this case, the memory extent occupied by the original variable x is changed because, within the function, we get access to this extent using the pointer
*px = 20;
Naturally the function must be called in main like this
f( &x );
Take into account that the parameter itself that is the pointer px is a local variable of the function. That is, the function creates this variable and initializes it with the address of variable x.
Now let's assume that in main you declared a pointer for example the following way
int main( void )
{
int *px = malloc( sizeof( int ) );
//..
And the function is defined like
void f( int *px )
{
px = malloc( sizeof( int ) );
printf( "px = %p\n", px );
}
As parameter px is a local variable, assigning to it any value does not influence the original pointer. The function changes a different extent of memory than the extent occupied by the original pointer px in main.
How to change the original pointer in the function? Just pass it by reference!
For example
f( &px );
//...
void f( int **px )
{
*px = malloc( sizeof( int ) );
printf( "*px = %p\n", *px );
}
In this case, the value stored in the original pointer will be changed within the function because the function is using dereferencing, accessing the same memory extent where the original pointer was defined.
Q: what is this (**)?
A: Yes, it's exactly that. A pointer to a pointer.
Q: what use does it have?
A: It has a number of uses. Particularly in representing 2 dimensional data (images, etc). In the case of your example char** argv can be thought of as an array of an array of chars. In this case each char* points to the beginning of a string. You could actually declare this data yourself explicitly like so.
char* myStrings[] = {
"Hello",
"World"
};
char** argv = myStrings;
// argv[0] -> "Hello"
// argv[1] -> "World"
When you access a pointer like an array the number that you index it with and the size of the element itself are used to offset to the address of the next element in the array. You could also access all of your numbers like so, and in fact this is basically what C is doing. Keep in mind, the compiler knows how many bytes a type like int uses at compile time. So it knows how big each step should be to the next element.
*(numbers + 0) = 1, address 0x0061FF1C
*(numbers + 1) = 3, address 0x0061FF20
*(numbers + 2) = 4, address 0x0061FF24
*(numbers + 3) = 5, address 0x0061FF28
The * operator is called the dereference operator. It is used to retrieve the value from memory that is pointed to by a pointer. numbers is literally just a pointer to the first element in your array.
In the case of my example myStrings could look something like this assuming that a pointer/address is 4 bytes, meaning we are on a 32 bit machine.
myStrings = 0x0061FF14
// these are just 4 byte addresses
(myStrings + 0) -> 0x0061FF14 // 0 bytes from beginning of myStrings
(myStrings + 1) -> 0x0061FF18 // 4 bytes from beginning of myStrings
myStrings[0] -> 0x0061FF1C // de-references myStrings @ 0 returning the address that points to the beginning of 'Hello'
myStrings[1] -> 0x0061FF21 // de-references myStrings @ 1 returning the address that points to the beginning of 'World'
// The address of each letter is 1 char, or 1 byte apart
myStrings[0] + 0 -> 0x0061FF1C which means... *(myStrings[0] + 0) = 'H'
myStrings[0] + 1 -> 0x0061FF1D which means... *(myStrings[0] + 1) = 'e'
myStrings[0] + 2 -> 0x0061FF1E which means... *(myStrings[0] + 2) = 'l'
myStrings[0] + 3 -> 0x0061FF1F which means... *(myStrings[0] + 3) = 'l'
myStrings[0] + 4 -> 0x0061FF20 which means... *(myStrings[0] + 4) = 'o'
It took me a lot of time to understand but it seems I finally got it.
All you have to do is add along second axis.
let's take :
np.c_[np.array([1,2,3]), np.array([4,5,6])]
But there isn't second axis. So we mentally add one.
so shape of both array becomes (3,1).
So resultant shape would be (3,1+1) which is (3,2). which is the shape of result -
array([[1, 4],
[2, 5],
[3, 6]])
Another ex.:
np.c_[np.array([[1,2,3]]), 0, 0, np.array([[4,5,6]])]
shapes:
np.array([[1,2,3]]) = 1,3
np.array([[4,5,6]]) = 1,3
0 so we can think of it as [[0]] = 1,1
So result 1,3+1+1+3 = 1,8
which is the shape of result : array([[1, 2, 3, 0, 0, 4, 5, 6]])
Use IPython's ? syntax to get more information:
In [2]: c_?
Type: CClass
Base Class: <class 'numpy.lib.index_tricks.CClass'>
String Form:<numpy.lib.index_tricks.CClass object at 0x9a848cc>
Namespace: Interactive
Length: 0
File: /usr/lib/python2.7/dist-packages/numpy/lib/index_tricks.py
Docstring:
Translates slice objects to concatenation along the second axis.
This is short-hand for ``np.r_['-1,2,0', index expression]``, which is
useful because of its common occurrence. In particular, arrays will be
stacked along their last axis after being upgraded to at least 2-D with
1's post-pended to the shape (column vectors made out of 1-D arrays).
For detailed documentation, see `r_`.
Examples
--------
>>> np.c_[np.array([[1,2,3]]), 0, 0, np.array([[4,5,6]])]
array([[1, 2, 3, 0, 0, 4, 5, 6]])