Why did these variable apparently change type? - c

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.

Related

Getting warning when trying to pass the address in a function

I get the warning:
expected 'const CHAR *' but argument is of type 'CHAR (*)[18]'
Why is the compiler giving me this warning? This is the line I am using to call to the api:
AG_AS(g_AgMgrCtx.audio_device.Profile_Type,&g_AgMgrCtx.SDevAddr);
The declaration of the function is
AG_AS(AG_DType d_type , CHAR *addr);
char SDevAddr[18];
What is the problem with passing the address? When I remove & the warning goes away.
What does the warning actually mean?
That means your call should be (without the &):
AG_AS(g_AgMgrCtx.audio_device.Profile_Type, g_AgMgrCtx.SDevAddr);
g_AgMgrCtx.SDevAddris an array of 18 chars. So when you pass it to a function, it gets converted into a pointer to its first element (thus matching the type that AG_AS() expects).
Relevant: What is array decaying?
When you have an object like this
T obj;
where T is some type then expression &obj has type T * that is it is a pointer to an object of type T.
This array declaration
char SDevAddr[18];
can be transformed to the form shown above using a typedef. For example
typedef char T[18];
T SDevAddr;
In this case expression &SDevAddr has type T * as it has been pointed above. however in this case T in turn an alias for the type char[18]. Thus &SDevAddr is a pointer to an object of type char[18] Its type looks like char ( * )[18].
However as it is seen from the function declaration
AG_AS(AG_DType d_type , CHAR *addr);
the second parameter has type char *. If you will pass as an argument the array itself like SDevAddr then it will be implicitly converted to pointer to its first element and will have type char * that is required for the function call.

Dubious syntax to access array content

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.)

C: a cast warning

I have a code, but have a warning when it was compiling, this was the warning:
1_redis.c: In function \342\200\230main\342\200\231:
1_redis.c:131:23: warning: assignment makes integer from pointer without a cast
[enabled by default]
it says assignment makes integer from pointer without a cast, but cFile and lQueryData are all char* type, why?
#define MAX_LINE_NUM 8000000
#define EACH_THREAD_NUM 10000
long i,posF,posDB;
for (i=0;i<DB_NUM;i++) { lQueryPerDB[i] = 0; }
char *lQueryData = (char *)malloc(DB_NUM*MAX_LINE_NUM*sizeof(char));
lLineCount = lFileLen / lLineLen;
for (i=0;i<lLineCount;i++) {
posF = i * lLineLen;
iDB = get_DB(cFile[posF]);
posDB = iDB * MAX_LINE_NUM + lQueryPerDB[iDB];
lQueryData[posDB] = &cFile[posF]; // this line have warning!!!!
lQueryPerDB[iDB]++;
}
If cFile is a char *, indexing it like cFile[posF] is the same as doing *(cFile + posF), or "give me the value of whatever is posF places from the start of the array." Your address-of operator (&) is unnecessary, and if it weren't, it would probably be the opposite of what you wanted (you want dereference — * — which is automatically done for you with the subscript notation).
As other people have suggested, the correct code is most likely:
lQueryData[posDB] = cFile[posF];
The reason you get the particular warning you do is because using &cFile[posF] gets the address of (i.e., a pointer to) the character in cFile at posF. However, when you then try to assign it to a place in the lQueryData array, it has to first cast that (internally) into a scalar type. A pointer is just a number that indexes into memory, and thus when used for anything but "pointing" to things, it's just an integer. Therefore, what you have is making an integer, from a pointer, without an explicit cast.
You might consider compiling with clang instead of (what I presume is) GCC if you have access to it, it has much more accessible error messages. For instance, the error message for your code above is: warning: incompatible pointer to integer conversion assigning to 'char' with an expression of type 'char *'; remove & (with a little visual pointer to the offending "&" sign).
If they are both char * then why are you using & operator. Just say
lQueryData[posDB] = cFile[posF];
The expression &cFile[posF] is a pointer to the value, so you should be taking its value like this:
lQueryData[posDB] = cFile[posF];

GCC compiler and converting const char* to char *

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.

expected ‘uint32_t’ but argument is of type ‘uint32_t *’

I am new in C, trying to call a function, but it gives me error that I can not understand why
int set_price(&colour->type.name);
it returns me expected ‘uint32_t’ but argument is of type ‘uint32_t *’. warning: passing argument ‘int set_price’ makes integer from pointer without a cast
where the pointer is
house_list *colour = NULL;
and
name is defined in struct as
uint32_t name;
the original function accept
int set_price(uint32_t name) { /do something here/ }
what do I do wrong? If in the struct member, name is defined as uint32_t, and I defined a pointer colour, than I believe that I need to use & before colour->type and use dot before name isn't it?
Thank you
set_price(&colour->type.name);
remove the & and you'll be fine
set_price(colour->type.name);
set_price expects an integer as an argument, not a pointer to integer.
I suggest that you should read a good C book.

Resources