Pointer to a struct accessed using index like an array - c

#include<stdio.h>
struct test{
int a;
int b;
}m;
int main()
{
m.a=5;m.b=7;
struct test *p;
p = &m;
printf("p[0] = %d\n",*(p+0));
printf("p[1] = %d\n",*(p+1));
return 0;
}
I get the following Ouput:
p[0] = 5
p[1] = 0
Can someone please explain this behavior? Is there a way to print all struct members using index?
Sorry about the typo.
(Updated)
As I could print 1st element using index 0 , though the type of pointer is of "struct test type" and 1st member is an integer , I could get the correct value. Hence, the question triggered about the cause of this behavior. And if all the members can be accessed using the index just like the 1st member was accessed.

In your code,
printf("p[0] = %d\n",*(p+0));
printf("p[0] = %d\n",*(p+1));
invokes undefined behavior as
you are passing wrong type of argument for %d format specifier.
You're accessing out of bound memory by saying *(p+1)
So, the output cannot be explained or justified in any way.
Now, that said, let's analyze the code a bit.
First of all,
p = &a;
is wrong, as there is no variable called a. There is a member variable a for the structure variable m, which can be accessed as m.a. So, you can somehow write
p = (struct test*) &m.a;
Now, using the address of the first element of the structure as the structure address is valid as there is no padding at the beginning of the structure. But that does not mean, you can apply pointer arithmetic directly on the structure pointer p and get each member variable, mainly because
There can be padding between two elements
The pointer p is of type struct test (Though you can get around by casting)

Get a proper compiler. Compiled with default settings on GCC 5, the compilation spits out:
foo.c: In function ‘main’:
foo.c:12:10: error: ‘a’ undeclared (first use in this function)
p = &a;
^
foo.c:12:10: note: each undeclared identifier is reported only once for each function it appears in
foo.c:13:12: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘struct test’ [-Wformat=]
printf("p[0] = %d\n",*(p+0));
^
foo.c:14:12: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘struct test’ [-Wformat=]
printf("p[0] = %d\n",*(p+1));
^
Perhaps you meant p = &m;.
After that your code invokes undefined behaviour. %d expects that the corresponding argument be an int, yet you provide a struct test. Furthermore on the latter print, the pointer is pointing to uninitialized memory after the m, and garbage is printed. Even the fact that you had 5 printed in the first case cannot be relied on. The C standard does not explain that why or why not this happens, the C standard says "at this time it is ok for the compiler to generate code that prints 42342, crash the program, or do whatever else, and it is fine by this standar d."
In C compilers the warnings usually mean "standard violations" in which case undefined behaviour is probable. A newcomer to C programming language should always consider all compiler warnings especially with default settings as being fatal errors.

I compiled your code with gcc and i got.
In function ‘main’:
error: ‘a’ undeclared (first use in this function)
p = &a;
^
note: each undeclared identifier is reported only once for each function it appears in
warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘struct test’ [-Wformat=]
printf("p[0] = %d\n",*(p+0));
^
warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘struct test’ [-Wformat=]
printf("p[0] = %d\n",*(p+1));
Assuming you meant p=&m, here is my explanation
printf("p[0] = %d\n",*(p+0));
printf("p[0] = %d\n",*(p+1));
First analyse whether deferring pointer like you did in printf function is correct or not.
The *(p+0) will give you the access to the struct test variabe m. As, you have assigned the address of a variable of type struct test to the pointer p, deferring pointer *p or *(p+0) will give you the access to the variable m. But the address assigned to the pointer is of a single variable not of the array of type struct test, deferring (p+1) will give you UNDEFINED BEHAVIOR. For example, *(p+1) would have worked if (not in printf statement)
struct test m[20];
struct test *p;
(*(p+0)).a=2;
(*(p+1)).a=5;
Is there a way to print all struct members using index?
You can. But there is no overloading of operators in c. So, you can achieve this by -
printf("a=%d, b=%d", p->a, p->b);
If pointer p points to array of type struct test, then you can use index in printf as
printf("a=%d, b=%d of %dth element", p[i].a, p[i].b, i);
My suggestion for your future code experimentation will be first analyse warning and error given by your code. The warnings can help to rectify most of the bugs in your code. I always compile my c/c++ code with gcc/g++ compiler and use flags like -Wall, -Wshadow, -Wextra. You can google a warning if you don't understand it.

You are assigning the address of a to p directly. a is a member of m and so cannot be accessed directly. And as of your code, you should use an integer pointer for your arithmetic.
you need to modify a part of your code
struct test *p;
p = &a;
to
int *p;
p = &m.a;
now the code should work as you desire.

