Can int store the base address of string in C? - c

Why do the code run without error ?
#include <stdio.h>
int main() {
int i="string"; //the base of string can be stored in a character pointer
printf("%s\n",i);
printf("%d",i);
return 0;
}
//compiling on ideone.com language c
OUTPUT:
string
134513984 //some garbage(address of "string")
Please explain if there is some flexibility in the pointer in c. I tried it for c++ which gives error: cannot convert ‘const char*’ to ‘int*’ in initialization

No, you cannot assume this in general. In part, this is because int may not be the same size as char * (in fact, on many 64-bit compilers it will not be the same size).
If you want to store a pointer as an integer, the appropriate type to use is actually intptr_t, from <stdint.h>. This is an integer which is guaranteed to be able to hold a pointer's value.
However, the circumstances when you'd actually want to do this are somewhat rare, and when you do do this you should also include an explicit cast:
intptr_t i=(intptr_t)"string"; //the base of string can be stored in a character pointer
This also complicates printing its value, you'll need to use a macro to be portable:
printf("%"PRIiPTR,i);
To print the original string, you should also cast:
printf("%s", (char *)i);

In general, no: the C standard states that conversions from pointers to integers are implementation defined. Further, this can be problematic on systems where sizeof(char *) and sizeof(int) are different (i.e. x86-64), for two reasons:
int i = "string"; can lose information, if the e.g. 64-bit pointer cannot fit in a 32-bit integer.
printf expects a pointer to be passed in, but gets a smaller integer. It winds up reading some garbage into the full pointer, and can crash your code (or worse).
Often times, however, compilers are "smart" enough to "fix" arguments to printf. Further, you seem to be running on a platform where pointers and integers are the same size, so you got lucky.

If you compiled this program with warnings (which you should) you'd get the following complaints:
main.c:3:9: warning: incompatible pointer to integer conversion initializing 'int' with an expression of type 'char [7]' [-Wint-conversion]
int i="string"; //the base of string can be stored in a character pointer
^ ~~~~~~~~
main.c:4:19: warning: format specifies type 'char *' but the argument has type 'int' [-Wformat]
printf("%s\n",i);
~~ ^
%d
2 warnings generated.
Warnings generally mean you're doing something that could cause unexpected results.

Most C compilers will let you do this, but that doesn't make it a good idea. Here, the address of the character array "string" gets stored in i. The printf options are determining how the integer is interpreted (as an address or an integer). This can be problematic when char* is not the same size as an int (e.g. on most 64 bit machines).
The C++ compiler is more picky and won't let you compile code like this. C compilers are much more willing, although they will usually generate warnings letting the programmer know it is a bad idea.

Your code is ill-formed in both C and C++. It is illegal to do
int i = "string";
in both languages. In both languages conversion from a pointer to an integer requires an explicit cast.
The only reason your C compiler accepted it is that it was configured by default for rather loose error checking. (A rather typical situation with C compilers.) Tighten up your C compiler settings and it should issue an error for the above initialization. I.e. you can use an explicit conversion
int i = (int) "string";
with implementation-dependent results, but you can't legally do it implicitly.
In any case, the warning your compiler emitted for the above initialization is already a sufficient form of a diagnostic message for this violation.

Related

C pointers: Assignment from incompatible pointer type

