0

In a C function, an argument of type char *a can be passed as a pointer or as an array. In the function below I'm using that fact to execute function func. This function replaces the first character of an array.

#include <stdio.h>

void func(char* var) {
  var[0] = 'X';
}

int main() {
  char a[] = "HELLO";
  func(a);  // OK

  char *b = "HELLO";
  func(b);  // ERROR
}

As an author of such a function, how can I guard against someone passing a pointer? In my function I want only arrays to be passed (so, a pointer to an actual array).

mnj
  • 2,539
  • 3
  • 29
  • 58
  • There isn't a distinction to make. C doesn't have a string type. The thing that is passed is a pointer, because that's the declared type. Declaring it like `char[] var` doesn't help either, due to pointer decay. Furthermore, none of that is related to the *actual problem you seem to have in mind*. The reason why `var[0] = 'X';` would cause a problem with `b` is **not** because it "is a pointer instead of an array". The problem is that the value is `const`, but the variable is not required to be `const` due to a backwards compatibility rule. – Karl Knechtel Dec 25 '21 at 10:12

3 Answers3

3

The actual issue here is whether the function is passed a pointer to data that may be changed or not, not whether the function is passed a string or not.

Per C 2018 7.1.1 1, “A string is a contiguous sequence of characters terminated by and including the first null character.” So, with char a[] = "HELLO";, the array contains a string.

With char *b = "HELLO";, b points to the first character of a string literal1. The data in a string literal ought to be treated as constant—a program should not attempt to modify it, and the C implementation may put it in read-only memory so that attempting to modify it results in a program exception.

If we were designing the C language from scratch, string literals would be qualified with const. However, const did not exist when string literals were created. Programmers simply had to know when data was part of a string literal, and it was, and still is, their responsibility to avoid attempting to change it.

To some extent, you can ameliorate this issue by adding const manually, as with const char *b = "HELLO";. Then attempting to pass b to func will result in a compiler diagnostic message about attempting to pass const char * argument for a char * parameter.

If it were deemed sufficiently important to guard against bugs, all string literals could be written as compound literals, such as (const char []) { "HELLO" }, and a macro could be defined to do this:

#define StringLiteral(x) ((const char []) { (x) })

Footnote

1 Technically, a string literal is the characters in the source code, such as "HELLO", including the quotation marks. When a program is compiled, a string literal in source code results in creation of an array containing a string. Informally, we speak of this array as a string literal.

Eric Postpischil
  • 195,579
  • 13
  • 168
  • 312
2

Using a question I asked earlier, How do I force a warning from using an array of wrong size when passed to function? here is a way:

void foo(char (*arg)[]) {
    char *p = *arg;
    p[0]='X';
}

int main(void) {
    char s[1];
    char *p;

    // These three will throw a warning
    foo(p);
    foo(&p);
    foo(s);

    // But this will not
    foo(&s);
}

So the drawback is that you need to use the & operator for this approach to work. Solving it without this requirement would be tricky and ugly if it is at all possible. The reason is that an array will automatically decay to a pointer in most situations.

klutt
  • 30,332
  • 17
  • 55
  • 95
  • The question is not about whether the function is passed a pointer to the first byte of several bytes versus a pointer to a single byte or a pointer to an array versus a pointer to a `char`. – Eric Postpischil Dec 25 '21 at 10:17
  • 1
    @EricPostpischil I think this answer is relevant to the question. It's probably as close as it can get. – klutt Dec 25 '21 at 10:19
1

As we know that an array is never actually passed to a function,Compiler treat both array and pointers as pointer when passed to a function since pointers and array are closed to each other but they are not same

char a[] = "hello";
char *p = "world";

       +---+---+---+---+---+---+
    a: | h | e | l | l | o |\0 |
       +---+---+---+---+---+---+
       +-----+     +---+---+---+---+---+---+
    p: |  *======> | w | o | r | l | d |\0 |
       +-----+     +---+---+---+---+---+---+

To address to the point guarding against pointer we can use GNU C built in function

__builtin_types_compatible_p() which is used compare two types return 1 on success otherwise 0

and GNU compiler extension typeof() which is used to determine type at runtime with the help of GNU extensions we can restrict passing a pointer to function

  if (__builtin_types_compatible_p(__typeof__(b), char[]))
    {
        printf("compatible with char [] so calling to func()");
        func(b); 
    }
  else 
    {
        printf("oops sorry only array is allow to passed");
    }
mohammed yaqub
  • 100
  • 1
  • 8