First, you have a typo in your code.
Please correct it from p = &a; to p = &m;, so that the error complained by the compiler shall be removed.
Second, I guess you expect the output to be p[0] = 5 and p[1] = 7.
To achieve that, modify your code from
printf("p[0] = %d\n",*(p+0));
printf("p[1] = %d\n",*(p+1));
to
printf("p[0] = %d\n",p->a);
printf("p[1] = %d\n",p->b);
can work.

Related

confused by pointer and local variable in C

according to what i learned about function variables they should not 'live' outside their function scope,thus returning a local function variable should cause an error, i do get compiling warnings that i do expect, but it makes no sense to me because the program do work and i am little confused
i wrote the following program:
int * test(int x) {
int f=x+1;
return f; //- int to pointer conversion,idk why this works
}
int main () {
int x = 10;
printf("%d\n",test(x));
}
I first expected the program to crash because we make conversion from int type to pointer at function return, but apparently the program prints 11 which i did not expect, i know this is really bad to write like this anyways it was just a coincidence i wrote something like this and it worked and I had alot of questions after that
the warnings i expected to get :
Solution.c: In function ‘test’:
Solution.c:10:12: warning: return makes pointer from integer without a cast [-Wint-conversion]
return f;
^
Solution.c: In function ‘main’:
Solution.c:15:14: warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Wformat=]
printf("%d\n",test(x));
~^ ~~~~~~~
%ls
can anyone explain how this program works ?
You tell the compiler that function test() should return return a pointer to an integer. You don't. You return a value of an integer.
The compiler says, "You're probably doing unintentional stuff here, just sayin'. I'll cast this integer to a pointer for you."
Then the compiler says, "You want to print the value of an integer here but what you're giving is a pointer. Just sayin'. I'll cast this pointer to an integer for you."

Pointers in C : assigning a literal to int *

What is really happening here when we provide a definition like below?
int * a = 2;
What is really happening behind the scenes?
SOLVED
Here a will point to memory address 2.
The result of conversion from integer to a pointer is implementation-defined in C, quoting C99 Section 6.3.2.3:
5 An integer may be converted to any pointer type. Except as previously specified, the result is implementation-defined, might not be correctly aligned, might not point to an entity of the referenced type, and might be a trap representation.
So you shouldn't rely on such conversion except when the literal is 0 which will give you a null pointer.
In practice you are likely to find that the pointer will hold the memory address specified by the integer constant:
#include <stdio.h>
int main(void) {
int * a = 2;
printf("%p", a); /* prints 0x2 in gcc */
return 0;
}
Most compilers will also warn about this unsafe cast :
$ gcc test.c test.c: In function ‘main’: test.c:4:13: warning:
initialization makes pointer from integer without a cast [enabled by
default] int * a = 2;
^
In here the memory for "a" will not be allocated. So, it gives the segmentation fault error.
It will try to take the 2 as a address and pointer a will try to point the address 2. But it may be our system does not have that address or it may be allocated for some other process.
So, in the compiling time it will give the one warning message.
Warning: initialization makes pointer from integer without a cast [enabled by default]

Redefinition of a pointer in global scope

In this question I am thoroughly confused about this seemingly basic aspect of C. Consider these two lines of code:
int *ptr;
*ptr = 2;
gcc will emit the following warnings:
main.cpp:4:1: warning: data definition has no type or storage class [enabled by default]
*ptr = 2;
^
main.cpp:4:2: warning: type defaults to 'int' in declaration of 'ptr' [enabled by default]
*ptr = 2;
^
main.cpp:4:8: warning: initialization makes pointer from integer without a cast [enabled by default]
*ptr = 2;
^
What type is ptr being defaulted to, int or int* (as in, is ptr a pointer, or an int)? If so, does this mean that ptr is pointing to address 2, or is that unchanged? I would assume that it's changed because it crashes unless you give ptr a valid address.
int i = 5;
int *ptr;
*ptr = &i;
int main(){
printf("%d", *ptr); // 5
}
I am aware of the possibility of undefined behavior and that you shouldn't ignore warnings, but I'm trying to see what actually happens here.
For context, see the comment chain under this answer.
Here is what's going on: since the two lines that you are showing are in the file scope (as opposed to a local scope) both lines are treated as declarations, not as a declaration and an assignment statement. This is because there could be no statements at the file scope - only declarations and definitions are allowed.
Old C rules allowed declarations of type int to omit type altogether. Therefore, the second line is
A declaration / definition of ptr
...which is a pointer, because it has an asterisk
...and it is also a pointer to int, because the type is missing.
That last rule is very archaic, and it has been deprecated in the ANSI version of the language standard. That is why you get a warning. If you change your code to
int *ptr;
int *ptr = &i;
your code is going to compile and run (demo).
Now one question remains: how come the compiler does not complain about duplicate declarations? It turns out that the compiler will treat multiple identical declarations as one and the same, as long as they are entirely identical to each other.