When I compile this code with gcc 7.3.0, I get an "assignment from incompatible pointer type".
int intVar = 1;
char* charPointer;
charPointer = &intVar;
printf("%d", *charPointer);
So far so good. I can deal with it by doing the pointer assignment this way:
charPointer = (char*)&intVar;
Now my doubt is: how is the second case different? I can still mess things up if I don't cast charPointer to int* when I, for example increment it by n or dereference it. So why does the compiler act differently in those two cases? Why should he care if pointer type does not match during an assignment? I would just like to understand the logic behind it.
Because of the casual type change int * to char *, the compiler warns of potential pitfalls. With a cast, the compiler assumes the coders know what they are doing.
In C, the various type of pointers can live in different places, have different sizes and be encoded differently.
It is very common for all object pointers (int*, char *, struct foo *) to share the same size and encoding, but, in general, that is not required.
In is not uncommon for Function pointers to be of a different size than object pointers.
char * and void * share the same size and encoding and character pointers point to data with a minimal alignment requirement. Converting a non-char* object pointer to char * always "work". The reverse is not always true. leading to undefined behavior (UB).
Converting to non-character pointer also risks anti-aliasing (the need for the compiler to keep track that changes of data via one pointer type is reflected in another). Very strange undefined behavior can result.>
Better code avoids pointer type changes1 and when needed, is explicit with a cast.
1 Exceptions include when changing an object pointer to void * or form void* when there is no alignment concerns.
Pointer conversions are kind of dangerous.
If the pointer type your converting from is insufficiently aligned for the target type, you get UB (==undefined behavior; read up on what it is unless you have already) already at the conversion.
Otherwise, if you get usually get UB on dereferencing because C's strict aliasing rules require that you access objects through lvalue types sufficiently compatible with their effective type.
While the last paragraph doesn't quite apply to conversions to char pointers as char pointers can alias any type, the warning (compilers could make it a hard error too) is still useful because the conversion is still kind of dangerous.
printf("%d", *(char*)&(int){0xFFFF});
will get you only the first byte (it is endianness dependent whether that is the most significant one or the least significant one), printing 255 (if the implementation's char type is unsigned) or -1 (if it is signed).
printf("%d", *&(int){0xFFFF});
will get you all the bytes that are in an int.
If the compiler lets you assign to a char * from an int * with just a warning, it should behave the same as with the cast, but you do need the cast for your C to be conformant (and for the compiler to be silent about the conversion).
As #Mike Holt says, it's not actually different, except that you have told the compiler "Don't worry, I mean to do this".
The compiler does worry because assigning to a pointer of a different type is usually not what you want to do. Your code is telling the compiler "Treat the memory holding this variable as if it were holding a variable of a different type". This is almost certainly platform specific behavior, and possibly undefined behavior, depending on the types.

Casting pointer to void when printing

I've been trying to work through some C fundamentals lately to try and build a basic understanding of lower level languages.
In one of the documents I've encountered (A tutorial on pointers and arrays in C)
the author uses a void pointer in a printf statement:
int var = 2;
printf("var has the value %d and is stored at %p\n", var, (void *) &var);
And states the reason:
I cast the pointers to integers into void
pointers to make them compatible with the %p conversion specification.
However, omitting the (void *) does not result in an error or warning, either compiling and running or running through valgrind.
int var = 2;
printf("var has the value %d and is stored at %p\n", var, &var);
Is casting to void here considered a best practice or standard, or is there something more sinister afoot?
Since printf is a variadic function, its declaration only specifies the type of the first parameter (the format string). The number and types of any remaining parameters are required to match the format string, but it's up to you, the programmer, to make sure they actually match. If they don't, the behavior is undefined, but the compiler isn't obliged to warn you about it. (Some compilers, including gcc, can do some checking if the format string is a literal.)
The %p format specifier requires an argument of type void*. If you pass a pointer of a different type, you have undefined behavior. In many implementations, all pointer types have the same size and representation, and are passed the same way as function arguments -- but the language doesn't guarantee that. By explicitly converting the pointer to void*, you guarantee that it will work correctly. By omitting the cast, you have code that will probably work as you expect it to on almost all implementations.
100% correct is better than 99% correct, especially if the only cost of that extra 1% is typing a few characters.

Different behavior of printf() in Turbo C and gcc when trying to print a pointer

The code below gives the output without error in Turbo C compiler, and gives the address of variable and its value both:
int a=5,*fp;
fp=&a;
printf("%d %d\n",fp,*fp);
But when I compile the same code in Linux with GCC compiler, it gives an error:
`warning: format ‘%d’ expects argument of type ‘int’, but argument 2 has type ‘int *’ [-Wformat=]
printf("%d %d\n",fp,*fp);`
But the same code with the %p format specifier works in GCC Compiler ,to which I agree. The question is: how come it's working on Turbo C platform?
P.S. The issue is not that (in Turbo C) the error is not reported but it's that on Turbo C
it gives a signed integer value that is unchanged on repeated execution of the program; can it be garbage?
P.P.S Turbo C is running on MSDOS platform and GCC on 64-bit Linux, if that helps.
printf() assumes you will pass a signed int for the %d specifier.
On the platform and compiler you are using, ints and pointers are probably the same size and printf() is able to display it correctly. Your pointer is reinterpreted as an int.
Compiling and running this on a different platform where, for example, ints are 32-bit and pointers are 64-bit (such as gcc on x64) would be undefined behaviour. If you are lucky enough, it would crash. If not, you'd get garbage.
The %d conversion specifier requires the corresponding argument to have type [signed] int. If the corresponding argument in fact has a different type then the behavior is explicitly undefined. This is the case regardless of the relative sizes of the expected and actual types, and regardless of whether implicit or explicit conversion between those types is possible.
Neither the behavior of the compiler nor that of any part of any resulting compiled program is defined when the program exhibits undefined behavior. Turbo C is not required to diagnose the problem. On the other hand, gcc is quite permitted to diagnose it, and even to reject the source code with an error instead of merely alerting you with a warning. As far as C is concerned, the whole program's behavior can be absolutely anything if any undefined behavior is triggered -- from what the author intended (whatever that may be) to emitting rude comments from the machine's speaker, and far beyond.
In practice, the undefined behavior is likely (but by no means certain) to manifest in a relatively benign way if the expected and actual types are the same size, and the conversion specifier is not %s. Otherwise, all bets are off. Note in particular that many C implementations for 64-bit platforms feature 32-bit ints and 64-bit pointers.
Each conversion specifier, such as %d, specifies both the type of the required argument and the format used to print it.
%d requires an argument of type int (equivalently signed int), and prints it in decimal.
%u requires an argument of type unsigned int and prints it in decimal.
%x requires an argument of type unsigned int and prints it in hexadecimal.
And so forth.
In your code:
int a=5,*fp;
fp=&a;
printf("%d %d\n",fp,*fp);
the second %d is correct, since the corresponding argument, *fp is of type int. But the first is incorrect, since the corresponding argument fp is of type int*.
If you pass an argument of the wrong type for a conversion specifier, the behavior is undefined. A compiler is not required to warn you if you do this, since it's not possible to detect the error in the most general case (the format string doesn't have to be a string literal). Some compilers, including gcc, will analyze format strings if they're string literals and warn about mismatches. Turbo C apparently does not (that's not surprising, it's a very old compiler).
The correct format for printing a pointer value is %p. This requires an argument of type void*, and prints it in an implementation-defined manner. A pointer of a type other than void* should be converted.
The correct version of your code is:
int a = 5, *fp;
fp = &a;
printf("%p %d\n", (void*)fp, *fp);
It's a warning, not an error. The first compiler could also say the same, but doesn't.
The warning doesn't stop the compilation, so it will work too. They're just different compilers. Also, compiler accepting your program doesn't mean that the program is correct.

