c - memcpy and pointers. Still work. Why? - c

EDIT:
What should I do to have a correct code then ?
EDIT2:
Ok, I correct the code below
Context
Fiddling with memcpy.
Linux, 64 bits.
gcc 4.8.x
Code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void d(char ** restrict a, char ** restrict b){
char r[20];
memcpy(r,*a, strlen(*a)+1);
// it is the same thing as before since *c is equivalent to &r (or put simply r).
char *restrict c = malloc(20);
memcpy(c,*a, strlen(*a)+1);
// that one bugs me. why b alone and not *b ??
// EDIT : this is incorrect
memcpy(b,*a, strlen(*a)+1);
// EDIT : this is correct
memcpy(*b,*a, strlen(*a)+1);
printf("pointer c -> hey %s\n",c);
printf("array r -> hey %s\n",r);
// EDIT : this is incorrect
printf("double pointer -> hey %s\n",b);
// EDIT : this is correct
printf("double pointer -> hey %s\n",*b);
}
int main(void)
{
char a[] = "YOU";
char * b = a;
char * c = malloc(20);
d(&b, &c);
return 0;
}
Question
I would like to undertsand why memcpy doesn't complain about me passing double pointer to it, while it needs a pointer only.
I know that with chars *b == &a == a and that an array is referenced by its first member up to '\0'. The problem really is with passing a double pointer to memcpy.
why didn't I have to do
memcpy(*b, *a, strlen(*a)+1);
since memcpy signature is
void * memcpy ( void * destination, const void * source, size_t num );
and first argument is a "Pointer to the destination array where the content is to be copied, type-casted to a pointer of type void*", according to cplusplus.com.
What is the "catch" here please ?
Thanks a lot

Well, a double pointer is a single pointer to single pointer, so it can be passed to a function that expects a void pointer.
It is of course another thing whether or not your code is correct... It's not and works only by coincidence. Note that not only you use memcpy() to write to a wrong location, but you also print the same wrong location of memory as a string in your printf(). The "coincidence" here is that both of these "wrong" locations are the same, so you falsely assumed that it works fine.
Try to really print the right thing and you'll see the mayhem:
printf("double pointer -> hey %s\n",*b);

Consider what would happen if you wanted to copy the representation of a pointer to another one, like this:
char *p;
char *q = NULL;
memcpy(&p, &q, sizeof q);
should the compiler really complain in this case? Nope.
The point is that void * is untyped. It can point to any object type. It's not a constraint that a void * can't point to a pointer-to-pointer. It absolutely can.
As to why it "works": It does not work. It only appears to be working. Because of the invalid pointer operation, the code has undefined behavior, so it can do anything. In the better case, it crashes and makes the problem apparent. In your case, the error remained silent and the program was pretending it worked.

Related

Is while(*p++) dangerous?

