Why do I receive a warning? pch is already the pointer I got and when I want to subtract the addresses I use &Origi for that.
C4047 '-' : 'char*' differs in levels of indirection from 'char (*)[12]'
// Substring
char Origi[] = { "Hallo Welt." };
char* pch = strstr(Origi, "Welt"); // “pch” is a pointer that in this example points 6 characters further than the start of “Origi”.
printf("%d\n", pch - &Origi);
printf("%c\n", Origi[pch - &Origi]);
In the snippet:
printf("%d\n", pch - &Origi);
Origi is already of type char* because when you pass an array as argument it decays to a pointer to its first element, passing its address will make it a pointer to array of chars, which is not the same as a pointer to char and will cause a type mismatch in the binary operation.
For the pointer arithmetic to work properly the operands must be of the same type. It should be:
printf("%d\n", pch - Origi);
|_____|____
|
---> same type -> char*
For the second case it's much the same logic, Origi is already a pointer to char. It should be:
printf("%c\n", Origi[pch - Origi]);
|_____|____
|
---> same type -> char*
I'll admit the error issued by msvc is not the best, it should be something like in gcc, for example, which is much more on point:
error: invalid operands to binary - (have 'char* ' and 'char (*)[12]')
Or better yet, in clang:
error: 'char* ' and 'char (*)[12]' are not pointers to compatible types
Related
I am getting the following warning:
warning: incompatible pointer types passing 'char ()' to parameter of type 'char ()[5]' [-Wincompatible-pointer-types]
printField(field[5]);
The printField function looks like this:
void printField(char (*field)[5])
{
...
}
and the field I am giving to it is defined as follows:
char (*field) = get_field(input);
Here is the function call:
printField(field);
Now, I do understand, that there is obviously some sort of mismatch happening, but I can't tell what to change for it not to be there anymore. I would appreciate it very much, if someone could help me.
Assuming that get_field return a pointer to the first element (character) of an array (null-terminated string), then that happens to be the same as the pointer to the array itself.
If we illustrate it with a simple array:
char s[5];
Then that will look something like this in memory (with pointers added):
+------+------+------+------+------+
| s[0] | s[1] | s[2] | s[3] | s[4] |
+------+------+------+------+------+
^
|
&s[0]
|
&s
Now as can be seen that there are two pointers pointing to the same location: &a[0] and &s.
&s[0] is what using the array decay to, and it's the type char *.
&s is a pointer to the array itself, and have the type char (*)[5].
When we apply it to your case, field is the first pointer, it points to the first element of an array. Which happens to be the same location as the pointer to the array itself which is what printField expects.
That's the reason it "works". But you should not do like that, you should fix the function to take an char * argument instead:
void printField(char *field);
The types are different.
char *ptr; declares the pointer to char
char (*ptr)[5]; declared the pointer to an array of 5 characters.
Those pointer types are not compatible - thus compiler warning.
In your case, both pointers refer to the same place in the memory (and that is the reason why the program works).
While reviewing my code I realized I had placed an extra & while passing a char array to strcpy and missed the resulting warning; regardless, everything worked as expected. I then reproduced the behavior in this example:
#include <string.h>
#include <stdio.h>
void main() {
char test1[32] = {0};
char test2[32] = {0};
strcpy(test1, "Test 1\n");
strcpy(&test2, "Test 2\n");
printf(test1);
printf(test2);
printf("%i %i\n", test2, &test2);
}
Here I copy a string to the address of test2 and the compiler complains accordingly:
main.c: In function ‘main’:
main.c:9:12: warning: passing argument 1 of ‘strcpy’ from incompatible pointer type [-Wincompatible-pointer-types]
9 | strcpy(&test2, "Test 2\n");
| ^~~~~~
| |
| char (*)[32]
In file included from main.c:1:
/usr/include/string.h:125:39: note: expected ‘char * restrict’ but argument is of type ‘char (*)[32]’
125 | extern char *strcpy (char *__restrict __dest, const char *__restrict __src)
| ~~~~~~~~~~~~~~~~~^~~~~~
However the code is still compiled and the result seems to ignore the second level of indirection. Even when printing the address &test2 it is the same as simply test2.
./a.out
Test 1
Test 2
-1990876288 -1990876288
I must admit this is a part of the C language that completely escapes me. Why is the & operand seemingly ignored when targeting an array?
The first byte of the first element in the array is at the same place as the first byte of the array, because they are the same byte.
Most C implementations use the memory address, or some representation of it, of the first byte of an object as a pointer to the object. The array contains its elements, and there is no padding: The first element of the array starts where the array starts. So the first byte in the first element is the first byte in the array. So they have the same memory address.
There is a rule in C that converting a pointer to an object to a char * produces a pointer to the first byte of an object (C 2018 6.3.2.3 7). So, given an array a, (char *) &a[0] is a pointer to the first byte of the first element, and (char *) &a is a pointer to the first byte of the array. These are the same byte, so (char *) &a[0] == (char *) &a.
However, &a[0] and &a have different types. If you attempt to compare them directly with &a[0] == &a, the compiler should issue a warning that the types do not match.
If you pass &a as an argument to a routine that expects &a[0], it will often work in most modern C implementations because they use plain memory addresses as pointers, so &a is represented with the same bits (a memory address) as &a[0], so the receiving routine gets the value it expected even though you passed a pointer of the wrong type. However, the behavior of your program will not be defined by the C standard, since you have violated the rules. This was more of a problem in older C implementations when memory models were not simple flat address spaces, and different types of pointers may have had different representations.
Your code works in this case because, once the pointer argument (with or without the extra &) gets to the strcpy function, it is interpreted as a simple char* value. Thus, any pointer arithmetic (such as the likely increments) performed in that function will be correct.
However, there are cases where using a pointer-to-char and pointer-to-array-of-char will yield significantly different results. Pointer arithmetic is such a case: if p is a char* variable, then ++p will add the size of a char (i.e. 1) to the address stored in p; however, if q is an array of char* pointers, then ++q will add the size of a pointer to the address in q. And, if r is the address of an array of character strings, then ++r will add the size of the entire array to the address stored in r.
So, it's good that the compiler warns you about that extra &. Be very careful about addressing (no pun intended) such issues, if ever your compiler warns you about them.
Why is the & operand seemingly ignored when targeting an array?
The conversion of char (*)[32] to char * is UB.
Is is not ignored by the compiler, hence the warning.
The compiler emitted code did convert the pointer from one type to the other in a common fashion resulting in acceptable behavior. Still remains UB.
Best if the programmer does not ignore the warning.
The output of this snippet of code:
char* str1 = "suchString";
printf("%s %s\n", &"suchString", &str1);
is:
suchString �WO:+(or any unknown symbols)
What is the difference and why the output not the same?
EDIT
If you trying to eliminate the & operator as follows:
char* str1 = "suchString";
printf("%s %s\n", "suchString", &str1);
The output is the same of the first snippet of code.
Now, is how the "suchString" is the same of &"suchString"?
This:
&"suchString"
takes the address of a string literal. String literals are arrays, not pointers.
Had you compiled with warnings enabled, you had knew already:
prog.c:5:14: warning: format '%s' expects argument of type 'char *', but argument 2 has type 'char (*)[11]' [-Wformat=]
printf("%s %s\n", &"suchString", &str1);
~^ ~~~~~~~~~~~~~
prog.c:5:17: warning: format '%s' expects argument of type 'char *', but argument 3 has type 'char **' [-Wformat=]
printf("%s %s\n", &"suchString", &str1);
~^ ~~~~~
The first is of type char (*)[11], while the second is of type char **.
As for the garbage values, you are providing an address of a pointer to %s format, thus ruining the print. If you used %p instead, you would see an address being printed out, e.g. "0x7ffe676f5588".
Same goes for when attempting to print the address of the string literal, with the string specifier in printf().
Moreover, you should cast to void*, like this:
printf("%p %p\n", (void*)"suchString", (void*)str1);
char* str1 = "suchString";
printf("%s %s\n", &"suchString", &str1);
The %s specifier requires an argument of type char*, which must be a pointer to a string. You've provided arguments of types char (*)[11] and char**. Your code has undefined behavior, which means that in principle it could do literally anything.
A string literal is an array. The address of an array refers to the same memory location as the address of the array's first element, but it's of a different type. Passing just "suchString" as the second argument would be correct, since the array "decays" to a pointer to its first element.
In an implementation where all pointers have the same representation (which is typical but not required), passing a pointer to the same memory location but with the wrong type is likely to "work". printf is special in that it can accept arguments of different types, so the compiler can't necessarily diagnose type errors. For a non-variadic function, an error like this would probably be rejected at compile time.
str1 is a pointer, not an array. You've initialized it to point to the initial character of the string literal. You could pass str1 to printf, but &str1 is the address of a pointer object, which is not only of the wrong type, but doesn't point to a string at all. Thus the output is garbage.
If you want to print the string values, you can do this:
const char* str1 = "suchString";
printf("%s %s\n", "suchString", str1);
(I've added const so the compiler will complain if you try to modify the contents of the string literal.)
In some legacy code I have to maintain, & operators are put in front of arrays names whenever the arrays are to be passed as (void *) arguments
Here is a simple example :
char val = 42;
char tab[10];
memcpy(&tab, &val, 1);
It compiles with gcc or clang without errors or warnings. It also gives the expected result.
Is this syntax legal ?
Why does this works ?
Notes : I usually use one of the following syntax :
memcpy(tab, &val, 1);
memcpy(&tab[0], &val, 1);
Epilog :
As an additional test, I used a function taking a (char*) argument instead of (void*)
I get the following warning if I try to compile with clang :
warning: incompatible pointer types passing 'char (*)[10]' to parameter of type 'char *' [-Wincompatible-pointer-types]
Edit 1 :
In the original example tab was given with a size of 1 element
I just changed the size to 10 for the sake of generality.
Edit 2 :
As mentionned in the answers, memcpy takes (void*) and not (char*)
memcpy's parameters are of type void*, not char*. Any argument of pointer type (excluding function pointers) is implicitly converted to void*. This is a special-case rule that applies only to void*.
Given the declaration
char tab[1];
either tab or &tab is valid as an argument to memcpy. They evaluate to pointers of different types (char* and char (*)[1]), but both pointing to the same memory location; converting either to void* yields the same value.
For a function that actually requires a char* argument, only tab is valid; &tab is of the wrong type. (For a variadic function like printf or scanf, the compiler may not be able to detect the type mismatch.)
Python spoiled me and trying to wrap my mind around C now is being a bloodbath of stupid errors. This is one I can't quite understand.
I wanted the C equivalent of Python's os.path.split, but there's no exact equivalent. strsep looks similar enough, but needs some massaging to be used simply.
First off, I defined my path type: a string of given length.
#define MAX_PATH_LEN 200 /* sigh */
typedef char t_path[MAX_PATH_LEN];
Then I wrote some code that does the actual massaging, attempting to avoid side effects -- just to keep things fool proof.
typedef struct {
t_path next;
t_path remainder;
} t_path_next
t_path_next path_walk_into(t_path path) {
t_path_next output;
t_path my_next, my_remainder = "/";
strncpy(my_next, path, MAX_PATH_LEN);
strsep(&my_next, my_remainder);
output.remainder = my_remainder;
output.next = my_next;
return output;
}
gcc, however, is not impressed.
badp#delta:~/blah$ gcc path.c -Wall
path.c: In function ‘path_walk_into’:
path.c:39: warning: passing argument 1 of ‘strsep’ from incompatible pointer type
/usr/include/string.h:559: note: expected ‘char ** __restrict__’ but argument is of type ‘char (*)[200]’
path.c:41: error: incompatible types when assigning to type ‘t_path’ from type ‘char *’
path.c:42: error: incompatible types when assigning to type ‘t_path’ from type ‘char *’
I am baffled by the note -- how are char ** and char (*)[200] really different -- but the error is even weirder. I want to assign a variable I declared t_path in a field of type t_path, but I don't get to.
Why is that?
For anybody interest here's the correctly working version of the function:
t_path_next path_walk_into(t_path path) {
t_path_next output;
t_path my_path, delim = "/";
char* my_path_ptr = my_path;
strncpy(my_path, path, MAX_PATH_LEN);
strsep(&my_path_ptr, delim); //put a \0 on next slash and advance pointer there.
if (my_path_ptr == NULL) //no more slashes.
output.remainder[0] = 0;
else
strncpy(output.remainder, my_path_ptr, MAX_PATH_LEN);
strncpy(output.next, my_path, MAX_PATH_LEN);
return output;
}
The errors: You can't directly assign to an array, such as a string, in C. You need to copy char by char, or call str(n)cpy, which does it for you.
For the warning : you are probably already aware that array may decay to pointer. That is, for example, what makes an array acceptable as an argument to a function where a pointer is expected. In your case, what you have is a pointer to an array : there is no reason for such a thing to get converted to a pointer to pointer.
For the record, the C99 standard says (6.3.2.1/3) :
Except when it is the operand of the sizeof operator or the unary & operator, or is a
string literal used to initialize an array, an expression that has type ‘‘array of type’’ is
converted to an expression with type ‘‘pointer to type’’ that points to the initial element of
the array object and is not an lvalue.
You're in the context of a unary & : no conversion for you.
For the error : it has already been answered, but array assignment is not directly possible. You might want to use strcpy or strncpy.