Forum: Compiler & IDEs warning: from incompatible pointer type


von Mitbewohner (Gast)


Lesenswert?

Hallo, ich habe bei meinem Array-Zugriff immer eine Warnung die ich 
nicht verstehe. Dies ist der relevante Code:
1
uint8_t array[4] = {1,2,3,4};
2
3
4
void main (void){
5
6
  funktion( ZielPort, &array );
7
8
}


Die Warnung:
../main.c:33: warning: passing arg 2 of `funktion' from incompatible 
pointer type


Das "&" muss ich doch angeben, da ich eine Adresse übergebe... (?)

von FBI (Gast)


Lesenswert?

Hi,

"&array" ist vom Type "uint8_t**" (Pointer auf Pointer!). Ich vermute 
(da Du den entsprechenden Code unterschlagen hast), daß Deine Funktion 
aber ein "uint8_t*" erwartet. Also entweder
1
  funktion(ZielPort, &array[0] );
oder einfach
1
  funktion(ZielPort, array );

CU

von Rolf Magnus (Gast)


Lesenswert?

> "&array" ist vom Type "uint8_t**" (Pointer auf Pointer!).

Nein, ist es nicht. array ist kein Pointer, als ist &array auch kein 
Pointer auf einen Pointer. Es ist vom Typ uint8_t (*)[4], also Zeiger 
auf Array aus 4 uint8_t.

von JJ (Gast)


Lesenswert?

@Rolf: Häh?

array ist ein pointer auf das erste Arrayelement.
Und &array ist somit die Adresse des pointers.

von kosmonaut_pirx (Gast)


Lesenswert?

hallo,
nein, "array" ist in dem sinne kein pointer. da hat rolf schon recht. 
man kann z.B. nicht "array++" machen.
bye kosmo

von Johannes M. (johnny-m)


Lesenswert?

JJ wrote:
> array ist ein pointer auf das erste Arrayelement.
Nein, array ist die Adresse des ersten Array-Elements.

> Und &array ist somit die Adresse des pointers.
Ob man &array[0], &array oder einfach array schreibt, ist in C in diesem 
Zusammenhang wurscht, da der Name des Arrays gleichzeitig die Adresse 
des ersten Elements ist.

von Johannes M. (johnny-m)


Lesenswert?

Um zum Thema zurückzukommen:
Eine solche Warnmeldung entsteht i.d.R. dann, wenn man z.B. einer 
Funktion, die einen Zeiger auf int erwartet einen Zeiger auf char 
übergibt.

von Sebastian (Gast)


Lesenswert?

Guten Tag,

array ist schon nen Pointer, allerdings ein konstanter Pointer. Daher 
ist der nicht per ++ veränderbar. Die Lösungen von FBI sind schon 
richtig.

uint8_t *c=array;
und anschließend
c++; wiederrum sollte gehen.

Gruss, Sebastian

von Karl H. (kbuchegg)


Lesenswert?

> array ist schon nen Pointer

Vorsicht mit der Nomenklatur!
Array ist kein Pointer!
Unter bestimmten Umständen wird der Name eines Arrays aber
automatisch in die Adresse zum ersten Array Element mit
dem von Rolf genannten Datentyp gewandelt. Aber das ist
nicht dasselbe wie die Aussage, dass ein Array ein Pointer
sei. Ein Array ist ein Array und ein Pointer ist ein
Pointer.

von kosmonaut_pirx (Gast)


Lesenswert?

hallo,

>uint8_t *c=array;
>und anschließend
>c++; wiederrum sollte gehen.

was ist denn das für eine logik? kopfschüttel

klar kann ich aus jedem datentyp nen pointer bilden, entweder durch 
die zuweisung oder durch den referenz-operator. dass ich mit dem dann 
machen kann, was ich will, ist auch klar.

von Rolf Magnus (Gast)


Lesenswert?

> array ist schon nen Pointer, allerdings ein konstanter Pointer.

Nein! array ist ein Arrray, und ein Array ist kein Zeiger. Die 
Annahme, daß das so sei, ist wohl mit Abstand der am häufigsten gemachte 
Anfängerfehler in C.

von yalu (Gast)


Lesenswert?

Jetzt mal von Anfang an:

array ist weder ein Pointer noch eine Adresse, sondern ein Array mit 4
Elementen vom Typ uint8_t. Das kann man daran erkennen, dass
1
sizeof array
die Größe des gesamten Arrays und nicht die Größe eines Pointers
liefert.

Aber: Wird array in einer Rechenoperation verwendet oder als Argument
an eine Funktion übergeben, wird anstelle des Arrays ein Pointer auf
das erste Element verwendet. Deswegen kann in der Funktionsdeklaration
als Formalparameter wahlweise ein Array oder ein Pointer auf den Typ
der Arrayelemente stehen.

Weil array ein Array und kein Pointer ist, ist &array auch kein
Pointer auf einen Pointer, sondern ein Pointer auf ein Array (auf das
ganze Array, nicht auf das erste Element). Der Unterschied zu einem
Pointer auf das erste Element wird bspw. bei der Pointerarithmetik
deutlich:

array+3 ist
  die Anfangsadresse plus 3 mal die Größe von uint8_t, also
  die Anfangsadresse plus 3.

&array+3 ist
  die Anfangsadresse plus 3 mal die Größe des Arrays, also
  die Anfangsadresse plus 12 mal die Größe von uint8_t, also
  die Anfangsadresse plus 12.


Folgendes sind also richtige Schreibweisen für die Funktionsdekla-
ration und den Funktionsaufruf (ich habe der Übersichtlichkeit wegen
den ZielPort weggelassen):


Alternative 1: Übergabe als Pointer auf das erste Element:

Funktionsdeklaration:
1
void funktion(uint8_t a[4]);
2
// oder
3
void funktion(uint8_t a[]);
4
// oder
5
void funktion(uint8_t *a);

Funktionsaufruf:
1
funktion(array);
2
// oder
3
funktion(&array[0]);


Alternative 2: Übergabe als Pointer auf das ganze Array:

Funktionsdeklaration:
1
void funktion(uint8_t (*a)[4]);
2
// oder
3
void funktion(uint8_t (*a)[]);

Funktionsaufruf:
1
funktion(&array);

von Jörg W. (dl8dtl) (Moderator) Benutzerseite


Lesenswert?

Rolf Magnus wrote:

> Die
> Annahme, daß das so sei, ist wohl mit Abstand der am häufigsten gemachte
> Anfängerfehler in C.

Die rührt sicher daher, dass das bloße Nennen des Namens eines
Arrays (ohne Dereferenzierung mit []) genauso wie das Nennen des
Namens einer Funktion (ohne ,,Dereferenzierung'' mit ())
stillschweigend dazu führt, dass die Adresse des Arrays bzw. der
Funktion gebildet und weiter benutzt wird.

von Rolf Magnus (Gast)


Lesenswert?

> Alternative 1: Übergabe als Pointer auf das erste Element:

> void funktion(uint8_t a[4]);

Nochmal zur Verdeutlichung: Der Parameter sieht hier zwar aus wie ein 
Array, ist aber keins. Im Falle von Parametern ist diese Syntax die für 
Zeiger. Die 4 wird ignoriert, weshalb auch

> void funktion(uint8_t a[]);

dieselbe Bedeutung hat. Nicht sonderlich intuitiv, aber so ist es in C 
nun mal definiert.

> Alternative 2: Übergabe als Pointer auf das ganze Array:

> void funktion(uint8_t (*a)[]);

Das geht nicht, denn ein Array muß immer eine Größe haben, und auch ein 
Zeiger auf ein Array muß eine Angabe für die Arraygröße enthalten. Du 
mußt nur daran denken, daß sizeof *a ja die Größe des Arrays zurückgeben 
muß und diese dafür bekannt sein muß.

von Mitbewohner (Gast)


Angehängte Dateien:

Lesenswert?

Hier noch der angepasste Code-Schnipsel.

Ist nicht viel, aber es kommt diese Warnung.

von Florian D. (code-wiz)


Lesenswert?

void funktion(  uint8_t (*array)[4])
...

void funktion( uint8_t (*array[4]));

Problem klar? Man achte auf die Klammersetzung.

von Mitbewohner (Gast)


Lesenswert?

leider brachte das gar nichts.

von Karl H. (kbuchegg)


Lesenswert?

Mitbewohner wrote:
> leider brachte das gar nichts.

Dann must du etwas mehr Code zeigen.

Der genannte Fehler ensteht aus dem Zusammenspiel zwischen
Aufrufer und aufgerufener Funktion.
Bisher hast du nur die Aufruferseite gezeigt. Wie
sieht denn die aufgerufene Funktion aus?

von Florian D. (code-wiz)


Lesenswert?

Vielleicht hat man mich nicht verstanden.


Die Funktion ist definiert als

void funktion(  uint8_t (*array)[4]) { ... }

und wird aufgerufen mit dem Prototyp

void funktion( uint8_t (*array[4]));

Ich frage mich auch, was das soll:
  uint8_t tmp = (((uint32_t)array[0]));

Dir ist schon klar, dass Du hier einen Pointer auf 8 Bit castest, oder?

Wenn Du die Werte aus dem Array haben möchtest, müsstest Du

void funktion(  uint8_t array[4]) { ... }

nehmen und im Prototypen dann genauso.

Außerdem ist es ein schlechter Stil, dass bei Dir sowohl die globale 
Variable als auch der Funktionsparameter 'array' heißt.

von Florian D. (code-wiz)


Lesenswert?

1
char array[4] = { 1, 2, 3, 4 };
2
3
void function( char tab[4] )
4
{
5
        char tmp;
6
7
        tmp = tab[0];
8
}
9
10
int main(void)
11
{
12
        function( array );
13
        return 0;
14
}

Keine Warnungen:

chef@localhost ~/000 $ gcc -Wall -o1 1.c
chef@localhost ~/000 $

von Johannes M. (johnny-m)


Lesenswert?

@Mitbewohner:
Ein Array übergibt man entweder direkt an die Funktion, z.B. mit
1
void funktion(uint8_t array[4])
2
{}
3
//....Code....
4
//Aufruf in main:
5
funktion(array);
...was eher unüblich ist, oder (besser) man übergibt einen Pointer auf 
das erste Element:
1
void funktion(uint8_t *pointer)
2
{}
3
//....Code....
4
//Aufruf in main:
5
funktion(array);
Beide Varianten funktionieren ohne Warnmeldungen.

von Rolf Magnus (Gast)


Lesenswert?

> Ich frage mich auch, was das soll:
>  uint8_t tmp = (((uint32_t)array[0]));
>
> Dir ist schon klar, dass Du hier einen Pointer auf 8 Bit castest, oder?

Tut er nicht. Er nimmt das erste Element des Arrays und castet diesen 
8-Bit-Wert auf 32 Bits hoch. Danach wird er wieder auf 8 Bits 
runterkonvertiert.

> Ein Array übergibt man entweder direkt an die Funktion, z.B. mit
> void funktion(uint8_t array[4])

> ...was eher unüblich ist, oder (besser) man übergibt einen Pointer auf
> das erste Element:
> void funktion(uint8_t *pointer)

Liest eigentlich keiner meine Antworten, oder wenigstens mal ein C-Buch? 
Nochmal zum Mitschreiben:
Beide Varianten sind 100% äquivalent und tun genau dasselbe. In beiden 
Fällen wird ein Zeiger auf das erste Element übergeben. Die erste Syntax 
ist auch nicht so unüblich, wobei aber meistens die Zahl weggelassen 
wird, da sie vom Compiler ignoriert wird. Siehe:

int main(int argc, char* argv[])

von Rolf Magnus (Gast)


Lesenswert?

>> Dir ist schon klar, dass Du hier einen Pointer auf 8 Bit castest,
>> oder?
>
> Tut er nicht. Er nimmt das erste Element des Arrays und castet diesen
> 8-Bit-Wert auf 32 Bits hoch. Danach wird er wieder auf 8 Bits
> runterkonvertiert.

Vielleicht hätte ich doch zuerst den Code ansehen und dann meine Antwort 
posten sollen.

> uint8_t tmp = (((uint32_t)array[0]));

In der Funktion ist 'array' ja ein Zeiger auf ein Array und nicht mehr 
das Array selbst. array[0] dereferenziert dann den Zeiger, man erhält 
das Array. Ein Array kann nicht nach uint32_t konvertiert werden, 
deshalb wird zunächst die Adresse des ersten Elements gebildet, diese 
dann nach uint32_t konvertiert. Das Ergebnis davon wird dann auf 8 Bit 
runtergebrochen.

Das Beispiel zeigt sehr schön, daß man in C nicht weiterkommt, ohne zu 
wissen, was man tut. Man kann natürlich solange rumprobieren, bis es 
durch den Compiler geht, aber da kommt gegebenenfalls immer noch 
irgendwas völlig anderes raus als geplant.

von Mitbewohner (Gast)


Lesenswert?

>> leider brachte das gar nichts.

> Dann must du etwas mehr Code zeigen.

> Der genannte Fehler ensteht aus dem Zusammenspiel zwischen
> Aufrufer und aufgerufener Funktion.
> Bisher hast du nur die Aufruferseite gezeigt. Wie
> sieht denn die aufgerufene Funktion aus?

die aufgerufene Funktion ist doch "funktion ( ... )"

oder wie genau meinst du das?

von Karl H. (kbuchegg)


Lesenswert?

Mitbewohner wrote:
>>> leider brachte das gar nichts.
>
>> Dann must du etwas mehr Code zeigen.
>
>> Der genannte Fehler ensteht aus dem Zusammenspiel zwischen
>> Aufrufer und aufgerufener Funktion.
>> Bisher hast du nur die Aufruferseite gezeigt. Wie
>> sieht denn die aufgerufene Funktion aus?
>
> die aufgerufene Funktion ist doch "funktion ( ... )"
>
> oder wie genau meinst du das?

Tschuldigung.
Andere hatten anscheinend mehr Glück. Ich konnte das
RAR File nicht öffnen und hab daher deinen Code
nicht gesehen.

von Rolf Magnus (Gast)


Lesenswert?

Müßte eigentlich auch nicht unbedingt in ein rar-File.  Ich entpack's 
mal und sende es so:

funktionen.c:
1
#include <stdio.h>
2
#include <avr/pgmspace.h>
3
#include "avrlibdefs.h"
4
#include <avr/io.h> 
5
#include <avr/iom16.h> 
6
#include "global.h" 
7
#include <avr/io.h> 
8
9
extern uint8_t array[4]; 
10
11
void funktion(  uint8_t (*array)[4])
12
{
13
  uint8_t tmp = (((uint32_t)array[0])); 
14
}

main.c:
1
#include <stdio.h>
2
#include <avr/pgmspace.h>
3
#include "avrlibdefs.h"
4
#include <avr/io.h> 
5
#include <avr/iom16.h> 
6
#include "global.h" 
7
#include <avr/io.h> 
8
9
// Prototyp 
10
void funktion( uint8_t (*array[4])); 
11
 
12
uint8_t array[4] = {1,2,3,4}; 
13
 
14
void main(void) { 
15
   funktion( &array ); 
16
}

von yalu (Gast)


Lesenswert?

@Rolf Magnus:

>> void funktion(uint8_t (*a)[]);
>
> Das geht nicht, denn ein Array muß immer eine Größe haben, und auch
> ein Zeiger auf ein Array muß eine Angabe für die Arraygröße
> enthalten.

Das geht schon, da hier kein Array definiert, sondern lediglich
deklariert wird. Für die meisten Array-Operationen ist die Größe des
Arrays unerheblich, lediglich die Anfangsadresse und der Typ der
Elemente müssen bekannt sein.

Übrigens wird auch wird auch bei dieser Schreibweise letztendlich nur
ein Pointer auf den Arrayanfang an die Funktion übergeben, so dass
sich im kompilierten Code kein Unterschied zwischen Alternative 1 und
2 ergeben sollte.

> Du mußt nur daran denken, daß sizeof *a ja die Größe des Arrays
> zurückgeben muß und diese dafür bekannt sein muß.

a darf in diesem Fall natürlich nicht dereferenziert werden, da der
Array-Typ wegen der fehlenden Größe unvollständig ist. Das heißt aber
nicht, dass die o.g. Deklaration falsch ist. Falsch ist lediglich die
Dereferenzierung.

Einen ähnlichen Fall unvollständiger Typen gibt es bei Strukturen. Es
ist richtig (und durchaus üblich) zu schreiben
1
struct data_t *dataptr;
auch dann, wenn data_t in der gerade kompilierten C-Datei nicht
bakannt ist. Man kann mit dataptr trotzdem einiges tun, möglich sind
bspw. Zuweisungen und die Übergabe als Argument an Funktionen. Was
logischerweise nicht geht, sind Dereferenzierungen (darunter fällt
auch der ->-Operator) und Pointer-Arithmetik.



@Mitbewohner:

Ich habe jetzt ein leicht schlechtes Gewissen, weil ich durch meinen
ersten Beitrag wohl etwas Verwirrung gestiftet habe. Das, was ich oben
als Alternative 2 bezeichnet habe, ist zwar korrekt, aber man muss
dann auch die Elementzugriffe entsprechend anpassen, also so:
1
void funktion(uint8_t (*array)[4])
2
{
3
  uint8_t tmp = (*array)[0];
4
}

In Alternative 1 sehen die Arrayzugriffe aber klarer aus und erfordern
weniger Tipparbeit:
1
void funktion(uint8_t array[4])
2
{
3
  uint8_t tmp = array[0];
4
}
Deswegen ist diese in 99,9% aller Fälle die bessere Wahl.

Ob du die Funktionsdeklaration nun so
1
void funktion(uint8_t array[4])
oder so
1
void funktion(uint8_t array[])
oder so
1
void funktion(uint8_t *array)
schreibst, ist Geschmacksache, sie sind sematisch äquivalent. Ich
benutze normalerweise (um die Benutzung des Parameters deutlich zu
machen)

- die erste Schreibweise, wenn ein Array als Argument übergeben wird,
  dessen Größe von vornherein bekannt ist (die Arraygröße ist dann
  typischerweise irgendwo als Konstante #definiert),

- die zweite Schreibweise, wenn ein Array als Argument übergeben wird,
  dessen Größe aber von Aufruf zu Aufruf unterschiedlich ist (in
  diesem Fall wird die Größe meist durch einen weiteren Parameter
  übergeben) und

- die dritte Schreibweise, wenn kein Array, sondern ein Pointer auf
  eine einzelne Variable übergeben wird (call by reference) mit der
  Absicht, den Wert dieser Variablen in der Funktion zu verändern
  (In/Out-Parameter), oder um bei gößeren Datenstrukturen  die
  Übergabe effizienter zu machen (keine größeren Kopiervorgänge
  erforderlich).

von Rolf Magnus (Gast)


Lesenswert?

>> Du mußt nur daran denken, daß sizeof *a ja die Größe des Arrays
>> zurückgeben muß und diese dafür bekannt sein muß.
>
> a darf in diesem Fall natürlich nicht dereferenziert werden, da der
> Array-Typ wegen der fehlenden Größe unvollständig ist. Das heißt aber
> nicht, dass die o.g. Deklaration falsch ist. Falsch ist lediglich die
> Dereferenzierung.

Du mußt den Zeiger ja dereferenzieren, wenn du auf das Array zugreifen 
willst. Ein (*a)[3] = 5; meldet zumindest bei gcc auch keinen Fehler. 
Aber ein sizeof(*a) tut das.

von yalu (Gast)


Lesenswert?

> Du mußt den Zeiger ja dereferenzieren, wenn du auf das Array
> zugreifen willst. Ein (*a)[3] = 5; meldet zumindest bei gcc auch
> keinen Fehler. Aber ein sizeof(*a) tut das.

Ok, dass man den Array-Pointer nicht dereferenzieren darf, war ungenau
ausgedrückt. Lass es mich deswegen folgendermassen salomonisch
ausdrücken: Man darf nur solche Operationen ausführen, für die die
Kenntnis der Array-Größe nicht erforderlich ist. Das sollte
hinreichend präzise sein ;-)

Genau genommen wird bei (*a)[5] das a aber auch gar nicht
dereferenziert. *a ist zwar theoretisch das ganze Array, da aber der
[]-Operator darauf angewendet wird, wird das Array durch einen Pointer
auf das erste Element ersetzt:

  (*a)[5] = *(&(*a)[0]+5]

Die Dereferenzierung *a und die später folgende Referenzierung mit &
heben sich in ihrer Wirkung teilweise auf. &(*a)[0] entspricht
letztendlich einer Typumwandlung, so dass schließlich

  (*a)[5] = *((uint8_t *)a +5)

ist. Es wird also in Wirklichkeit kein Pointer auf ein Array, sondern
ein Pointer auf ein uint8_t (nämlich auf das erste Element des Arrays)
dereferenziert.

von yalu (Gast)


Lesenswert?

> ein Pointer auf ein uint8_t (nämlich auf das erste Element des
> Arrays)

Quatsch, ich meinte natürlich das sechste Element (das mit dem
Index 5).

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.