Is using while(*p++) for checking if an array has more elements dangerous?
Could there be problems if on the next memory location there is some value and this value is not a part of an array.
This simple code:
#include <stdio.h>
void f(float *p) {
printf("%p : %.f\n", p, *p);
while(*p++){
printf("%p : %.f\n", p, *p);
}
printf("%p : %.f\n", p, *p);
}
int main() {
float p[] = {2.2, 3.2};
f(p);
return 0;
}
gives this output:
0x7fffed4e8f10 : 2
0x7fffed4e8f14 : 3
0x7fffed4e8f18 : 0
0x7fffed4e8f1c : 0
So, if on 0x7fffed4e8f18 the value was ≠ 0 would it make my code wrong?
As soon as p goes outside of the bounds the array, dereferencing it invokes undefined behavior. So yes, doing this is dangerous.
There are already a lot of answers that explain the undefined behavior that arises from reading past the array's boundaries pretty well. But there is yet another (potential) problem nobody has mentioned yet.
If your array can contain 0 as a valid value, you'll assume the wrong size. That is,
void
f(float * p)
{
do
{
printf("% .01f\n", *p);
}
while(*p++);
}
int
main()
{
float p[] = {-1.0f, -0.5f, 0.0f, 0.5f, 1.0f};
f(p);
return 0;
}
will output
-1.00
-0.50
0.00
and “overlook” the remaining elements. While this particular example does not invoke undefined behavior, it might not be what you've expected.
I guess you've come over this pattern with string programming. By convention, we require a character array that is meant to represent a text string to
not contain any NUL bytes and
be terminated by placing a NUL byte after the last byte.
If both requirements are fulfilled, doing
void
roll_all_over_it(char * string)
{
while(*string);
{
putchar(*string++);
}
}
is safe by contract.
This makes using strings a little more convenient since we don't have to maintain an integer alongside every string to keep track of its length. On the other hand, it has made quite a few (carelessly written) programs vulnerable to buffer overrun attacks and there are other problems that arise from this assumption. See for example the discussion of fgets in the GNU libc manual:
Warning: If the input data has a null character, you can't tell. So don't use fgets unless you know the data cannot contain a null. Don't use it to read files edited by the user because, if the user inserts a null character, you should either handle it properly or print a clear error message. We recommend using getline instead of fgets.
In C language there's only one way to determine how many elements an array has: it is to look up that value from in original array type, the one that was used in array definition. Any other attempts to figure out the number of elements by some invented run-time mechanisms are useless.
In your specific example, function f has no access to the original array type (since you converted your array to pointer as you were passing it to f). This means that it is impossible to restore the original size of the array inside f, regardless of what you do.
If you insist on passing your array to f as a float * pointer, a good idea would be to pass the original array size manually from the caller (i.e. from main), as an extra parameter of function f.
The "terminating value" technique, where you use some sort of special element value to designate the last element of the array (zero, for example) can also be used for that purpose, but is generally worse that just passing the size from the outside. In any case, you have make sure to include that terminating value into the array manually. It won't appear there by itself.
Yes, it is: your function takes a pointer argument, but you're not checking to make sure it's not a NULL-pointer. Dereferencing a null pointer is not allowed.
As others have pointed out: dereferencing an invalid pointer (out of bounds) results in undefined bahviour, which is bad.
Also, you are using the correct format string to print a pointer (%p), but compile your code with -Wall -pedantic. printing a pointer value is one of the few cases where you have to cast a pointer to void *. IE change:
printf("%p : %.f\n", p, *p);
to
printf("%p : %.f\n", (void *) p, *p);
update:
In response to your comment: it would seem that you're actually trying to determine the length of an array that is passed as an argument. The simple fact of the matter is that you can't. An array decays into a pointer. A pointer is not an array, and therefore, so you can't determine the length of the original array. At least: not reliably.
If you are working on an array, and you want to know its length: have the caller of your function pass the length as an argument:
void f(float *arr, size_t arr_len)
{
if (arr == NULL)
exit( EXIT_FAILURE );//handle error
//do stuff
}
In short, your function relies heavily on there being a 0 after the array. The function itself can be invoked passing NULL, too and dereferencing null is illegal. So yes, your code is dangerous.
//example setup
float foo = 123.4f
float *bar = malloc(123 * sizeof *foo);//<-- uninitialized memory, contains junk
//omitting if (bar == NULL) check, so bar might be null
//dangerous calls:
f(&foo);
f(bar);//<-- bar could be null if malloc failed, and contains junk if it didn't dangerous
f(NULL);
What you did is indeed highly dangerous !
You make no control of the last element of the array to be null, and you dereference a pointer that will be after the last element of your array. Two points to Undefinit Behaviour !
Here is what you should do :
#include <stdio.h>
void f(float *p) {
printf("%p : %.f\n", p, *p);
while(*p++){
printf("%p : %.f\n", p, *p);
}
/* because of ++, p is now one element to far : stop and do not print ... */
}
int main() {
float p[] = {2.2, 3.2, 0}; /* force a 0 as a last element marker */
f(p);
return 0;
}
Yes, the way you have written your code yields undefined behavior if there is no zero element in your array.
That is, while(*p++) in and of itself is not bad, but applying it to an array that you have not explicitly terminated with a zero element is. I. e. the following version is safe:
#include <stdio.h>
//p must point to a zero terminated array
void f(float *p) {
printf("%p : %.f\n", p, *p);
while(*p++){
printf("%p : %.f\n", p, *p);
}
//printf("%p : %.f\n", p, *p); //p points past the end of the array here, so dereferencing it is undefined behavior
}
int main() {
float array[] = {2.2, 3.2, 0}; //add a terminating element
f(array);
return 0;
}
However, I usually prefer explicitly passing the size of the array:
#include <stdio.h>
void f(int floatCount, float *p) {
printf("%p : %.f\n", p, *p);
for(int i = 0; i < floatCount; i++) {
printf("%p : %.f\n", p+i, p[i]);
}
}
int main() {
float array[] = {2.2, 3.2};
int count = sizeof(array)/sizeof(*array);
f(count, array);
}
Btw: Arrays are not pointers, but they decay into pointers when you pass them to a function. I changed the naming to reflect this.

