I have a couple of questions that came up when I was reading and writing code..
I already get another approach, but this questions still in my mind and I didn't find any good answer, so let's see:
Can I assign a pointer to a variable in a portable way, even losing
information? See the situation, I have a function that could return
NULL if an error, or a char, supposing that NULL is all 0, the char
will be nul, is this correct?
void * function_returning_null_or_char ();
An int variable is suppose to hold a pointer? (I read it in somewhere)..
My implementation of NULL is #define NULL (void *) 0. Would be better if it was #define NULL 0? So
it could be assigned to variable and pointers, like a char or an int
*. (see question 1)
When I assign a pointer to NULL, when I cast it to short or char
or another type, the value will be 0? Or The value will be the
special value of NULL cutted to fit on that type?
I think that all questions are resumed in:
The compiler is smart enough to convert a NULL pointer in 0s when it is cast to a variable?
example code:
int *p = NULL;
char c = (char) p; // This works, I dont know why, and char is equal to '/0';
char c = NULL; //This works because the compiler is smart to convert NULL to '/0' event stddef.h defining NULL as (void *)0;
char c = (char) function_returning_null_or_char (); //if this function return NULL whats the value of c? If NULL have a special value, c will have a special value too, or it will be 0?
It doesn't make sense to have a function that can return either NULL (a pointer value) or a char value. There is a null character '\0', but it's not related to the null pointer. There are various ways you can have a function return either a char value or some indication that there is no valid value. For example, return a structure.
No, an int variable is not supposed to hold a pointer. An int variable holds an integer value; to hold a pointer, use a pointer variable. (You can convert between integer and pointer types, but doing so makes sense far less often than you might think.)
NULL is defined for you by the implementation, in <stddef.h> and several other standard headers. Do not try to redefine it yourself.
Converting a null pointer value to an integer type yields an implementation-defined value. If you care what that value is, you're probably doing something wrong.
Pointers are not integers; don't try to treat them as integers. The representation of a null pointer is implementation-defined; it's commonly all-bits-zero, but there's no guarantee of that.
Take a look at the comp.lang.c FAQ, particularly sections 4 (Pointers) and 5 (Null Pointers).
Related
For example,
char * integerToString(void);
int main() {
char *myString;
do {
myString = integerToString();
} while (myString == (char *)-1); // worked as intended
free(myString);
return 0;
}
char * integerToString(void) {
int userInput;
printf("Enter an integer: ");
scanf("%d", &userInput);
if (userInput < 0 || userInput > 99)
return (char *)-1; // what happens here?
char *myString = (char *)malloc(sizeof(char) * 2);
myString[0] = (int)floor(userInput/10.0) + '0';
myString[1] = userInput%10 + '0';
return myString;
}
and the program worked as intended, but what exactly happens when you type cast an integer value (without assigning the integer to a variable) into a character pointer? Will this program always work?
Thanks.
C99:
6.3.2.3 Pointers
An integer constant expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant. If a null pointer constant is converted to a
pointer type, the resulting pointer, called a null pointer, is guaranteed to compare unequal
to a pointer to any object or function.
[...]
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 casting -1 to a pointer has implementation-defined results. Therefore the answer is no: This is not guaranteed to work in general.
In particular: If it does turn out to be a trap representation, your code runs afoul of:
6.2.6 Representation of types
6.2.6.1 General
[...]
Certain object representations need not represent a value of the object type. If the stored
value of an object has such a representation and is read by an lvalue expression that does
not have character type, the behavior is undefined. If such a representation is produced
by a side effect that modifies all or any part of the object by an lvalue expression that
does not have character type, the behavior is undefined. Such a representation is called
a trap representation.
I.e. while (myString == (char *)-1); has undefined behavior if myString is a trap representation.
This program is an example of improper error handling. The value of (char *)-1 appears to be implementation defined, see the other answers. Since this address is likely not a valid memory address that would be returned from malloc, this is used as a sentinel value in the program. The actual value is not of interest, it is compared to the same expression in the other function.
If you run this, malloc just might return whatever value that (char *)-1 evaluates to. Then it will be interpreted as an error, although it is a valid memory address.
A better way would be to have an argument to integerToString of type int * and use this as a boolean to indicate failure. Then one would not reserve one char * value for error handling.
Or use C++ and an exception.
What happens when you type cast an integer value into a char pointer?
In general, that is undefined behavior (at least as soon as you dereference it). Be very scared. Read a lot more about UB (it is a tricky subject).
In some documented cases, you can case an uintptr_t or intptr_t integral value into a valid pointer.
In your case, your heap allocated string is too short (so you have a buffer overflow, which is one of the many examples of UB). You forgot the space for the terminating NUL byte, and you forgot to check against failure of malloc. BTW, sizeof(char) is always 1.
You could code:
if (userInput < 0 || userInput > 99)
return NULL;
char *myString = (char *)malloc(3);
if (!myString) { perror("malloc myString"); exit(EXIT_FAILURE); };
myString[0] = (int)floor(userInput/10.0) + '0';
myString[1] = userInput%10 + '0';
myString[2] = (char)0;
return myString;
On most systems (but not all), (char*)-1 is never a valid address (always outside of the virtual address space) and can never be given by system (or standard) functions. On My Linux/x86-64 desktop, I know that (char*)-1 is not a valid address (e.g. because it is MAP_FAILED), and I could (sometimes) use that as a sentinel non-null pointer value (which should not be derefenced). But that makes my code less portable.
So you could decide and document that your integerToString gives (char*)-1 on non-integer input and NULL on heap allocation failure. That would work on my Linux/x86-64 desktop (so I sometimes do that). But that is not pure (portable) C11 code.
But if you stick to the C11 standard (read n1570) it is implementation defined what and if (char*)-1 is meaningful. It might be some trap representation that you are not even allowed to compare (even if I don't know any actual C implementation doing that).
Actually your example illustrates that people never code for pure standard C11; they always (and so do I) make additional assumptions on the C implementation; but you do need to be aware of them, and these assumptions could make the porting of your code to some hypothetical future machine a nightmare.
Will this program always work?
This is a too general question. Your original program didn't even handle failure of malloc and had a buffer overflow (because you forgot the space for the terminating zero byte). However, sadly for you, it would apparently often seems to work (and that is why UB is so scary). Consider however this (standard conforming, but non-realistic) malloc implementation as food for thought.
(explaining exactly why your program appears to behave like you want is really difficult, because you need to dive into several implementation details)
Which of the following methods is a more correct way to initialize a variable?
int x = 0;
int x = NULL;
What about the pointers? I have been told discordant things about NULL, like : "NULL is best for initializing pointers" or "Don't use NULL but 0 to initialize a variable" and so on... Now, I read on the internet that NULL is equal to 0 and I tested that myself. So what's the point? Why are some people saying that using NULL isn't a good choice? Is there something that I'm missing?
NULL is a pointer constant. You use this to initialize a pointer to a value that says it doesn't point to anything.
On most C implementations, it is defined as:
#define NULL ((void *)0)
But there's no guarantee of that.
Which of the following methods is a more correct way to initialize a variable?
// int x = NULL; `NULL` implies pointer
int x = 0; // 0 implies integer value and x is an integer.
What about the pointers?
void *p = NULL; // `NULL` implies pointer and p is a pointer.
//void *p = 0; // 0 implies integer value
that NULL is equal to 0
It is equal in value, though maybe not in bit pattern. See below +0.0, -0.0 example.
If NULL was equal to 0, always, then there would not be a need for it in C.
NULL is a null pointer consonant - it often, but does not always have a bit pattern of zeros. A pointer with a NULL value will always compare equally to 0: same value, different bit patterns.
Remember that == compares values, not bit patterns.
void *a = NULL;
if (a == NULL) Always_True();
if (a == 0) Always_True();
Example that may help. +0.0 and -0.0 have the same value, same type, but different bit patterns. Similar thing may happen with pointers.
int main(void) {
double pz = +0.0;
double nz = -0.0;
printf("=:%d bits:%d\n", pz == nz, memcmp(&pz, &nz, sizeof pz) == 0); // =:1 bits:0
return 0;
}
NULL is a null-pointer constant. You should use that to initialize pointer types.
From C11, chapter §7.19, stddef.h
NULL
which expands to an implementation-defined null pointer constant.
and for null-pointer constant in C, chapter §6.3.2.3
An integer constant expression with the value 0, or such an expression cast to type
void *, is called a null pointer constant.[...]
So, the type is that of a pointer.
For other non-pointer variables, you should be using 0.
Don't use NULL for initializing non-pointer types, as it may expand to something like (void *) 0 depending on the implementation. Use plain 0 (or a macro or const-qualified variable that evaluates to plain 0) for integral types.
Definitely use NULL when you're initializing (or comparing) a pointer; it's good style.
Definitely don't use NULL when you're working with integers; it's poor style (and it might very well not work.)
Don't listen to anyone suggesting that you shouldn't use NULL with pointers; they're probably confused.
See the C FAQ list at http://c-faq.com/null/index.html for more information.
Theoretically NULL can be pointer to address different from 0. But in practice such definition will crash large percent of software. And it is 0 (or (void *)0) in 99.99% of cases.
There are/were some architectures deployed for which NULL is not zero: Prime 50, Honeywell-Bull, CDC Cyber 180 series and others. In these cases, NULL is most definitely not interchangeable with zero.
See this answer to another question for details of the machines affected.
In the book Learn C The Hard Way at excercise 15 there is suggestion to break program by pointing integer pointer at array of strings and using C cast to force it. How can I do it?
Here is a small example. the result depends on the endianness of your system and the size of int. I would expect the first or fourth character to change to the next character in the alphabet.
#include<stdio.h>
int main(void) {
char string[100] = "Somestring";
int *p;
/* Let p point to the string */
p = (int*)string;
/* modify a value */
(*p)++;
/* Let's see if any character got changed */
printf("%s", string);
return 0;
}
It should be pointed out that not all casts are safe and that the result could be implementation defined or undefined. This example is actually undefined, since int could have stricter alignment constraints than char.
When writing portable code you need to take great care when using casts.
The code above could break on any system where sizeof(int) is greater than the string length regardless of alignment issues. In this case, where the string has size 100, we wouldn't expect that to happen in a long while. Had the string been 4-7 bytes it could happen sooner. The jump from 32- to 64-bit pointers broke a lot of old code that assumed that pointers and int were the same size.
Edit:
Is there an easy fix to the alignment problem? What if we could somehow make sure that the string starts in an address that is also suitable for an int. Fortunately, that is easy. The memory allocation function malloc is guaranteed to return memory aligned at an address that is suitable for any type.
So, instead of
char string[100] = "Somestring";
we can use
char *string = malloc(100);
strcpy(string, "Somestring");
The subsequent cast is now safe alignment-wise and is portable to systems where int is smaller than 100.
Note that malloc is declared in stdlib.h, so we should add the following at the top of our code file:
#include<stdlib.h>
That's simply an abusive way of casting.
// setup the pointers to the start of the arrays
int *cur_age = ages;
char **cur_name = names;
What the author of that link meant by "to break program by pointing integer pointer at array of strings and using C cast to force it." He meant that you can write something like this int *cur_age = (int *)names; That is to cast a pointer to pointer to char to a pointer to int. You can do that in C, which allows you to cast from one type of pointer to another type of pointer; but be warned you need to know what you are doing.
Here the author wanted to show how to break a program by pointing a pointer to a wrong type. His example, however, is probably making you more confused rather than helping you to understand pointers.
To cast, use the cast operator: (type)expression. For example, to cast an expression of type double to int:
(int)sqrt(2);
In your specific case, cast names to int* (the type of cur_age) to break the program:
cur_age = (int*)names;
To point incompatible pointer in c you only need to cast it to void.
//array of string declaration
char aStr[50][50];
Int *pint;
//do whatever you need with string array
pint = (*int)(*void)aStr;
I'm writing this from my cell phone.
if you increment your pointer past the allocated memory, you might end up in your program stack and change value to it.
This question already has answers here:
Comparison between pointer and integer in C
(8 answers)
Closed 7 years ago.
Below there is a small program which I came across and thought to search more on it. It gives a warning: comparison between pointer and integer
if ( NULL == '\1' )
I'm just unable to figure out that which one is pointer and which one is integer and more important why so?
I can just say that NULL is pointer here but don't know why so?
Yes, one more thing is there when we replace the NULL with '\0' then there is no compilation warning. Why?
The full code is :-
#include <stdio.h>
int main()
{
if ( NULL == '\1' )
{
printf (" \nTrue ");
}
else
{
printf (" \nFalse");
}
return 0;
}
NULL is a null pointer constant. It's typically defined as:
#define NULL 0
or
#define NULL ((void *)0)
Based on the warning message you get, it looks like your implementation has the latter definition.
Hence, the comparison produces the warning message as you are comparing a pointer and an int. Character literals are of type int in C.
Yes, one more thing is there when we replace the NULL with '\0' then
there is no compilation warning. Why?
That's because comparing an int ('\0') with another int ('\1') is fine.
Well you can only compare things that are comparable in nature. Integers and addresses are too different to be comparable; integers are values, while addresses are pointers to values, their usages are too different to consider them comparable.
NULL is defined as ((void *)0) which is a special constant for pointers that denotes a pointer that never points to something... It is typed as a pointer. void * is the universal type for pointers.
\1 is a constant for char (it is the character of index 1 in the ASCII table, it is known as SOH char). A char is roughly a number that can be easily translated to a char when printed. You literal is 1 typed as a char, but for efficiency, in almost all arithmetic and comparison expressions, every such char is converted to an int.
So you basically compare a pointer to an int as the compiler says. This is not strictly forbidden, but the compiler as usual has a serious doubt about this and warns you. Effectively, you expression has no sense.
If you really want to compare both (again probably non sense), then you can either:
convert the pointer to an int (this is dangerous as you surely lose information in that conversion) : `((int)NULL) == '\1',
convert the literal value to a pointer (less dangerous but also stupid) : `NULL == (void *)'\1'
NULL, 0 and '\0' have a slightly different meanings. See here for more information. NULL is designt to be the null pointer while 0 is a constant literal of the integer 0. You can use constant literals of the the integer 0 for pointers too. Thay have some special meaning in such situations. This mean you can use 0 in all such situations but NULL should only be used for pointers. If you would have an integer which is not 0, it would not make much sense to compare it with a pointer. Remember, while using C/C++, you don't know how pointers are represented at the hardware.
In C NULL is often implemented as ((void*)0). In C++ it may be implemented as 0.
If you write NULL == 0 in C, you are comparing an integer with a pointer. Since 0 is a constant literal for the integer 0, it has a special meaning and doesn't generate any warning or error. But if you have another integer there, maybe 1, it does not make much sense to compare it with NULL.
It only really makes sense to compare NULL to memory addresses. 1 is clearly not a meaningful memory address but 0 is a commonly referenced memory address. If you are comparing NULL to any non-pointer value other than 0, it is highly suggestive of a logic error, and that's why you get a warning.
Perhaps you were thinking of the unofficial-but-common macro NUL which is defined as '\0'.
I keep getting this error when compiling my program. This is just a small part of my code, so if needed I will provide the rest of the code. Any ideas on why this is occuring?
void strip_quotes(char s[]) {
if (s[0]=='"') s=s+1;
if (s[strlen(s)-2]=='"') s[strlen(s)-2]=NULL;
}
You are setting a character of s to NULL. The proper way to add a null character to a string is to use '\0'.
To explain the message, NULL is likely defined as (void*)0, so when you assign it, you are converting void* to char, hence the warning.
As Dave has already correctly pointed out the reason for the compiler error:
s[strlen(s)-2]=NULL; /* = (void*)0 */
There is another bug in the code that won't cause a compiler error:
if (s[0]=='"') s=s+1;
the increment of s will not be visible to the caller, as C passes by value including pointers (see http://c-faq.com/ptrs/passptrinit.html). Options for correcting:
shift the content of the array to the left using memmove() (or some other copy mechanism)
pass the address of the pointer (a char**)
return a pointer to s
Changing the content of s is preferable as it avoids a possible problem if the array was dynamically allocated: only pointers returned by malloc() (or calloc() and realloc()) can be passed to free(). If the value of s is changed then it cannot be free()d via s.
Note that:
void strip_quotes(char s[]) {
is equivalent:
void strip_quotes(char* s) {
incase you were confused as to were pointers are used in the code.
Dave got it, but I'll try to add a bit.
NULL is a pointer of type void*, which can be assigned to any pointer type. If you are setting a pointer to a value that can never be used to represent valid memory, use NULL.
'\0', aka NUL, is ascii value 0 and is used to terminate strings. It is of type char. http://www.december.com/html/spec/ascii.html .
void strip_quotes(char s[]) {
int len = strlen(s);
if(s[len-1] == '"')
s[--len] = '\0';
if(s[0]=='"')
memmove(s, s+1, len);
}