I am trying to build the M-SIM architecture simulator, but when I run the make utility, gcc reports this error (it is not even a warning)
note: expected 'char *' but argument is of type 'const char *'
Since when this is considered an error. Is there any flags that can bypass this check?
This is an error because passing a const char* argument to a function that takes a char* parameter violates const-correctness; it would allow you to modify a const object, which would defeat the whole purpose of const.
For example, this C program:
#include <stdio.h>
void func(char *s) {
puts(s);
s[0] = 'J';
}
int main(void) {
const char message[] = "Hello";
func(message);
puts(message);
return 0;
}
produces the following compile-time diagnostics from gcc:
c.c: In function ‘main’:
c.c:10:5: warning: passing argument 1 of ‘func’ discards qualifiers from pointer target type
c.c:3:6: note: expected ‘char *’ but argument is of type ‘const char *’
The final message is marked as a "note" because it refers to the (perfectly legal) declaration of func(), explaining that that's the parameter declaration to which the warning refers.
As far as the C standard is concerned, this is a constraint violation, which means that a compiler could treat it as a fatal error. gcc, by default, just warns about it and does an implicit conversion from const char* to char*.
When I run the program, the output is:
Hello
Jello
which shows that, even though I declared message as const, the function was able to modify it.
Since gcc didn't treat this as a fatal error, there's no need to suppress either of the diagnostic messages. It's entirely possible that the code will work anyway (say, if the function doesn't happen to modify anything). But warnings exist for a reason, and you or the maintainers of the M-SIM architecture simulator should probably take a look at this.
(Passing a string literal to func() wouldn't trigger these diagnostics, since C doesn't treat string literals as const. (It does make the behavior of attempting to modify a string literal undefined.) This is for historical reasons. gcc does have an option, -Wwrite-strings, that causes it to treat string literals as const; this actually violates the C standard, but it can be a useful check.)
As I mentioned in a comment, it would be helpful if you'd show us the code that triggers the diagnostics.
I even downloaded and built the M-SIM architecture simulator myself, but I didn't see that particular message.
Pointers to const-qualified types do not implicitly convert to pointers to non-const-qualified types. An explicit conversion via a cast is necessary, for example:
foo((char *)bar)
First in a function call (of a function defined with a prototype), the arguments are converted to the type of the parameters as if by assignment.
You can assign a value of type char * to an object of type const char * but you cannot assign a const char * value to a char * object.
This constraint appears in the constraints of assignment operator:
(C99, 6.5.16.1p1) "One of the following shall hold: [...] - both operands are pointers to qualified or unqualified versions of compatible types, and the type pointed to by the left has all the qualifiers of the type pointed to by the right;"
This constraint permits the first assignment but disallows the second.
Declaring a pointer with type const char * means you won't modify the object pointed to by the pointer. So you can assign the pointer a value of char * type, it just means the object won't be modified through the const char * pointer.
But declaring a pointer of type char * means you could modify the object pointed to by the pointer. It would not make sense to assign it a value of const char *.
Remember that in C, const does not mean constant but rather read-only. The const qualifier put before pointer types means you promise not to modify objects through objects of these pointers types.
Take these steps if you haven't already:
Declare a char pointer.
If necessary, allocate space and copy contents from the constant string. (e.g. by
using strdup())
And substitute the constant char pointer with the new char pointer.
Related
Let's say you have a function taking a string as an argument:
void foo(char *arg);
If we know for certain that the array (not to be confused with string length, thanks chux) will always have a certain size, let's say 8, then we can instead do:
void bar(char (*arg)[8]);
and then call it like this:
char str[8] = "Hello";
bar(&str);
We need to add the & for this to work properly, but the above code will emit a warning if you pass an array of wrong size or type, which is exactly what I want to achieve. But we will obviously need to modify the body a bit. So my question is simply if this wrapper technique would work:
void bar(char (*arg)[8]) {
char *tmp = (char*) arg;
foo(tmp);
}
What I'm trying to achieve here is that warnings should be emitted if called with an array of wrong size. Is the above solution safe? Is it safe to cast pointer to array of char to pointer to char?
I tried it, and it works, and emits no warnings with -Wall -Wextra -pedantic. And as soon as I change the size of str I get:
<source>: In function 'main':
<source>:18:9: warning: passing argument 1 of 'bar' from incompatible pointer type [-Wincompatible-pointer-types]
18 | bar(&str);
| ^~~~
| |
| char (*)[9]
<source>:9:17: note: expected 'char (*)[8]' but argument is of type 'char (*)[9]'
9 | void bar(char (*arg)[8]) {
| ~~~~~~~^~~~~~~
which is exactly what I want. But is it safe, or is it UB? I would like to do this, not only via a wrapper, but also by rewriting the original function, like
void foo(char (*argaux)[8]) {
char *arg = *argaux;
// Copy body of original foo
I know that I can achieve basically the same thing using structs, but I wanted to avoid that.
Runnable code: https://godbolt.org/z/GnaP5ceMr
char *tmp = (char*) arg; is wrong, these are not compatible pointer types. You can fix this easily though:
char *tmp = *arg;
*arg gives a char[8] which then decays into a pointer to its first element. This is safe and well-defined. And yes, pointers have much stronger "typing" in C than pass-by-value, so the compiler will recognize if an array of wrong size is passed.
Please note however that this leads to other problems: you can no longer have const correctness.
See Const correctness for array pointers?
This is not safe:
char *tmp = (char*) arg;
Because you're attempting to convert a char (*)[8] to a char *. While you might get away with it since a pointer to an array will (at least on x86-64) have the same numeric value as a pointer to the first member of an array, the standard doesn't guarantee that it will work. You would first need to dereference the parameter:
char *tmp = *arg;
In theory you should be able to do this:
void foo(char arg[static 8]);
This means that arg must be an array of at least that size.
The description of this syntax is in section 6.7.6.3p7 of the C standard:
A declaration of a parameter as ‘‘array of type’’ shall be adjusted
to ‘‘qualified pointer to type’’, where the type qualifiers (if
any) are those specified within the [ and ] of the array
type derivation. If the keyword static also appears within the
[ and ] of the array type derivation, then for each call
to the function, the value of the corresponding actual argument
shall provide access to the first element of an array with at least as
many elements as specified by the size expression.
However, most implementations don't enforce this restriction and it doesn't prevent you from passing an array larger than expected.
#include <stdio.h>
int main() {
const char a[99]="hello-hi";
printf("%s\n",a);
char *p=strtok(a,"-");
printf("%s",a);
return 0;
}
output:
hello-hi
hello
why a is modified here?? I made it const but still why it is modified??
The definition of const is not “the computer will prevent you from modifying the object”. The definition of const in C 2018 6.7.3 7 is:
If an attempt is made to modify an object defined with a const-qualified type through use of an lvalue with non-const-qualified type, the behavior is undefined…
So defining an object with const does not create a promise from the computer to you that the object will not be modified. It is actually a promise in the other direction: It is a promise from you to the computer that you will not attempt to modify the object. This gives the compiler permission to put the object in memory that is marked read-only.
If you violate the promise, the behavior is not defined by the C standard. The object might be in read-only memory, and attempting to modify it will cause a trap and alert you to a bug in your program. Or the object might be in modifiable memory, and attempting to modify it will modify it. Or, with program optimization, other behaviors may occur.
The C standard does give you some help with this. When you pass a const char * to strtok, which expects a char *, the compiler is required to issue a diagnostic message. Pay attention to the warnings and errors the compiler reports and use them to fix your program. Preferable, use a compiler switch to elevate warnings to errors. (-Werror with GCC or Clang, /WX with Microsoft Visual C++.)
1. const char array does not have to be in read-only memory. Attempt to modify it is an Undefined Behaviour. Anything may happen segfault, modification, virus activation, bank account transfer, disk erase etc etc.
Read the compiler warnings
<source>:9:20: warning: passing 'const char [99]' to parameter of type 'char *' discards qualifiers [-Wincompatible-pointer-types-discards-qualifiers]
char *p=strtok(a,"-");
int main() should be int main(void)
why a is modified here??
strtok modifies the string. Modifying const object results in undefined behaviour. The behaviour of the example program is undefined.
In fact, the const array doesn't convert to pointer to non-const (without cast), so the program is ill-formed.
Other problems:
You don't include the header that declares strtok.
int main() is non-standard in C.
From the C Standard (6.7.3 Type qualifiers)
6 If an attempt is made to modify an object defined with a
const-qualified type through use of an lvalue with non-const-qualified
type, the behavior is undefined.
The compiler should issue at least a warning that the call of strtok discards the qualifier const from the passed argument expression.
In this call of strtok
char *p=strtok(a,"-");
the array a is implicitly converted to pointer of the type const char * to its first element while the corresponding parameter of the function does not have the qualifier const.
I thought I understood how C arrays, pointers and pointers to arrays worked, but now I stumbled upon a piece of working code which I don't understand:
int sum(const int * const buf, int len)
{
int result = 0;
const int (*p)[];
int n;
p = (const int(*)[]) buf;
// p = buf without cast gives compiler warning here (but works)
// p = (const int(*)[]) &buf; // Doesn't work! Segfault!
for( n=0; n<len; n++)
{
result += (*p)[n];
}
return result;
}
This actually works. But how? The question is: how does that assignment from "buf" to "p" work? I thought this declaration of p was similar to "pointer to pointer to int", so I would have expected a "&" necessesary at the assignment. Why is this not necessary?
In fact the following prototype also works (with the same function body above):
int sum(const int buf[const], int len)
Now it seems even more clear that the declaration of "p" adds one level of pointer re-direction compared to the declaration of "buf". Still ... the assignment works fine without any "&" necessary. Can someone explain it?
First of all, please note that this code is not good practice. The pointer conversion is fishy-looking and unusual, and in particular it casts away const qualifiers, which is bad practice. (Declaring a parameter as ...* const buf is pretty fishy to begin with though.)
This actually works. But how?
There is a pointer conversion from (qualified) int pointer to int array pointer. Given that these two pointer types do not have different representation or different alignment (highly unlikely but theoretically possible), the pointer conversion in itself is ok (as per C11 6.3.2.3/8).
What matters then is the effective type of the data, which is an (array of) int. Effective type is a formal C term used for determining what type that is actually stored at a location, no matter the pointer types used for accessing. As long as the data is accessed through a pointer type that is compatible with the effective type of what's stored there, the code should work fine.
The formal reason why this is fine is C11 6.5/7 ("the strict aliasing rule"):
An object shall have its stored value accessed only by an lvalue expression that has one of
the following types:
— a type compatible with the effective type of the object,
— a qualified version of a type compatible with the effective type of the object,
/--/
— an aggregate or union type that includes one of the aforementioned types among its
members
Where "aggregate" is C standard gibberish for arrays and structs. If we access the data through an expression of array type, where the array element type is a type/qualified type compatible with the actual type, int in this case, everything is fine.
As for how it works, it simply de-references an array pointer. p = (const int(*)[]) buf; says "threat what's stored in this variable as a pointer to array of integer (and qualifiers be damned)". Then (*p)[n] takes the array pointer, de-references it to get the actual array, then uses the array index.
p = buf without cast gives compiler warning here
If you compile with gcc -pedantic-errors you get a more correct error. The pointer types are not compatible and so the compiler must generate a diagnostic message here - since p = buf is not valid C.
p = (const int(*)[]) &buf; // Doesn't work! Segfault!
This is because what's stored at &buf is not an array of int, it is just the pointer buf itself, likely allocated on the stack as a parameter to your function.
This question already has answers here:
What Does Adding One to a Character Array in C Do?
(4 answers)
Closed 9 years ago.
If I have a pointer to a pointer like the variable 'bs' in this sample:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
char **bs = {"this", "is", "a", "test"};
puts(bs);
return 0;
}
the puts statement shows 'this' on the screen. How would I go about getting it to show 'is' and so forth. I though I could just do something like:
puts(bs+1);
What does adding a number to a pointer variable actually do? Does it point to the next address, or does it take the type of the pointer into consideration and uses the size of the variable it is pointing to to determine where the next variable starts in memory?
the puts statement shows 'this' on the screen.
That's just (bad) luck, the code invokes undefined behavior, if the compiler doesn't refuse to compile it at all.
The warnings produced by clang (by default) for the code are
$ clang badpoint.c
badpoint.c:6:18: warning: incompatible pointer types initializing 'char **' with an
expression of type 'char [5]' [-Wincompatible-pointer-types]
char **bs = {"this", "is", "a", "test"};
^~~~~~
badpoint.c:6:26: warning: excess elements in scalar initializer
char **bs = {"this", "is", "a", "test"};
^~~~
badpoint.c:8:10: warning: incompatible pointer types passing 'char **' to parameter of
type 'const char *'dereference with * [-Wincompatible-pointer-types]
puts(bs);
^~
*
/usr/include/stdio.h:688:32: note: passing argument to parameter '__s' here
extern int puts (__const char *__s);
^
3 warnings generated.
gcc generates a warning for each excess initializer, while clang gives only one for the first of the three, otherwise the warnings from gcc (also by default) are a bit less informative, but the same.
So what's going on?
You are trying to initialize a scalar (char**), and provide {"this", "is", "a", "test"}, a brace-enclosed initializer-list containing four _initializer_s. Per 6.7.9 (11)
The initializer for a scalar shall be a single expression, optionally enclosed in braces. The initial value of the object is that of the expression (after conversion); the same type constraints and conversions as for simple assignment apply, taking the type of the scalar to be the unqualified version of its declared type.
That violates a "shall" requirement, and hence invokes undefined behavior.
If the compiler does not terminate the translation there, it probably just ignores the excess initializers - clang and gcc both do - and treats the declaration as if it was
char **bs = "this";
Which causes the warning about initialization from an incompatible type, since that tries to initialize a char** with a char[5], but per 6.5.16.1 the type of the initializer must be compatible with char**.
Nevertheless, if the compiler successfully translates the program, it is likely to result in bs containing the address of the first char in the string literal (the array "this" is converted to a pointer to its first element on the right hand side of an assignment operator).
Then the call
puts(bs)
Passes a pointer of the wrong type (char**) to puts, which is a constraint violation and requires a diagnostic message from the compiler (and makes the program invalid).
But the pointer happens to contain the correct address, and the undefined behavior manifests as the program printing "this".
How would I go about getting it to show 'is' and so forth
By correcting the program. As is, the strings "is", "a", and "test" do not even appear in the object file produced by gcc or clang.
One way to correct it would be changing the declaration to
char *bs[] = {"this", "is", "a", "test"};
So that bs is an array of four char*, pointing to the (respective first elements of the) four strings.
Then you have to adjust the call to puts, dereferencing or subscripting bs,
puts(bs[0]);
To print "this";
for(int i = 0; i < 4; ++i) {
puts(bs[i]);
}
To print all four strings on separate lines.
Why is it that I can assign a string to a variable of type int? Eg. the following code compiles correctly:
int main(int argv, char** argc){
int a="Hello World";
printf(a);
}
Also, the program doesn't compile when I assign a string to a variable of a different type, namely double and char.
I suppose that what is actually going on is that the compiler executes int* a = "Hello World"; and when I write double a="Hello World";, it executes that line of code as it is.
Is this correct?
In fact, that assignment is a constraint violation, requiring a diagnostic (possibly just a warning) from any conforming C implementation. The C language standard does not define the behavior of the program.
EDIT : The constraint is in section 6.5.16.1 of the C99 standard, which describes the allowed operands for a simple assignment. The older C90 standard has essentially the same rules. Pre-ANSI C (as described in K&R1, published in 1978) did allow this particular kind of implicit conversion, but it's been invalid since the 1989 version of the language.
What probably happens if it does compile is that
int a="Hello World";
is treated as it if were
int a = (int)"Hello World";
The cast takes a pointer value and converts to int. The meaning of such a conversion is implementation-defined; if char* and int are of different sizes, it can lose information.
Some conversions may be done explicitly, for example between different arithmetic types. This one may not.
Your compiler should complain about this. Crank up the warning levels until it does. (Tell us what compiler you're using, and we can tell you how to do that.)
EDIT :
The printf call:
printf(a);
has undefined behavior in C90, and is a constraint violation, requiring a diagnostic, in C99, because you're calling a variadic function with no visible prototype. If you want to call printf, you must have a
#include <stdio.h>
(In some circumstances, the compiler won't tell you abut this, but it's still incorrect.) And given a visible declaration, since printf's first parameter is of type char* and you're passing it an int.
And if your compiler doesn't complain about
double a="Hello World";
you should get a better compiler. That (probably) tries to convert a pointer value to type double, which doesn't make any sense at all.
"Hello World" is an array of characters ending with char '\0'
When you assign its value to an int a, you assign the address of the first character in the array to a. GCC is trying to be kind with you.
When you print it, then it goes to where a points and prints all the characters until it reaches char '\0'.
It will compile because (on a 32-bit system) int, int *, and char * all correspond to 32-bit registers -- but double is 64-bits and char is 8-bits.
When compiled, I get the following warnings:
[11:40pm][wlynch#wlynch /tmp] gcc -Wall foo.c -o foo
foo.c: In function ‘main’:
foo.c:4: warning: initialization makes integer from pointer without a cast
foo.c:5: warning: passing argument 1 of ‘printf’ makes pointer from integer without a cast
foo.c:5: warning: format not a string literal and no format arguments
foo.c:5: warning: format not a string literal and no format arguments
foo.c:6: warning: control reaches end of non-void function
As you can see from the warnings, "Hello World" is a pointer, and it is being converted to an integer automatically.
The code you've given will not always work correctly though. A pointer is sometimes larger than an int. If it is, you could get a truncated pointer, and then a very odd fault when you attempt to use that value.
It produces a warning on compilers like GCC and clang.
warning: initialization makes integer from pointer without a cast [enabled by default]
The string literal "Hello World" is a const char *, so you are assigning a pointer to an int (i.e. casting the address of the first character in the string as an int value). On my compilers, gcc 4.6.2 and clang-mac-lion, assigning the string to int, unsigned long long, or char all produce warnings, not errors.
This is not behavior to rely on, quite frankly. Not to mention, your printf(a); is also a dangerous use of printf.