Casting char pointer to int pointer - buffer error 10

In this answer, the author discussed how it was possible to cast pointers in C. I wanted to try this out and constructed this code:
#include <stdio.h>
int main(void) {
char *c;
*c = 10;
int i = *(int*)(c);
printf("%d", i);
return 1;
}
This compiles (with a warning) and when I execute the binary it just outputs bus error: 10. I understand that a char is a smaller size than an int. I also understand from this post that I should expect this error. But I'd really appreciate if someone could clarify on what is going on here. In addition, I'd like to know if there is a correct way to cast the pointers and dereference the int pointer to get 10 (in this example). Thanks!
EDIT: To clarify my intent, if you are worried, I'm just trying to come up with a "working" example of pointer casting. This is just to show that this is allowed and might work in C.
c is uninitialized when you dereference it. That's undefined behaviour.
Likewise, even if c were initialized, your typecast of it to int * and then a dereference would get some number of extra bytes from memory, which is also undefined behaviour.
A working (safe) example that illustrates what you're trying:
int main(void)
{
int i = 10;
int *p = &i;
char c = *(char *)p;
printf("%d\n", c);
return 0;
}
This program will print 10 on a little-endian machine and 0 on a big-endian machine.
These lines of code are problematic. You are writing through a pointer that is uninitialized.
char *c;
*c = 10;
Change to something like this:
char * c = malloc (sizeof (char));
Then, the following line is invalid logic, and the compiler should at least warn you about this:
int i = *(int*)(c);
You are reading an int (probably 4 or 8 bytes) from a pointer that only has one byte of storage (sizeof (char)). You can't read an int worth of bytes from a char memory slot.
First of all your program has undefined behaviour because pointer c was not initialized.
As for the question then you may write simply
int i = *c;
printf("%d", i);
Integral types with rankes less than the rank of type int are promoted to type int in expressions.
I understand that a char is a smaller size than an int. I also understand from this post that I should expect this error. But I'd really appreciate if someone could clarify on what is going on here
Some architectures like SPARC and some MIPS requires strict alignment. Thus if you want to read or write for example a word, it has to be aligned on 4 bytes, e.g. its address is multiple of 4 or the CPU will raise an exception. Other architectures like x86 can handle unaligned access, but with performance cost.
Let's take your code, find all places where things go boom as well as the reason why, and do the minimum to fix them:
#include <stdio.h>
int main(void) {
char *c;
*c = 10;
The preceding line is Undefined Behavior (UB), because c does not point to at least one char-object. So, insert these two lines directly before:
char x;
c = &x;
Lets move on after that fix:
int i = *(int*)(c);
Now this line is bad too.
Let's make our life complicated by assuming you didn't mean the more reasonable implicit widening conversion; int i = c;:
If the implementation defines _Alignof(int) != 1, the cast invokes UB because x is potentially mis-aligned.
If the implementation defines sizeof(int) != 1, the dereferencing invokes UB, because we refer to memory which is not there.
Let's fix both possible issues by changing the lines defining x and assigning its address to c to this:
_Alignas(in) char x[sizeof(int)];
c = x;
Now, reading the dereferenced pointer causes UB, because we treat some memory as if it stored an object of type int, which is not true unless we copied one there from a valid int variable - treating both as buffers of characters - or we last stored an int there.
So, add a store before the read:
*(int*)c = 0;
Moving on...
printf("%d", i);
return 1;
}
To recap, the changed program:
#include <stdio.h>
int main(void) {
char *c;
_Alignas(in) char x[sizeof(int)];
c = x;
*c = 10;
*(int*)c = 0;
int i = *(int*)(c);
printf("%d", i);
return 1;
}
(Used the C11 standard for my fixes.)

