Indirection in C with Multidimensional Arrays - c

I would like to execute something like the following code, but I keep getting a warning for levels of indirection.
It is my understanding that in the following,
int Array1[5];
int Array2[2][4];
int *pointer1 = Array1;
int *pointer2 = Array2;
all 4 examples have the level of indirection of (int*).
But it seems like no matter what combination of the following ideas I try to compile the code, I get indirection warnings:
void loadIntArrayData(int size, int *buffer);
void passArrayIntoThisFunction(char count, int *structure){ //idea 1
void passArrayIntoThisFunction(char count, int **structure){ //idea 2
void passArrayIntoThisFunction(char count, int structure[][3]){ //idea 3
loadIntArrayData(3*count*sizeof(int), structure); //idea 1
loadIntArrayData(3*count*sizeof(int), &(structure[0][0])); //idea 2
loadIntArrayData(3*count*sizeof(int), &(structure[0])); //idea 3
loadIntArrayData(3*count*sizeof(int), structure[0]); //idea 4
}
int main(void){
passArrayIntoThisFunction(2, (int[][3]){{1,2,3},{4,5,6}});
//I want to have a multi-level array ^ here ^ for readability
return 0;
}
Could someone explain levels of indirection when it comes to multi-dimensional arrays?
Here is the warning (small variations depending on which combination used):
'function': 'int *' differs in levels of indirection from 'int[2][3]'

In C, the name of an array decays to the pointer of its first element in all cases but three: usage of the & (address-of) operator, usage of the sizeof operator, and literal string initialization (e.g. char a[] = "foobar";)
Therefore, in int *pointer2 = Array2; your Array2 decays to a int (*)[] (pointer-to-array-of-int) and not an int *.

If you have an array like for example this
int Array2[2][4];
when used in expressions (with rare exceptions) in particularly as an initializer it is converted to pointer to its first element. The elements of the array have the type int[4]. So a pointer to the first element of the array will have the type int( * )[4].
Thus you may write
int Array2[2][4];
int ( *pointer2 )[4] = Array2;
As for this function declaration
void passArrayIntoThisFunction(char count, int structure[][3]);
and its call
passArrayIntoThisFunction(2, (int[][3]){{1,2,3},{4,5,6}});
then the compound literal used as an argument has the type int ( * )[3] and the corresponfing function parameter is adjusted by the compiler to int ( *structure )[3].
So if you want to pass this pointer further to another function then either a corresponding parameter of the function must have the same type or it can have the type int * but in this case you have explicitly to cast the passed pointer to the type int *.

Related

Passing array in a c function using address operator gives a warning

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;
}
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.
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.

C pointer to 2D arrays

Throughout my c codes. ignore confusing identifier name. Reason what in assignment of ptr61 pointer i have to put address (&) operator, while & is needless in assignment of ptr62 pointer.
int var6[3][4] = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
int (*ptr61)[][4];
ptr61 = &var6;
printf("lvl 9: %d\n", (*ptr61)[2][0]);
int (*ptr62)[4];
ptr62 = var6;
printf("lvl 9: %d\n",*(*(ptr62 + 1)+2));
An array designator used in expressions like for example as initializer is implicitly converted to pointer to its first element.
So if you have an array like this
int var6[3][4] = {
{1,2,3,4},
{5,6,7,8},
{9,10,11,12}
};
then this declaration declares an array of 3 elements of the type int[4]. Thus pointer to element of the array will have the type int ( * )[4] and you may write
int (*ptr62)[4] = var6;
If you will write
ptr62 = &var6;
you will get a compiler error because in the left side hand of the assignment there is an object of the type int( * )[4] while in the right hand side there is an object of the type int( * )[3][4] and these types are not compatible and there is no implicit conversion from one type to another.
If you want to declare a pointer to the whole array that has the type int[3][4] then such a pointer will have the type int ( * )[3][4] and you may write
int (*ptr61)[3][4] = &var6;
As for this declaration
int (*ptr61)[][4] = &var6;
then it declares a pointer to the incomplete array type int[][4].

Different semantics between `char *` and `int *`

I want to know why declaring and assigning a char * versus an int * in C have different semantics.
All of the following code is compiled with clang using the flags -Wall -Werror -std=gnu99 --pedantic.
I am trying to understand whether the difference is something that just is, or whether there's some bigger difference between int * and char * that I have yet to realize.
// this will compile
int main(int argc, const char *argv[]) {
char *a;
a = "1";
printf("a: %s\n", a);
return EXIT_SUCCESS;
}
// this will not compile. It's a parallel construction, substituting char for int
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[]) {
int *a;
a = 1;
printf("a: %d\n", a);
return EXIT_SUCCESS;
}
// but this will compile
#include <stdio.h>
#include <stdlib.h>
int main(int argc, const char *argv[]) {
int *a;
int b = 1;
a = &b;
printf("a: %d\n", *a);
return EXIT_SUCCESS;
}
The gist of it:
The data type of "1" is char*. So you are assigning a char* to a char* variable.
The data type of 1 is int. So you are assigning an int to an int* variable which won't work since the types don't match.
The second example won't compile because you have -Werror on, and you're attempting to assign an integer to a pointer without a cast, which is normally just a warning. The same would happen if you did char* a; a='a'; because you'd be attempting to assigning a char to char*.
In terms of different semantics between pointer types - what is stored in the memory as the pointer, it's likely not going to be different, but the pointer type will affect compilation. Mainly when it comes to pointer arithmetic, array indexing, and using pointers to aggregate (struct) types and functions.
Things start acting differently when you start using pointer arithmetic/array indexing. For example:
int value = 4
char *charp = &value;
int* intp = &value;
These will both be valid pointers. You'll also see different behaviors during compilation if you're working with function pointers, which you can use function call syntax with, and struct pointers, where you can access members of the referenced structure with the -> operator.
You're dealing with a difference between array, pointer, and scalar semantics.
Except when it is the operand of the sizeof or unary & operators, or is a string literal used to initialize a character array in a declaration, an expression of type "N-element array of T" will be converted ("decay") to an expression of type "pointer to T", and the value of the expression will be the address of the first element of the array.
The string literal "1" is an array expression of type char [2]. In this context, it "decays" to an expression of type char *, and the value of the expression is the address of the first character of the string. So you're assigning like to like:
a = "l"; // char * = char *
This doesn't happen with the integer variant. The literal 1 has type int, but you're trying to assign that int value to an int * object:
a = 1; // int * = int
which should trigger a diagnostic. You can only assign an int * value to an int * object - you would need to explicitly cast the 1:
a = (int *) 1;
which will suppress the diagnostic, but 1 is very likely not a valid memory address on your platform, and attempting to use it will cause problems.