mud compile - warning: cast from pointer to integer of different size

Trying to compile a MUD (online text game) on Ubuntu 14.04 LTS & gcc 4.4.7 and I keep getting this:
vt100.c: In function ‘get_tactical_map’:
vt100.c:1741: warning: cast from pointer to integer of different size
vt100.c:1805: warning: cast from pointer to integer of different size
The code on each of the above lines is the same:
*pto++ = 'a' + ((int) fch % 25);
I can link the entire file if needed, just not sure where to upload it.
I'm guessing you're trying to compile 32-bit code written by someone unaware of or unconcerned with portability issues of the C language on a 64-bit system.
The problem is that converting a pointer to an integer that cannot represent its value is undefined behaviour. See C11 6.3.2.3 §6:
Any pointer type may be converted to an integer type. [...] If the result cannot be represented in the integer type, the behavior is undefined.
Portable code would have used a cast to uintptr_t (or size_t in the pre-C99 era) instead of int.
Apparently, the address held by the pointer is used as a source of randomness to get an arbitrary lower-case letter (I think it's unlikely that Mints97 is correct and the author just forgot to dereference the pointer, but without some more code, no one can say for sure).
If you ignore the warning, in principle, the code might actually blow up on architectures that raise signals on integer overflow. The more likely case is a possibly harmless bug (depending on how the result is used): Instead of a lower-case letter, you might get a special character or upper-case letter if (int)fch happens to be negative.
I can't say more without variable declarations, but this error normally occurs when you cast a pointer variable to a non-pointer type (for example, void * to int). In your code, the problem is most likely in (int) fch: fch is a pointer, and you are trying to convert it to int. If you want to extract the value that fch points to, try using (int) *fch, e.g.
*pto++ = 'a' + ((int)*fch % 25);

Assigning a string to a variable of type int

Why is it that I can assign a string to a variable of type int? Eg. the following code compiles correctly:
int main(int argv, char** argc){
int a="Hello World";
printf(a);
}
Also, the program doesn't compile when I assign a string to a variable of a different type, namely double and char.
I suppose that what is actually going on is that the compiler executes int* a = "Hello World"; and when I write double a="Hello World";, it executes that line of code as it is.
Is this correct?
In fact, that assignment is a constraint violation, requiring a diagnostic (possibly just a warning) from any conforming C implementation. The C language standard does not define the behavior of the program.
EDIT : The constraint is in section 6.5.16.1 of the C99 standard, which describes the allowed operands for a simple assignment. The older C90 standard has essentially the same rules. Pre-ANSI C (as described in K&R1, published in 1978) did allow this particular kind of implicit conversion, but it's been invalid since the 1989 version of the language.
What probably happens if it does compile is that
int a="Hello World";
is treated as it if were
int a = (int)"Hello World";
The cast takes a pointer value and converts to int. The meaning of such a conversion is implementation-defined; if char* and int are of different sizes, it can lose information.
Some conversions may be done explicitly, for example between different arithmetic types. This one may not.
Your compiler should complain about this. Crank up the warning levels until it does. (Tell us what compiler you're using, and we can tell you how to do that.)
EDIT :
The printf call:
printf(a);
has undefined behavior in C90, and is a constraint violation, requiring a diagnostic, in C99, because you're calling a variadic function with no visible prototype. If you want to call printf, you must have a
#include <stdio.h>
(In some circumstances, the compiler won't tell you abut this, but it's still incorrect.) And given a visible declaration, since printf's first parameter is of type char* and you're passing it an int.
And if your compiler doesn't complain about
double a="Hello World";
you should get a better compiler. That (probably) tries to convert a pointer value to type double, which doesn't make any sense at all.
"Hello World" is an array of characters ending with char '\0'
When you assign its value to an int a, you assign the address of the first character in the array to a. GCC is trying to be kind with you.
When you print it, then it goes to where a points and prints all the characters until it reaches char '\0'.
It will compile because (on a 32-bit system) int, int *, and char * all correspond to 32-bit registers -- but double is 64-bits and char is 8-bits.
When compiled, I get the following warnings:
[11:40pm][wlynch#wlynch /tmp] gcc -Wall foo.c -o foo
foo.c: In function ‘main’:
foo.c:4: warning: initialization makes integer from pointer without a cast
foo.c:5: warning: passing argument 1 of ‘printf’ makes pointer from integer without a cast
foo.c:5: warning: format not a string literal and no format arguments
foo.c:5: warning: format not a string literal and no format arguments
foo.c:6: warning: control reaches end of non-void function
As you can see from the warnings, "Hello World" is a pointer, and it is being converted to an integer automatically.
The code you've given will not always work correctly though. A pointer is sometimes larger than an int. If it is, you could get a truncated pointer, and then a very odd fault when you attempt to use that value.
It produces a warning on compilers like GCC and clang.
warning: initialization makes integer from pointer without a cast [enabled by default]
The string literal "Hello World" is a const char *, so you are assigning a pointer to an int (i.e. casting the address of the first character in the string as an int value). On my compilers, gcc 4.6.2 and clang-mac-lion, assigning the string to int, unsigned long long, or char all produce warnings, not errors.
This is not behavior to rely on, quite frankly. Not to mention, your printf(a); is also a dangerous use of printf.

Resources