Warnings in a very basic Pointer Program in C

I've just started with pointers in c. This program gives the desired output but I am getting the below warnings when I compile it, What am I doing wrong?
pointer.c:3: warning: initialization makes pointer from integer without a cast
pointer.c:5: warning: incompatible implicit declaration of built-in function ‘printf’
pointer.c:8: warning: assignment from incompatible pointer type
pointer.c:12: warning: assignment makes pointer from integer without a cast
And my code is:
int main (int argc, char *argv[])
{
int *x=2, *ampx, *starampx, starx;
printf("x=");
printf("%d\n",x);
ampx=&x;
printf("&x=");
printf("%d\n",&x);
starampx=*ampx;
printf("*&x=");
printf("%d\n",starampx);
return 0;
}
Here is why:
initialization makes pointer from integer without a cast - int *x = 2, ... should be int x = 2, ...
incompatible implicit declaration of built-in function ‘printf’ - You need to add an #include <stdio.h> line at the top
assignment from incompatible pointer type - This will be fixed by the #1 change
assignment makes pointer from integer without a cast - *starampx should be starampx in the declaration.
In addition, printing pointers should be done with %p specifier, not %d:
printf("%p\n",&x);
Here is your fixed program on ideone: link.
int *x=2
is actually causing your program to have undefined behavior. It tells the compiler to point at an address 2, which may or may not be valid and derferencing the pointer will causes an undefined behavior.
Whenever you are using pointers, You need to make the pointer point to a valid memory big enough to store a int before you can store anything. So you either need:
int i = 2;
int *x = &i;
or you simply use:
int x = 2;
Considering, how you use x later in the program the latter is what you need.
You need to include stdio.h which tells the compiler that printf is an standard c library function.
In your code, in this statement, you have a problem.
int *x=2, *ampx, *starampx, starx;
^ ^
| |
*x is a pointer. To store the value of 2, you would need to allocate space for the same. Instead of the current implementation, please try with
int x=2, *ampx, starampx, starx;

Isn't const supposed to be constant?

Check out this code
#include<stdio.h>
int main()
{
const int a=7;
int *p=&a;
(*p)++;
printf("*p=%d\np=%u\na=%d\n&a%u",*p,p,a,&a);
getch();
}
The output you get for this is
*p=8
p=1245064
a=8
&a1245064
How is this possible?? We declared variable a as constant. Doesnt this mean that the location pointed to by a can never be changed during the course of pgm execution??
That's undefined behavior - in your case it is working as you described, but it could just as well crash the program or cause any other problems. In your case const doesn't prevent the compiler from allocating the variable in modifiable memory, so you technically can modify it by obtaining a pointer to that variable and working through the pointer.
Undefined behaviour is not to be relied upon ;-)
Would you really want to use C if it actually prevented that from working? The fact it works that way is very much in the spirit of the language.
Reading your comment "it only gives a warning saying suspicious pointer conversion" it should be clear enough to deduce that you are doing something illegal.
You are assigning to a int * variable a const int * value.
The fact that C hasn't any runtime checkings to prevent you from modifying that memory address doesn't imply that it's allowed! (and in fact, the static type system checking is telling you that).
If yours doesn't detect this automatically, just get yourself a decent compiler. E.g clang gives me 4 problems with your code:
clang -c -o test-const.o test-const.c
test-const.c:17:7: warning: initializing 'int const *' discards qualifiers, expected 'int *' [-pedantic]
int *p=&a;
^ ~~
test-const.c:19:20: warning: conversion specifies type 'unsigned int' but the argument has type 'int *' [-Wformat]
printf("*p=%d\np=%u\na=%d\n&a%u",*p,p,a,&a);
~^ ~
test-const.c:19:32: warning: conversion specifies type 'unsigned int' but the argument has type 'int const *' [-Wformat]
printf("*p=%d\np=%u\na=%d\n&a%u",*p,p,a,&a);
~^ ~~
test-const.c:20:2: warning: implicit declaration of function 'getch' is invalid in C99 [-Wimplicit-function-declaration]
getch();
^
4 diagnostics generated.
These are all serious problems.

Resources