0
#include<stdio.h>
#include<stdlib.h>
int *func(int *);
int main(void)
{
        int i,size;
        const int *arr=func(&size);
        for(i=0;i<size;i++)
        {
                printf("Enter a[%d] : ",i);
                scanf("%d",&arr[i]);
        }

        for(i=0;i<size;i++)
        {
                printf("%d\t",arr[i]);
        }
        return 0;
}
int *func(int *psize)
{
        int *p;
        printf("Enter the size: ");
        scanf("%d",psize);

        p=(int *)malloc(*psize *sizeof(int));
        return p;
}

Enter the size: 3

Enter a[0] : 1

Enter a[1] : 2

Enter a[2] : 3

1 2 3

  • Here, in this code, i use const keyword to not modify data which is pointed by 'arr' pointer.
  • if i use const keyword they why its give me output ?
  • Okay but compiler should not give me output. its should give an error..... – Devang Kubavat Jan 13 '16 at 08:44
  • Olaf gave you the correct answer. However on most microcontrollers, const variables belongs to ROM memory, that is a real read only memory. – LPs Jan 13 '16 at 08:45
  • `GCC` talking use `-Wall` option, and errors will be shown. – LPs Jan 13 '16 at 08:46
  • then why it gave me output....? it should display error... – Devang Kubavat Jan 13 '16 at 08:46
  • @Superlokkus: Please point me at where the standard requires the compiler to detect the type of a **variadic** function argument! In my answer I elaborated this (the reason I withdrew my comment). – too honest for this site Jan 13 '16 at 09:01
  • [Please see this discussion on why not to cast the return value of `malloc()` and family in `C`.](http://stackoverflow.com/q/605845/2173917). – Sourav Ghosh Jan 13 '16 at 09:13
  • You should have gotten a warning from your compiler for the call to `scanf`. I just tried it with `gcc`, just using the default options, and it gave a warning. Are you using a different compiler? Whatever compiler you're using should have warned about it. – Tom Karzes Jan 13 '16 at 09:39
  • @TomKarzes: While I agree a compiler should warn, it is not required by the standard. Warning about wrong types for variadic functions is hard to accomplish anyway, as the actual type is determined programatically. C ist just not designed for dynamic typing, as it does not include type-information in the code, thus looses all type-safety. For `scanf`/`prinf`, the compiler just "knows" the syntax of the format string. Note that anything tha using a _string literal_ directly likely inhibits the check, too. – too honest for this site Jan 13 '16 at 09:46
  • @Olaf I must confess, my statement "plain wrong" is over the edge, I should have written e.g. "not entirely true", because to let the compiler check that I don't change my value unintended, was AFAIK one of the two (also AFAIK) primary goals behind `const`. The other should be optimization. But I overlooked the `scanf` usage, so you're right, variadic argument functions are relevant too. I forgot how "soft" C is in comparison to C++, which I was think in to much. So: I'm sorry, you wrote an excellent answer. – Superlokkus Jan 13 '16 at 09:48
  • @Olaf Yes, I agree that the warning isn't required. In order to issue warnings about argument types to variable argument library functions like `printf` and `scanf` requires the compiler to be able to parse the format strings. But `gcc` seems to do a good job at it provided the format arguments are string constants. – Tom Karzes Jan 13 '16 at 09:51
  • @TomKarzes: See the headers. gcc actually provides an attribute to tag such functions and allow your own functions to be checked. Althought you have to follow mostly the `printf` syntax. – too honest for this site Jan 13 '16 at 10:50

2 Answers2

7

You just encountered one of the many cases C allows you to shoot your foot - for good and bad.

Some general remarks about your assumptions: const is a guarantee you give to the compiler. So you have to make sure not to violate the contract. C does not have true constants [1]. Semantically const is a qualifier` which allows the compiler additional error-checking. But that requires the type of the argument to be known to the compiler. This is true for functions with a proper prototype, but (normally [2]) not for those with a variable number of argument ("variadic functions"), as their types are not given at compile-time (and not explicitly available at run-time).

In

scanf("%d",&arr[i]);

You actually pass a problematic (see below) pointer type to scanf. The function itself does not check, but just expects the correct type. It cannot, because C does not provide the type of an object at run-time.

A modern compiler should warn about argument type missmatch for printf and scanf. Always enable warnings (for gcc at least use -Wall -Wextra -Wconversions) and pay heed to them.

Edit: After heavy discussion, I have to change my mind. It seems to be not undefined behaviour [3] for the reason given initially: passing a const int * to scanf which expects a int *.

This because the object malloced in func has no effective type until the first write (6.5p6). That occurs in scanf using an int *. Thus the object has type int - no const. However, your further accesses through a const int * are valid. 6.7.3p6 only makes the other direction undefined behaviour (for good reason).

Getting through undetected is only possible for variadic functions, because there is no information about the expected type available in the function declaration. Consinder something like:

void f(int *p)
{
    *p = 0;
}

int main(void)
{
    const int *p = ...;
    f(p);
}

Here the compiler will generate a warning. Variadic functions are of the cases C cannot check for qualifier-correctness (this includes e.g. volatile, too). There are more and some are quite subtle.

The only case of undefined behaviour here is to pass an incompatible differently qualified pointer than expected (6.7.6.1p2).

Recomendation: Enable warnings, but do not rely on the compiler detecting all flaws (not only true for const-correctness). If you need more saffety, C is not the right language. There are good reasons higher-level languages like Python, Java, etc. exist (C++ is somewhere in-between). OTOH the open ends in C allow things very hard to accomplish (if at all) in these languages where required. As allways: know your tools.


Note: You should not cast the result of malloc & friends in C. And sizeof(char) is useless. It is defined by the standard to yield 1.


[1] As violating the contract is undefined behaviour, the compiler is actually free to store such data in read-only memory. This is vital for microcontrollers which run code and read some data straight from ROM, for example.

[2] Modern compilers can parse the format-string of printf and scanf family for the argument types and warn about missmatch. That requires this string to be a string literal (not a variable), though. That is a courtesy of the compiler writes a these functions are widely used.

[3] Basically undefined behaviour means anything can happen - Your computer might run away, nasal daemons may appear, or it might work. But all not guaranteed reliable or deterministic. So next time you start something else might happen.

too honest for this site
  • 12,050
  • 4
  • 30
  • 52
  • 1
    @DevangKubavat: Yes, there is a lot to know as a good developer. Even if you might not understand everything, you can do some reserarch about the terms I used. Either way, you really should keep that in the backyard of your mind. If you continue learning, you likely will notice some of the issues I mentioned. – too honest for this site Jan 13 '16 at 09:05
  • 2
    Can you elaborate (give arguments in favor) what is causing undefined behavior? – 2501 Jan 13 '16 at 09:06
  • @2501 What do you mean? If you want a list: that is given in annex J of the C standard (while informative, it is mandatory, as it just summarises causes included in other sections). **No reason for a downvote!** – too honest for this site Jan 13 '16 at 09:16
  • 1
    You can list the reasons from the annex J, but I would prefer actual quotes. (Don't forget that downvotes are anonymous. Bringing them up in a debate is unproductive, as is *guessing* who downvoted) – 2501 Jan 13 '16 at 09:30
  • @2501: Downvoting without leaving a statement or a clear reason is too. However, you do know Annex J.2 according to your highest-voted answer. So you should already know the answer. Anyway, I added the quote. – too honest for this site Jan 13 '16 at 09:37
  • The object is not defined with const. `int* p=(int *)malloc(*psize *sizeof(int));` The quote is not relevant. The behavior is defined. – 2501 Jan 13 '16 at 09:44
  • I have to agree with 2501 here partially. Quoted part of standard would be relevant *if* `malloc` would returned `const void *`. UB comes from passing the `const char *` to `scanf` expecting `char *`. `scanf("%d",(char*)&arr[i]);` would make this code legal again, since the original address is not `const` qualifed. – user694733 Jan 13 '16 at 09:48
  • @2501: The object is very well _declared_ `const` qualified: `const int * = `func(&size)` in `main`. Assignment of an `int *` to a `const int *` is allowed in C. – too honest for this site Jan 13 '16 at 09:49
  • Objects are not defined by what points to them, but in this case, what is written into them. Please read: 6.5.6. – 2501 Jan 13 '16 at 09:50
  • Effective type is my argument here. scanf writes an int into the object giving it the type int. – 2501 Jan 13 '16 at 09:52
  • 1
    The only UB here is passing `const int *` to scanf when it expects an `int *`. The object itself is writable. A simple cast in scanf (as user694733 said) or `const int p*=func(&size); int *arr = (int*) p;` would make the code perfectly legal. – P.P Jan 13 '16 at 10:03
  • @l3x Where does is specify that %d must be an int*? It only says: *The corresponding argument shall be a pointer to signed integer.* const int* is compatible with int* and is a pointer to a signed integer. I would really like to refute my arguments, but I can't find anything in the Standard. – 2501 Jan 13 '16 at 10:14
  • @l3x Whoops, I accidentally used `char` instead of `int`, but we agree. You can cast pointer-to-const to pointer-to-non-const as long as memory pointed at was non-const. Without it, I think that some standard functions, like `char *strchr(const char *s, int c);` would have been impossible. – user694733 Jan 13 '16 at 10:14
  • @2501 Because `int *x; const int * p = x;` is fine but `const int *x; int *p = x;` is *not* valid. "Compatible" is not applied in both ways. – P.P Jan 13 '16 at 10:18
  • @Olaf I'm glad you listened to my arguments. I gave you an upvote. – 2501 Jan 13 '16 at 10:26
  • @l3x They are compatible both ways. Standard only states that modification is undefined. This is the best answer I could find in a short time: https://stackoverflow.com/questions/14437699/does-removing-const-from-a-pointer-to-const-obey-strict-aliasing-in-c-and-refer There are other (better) ones. – 2501 Jan 13 '16 at 10:27
  • All: See my edit. After the discussion I had to change my mind. – too honest for this site Jan 13 '16 at 10:33
  • @2501 I don't remember exactly where it's mentioned in C std. Note the linked answer doesn't say/imply it's valid. There's a cast that makes it valid (linked question). i.e. `const int *x; int *p = (int*) x;` is fine (whether `*p=42;` is valid depends on whether the object pointed to by `x` is modifiable). – P.P Jan 13 '16 at 10:38
  • @2501: I fight against it, but I am very well able to change my mind if I'm proved wrong or get enough reasons to verify my claims. Had to dig quite dep in the standard to see the line of argument. The question is much more complicated than initially thought. However, I'm still not sure about the "compatible type" for the pointer (resp. the object it points to). I suspect they are not compatible, but as a non-native speaker the previous research has exhausted me. I need some rest to get my mind clear. – too honest for this site Jan 13 '16 at 10:38
  • @l3x: As a cast tells the compiler to shut up, it is useless as an argument. Without the cast it is UB. But the other direction is ok. Problems arise with more indirection. C is a minefield with qualifier-correctness and pointers. – too honest for this site Jan 13 '16 at 10:42
  • @user694733: You can basically cast anything you want. Problem is not all are valid. Some are implementation defined, some invoke undefined behaviour. That's why one should use a cast only iff(!) 1) it is really necessary and 2) **all** implications are understood and 3) accepted. FYI: I changed my answer. Welcome to read. – too honest for this site Jan 13 '16 at 10:45
  • Standard says that *"For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types."*, which would imply that casting const away before `scanf` is required. However, it also says *"pointers to qualified or unqualified versions of compatible types shall have the same representation and alignment requirements"*, which means that means that in practice on existing systems, even if you don't cast it, it most probably just works. C is so much fun. – user694733 Jan 13 '16 at 11:12
  • @user694733: As I see the types are actually not compatible. Thus UB. One reson for UB is enough. But yes, C is very incomplete here. OTOH this allows very low-level manipulations, which are - strictly skeking UB, but work nevertheless. Sometimes you have to - just know what you do. – too honest for this site Jan 13 '16 at 11:48
1

It is quite evident that you have turned off all compiler warnings.

Your program invokes undefined behaviour by assigning the result of a function returning int to const int*. The compiler should have told you, maybe you ignored it, but from then on all odds are off.

You pass a const int* to scanf. Again, the compiler should have warned you, maybe you ignored it, but again all odds are off.

const int* doesn't make the object pointed to unmodifiable. It tells the compiler to not let you modify anything through that pointer, that's all. The storage area returned by malloc is never unmodifiable.

gnasher729
  • 51,477
  • 5
  • 75
  • 98