I was trying to pass an array to a method. I tried following ways:
- func2(ARRAY_NAME, length) => WORKS, no warning
- func2(&ARRAY_NAME[0], length) => WORKS, no warning
- func2(&ARRAY_NAME, length) => WORKS, but WITH WARNING
I dont understand why the last one (#3) gives warning. &ARRAY_NAME works without warnings in memset, memcpy etc. Why its a problem in custom method?
WARNING message:
functionTest.c:35:11: warning: passing argument 1 of ‘func2’ from incompatible pointer type [-Wincompatible-pointer-types]
35 | func2(&temp, ARRAY_SIZE);
| ^~~~~
| |
| unsigned char (*)[200]
functionTest.c:8:27: note: expected ‘unsigned char *’ but argument is of type ‘unsigned char (*)[200]’
8 | void func2(unsigned char* buf, int length)
CODE
#include <stdio.h>
#include <stdint.h>
#include <string.h>
#define ARRAY_SIZE 200
void func2(unsigned char* buf, int length)
{
// Change data of any index
buf[0] = 100;
buf[10] = 200;
}
void func1()
{
unsigned char temp[ARRAY_SIZE];
// Initialize
memset(temp, 0, sizeof(temp));
for(int i = 0; i < ARRAY_SIZE; i )
{
printf("\t%d", temp[i]);
}
printf("\n-----------------------------------------------\n");
printf("Address : %p\n", &temp);
printf("Address of 0th index : %p\n", &temp[0]);
printf("\n-----------------------------------------------\n");
// Pass array in func2
func2(&temp, ARRAY_SIZE); // WARNING
func2(&temp[0], ARRAY_SIZE); // NO WARNING
for(int i = 0; i < ARRAY_SIZE; i )
{
printf("\t%d", temp[i]);
}
printf("\n-----------------------------------------------\n");
}
int main()
{
func1();
return 0;
}
CodePudding user response:
As it is clear written in the error message
functionTest.c:8:27: note: expected ‘unsigned char *’ but argument is of type ‘unsigned char (*)[200]’
8 | void func2(unsigned char* buf, int length)
the function expects a pointer of the type unsigned char *
but the argument expression &temp
has the type unsigned char ( * )[200]
and there is no implicit conversion from one pointer type to another though values of the pointers are the same: the address of the extent of memory occupied by the array.
As for the functions memset
and memcpy
then they deal with pointers of the type void *
. For example the function memset
has the following declaration
void *memset(void *s, int c, size_t n);
And a pointer to object of other type can be implicitly converted to pointer of the type void *
.
From the C Standard (6.3.2.3 Pointers)
1 A pointer to void may be converted to or from a pointer to any object type. A pointer to any object type may be converted to a pointer to void and back again; the result shall compare equal to the original pointer.
CodePudding user response:
func2
is declared to have a first parameter of type unsigned char *
, so it should be passed a pointer to a type compatible with unsigned char
or a pointer to void
, which will be automatically converted to unsigned char *
.
With func2(temp, ARRAY_SIZE)
, the array temp
is automatically converted to a pointer to its first element. This pointer is an unsigned char *
, so it satisfies the requirements.
With func2(&temp[0], ARRAY_SIZE)
, temp[0]
is an unsigned char
, so &temp[0]
is a pointer to an unsigned char
. This satisfies the requirements.
With func2(&temp, ARRAY_SIZE)
, &temp
is a pointer to an array of 200 elements of unsigned char
. It points to the same place as &temp[0]
, bit its type is different. It is a pointer to an array, not a pointer to unsigned char
or to a compatible type nor a pointer to void
. So it does not satisfy the requirements, and the compiler complains.
Pointers to unsigned char
and pointers to arrays are different. If the type of pu
is unsigned char *
and the type of pa
is unsigned char (*)[200]
(a pointer to an array of 200 unsigned char
), then adding 1 to pu
, as in pu 1
, produces a pointer to the next unsigned char
after pu
, but adding 1 to pa
produces a pointer to the next array after pa
. In other words, pu 1
points to the next byte in memory, but pa 1
points 200 bytes further along in memory.
One purpose of C’s type system is to help avoid errors. When a non-matching type is passed to a function, the programmer may be expected some behavior other than what the language defines. So the compiler issues a diagnostic message.
With memset
, its first parameter is declared to be void *
. void
is an incomplete type; it acts as a placeholder for other types. memset
is designed to work on the bytes of any object, so it accepts a pointer to any object type. When any pointer to an object type is passed for a void *
parameter, it is automatically converted to void *
, without any diagnostic message.