How do I fix warning: assignment from incompatible pointer type?

I wanted to ask how to get rid of warning that says assignment from incompatible pointer type.
This is the code I use:
#include<stdio.h>
int main()
{
int a2d[2][2]={{1,2},{3,4}};
int **pa;
pa=a2d;
}
And getting this:
7:5: warning: assignment from incompatible pointer type pa=a2d;
When this
pa=a2d;
executes, compiler shoots the warning/error(depends upon whether you are using -Werror or not) as
main.cpp:7:8: error: cannot convert 'int [2][2]' to 'int**' in assignment
7 | pa=a2d;
| ^~~
| |
| int [2][2]
and this error is valid as a2d is two dimensional array, that doesn't mean its of int** type, similar to the concept of array is not pointer.
To make things works pa should be pointer to an array not double pointer int**. For example
int a2d[2][2]={{1,2},{3,4}};
int (*pa)[2] = a2d; /* pa is pointer to an array */
here pa can point to 2 elements at a time i.e pa points to a2d[0] base address and pa+1 will points to a2d[1] base address.
You are probably looking to decay your 2d array to array of pointers to array elements. In this case:
int a2d[2][2] = { { 1,2 },{ 3,4 } };
int (*pa)[2] = a2d;
To access 4:
printf("%d\n", pa[1][1]);
Your assignment is wrong, you can't parse 2d array to pointer to pointer.
Your code should like this:
#include<stdio.h>
int main()
{
int a2d[2][2]={{1,2},{3,4}};
int * pa;
pa=&a2d[0][0];
}
Becasue 2d array isn't double pointer. But this works:
#include<stdio.h>
int main()
{
int ad[2]={4,5};
int * pa;
pa=ad;
}
But it's passing its address. But if you write:
int a2d[2][2]={{1,2},{3,4}};
int ** pa;
pa=a2d;
It passing address of a2d array to double pointer...

How to pass a multidimensional array as pointer? [duplicate]

This question already has answers here:
Passing a 2D array of structs
(2 answers)
Closed 6 years ago.
I have this two functions and I need to pass the table array and modify his content.
void play_game(int mines, int c, int r) {
char (*table)[c][r];
build_field(*table, c, r);
}
void build_field(char *table ,int tamanhox, int tamanhoy) {
int i, x;
for(i=1;i<tamanhoy+1;i++){
table[tamanhoy][tamanhox] = '-';
for(x=0;x<tamanhox-1;x++){
table[tamanhoy][tamanhox] = '-';
}
}
In this code,there's errors at table[tamanhoy][tamanhox].
You get an error because you declare the argument table to be a pointer to a char, not a pointer to an array of arrays. You also don't make table actually point anywhere.
Both these problems can be solved by not making table a pointer to an array of array, and by changing the order you pass the arguments to the build_field function:
char table[c][r];
build_field(c, r, table);
...
void build_field(int tamanhox, int tamanhoy, char table[][tamanhoy])
{
// Use table like a normal array of array (i.e. table[x][y] = z)
}
There are a couple of other problems with the code you show as well, like you always using table[tamanhoy][tamanhox] in the build_field function, when you probably mean table[x][i].
You are also looping out of bounds in the function, remember that indexes are zero-based, so valid indexes for the first "dimension" is 0 to tamanhox - 1 (inclusive).
This declaration
char (*table)[c][r];
does not declare an array. It is declares a pointer to an object of type char[c][r]. By the way why is not ?:)
char[r][c]
^^^^^
Thus before using this pointer it shall be correctly initialized.
In this function declaration
void build_field(char *table ,int tamanhox, int tamanhoy);
the first parameter has type char *. Types char ( * )[c][r] and the type char * are different incompatible types.
You could write the function declaration for example the following type
void build_field( int tamanhox, int tamanhoy, char ( *table )[tamanhox][tamanhoy] );
But inside the function you have to dereference the pointer. For example
( *tablw)[I][j]
The exact parameter declaration depends on how the pointer is initialized and what you are going to do. Maybe you mean the following declarations
char (*table)[r];
and
void build_field( int tamanhox, int tamanhoy, char ( *table )[tamanhoy] );
The compiler issues an error because the function parameter table has type char *. So table[I] is a scalar object of type char. Hense you may not apply to it the subscript operator like table[I][x]. And moreover this expression
table[tamanhoy][tamanhox]
^^^^^^^^ ^^^^^^^^^
in any case does not make sense because at least you should write
table[i][x]
^^^^^^

Resources