mikrocontroller.net

Forum: Compiler & IDEs warning: from incompatible pointer type


Autor: Mitbewohner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
Hallo, ich habe bei meinem Array-Zugriff immer eine Warnung die ich 
nicht verstehe. Dies ist der relevante Code:
uint8_t array[4] = {1,2,3,4};


void main (void){

  funktion( ZielPort, &array );

}



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

Autor: FBI (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
  funktion(ZielPort, &array[0] );
oder einfach
  funktion(ZielPort, array );

CU

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: JJ (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
@Rolf: Häh?

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

Autor: kosmonaut_pirx (Gast)
Datum:

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

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Johannes M. (johnny-m)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Sebastian (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: kosmonaut_pirx (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
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:
void funktion(uint8_t a[4]);
// oder
void funktion(uint8_t a[]);
// oder
void funktion(uint8_t *a);

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


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

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

Funktionsaufruf:
funktion(&array);

Autor: Jörg Wunsch (dl8dtl) (Moderator) Benutzerseite
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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ß.

Autor: Mitbewohner (Gast)
Datum:
Angehängte Dateien:

Bewertung
0 lesenswert
nicht lesenswert
Hier noch der angepasste Code-Schnipsel.

Ist nicht viel, aber es kommt diese Warnung.

Autor: Florian Demski (code-wiz)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
void funktion(  uint8_t (*array)[4])
...

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

Problem klar? Man achte auf die Klammersetzung.

Autor: Mitbewohner (Gast)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
leider brachte das gar nichts.

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Florian Demski (code-wiz)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Florian Demski (code-wiz)
Datum:

Bewertung
0 lesenswert
nicht lesenswert
char array[4] = { 1, 2, 3, 4 };

void function( char tab[4] )
{
        char tmp;

        tmp = tab[0];
}

int main(void)
{
        function( array );
        return 0;
}

Keine Warnungen:

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

Autor: Johannes M. (johnny-m)
Datum:

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

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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[])

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Mitbewohner (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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?

Autor: Karl Heinz (kbuchegg) (Moderator)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: Rolf Magnus (Gast)
Datum:

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

funktionen.c:
#include <stdio.h>
#include <avr/pgmspace.h>
#include "avrlibdefs.h"
#include <avr/io.h> 
#include <avr/iom16.h> 
#include "global.h" 
#include <avr/io.h> 

extern uint8_t array[4]; 

void funktion(  uint8_t (*array)[4])
{
  uint8_t tmp = (((uint32_t)array[0])); 
}

main.c:
#include <stdio.h>
#include <avr/pgmspace.h>
#include "avrlibdefs.h"
#include <avr/io.h> 
#include <avr/iom16.h> 
#include "global.h" 
#include <avr/io.h> 

// Prototyp 
void funktion( uint8_t (*array[4])); 
 
uint8_t array[4] = {1,2,3,4}; 
 
void main(void) { 
   funktion( &array ); 
}

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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
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:
void funktion(uint8_t (*array)[4])
{
  uint8_t tmp = (*array)[0];
}

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

Ob du die Funktionsdeklaration nun so
void funktion(uint8_t array[4])
oder so
void funktion(uint8_t array[])
oder so
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).

Autor: Rolf Magnus (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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.

Autor: yalu (Gast)
Datum:

Bewertung
0 lesenswert
nicht 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).

Antwort schreiben

Die Angabe einer E-Mail-Adresse ist freiwillig. Wenn Sie automatisch per E-Mail über Antworten auf Ihren Beitrag informiert werden möchten, melden Sie sich bitte an.

Wichtige Regeln - erst lesen, dann posten!

  • Groß- und Kleinschreibung verwenden
  • Längeren Sourcecode nicht im Text einfügen, sondern als Dateianhang

Formatierung (mehr Informationen...)

  • [c]C-Code[/c]
  • [avrasm]AVR-Assembler-Code[/avrasm]
  • [code]Code in anderen Sprachen, ASCII-Zeichnungen[/code]
  • [math]Formel in LaTeX-Syntax[/math]
  • [[Titel]] - Link zu Artikel
  • Verweis auf anderen Beitrag einfügen: Rechtsklick auf Beitragstitel,
    "Adresse kopieren", und in den Text einfügen




Bild automatisch verkleinern, falls nötig
Bitte das JPG-Format nur für Fotos und Scans verwenden!
Zeichnungen und Screenshots im PNG- oder
GIF-Format hochladen. Siehe Bildformate.
Hinweis: der ursprüngliche Beitrag ist mehr als 6 Monate alt.
Bitte hier nur auf die ursprüngliche Frage antworten,
für neue Fragen einen neuen Beitrag erstellen.

Mit dem Abschicken bestätigst du, die Nutzungsbedingungen anzuerkennen.