Dereference void pointer

Even after casting a void pointer, I am getting compilation error while dereferencing it.
Could anyone please let me know the reason of this.
int lVNum = 2;
void *lVptr;
lVptr = (int*)&lVNum;
printf("\nlVptr[60 ] is %d \n",lVptr[1]);
It doesn't make sense to dereference a void pointer. How will the compiler interpret the memory that the pointer is pointing to? You need to cast the pointer to a proper type first:
int x = *(int*)lVptr;
printf("\nlVptr[60 ] is %d \n", *(int*)lVptr);
This will cast the void pointer to a pointer to an int and then dereference it correctly.
If you want to treat it as an array (of one), you could do a slightly ugly ((int *)lVptr)[0]. Using [1] is out of bounds, and therefore not a good idea (as for lVptr[60]...)
It's still a void* because that's what you declared it as. Any pointer may be implicitly converted to a void*, so that cast does nothing and you are left with a pointer to void just as you began with.
You'll need to declare it as an int*.
void *some_ptr = /* whatever */;
int *p = (int*)some_ptr;
// now you have a pointer to int cast from a pointer to void
Note that the cast to an int* is also unnecessary, for the same reason you don't have to (and should not) cast the return value of malloc in C.
void*'s can be implicitly converted to and from any other pointer type. I added the cast here only for clarity, in your code you would simply write;
int *p = some_void_ptr;
Also, this:
lVptr[1]
Is wrong. You have a pointer to a single int, not two. That dereference causes undefined behavior.
You can not dereference a void pointer because it doesn't have a type,
first you need to cast it(int *)lVptr, then dereference it *(int *)lVptr.
int lVNum = 2;
void *lVptr;
lVptr = &lVNum;
printf("\nlVptr[60 ] is %d \n",*(int *)lVptr);
Example of what you might be trying to do:
#include <stdio.h>
int main () {
void *v;
unsigned long int *i = (unsigned long int *)v;
*i = 5933016743776703571;
size_t j = sizeof(i);
printf("There are %ld bytes in v\n", j);
size_t k;
for (k = 0; k < j; k++) {
printf("Byte %ld of v: %c\n", k, ((char *)v)[k]);
}
}
Output:
There are 8 bytes in v
Byte 0 of v: S
Byte 1 of v: T
Byte 2 of v: A
Byte 3 of v: C
Byte 4 of v: K
Byte 5 of v: O
Byte 6 of v: V
Byte 7 of v: R
A void pointer is just that, a pointer to a void (nothing definable).
Useful in some instances.
For example malloc() returns a void pointer precisely because it allocated memory for an UNDEFINED purpose.
Some functions may likewise take void pointers as arguments because they don't care about the actual content other than a location.
To be honest, the snippet you posted makes absolutely no sense, can't even guess what you were trying to do.
# Code-Guru
I tried to compile it in visual studio. It gives error - expression must be a pointer to complete object.
Thanks teppic,
As you suggested, the following compiles and yields right result.
#include<stdio.h>
void main(){
printf("study void pointers \n");
int lvnum = 2;
void *lvptr;
lvptr = &lvnum;
printf("\n lvptr is %d\n",((int *)lvptr)[0]);
}
However if I try printf("\n lvptr is %d\n",((int *)lVptr)[60]);
It compiles and runs but gives random number.
Thanks a lot, friends for all the suggestions. Apologies that I assigned a void pointer to unnecessarily casted int pointer and expected it to get dereferenced. However I should have casted it when I want to dereference it.
Purpose of the snippet:
In my sources I found klocwork error which was caused by similar situation. On the contrary the program not only compiled but also gave correct results. Reason- it is a low level code (no OS) where the memory assigned to the void pointer is already reserved till the count of like 60. But the klocwork tool was unable to parse the files having that limit resulting in error. I did a lot of brain storming and ended up in something silly.
Saurabh

