The following is the C code:
char *ptr[100];
printf("%s\n",*ptr++);
My question is: we know that array name is not a variable (Ritchie's book "The C programming language" Page 99), so if we define
int *pa,a[5];
This is legal:
pa = a; pa++;
while this is illegal:
a++;
Here char *ptr[100] defines a char pointer array, so ptr represents the initial address of the array. For the above code, if *ptr++ means *(ptr++), this is illegal because array name cannot be used as a variable, and also it's meaningless because *(ptr++) still gets an address as opposed to %s. However, if *ptr++ means (*ptr)++, it also looks strange... Can anyone help to understand this?
The above is my thinking process, my question is: how can *ptr++ give us a string as the code printf("%s\n",*ptr++); says?
Postfix increment ++ has a higher precedence than dereference *.
Thus, *ptr++ is interpreted as *(ptr++). And as you've correctly figured out, ptr++ isn't allowed if ptr is an array (of anything, values or pointers).
If ptr is a pointer, then *ptr++ will indeed increment the pointer (so it will point to the next value after the operation), and this expression will return the current pointed-to value (before increment of the pointer). This expression is indeed sometimes used, e.g. for copying memory areas, e.g.:
while (...) {
*dest++ = *src++; // Copy the current element, then increment both pointers
}
*ptr++ doesn't necessarily give you a string — it gives you a string if and only if ptr is pointing to a string. And in this case, it's not necessary to post-increment just to get the string — *ptr would be enough. ++ is done for another purpose.
For example, it ptr is a pointer to an "array" of strings (i.e. a pointer to a pointer to achar, i.e. char **ptr), then you could print all the strings like this:
int i;
for (i = 0; i < N; ++i) {
printf("%s\n",*ptr++); // '++' "jumps" to the next string
}
I believe this link should help you.
C/C++ int[] vs int* (pointers vs. array notation). What is the difference?
The array notation has a bounds limit while the pointer notation does not.
If you wanted to access the next in the array with array notation it would look something like a[n++] n being whatever the current index of the array you are at. Where as assigning ptr to a sets ptr to the first index of the array and you can increment a pointer.
*ptr++ is parsed as *(ptr++) - apply the ++ operator to ptr, then dereference the result.
As written, this results in a constraint violation - ptr is an array of pointers to char, and an expression of array type is a non-modifiable lvalue, and as such may not be the target of an assignment or the operand of the ++ and -- operators.
The line
printf("%s\n",*ptr++);
should (indeed, must) result in the compiler issuing a diagnostic on the order of "invalid lvalue in increment" or something along those lines.
No. 1:
void main(void)
{
int i;
char *ptr[3]={"how","are","you"};
for (i=0;i<3;i++)
printf("%s\n",ptr[i]);
}
This prints out correct answer. Good!
No.2:
void main(void)
{
int i;
char *ptr[3];
*ptr[0] = "how";
*ptr[1] = "are";
*ptr[2] = "you";
for (i=0;i<3;i++)
printf("%s\n",ptr[i]);
}
warning: assignment makes integer from pointer without a cast [enabled by default].....Why can't initialize like this?
No.3:
void main(void)
{
int i;
char *ptr[3]={"how","are","you"};
printf("%s\n",*ptr++);
}
error: lvalue required as increment operand....Why can't we use *ptr++?
Related
a beginner question over here but why exactly do we do that? like how does that work/what's the logic behind it?
example of what I mean (I'm also wondering why we only did that with the Create function)
#include<stdio.h>
void create(int *T,int n){ int i; printf("tab: ");
for(i=0;i<n;i++) scanf("%d",&T[i]);
}
void print(int T[],int n){ int i;
for(i=0;i<n;i++) printf("[%d]",T[i]);
}
int main(){ int n;
printf("size: "); scanf("%d",&n);
int T[100];
create(T,n);
print(T,n);
}
Unless it is the operand of the sizeof, _Alignof, or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted, or "decay", to an expression of type "pointer to T" and its value will be the address of the first element of the array.
So when you call a function with an array expression as a parameter:
T arr[N];
...
foo( arr );
what the function actually receives is a pointer to the first element of the array - it's equivalent to calling the function as
foo( &arr[0] );
There is a reason for this behavior. Ritchie derived C from an earlier language called B, and when you created an array in B the compiler set aside an extra word to store an offset to the first element. The subscript operation a[i] was defined as *(a + i) - given the starting address stored in a, offset i words from that address and dereference the result.
Ritchie wanted to keep that subscript behavior (a[i] == *(a + i)), but he didn't want to keep the explicit pointer that behavior required, so instead he created the rule that any time the compiler saw an array expression it would convert that expression to a pointer to the first element, except in the cases listed above.
No more need to store a separate pointer, but now arrays lose their "array-ness" under most circumstances, including when they are passed as function arguments.
In the context of a function parameter declaration, T a[N] and T a[] are "adjusted" to T *a. Most of the time we just declare it as a pointer, since that's what we actually receive.
int T[] is the same as int* T in a function parameter. Arrays always decay to pointers in C when passed to functions. So int T[] and int* T are different syntax for the same thing, and you can use either or.
A pointer is just a memory address. When we say int* a, it means "a memory address where we expect an element of type int to be stored".
Arrays are internally treated as pointers. Lets look at the following code so that you can understand this better.
int b[4] = {1,2,3,4};
*b == 1, because b is the memory address where the first element of the array is stored.
What happens when we access b[1]? Well, the compiler knows that each element of the array is of type int, and it knows the size of type int.
So it just adds that to the original memory address of the b pointer and returns the content of that address.
Of course, you can replicate this behavior without the use of arrays.
For example:
*(b) = b[0]
*(b+1) = b[1]
*(b+2) = b[2]
and so on.
Hope this helps. I recommend you to start tinkering with this until you really understand it: pointer arithmetic is very important to understand C programming.
I'm learning pointers in C but I'm slightly confused with this example. What is the pointer logistic for the pointers in the three printf() statements below? What are these: *(char*)ptr, *(int*)ptr, (char*)ptr+2, exactly doing?
#include<stdio.h>
int main()
{
void *ptr;
char ch=74, *cp="CS107";
int j=65;
ptr=&ch;
printf("%c\n", *(char*)ptr);
ptr=&j;
printf("%c\n", *(int*)ptr);
ptr=cp;
printf("%s\n", (char*)ptr+2);
return 0;
}
I believe you're already got your answer, but just to clarify a hidden point, let me add some more info to already existing answers.
printf("%c\n", *(char*)ptr);
Cast the void pointer ptr to a char pointer, then de-reference to print the char value.
printf("%c\n", *(int*)ptr);
Cast the void pointer ptr to an int pointer, then de-reference to print the char representation of that int value.
printf("%s\n", (char*)ptr+2);
Here, the operator precedence comes into play. As the cast operator will take precedence over binary addition, first the ptr will be casted to char *, and then, the pointer arithmetic will come into effect, incrementing the pointer to point to the 3rd char element (0 based indexing, remember?).
(char*)ptr is called casting. A pointer of one type(ptr) is cast to point to a variable of another type(char*).
In your example, ptr is a pointer of type void, and it is used at various places to point to different types of variables.
ptr=&ch; this makes it point to a variable of type char.
However, the pointer ptr itself is of type void only, so later in printf() statement, it has to be explicitly casted to type char* to make it work.
printf("%c\n", *(char*)ptr);
^^^^^^^
Then, it is dereferenced to access the element residing in that memory.
printf("%c\n", *(char*)ptr);
^
Same goes for other types which follows.
*(char*)ptr : Treat the value of ptr as a pointer that pointing to char data, then read the char data pointed by ptr
*(int*)ptr : Treat the value of ptr as a pointer that pointing to int data, then read the int data pointed by ptr
(char*)ptr+2 : Treat the value of ptr as a pointer that pointing to char data, then calculate a pointer pointing to a char data which is 2 elements ahead from the element pointed by ptr
void check(void* elemAddr){
char* word = *((char**)elemAddr);
printf("word is %s\n",word);
}
int main(){
char array[10] = {'j','o','h','n'};
char * bla = array;
check(&bla);
check(&array);
}
Output:
word is john
RUN FINISHED; Segmentation fault; core dumped;
First one works, but second not. I don't understand why this happens.
The problem is, when we do &array, we are getting a char (*)[10] from an char [10], instead of a char **.
Before we do our experiment, I will emphasize that, when we pass an array as an argument to a function, C actually casts the array to a pointer. The big bucket of data is not copied.
Thus, int main(int argc, char **argv) is identical to int main(int argc, char *argv[]) in C.
This made it available for us to print the address of an array with a simple printf.
Let's do the experiment:
char array[] = "john";
printf("array: %p\n", array);
printf("&array: %p\n", &array);
// Output:
array: 0x7fff924eaae0
&array: 0x7fff924eaae0
After knowing this, let's dig into your code:
char array[10] = "john";
char *bla = array;
check(&bla);
check(&array);
bla is char *, and &bla is char **.
However, array is char [10], and &array is char (*)[10] instead of char **.
So when you pass &array as an argument, char (*)[10] acts like a char * when passing as an argument, as is said above.
Therefore **(char **) &bla == 'j' while *(char *) &array == 'j'. Do some simple experiments and you will prove it.
And you are casting void *elemAddr to a char ** and try to deference it. This will only work with &bla since it is char **. &array will cause a segfault because "john" is interpreted as an address as you do the cast.
For check(&bla); you are sending pointer to pointer
void check(void* elemAddr){
char* word = *((char**)elemAddr); // works fine for pointer to pointer
printf("word is %s\n",word);
}
This is working fine.
But, for check(&array); you are passing pointer only
void check(void* elemAddr){
char* word = *((char**)elemAddr); // This is not working for pointer
char* word = *(char (*)[10])(elemAddr); // Try this for [check(&array);]
printf("word is %s\n",word);
}
Full Code--
Code for check(array);:
void check(void* elemAddr){
char* word = *(char (*)[10])(elemAddr);
printf("word is %s\n",word);
}
int main() {
char array[10] = {'j','o','h','n'};
check((char*)array);
return 0;
}
Code for check(&bla);:
void check(void* elemAddr){
char* word = *((char**)elemAddr);
printf("word is %s\n",word);
}
int main() {
char array[10] = {'j','o','h','n'};
char* bla = array;
check(&bla);
return 0;
}
The C specification says that array and &array are the same pointer address.
Using the name of an array when passing an array to a function will automatically convert the argument to a pointer per the C specification (emphasis mine).
6.3.2.1-4
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.
So calling func(array) will cause a pointer to char[] to be passed to the function. But there is a special case for using the address-of operator on an array. Since array has type "array of type" it falls into the 'Otherwise' category of the specification (emphasis mine).
6.5.3.2-3
The unary & operator yields the address of its operand. If the operand
has type ‘‘type’’, the result has type ‘‘pointer to type’’. If the
operand is the result of a unary * operator, neither that operator nor
the & operator is evaluated and the result is as if both were omitted,
except that the constraints on the operators still apply and the
result is not an lvalue. Similarly, if the operand is the result of a
[] operator, neither the & operator nor the unary * that is implied by
the [] is evaluated and the result is as if the & operator were
removed and the [] operator were changed to a + operator. Otherwise,
the result is a pointer to the object or function designated by its
operand
So calling func(&array) will still cause a single pointer to be passed to the function just like calling func(array) does since both array and &array are the same pointer value.
Common-sense would lead you to believe that &array is a double pointer to the first element of the array because using the & operator typically behaves that way. But arrays are different. So when you de-reference the passed array pointer as a double pointer to the array you get a Segmentation fault.
This is not a direct answer to your question, but it might be helpful to you in the future.
Arrays are not pointers:
type arr[10]:
An amount of sizeof(type)*10 bytes is used
The values of arr and &arr are necessarily identical
arr points to a valid memory address, but cannot be set to point to another memory address
type* ptr = arr:
An additional amount of sizeof(type*) bytes is used
The values of ptr and &ptr are typically different, unless you set ptr = (type*)&ptr
ptr can be set to point to both valid and invalid memory addresses, as many times as you will
As with regards to your question: &bla != bla == array == &array, and therefore &bla != &array.
One problem is that your char array is NOT NECESSARILY going to be null-terminated. Since array is an automatic variable that is allocated locally on the stack, it is not guaranteed to be zeroed-out memory. So, even though you are initializing the first 4 chars, the latter 6 are left undefined.
However ...
The simple answer to your question is that &bla != &array so your check() function is assuming it will find null-terminated character arrays at 2 different addresses.
The following equations are true:
array == &array // while not the same types exactly, these are equivalent pointers
array == bla
&array == bla
*bla == array[0]
&bla is never going to equal anything you want because that syntax references the address of the bla variable on the local stack and has nothing to do with its value (or what it points to).
Hope that helps.
I am just trying to understand the pointers concept in C and came across this error.
I got the "lvalue required as increment operand" error for the following code. Please help me understand what is wrong.
#include<stdio.h>
int main()
{
char *s[] = {"black", "white", "pink", "violet"};
++s; //error at this line
printf("%s\n", *s);
char **ptr[] = {s+3, s+2, s+1, s}, ***p;
p = ptr;
++p;
printf("%s", **p+1);
return 0;
}
s is an array of pointers to char. Array names are non-modifiable l-value. ++s is modifying s which can't be modified.
Array Names vs Pointer Variables:
As we have seen, when we declare an array, a contiguous block of memory is allocated for the cells of the array and a pointer cell (of the appropriate type) is also allocated and initialized to point to the first cell of the array. This pointer cell is given the name of the array. When memory is allocated for the array cells, the starting address is fixed, i.e. it cannot be changed during program execution. Therefore, the value of the pointer cell should not be changed. To ensure that this pointer is not changed, in C, array names may not be used as variables on the left of an assignment statement, i.e. they may not be used as an Lvalue. Instead, if necessary, separate pointer variables of the appropriate type may be declared and used as Lvalues.
Suggested reading: Is array name a pointer in C?
The problem is that in expressions an array name is converted to an rvalue of pointer to the first element of the array. That is a temporary object of type in your case char ** is created. You may not increment a temporary object. The situation is similar to what occur in the following code snippet
int x = 10;
++( x + 5 );
The compiler will issue a similar error message.
For example in your code snippet you may not write
char **ptr[] = { ++(s+3), s+2, s+1, s};
The compiler will issue the same error. In fact when you write ++s then it is equivalent to ++( s + 0 ) that is you deal not with the original array but with some temporary pointer that is rvalue`
I am getting an odd error in a small C program in which I'm trying to learn how to use pointers.
I assumed everything was working because it compiled but when I try to run the code, it says C program has stopped responding. I must be doing something incorrect... Any help?
#include <stdio.h>
int main()
{
int k = 22;
int z = 11;
int *ptr = k;
printf("%d \n",*ptr);
ptr++;
printf("%d \n",*ptr);
getchar();
return 0;
}
You need to assign address of k not it's value..
int *ptr = &k;
Also after ptr++; *ptr would print garbage value(though behavior is undefined)
It is very simple to understand pointer, the pointer is a value to store an memory address, and you must always ensure the memory address is avaiable!
so here is the 1st error;
int *ptr = k;
so 1st you shall assign an valid memory address to the ptr before you use it, correct one shall be
int *ptr;
ptr = &K
star (*) is use to get the value which is stored in the memory address you assigned. so to increase the value , you shall
(*ptr)++
instead of
ptr++
this will increase the address instead of value itself, and you r really unknown what is stored there.
Your problem is here:
int *ptr = k;
you are assigning the value of k to ptr not the address of, so you are missing the address operator(&):
int *ptr = &k;
^
gcc by default gives the following warning:
warning: initialization makes pointer from integer without a cast [enabled by default]
So if you are not seeing a waring then you should enable warnings.
Performing indirection on ptr after you increment here:
ptr++;
printf("%d \n",*ptr);
^
indirection
it is undefined behavior in this case since it will be pointing to one past the last element, since pointers to non-arrays are treated as 1 element arrays for this case as per C99 section 6.5.6 Additive operators paragraph 7.