Is (* p ++) dangerous?

Does it use while(*p++)

to check if an array contains more elements?

Could there be problems if there is some value in the next memory location, and this value is not part of the 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 result:

0x7fffed4e8f10 : 2
0x7fffed4e8f14 : 3
0x7fffed4e8f18 : 0
0x7fffed4e8f1c : 0

      

So if at 0x7fffed4e8f18 the value was โ‰  0, would that make my code wrong?

+3


source to share


6 answers


There are already many answers already explaining the undefined behavior that comes from reading array bounds pretty well. But there is another (potential) problem that no one has mentioned yet.

If your array can contain 0 as a valid value, then you will allow the wrong size. I.e

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;
}

      

deduces

-1.00
-0.50
 0.00

      

and "ignore" the rest of the elements. While this particular example does not trigger undefined behavior, it may not be what you expected.

I think you arrived at this pattern with string programming. By convention, we need a character array to represent a text string for



  • contains no NUL bytes and
  • terminate by placing a NUL byte after the last byte.

If both requirements are met, do

void
roll_all_over_it(char * string)
{
  while(*string);
    {
      putchar(*string++);
    }
}

      

safe by contract.

This makes using strings a little more convenient since we don't need to maintain an integer next to each string to keep track of its length. On the other hand, it makes quite a few (sloppy) programs vulnerable to buffer overflows and there are other problems that arise from this assumption. See, for example, discussion fgets

in the GNU libc manual :

Warning: If the input is null character, you cannot tell. So don't use fgets

unless you know the data cannot contain null. Don't use it to read user-edited files, because if the user inserts a null character, you must either handle it properly or print a clear error message. We recommend using getline

instead fgets

.

+3


source


As soon as it p

goes out of bounds, the array, dereferencing it, causes undefined behavior. So yes, it is dangerous to do so.



+8


source


Yes, it is: your function takes a pointer argument, but you don't check if it is a NULL pointer. Null pointer calls are not allowed.
As others have pointed out, dereferencing an invalid pointer (out of bounds) results in an undefined bahviour error, which is bad.

Also, you are using the correct format string to print the pointer ( %p

), but compile your code with -Wall -pedantic

. printing a pointer value is one of the few cases where you need to overlay 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 are actually trying to determine the length of the array passed as an argument. The simple fact is that you cannot. The array decays to a pointer. The pointer is not an array, so you cannot determine the length of the original array. At least: not reliable. If you are working on an array and 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 having 0

after the array. The function itself can be called by skip as well NULL

, and null dereferencing 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);

      

+2


source


In C, there is only one way to determine how many elements an array has: it must look for that value from the original array type that was used in the array definition. Any other attempt to quantify cardinality using some invented runtime mechanisms is useless.

In your specific example, the function f

does not have access to the original type of the array (since you converted your array to a pointer when you passed it to f

). This means that it is impossible to restore the original size of the array internally f

, no matter what you do.

If you insist on passing your array in f

as a pointer float *

, it would be a good idea to pass the original size of the array manually from the caller (i.e. from main

) as an additional parameter to the function f

.

The "terminating value" method can also be used for this purpose, in which you use some special element value to denote the last element of the array (zero, for example), but is generally worse by simply passing the size outside. In any case, you will be sure to manually include this trailing value in the array. He himself will not appear.

+2


source


Yes, the way you wrote your code gives undefined behavior if there is no null element in your array.

That is, while(*p++)

not bad in and of itself, but applying it to an array that you have not explicitly terminated with a null element. I. e. next 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 to explicitly pass 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 decompose into pointers when you pass them to a function. I've changed the title to reflect this.

+1


source


What you have done is very dangerous indeed!

You are not controlling the last element of the array as null, and you are looking for a pointer after the last element of your array. Two points for defining undefined behavior!

Here's 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;
}

      

+1


source







All Articles