when assigning to void pointer in C

I thought when assigning a vaule to a pointer, you should use * operator, but I saw a code like
char *a;
void *b;
b = "Hello";
a = b;
printf("%s", a);
This is legal when I compiled it and prints Hello. Doesn't need a pointer to void dereferencing?
This "works" because a void * and a char * are able to hold any type of pointer. You could get into trouble, if for example you used int *a; instead of void *a;.
However, your code isn't dereferencing a void pointer, and the printf function converts it to a char * when it pulls the argument out of the variable arguments in the list. So there is no dereference of a void pointer in your code. If your pointer didn't perfectly convert to a char * (for example if we had an int *a;) on some types of machines that don't address bytes without "extra information" (some machines have only 'pointers' to whole machne words, and extra information is used to store which byte within that word you want when reading bytes), then your printf may well have failed to operate correctly ["undefined behaviour"].
There is no string type in C. You can treat a char* pointing to the beginning of a char array as a string. And that's exactly how printf treats a here.
As far as I remember, the C Standard demands that char* and void* be interchangeable.
A pointer to void does not need dereferencing, infact, dereferencing a void pointer is illegal. You can obviously cast any pointer to a void pointer, and cast a void pointer to any other pointer type.
That's why:
void *b = "hello world"; worked, so did char *a = b and then printing a out.
what happens here is:
char *a; // declares a as a pointer to char
void *b; // declares b as a void pointer(which can hold an address)
b = "Hello"; // 'b' now holds the address, that points to the start of "Hello"
a = b; // now, 'a' contains the address that 'b' does
printf("%s", a); // prints the string, starting from the address pointed by 'a'.
Hence this is perfectly legal.
b = "Hello";
This line allocates a bunch of char memory and assigns its address to void pointer variable b.
void pointers can store address of any other datatype.
The only limitations with void pointers are:
cannot dereference void pointer for obvious reasons
sizeof(void) is illegal
you cannot perform pointer arithmetic on void pointers
However GCC assumes that sizeof(void) is 1 and allows pointer arithmetics on void pointers.
a=b; This is the typical char pointer a initialized to the address contained in void pointer b. This is legal but, if misused might have implications.
printf("%s", a); This is a simple printf statement.
Everything in this code is fine.
And yes you need to use * to assign a value to an allocated memory in pointer:
Eg:
char *c=malloc(sizeof(char));
*c='a';
or
char a='a';
char *c=&a;
*c='b';
Also you would be using the * to initialize another pointer when using double pointers.
char *a=NULL;
mymalloc(&a);
void mymalloc(char **a)
{
*a=malloc(10);
return;
}
Hope this helps.

Can I use a void pointer in C as an array?

I tried doing this in C and it crashes:
int nValue=1;
void* asd;
asd[0]=&nValue;
With the following error:
*error C2036: 'void**' *: unknown size
error C2100: illegal indirection*
Can I use a void pointer in C as an array?
And if I can, what is the correct way to do so?
Can I use a void pointer in C as an array?
Nope. You can convert to and from void*, but you cannot derference it, and it makes sense when you think about it.
It points to an unknown type, so the size is also unknown. Therefore, you cannot possibly perform pointer arithmetic on it (which is what asd[0] does) because you don't know how many bytes to offset from the base pointer, nor do you know how many bytes to write.
On a side note, asd (likely) points to invalid memory because it is uninitialized. Writing or reading it invokes undefined behavior.
you can write like this:
int main()
{
int iv = 4;
char c = 'c';
void *pv[4];
pv[0] = &iv;
pv[1] = &c;
printf("iv =%d, c = %c", *(int *)pv[0], *(char *)pv[1]);
return 0;
}

Resources