Forum: PC-Programmierung Zeiger Array


von M.S. (Gast)


Lesenswert?

Hallo,

ich habe Probleme beim Übergeben von Daten aus einem Array in eine 
Funktion.

Die Struktur sieht wie folgt aus:
Zuerst habe ich die Klasse in der Bberklasse abc erstellt:

class ABC{
class xyz{
    public:
        float value;
        ...
}
public:
xyz * array = NULL;
}

Auf die Elemente kann ich nun mit array[].value zugreifen.

nun möchte ich diese Werte in einem Funktionsaufruf übergeben. Wie mache 
ich dies?

Meine Idee war:
void funktion(float feld[]){
}

rufe ich diese Funktion so auf, erhalte ich die Werte, allerdings mit 
vier Nullen zwischen jeder Zahl:
funktion(&array[0].value);


Daher meine Frage:
Wie schiebe dich die Werte als Array in den Funktionsaufruf, sodass 
diese von der Funktion richtig ausgegeben werden können?

von Hört sich gut an (Gast)


Lesenswert?

Du musst sie vorher umpacken. Einen Aufruf der nur jedes x-te Element 
beachtet gibt es nicht. Noch einfach wäre einfach einen zeiger auf das 
array zu übergeben.

von M.S. (Gast)


Lesenswert?

Hallo,
vielen Dank für deine Antwort.
Kannst du mir ein Beispiel für den Zeiger auf das Array geben?

von Hört sich gut an (Gast)


Lesenswert?

funktion(&array);

von Hört sich gut an (Gast)


Lesenswert?

Nur so als Tip, ein array nennt man nicht array (obwohl es sich 
anbietet) sondern vergibt einen sinnvollen Namen. In anderen 
Programmiersprachen ist array ein keywort und kann sehr blöde Effekte 
und Fehler verursachen. Das Gleiche gilt für die Funktion "funktion".

von M.S. (Gast)


Lesenswert?

dann bekomme ich den Fehler:
cannot inizialize a parameter of type 'float *' with an rvalue of type 
'xyz**'

von Hört sich gut an (Gast)


Lesenswert?

Das Array ist nicht array of float sondern array of xyz. Ändere als 
deine Parameterdefinition des Parameters feld in der Funktion.

von M.S. (Gast)


Lesenswert?

Funktioniert super, vielen herzlichen Dank für deine Hilfe!

von Hört sich gut an (Gast)


Lesenswert?

Gerne!

von Wilhelm M. (wimalopaan)


Lesenswert?

Nimm wenigstens einen SmartPointer wir unique_ptr oder shared_ptr, je 
nachdem was Du brauchst.

Besser: nimm statt eines rohen C-Arrays in C++ ein std::vector oder 
std::array.

von PittyJ (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Nimm wenigstens einen SmartPointer wir unique_ptr oder shared_ptr,
> je
> nachdem was Du brauchst.
>
> Besser: nimm statt eines rohen C-Arrays in C++ ein std::vector oder
> std::array.

Man muss nicht gleich alles noch doppelt verpacken. Mache ich bei Gurken 
und Bananen um Supermarkt auch nicht.
Manchmal reicht ein Array. Man muß nur wissen, was man tut.

von Rolf M. (rmagnus)


Lesenswert?

PittyJ schrieb:
> Man muss nicht gleich alles noch doppelt verpacken.

Ist es ja nicht. Es steckt nur statt in einem Array in einem 
std::vector.

PittyJ schrieb:
> Mache ich bei Gurken und Bananen um Supermarkt auch nicht.

Aber du nimmst sie vielleicht in einer Tasche oder einem Korb mit nach 
hause und nicht auf deinem Kopf balancierend.

> Manchmal reicht ein Array. Man muß nur wissen, was man tut.

Welchen Nachteil siehst du beim vector?

: Bearbeitet durch User
von Wilhelm M. (wimalopaan)


Lesenswert?

PittyJ schrieb:
> Man muss nicht gleich alles noch doppelt verpacken.

std::array hat zero-overhead, std::vector hat das zusätzliche feature 
der dynamischen Größe zur Laufzeit, beim Elementzugriff auber wieder 
zero-overhead im Vergleich zum rohen C-Array mit seinen bekannten 
Nachteilen: "Ein C-Array ist eine so dumme Datenstruktur, die nicht 
einmal seine eigene Länge kennt!". O-Ton Bjarne.

PittyJ schrieb:
> Manchmal reicht ein Array. Man muß nur wissen, was man tut.

Du meinst ein C-Array? Nein, ein C-Array in C++ ist immer falsch.

von Kaj (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Nimm wenigstens einen SmartPointer wir unique_ptr oder shared_ptr, je
> nachdem was Du brauchst.
>
> Besser: nimm statt eines rohen C-Arrays in C++ ein std::vector oder
> std::array.
Noch besser:
Der TO verabschiedet sich von C und C++ und nutzt direkt Rust...

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> ein C-Array in C++ ist immer falsch.

Und Pauschalisierungen sind immer falsch ;-)

Natürlich gibt es auch Anwendungen für C-Arrays. Ein typisches Beispiel
ist ein statischer Text, repräsentiert durch eine Sequenz einzelner
Textzeilen.

Die einfachste und eleganteste Lösung dafür sieht IMHO so aus:

1
const char *const text[] = {
2
  "erste Zeile",
3
  "zweite Zeile",
4
  "eine weitere Zeile",
5
  nullptr
6
};

Hier werden C-Arrays gleich in zweierlei Weise verwendet: Arrays von
Zeichen für die einzelnen Zeilen und ein Array von Zeigern für den
gesamten Text.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Natürlich gibt es auch Anwendungen für C-Arrays.

In C++-Code nicht bzw. nur an der Schnittstelle zu C-Code.

Yalu X. schrieb:
> Die einfachste und eleganteste Lösung dafür sieht IMHO so aus:

Für mich so:
1
    const std::array a{"abc"s, "def"s};

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Für mich so:
>     const std::array a{"abc"s, "def"s};

Kompiliert nicht.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Kompiliert nicht.

Compiler? Flags? vollständiger Code?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Yalu X. schrieb:
>> Kompiliert nicht.
>
> Compiler?

GCC, das aktuelle Release (9.2.0)

> Flags?

g++ -std=c++2a -c test.cpp

> vollständiger Code?

test.cpp:
1
#include <array>
2
#include <string>
3
4
const std::array a{"abc"s, "def"s};

von Wilhelm M. (wimalopaan)


Lesenswert?

Die UDL-Ops aus std sind aus gutem Grund in einem extra NS, daher ist
1
    using namespace std::literals;

notwendig.
1
#include <array>
2
#include <vector>
3
#include <string>
4
#include <algorithm>
5
#include <iostream>
6
7
int main() {
8
    using namespace std::literals;
9
10
    const std::array a{"abc"s, "def"s};
11
    std::vector v{"xyz"s, "zyx"s};
12
13
    std::copy(std::begin(a), std::end(a), std::back_inserter(v));
14
15
    for(const auto& s : v) {
16
        std::cout << s << '\n';
17
    }
18
}

Natürlich geht mit CTAD auch
1
    const std::array a2{"abc", "def"};

aber dann gilt eben
1
    static_assert(std::is_same_v<const char*, decltype(a2)::value_type>);

von DPA (Gast)


Lesenswert?

So, das Problem ist ja folgendes:
1
struct sometype {
2
  int a;
3
  float b;
4
};
5
struct sometype somearray[10];
6
7
void some_function(float* x);
8
...
9
some_function(somearray[*].b); // Kein Array von b floats, nicht machbar
Da ist es egal, ob C, C++, rust, etc. Das muss halt umgepackt werden, 
wenn man das Argument nicht ändern will.

Nachfolgend gehe ich mal die Möglichkeiten in plain C durch.

Wenn man das Argument von some_function ändert, hat man folgende 
Möglichkeiten:
  1) struct sometype übergeben. Dann gehen aber float arrays und andere 
typen nicht mehr.
  2) Lückengrösse übergeben, also wieviele elemente zu überspringen. ( 
"some_function(&somearray[0].b, 
sizeof(somearray[0])/sizeof(somearray[0].b))" ). Geht halt dann nur mit 
arrays, aber nicht mit linked lists oder so.
  3) Ein Iterator. Das ist ja normalerweise auch der C++ Ansatz. Ich 
versuch mich mal an nem generischen Iterator in c.

(alles ungetestet)
iterator.h
1
#ifndef ITERATOR_H
2
#define ITERATOR_H
3
4
#define container_of(ptr, type, member) \
5
  ((type*)( (ptr) ? (char*)(ptr) - offsetof(type, member) : 0 ))
6
7
#define DECLARE_ITERATOR_TYPE(T) \
8
  struct iterator_type__ ## T { \
9
    size_t size; \
10
    T* next(struct iterator__ ## T*); \
11
  }; \
12
  struct iterator__ ## T { \
13
    const struct iterator_type__ ## T* type; \
14
  };
15
16
#define IT_NEXT(X) (X)->type->next(X)
17
#define IT_DUP(T, N, X) struct iterator__ ## T* N = alloca(X->type->size); N=X; memcpy(N,X,X->type->size);
18
19
DECLARE_ITERATOR_TYPE(void);
20
21
#endif

array_iterator.h
1
#ifndef ARRAY_ITERATOR_H
2
#define ARRAY_ITERATOR_H
3
4
#include <iterator.h>
5
6
extern struct iterator_type__void array_iterator_type;
7
8
struct array_iterator {
9
  const struct iterator_type__void* type;
10
  size_t size, count;
11
  void* current;
12
};
13
14
#define GENERIC_ARRAY_ITERATOR(S, N, X) \
15
  ((struct iterator__ ## T*)&(struct array_iterator){ \
16
    .type = &array_iterator_type;
17
    .size = S, \
18
    .count = (N), \
19
    .current = (X)
20
  })
21
22
#define ARRAY_ITERATOR(T, N, X) SPARSE_ARRAY_ITERATOR(sizeof(T), N, (T*){(X)})
23
#define SPARSE_ARRAY_ITERATOR(T, S, N, X) SPARSE_ARRAY_ITERATOR(sizeof(T) * S, N, (T*){(X)})
24
#define ARRAY_MEMBER_ITERATOR(T, N, X, M) GENERIC_ARRAY_ITERATOR(T, sizeof(*X), N, (T*){&X->M})
25
26
#endif

array_iterator.c
1
#include <array_iterator.h>
2
3
void* next(struct iterator__void* it){
4
  if(!it->count)
5
    return 0;
6
  it->count -= 1;
7
  void* res = it->current;
8
  it->current = (char*)res + it->size;
9
  return res;
10
}
11
12
struct iterator_type__void array_iterator_type {
13
  .size = sizeof(struct array_iterator);
14
  .next = next
15
};

main.c
1
#include <array_iterator.h>
2
3
struct sometype {
4
  int a;
5
  float b;
6
};
7
struct sometype somearray[] = {
8
  {1, 1.1},
9
  {2, 2.2},
10
  {3, 3.3},
11
  {4, 4.4}
12
};
13
14
DECLARE_ITERATOR_TYPE(float);
15
16
void some_function(struct iterator__float* it){
17
  float* x;
18
  while(x=IT_NEXT(it))
19
    printf("%f\n", *x);
20
}
21
22
23
int main(){
24
  some_function( ARRAY_MEMBER_ITERATOR(float, 4, somearray, b) );
25
}

Später kann man dann natürlich auch andere Iteratoren dazufügen, z.B. 
welche für linked list usw. Schön generisch eben.

Beitrag #6134069 wurde vom Autor gelöscht.
von Wilhelm M. (wimalopaan)


Lesenswert?

DPA schrieb:
> Das muss halt umgepackt werden,
> wenn man das Argument nicht ändern will.

Was meinst Du damit?

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> using namespace std::literals;

Ok, damit geht es.

Danke, wieder etwas dazugelernt. Diese s-Literals kannte ich noch nicht.

Ungeschickt ist die Verwendung von std::array in meinem obigen Beispiel
dennoch, da die Größe des Arrays Bestandteil des Typs ist. Das bedeutet,
dass eine Funktion, die Texte mit unterschiedlicher Zeilenzahl als
Argument entgegennehmen soll, als Template-Funktion realisiert werden
muss, was bei umfangreichem Funktionscode und vielen unterschiedlich
langen Texten sehr schnell zu Code-Bloat führt.

Des Weiteren wird bei "abc"s das zunächst als C-String vorliegende "abc"
bei der Erzeugung des std-string-Objekts kopiert und liegt damit zweimal
im Speicher. Insbesondere bei langen Texten wird sich das deutlich im
Speicherverbrauch bemerkbar machen.

Fazit: Dein Vorschlag ist zwar rein formal in Ordnung, belegt aber
unnötig viel Speicherplatz, sowohl für Code als auch für Daten. Der
klassische Weg über C-Arrays hat dieses Problem nicht.

von DPA (Gast)


Lesenswert?

Wilhelm M. schrieb:
> DPA schrieb:
>> Das muss halt umgepackt werden,
>> wenn man das Argument nicht ändern will.
>
> Was meinst Du damit?

Na wenn man eine Funktionssignatur f(T x[]) hat, die man nicht ändern 
kann oder will, aber im speicher liegt nicht xxx, sondern z.B. 
yxyyyxyyyxyy, oder die Typen sind anderweitig inkompatibel, dann muss 
man das halt umkopieren. Dass kann auch ein std::array/vector in C++ 
sein, oder ein Vec in rust. Nur, wenn man f etwas generischeres 
(sinvolleres?) wie z.B. ein Iterator übergibt, oder umbaut und f zu ner 
callback Funktion macht falls möglich, ist das nicht mehr nötig. 
Nachteil ist dann aber, dass man bei Funktionen, die wirklich nur Arrays 
durchlaufen müssen, mehr Overhead hat. Ist halt immer ein tradeoff 
zwischen vielen Faktoren. Und eben, manchmal kann man das gar nicht 
einach so ändern, z.B. wenn die Funktion von ner fremden Library kommt.

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Ungeschickt ist die Verwendung von std::array in meinem obigen Beispiel
> dennoch, da die Größe des Arrays Bestandteil des Typs ist.

Das ist ein theoretisches Argument. In der Praxis tragen andere Faktoren 
wesentlich mehr zum Code-Bloat bei. Zudem sidn wir hier auf dem PC. Und 
zudem gilt 3) s.u.

Ich habe doch auch std::vector dazu geschrieben.

Yalu X. schrieb:
> Das bedeutet,
> dass eine Funktion, die Texte mit unterschiedlicher Zeilenzahl als
> Argument entgegennehmen soll, als Template-Funktion realisiert werden
> muss, was bei umfangreichem Funktionscode und vielen unterschiedlich
> langen Texten sehr schnell zu Code-Bloat führt.

1) Spielt das auf dem PC eine Rolle?
2) führt ggf. zu schnellerem Code (speed vs size)
3) dafür gibt es std::span
4) zur Not eben mit const char*

Yalu X. schrieb:
> Des Weiteren wird bei "abc"s das zunächst als C-String vorliegende "abc"
> bei der Erzeugung des std-string-Objekts kopiert und liegt damit zweimal
> im Speicher. Insbesondere bei langen Texten wird sich das deutlich im
> Speicherverbrauch bemerkbar machen.

Spielt das beim PC eine Rolle? Wohl kaum.

Auf dem µC kann man ja die andere Variante (mit const char*) nehmen, 
oder einen noch besseren Ansatz ggf. über flash-strings (hatte ich in 
anderen Beiträgen u.a. für PGM für AVR schon öfters mal gepostet).

Der Vorteil von std::array oder std::vector als Container bleibt in 
jedem Fall erhalten.

von Wilhelm M. (wimalopaan)


Lesenswert?

DPA schrieb:
> Na wenn man eine Funktionssignatur f(T x[]) ha

Und genau das ist ja hier das Problem: der TO übergibt einen Zeiger auf 
T (hier float*). Es handelt sich aber gar nicht um eine Array (also eine 
zusammenhängende Folge von float). Deswegen sind Zeiger, die wie C-Array 
behandelt werden, einfach nur großer Mist.

Der TO hat ein Array mit Elementtyp xyz, dann sollte er auch das an 
seine Funktion übergeben, und nichts anderes.

: Bearbeitet durch User
von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> In der Praxis tragen andere Faktoren wesentlich mehr zum Code-Bloat
> bei.

Auch jeder dieser anderen Faktoren trägt – für sich alleine gesehen –
nur wenig zum Gesamt-Bloat bei. Also tut man am besten überhaupt nichts
und lässt den Code fröhlich weiter bloaten, oder?

Ich kann es nachvollziehen, wenn man Bloat zugunsten der Produktivität
bei der Entwicklung bewusst in Kauf nimmt, wie es bspw. bei den meisten
VHLLs der Fall ist.

Im vorliegenden Fall kann aber der anteilige Bloat durch die Verwendung
von C-Arrays ohne jeglichen Mehraufwand vermieden werden. Man muss
lediglich pauschalisierende und unzutreffende Regeln wie

Wilhelm M. schrieb:
> ein C-Array in C++ ist immer falsch.

ignorieren ;-)

von Wilhelm M. (wimalopaan)


Lesenswert?

Yalu X. schrieb:
> Man muss
> lediglich pauschalisierende und unzutreffende Regeln wie
>
> Wilhelm M. schrieb:
>> ein C-Array in C++ ist immer falsch.
>
> ignorieren ;-)

Und genau das führt dann zu solchen Fehlern des TO.

von DPA (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Der TO hat ein Array mit Elementtyp xyz, dann sollte er auch das an
> seine Funktion übergeben, und nichts anderes.

Nein. Der Algorithmus der Funktion wird höchstwahrscheinlich nichts mit 
dem Elementtyp xyz zutun haben, und könnte eventuell an anderen Orten 
wiederverwendet werden. Also sollte er vermutlich einen Iterator nehmen. 
Bzw., In C++ einen start und end iterator.

von Wilhelm M. (wimalopaan)


Lesenswert?

DPA schrieb:
> Nein.

Der TO hat
1
xyz * array = NULL;

Er interpretiert(!) diesen Zeiger als ein Array: an dem Typ xyz* kann 
man das nicht erkennen.

Dann übergibt er einen Zeiger float* an die Funktion:
1
funktion(&array[0].value);

Das ist ein Zeiger auf das Member
1
class xyz{
2
    public:
3
        float value;
4
        ...
5
};

und kein(!) Array.

Diese Fehler kann man mit C-in-C++ machen, weil er auf Annahmen(!) 
basiert, die nicht im Typsystem sich wiederfinden, und somit auch nicht 
vom Compiler geprüft werden können. Dasselbe gilt ja für C-Strings 
char*.

Hier und nirgendwo anders liegt der Fehler in diesem Code.

Weitere Probleme ergeben sich dann aus der Länge des vermeintlichen 
Arrays. Da der Elementtyp float ist, hat man keine Sentinel-Möglichkeit. 
Insofern ist die Funktionssignatur schon aus diesem Grunde falsch.

Lösungen dafür in C++ gibt es viele. Die hängen natürlich auch davon ab, 
ob z.B. die Funktion zum Bereich des TO gehört. Also viele Fehler, die 
fast ausschließlich damit zu tun haben, dass man C-Arrays verwendet.

: Bearbeitet durch User
von DPA (Gast)


Lesenswert?

Wir scheinen hier 2 komplett verschiedene Kriege zu führen. Klar, in C++ 
hat man std::array, in C könnte man ein "struct array_T { size_t size; 
T* daten; }", machen, um die selbe Typsicherheit zu erreichen, aber 
darum geht es mir doch gar nicht. Und erklären, was der TO falsch macht, 
brauchst du mir auch nicht, du hast meine Beiträge doch gelesen, da 
weisst du doch, das ich das schon längst weis.

Mir get es darum, wie über die Werte iteriert werden, und insbesondere, 
das wenn er nur an den Floats interessiert ist, die Funktion wohl kaum 
etwas mit dem strukt/class, in dem sie sind, zutun hat. Demnach wäre es 
unsinnig, die Funktion von dem Datentyp abhängig zu machen. Dieser 
Aspekt ist unabhängig von C, C++ oder Rust zu in Erwägung zu ziehen.

Um mal ein Beispiel zu geben, nehmen wir mal an, der TO hätte eine 
Funktion wie folgt:
1
void sum(size_t size, float f[size]){
2
  size_t result;
3
  for(size_t i=0; i<size; i++)
4
    result += f[i];
5
  return result;
6
}

Würdest du da wirklich vorschlagen, daraus sowas zu machen?
1
void sum(size_t size, xyz f[size]){
2
  size_t result;
3
  for(size_t i=0; i<size; i++)
4
    result += f[i]->value;
5
  return result;
6
}

Wenn man das für jeden Typ wiederholt, wo führt das dann hin? Ins chaos!

Es gibt mehrere Wege, das stattdessen allgemein zu lösen. Eine davon 
wären entsprechende Iteratoren. In dem Beispiel oben, falls man sich auf 
Arrays beschränkt, könnte man alternativ auch z.B. mitgeben, wie weit 
die einzelnen Werte entfernt sind, oder ein callback/lambda mitgeben, 
der den Wert zurück gibt. Das ist alles besser, als xyz zu übergeben, 
weil wiederverwendbarer an anderen Orten. Darum geht es mir. Dass du C 
Pointerarithmetik scheisse findest, ist mir scheiss egal, darum geht es 
mir hier doch gar nicht.

von Wilhelm M. (wimalopaan)


Lesenswert?

DPA schrieb:
> Würdest du da wirklich vorschlagen, daraus sowas zu machen?void
> sum(size_t size, xyz f[size]){
>   size_t result;
>   for(size_t i=0; i<size; i++)
>     result += f[i]->value;
>   return result;
> }

Nein.

Aber warum um alles in der Welt willst Du krampfhaft in ein C++-Code das 
C-in-C++ einführen? Das ist doch kompletter Unsinn.

Ganz oben habe ich dem TO vorgeschlagen, statt eines C-Arrays ein 
std::array oder std::vector zu nehmen. Das ist die Lösung. Und doch 
nicht hier einen archaischen C-Code einzuführen!

Wenn man schon wie Du die Signatur von der Funktion ändert, dann 
richtig:
1
auto function(const std::vector<float>&);

Und dann ein
1
std::vector<float> values;
2
3
std::transform(std::begin(xyz_array), std::end(xyz_array), std::back_inserter(values), [](auto x){return x.value;});
4
5
function(values);

Dann kann man sich Deinen Krampf-Pseudo-Iterator-in-C-Kram sparen.

von Wilhelm M. (wimalopaan)


Lesenswert?

DPA schrieb:
> Wenn man das für jeden Typ wiederholt, wo führt das dann hin? Ins chaos!

Das ist halt dann die C-Lösung, die Du hier vorschlägst.

von Vincent H. (vinci)


Lesenswert?

DPA schrieb:
> ...

literally everyone: "Heavily templated C++ code is the ugliest thing 
there is."
DPA: "Hold my beer!"

von Wilhelm M. (wimalopaan)


Lesenswert?

DPA schrieb:
> Dass du C
> Pointerarithmetik scheisse findest, ist mir scheiss egal, darum geht es
> mir hier doch gar nicht.

Wo habe ich das denn geschrieben. Lies doch mal!

Es ging doch überhaupt nicht um Zeigerarithmetik. Es geht mir um diese 
üblen C-Arrays.

von leo (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Es geht mir um diese
> üblen C-Arrays.

Genau,
1
std::transform(std::begin(xyz_array), std::end(xyz_array), std::back_inserter(values), [](auto x){return x.value;});
... ist viel schoener, lesbarer und kompakt ;-)

leo

von DPA (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Aber warum um alles in der Welt willst Du krampfhaft in ein C++-Code das
> C-in-C++ einführen? Das ist doch kompletter Unsinn.

Dass ist doch nur ein Beispiel, weil ich mich in C besser auskenne als 
in C++. Wie schon gesagt, ob das jetzt C, C++ oder Rust ist, spielt bei 
dieser Problematik keine Rolle.

Wilhelm M. schrieb:
> Ganz oben habe ich dem TO vorgeschlagen, statt eines C-Arrays ein
> std::array oder std::vector zu nehmen. Das ist die Lösung.

Nein, dann hast du wieder exakt das selbe Problem! Dann musst du die 
Daten erst kopieren, bevor du sie übergibst, du kannst sie nicht einfach 
so übergeben. Deshalb sag ich, Übergib Iteratoren um über float werte zu 
iterieren, nicht einen konkreten Container. Man braucht nur sowas wie 
den transform_iterator von boost (von der standard library konnte ich 
grad nichts vergleichbares finden), und schon kann man über die Floats 
drüber iterieren, ohne die Werte erst kopieren zu müssen.

von Wilhelm M. (wimalopaan)


Lesenswert?

DPA schrieb:
> Dann musst du die
> Daten erst kopieren, bevor du sie übergibst, du kannst sie nicht einfach
> so übergeben. Deshalb sag ich, Übergib Iteratoren um über float werte zu
> iterieren, nicht einen konkreten Container.

Oh, man. Das habe ich doch schon gesagt: es kommt halt darauf, ob man 
die Signatur von
1
auto function(float*);
2
[c]
3
4
zu etwas andrem ändern kann. Aber so ist die Signatur halt Mist!
5
6
Wenn das möglich ist, dann sollte man das tun, und einen Adaptor (Iterator) dazwischen hängen. Das wäre idiomatisches C++ und absolut normal. 
7
8
[c]
9
template<typename It>
10
auto function(It first, It end);

und dann
1
function(adapter_begin(values), adapter_end(values));

von Wilhelm M. (wimalopaan)


Lesenswert?

Vielleicht ist ja auch ein Lifting möglich:
1
std::for_each(std::begin(xyz_values), std::end(xyz_values), [](auto v){function(v.value);});

Aber das weiß wohl nur der abwesende TO.

: Bearbeitet durch User
von DPA (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Es ging doch überhaupt nicht um Zeigerarithmetik. Es geht mir um diese
> üblen C-Arrays.

Du weisst schon, dass bei weder bei f(T* x) noch f(T* y[]) C Arrays 
involviert sind? x[n] ist in dem fall Pointerarithmetik. Die Arrays sind 
da schon längst zu Zeigern zerfallen. Das Problem ist halt, das in C oft 
Zeiger als Iteratoren verwendet werden.


Wilhelm M. schrieb:
> Wenn das möglich ist, dann sollte man das tun, und einen Adaptor
> (Iterator) dazwischen hängen. Das wäre idiomatisches C++ und absolut
> normal.

Na also, dort sind wir uns doch einig. Man muss nur mal über die 
Sprachgrenzen hinwegsehen, letztendlich sind die Konzepte hinter all dem 
doch immer übertragbar.

von Wilhelm M. (wimalopaan)


Lesenswert?

DPA schrieb:
> Du weisst schon, dass bei weder bei f(T* x) noch f(T* y[]) C Arrays
> involviert sind?

Natürlich sind das C-Arrays. Und ja, die Array-Bezeichner sind da schon 
zu Zeigern zerfallen. Und weil das so ist, fehlt sowohl die Information, 
ob es überhaupt ein Array ist als auch die Elementanzahl.

Wie gesagt: "Ein C-Array ist eine so dumme Datenstruktur, dass sie noch 
nicht einmal ihre Länge weiß". Und das ist hier immer noch das Problem.

Und nun zum x-ten Mal:

man kann es in der Signatur

- mit einem konkreten Container
- mit einem generischen Container
- mit einem konkreten Iterator-Paar
- mit einem generischen Iterator-Paar
- mit einem konkreten Range
- mit einem generischen Range

lösen. Aber ein C-Array ist und bleibt der falsche Weg in C++.

von Frank M. (ukw) (Moderator) Benutzerseite


Lesenswert?

Wilhelm M. schrieb:
1
std::for_each(std::begin(xyz_values), std::end(xyz_values), [](auto v){function(v.value);});
> Aber das weiß wohl nur der abwesende TO.

Meinst Du wirklich, dass der TO, der offensichtlich blutiger 
C/C++-Anfänger ist, Deine Zeile, die mehr Sonderzeichen als Buchstaben 
enthält, überhaupt versteht?

Ich bezweifle das. Der TO ist längst wegen Überforderung raus. Der 
kryptische Code schreckt eher ab als sich näher damit zu beschäftigen. 
Sorry, Du machst es einem wirklich nicht sehr schmackhaft, C komplett 
über Bord zu werfen. Wieviele Klammer-Fehler kann man in diesem 
Dschungel machen?

: Bearbeitet durch Moderator
von Wilhelm M. (wimalopaan)


Lesenswert?

Frank M. schrieb:
> Meinst Du wirklich, dass der TO, der offensichtlich blutiger
> C/C++-Anfänger ist, Deine Zeile, die mehr Sonderzeichen als Buchstaben
> enthält, überhaupt versteht?

Mag sein, dass er das Lifting nicht versteht. Aber das ist nicht der 
entscheidende Punkt.

Der wichtige Punkt gerade für einen C++-Anfänger ist, keine C-Arrays zu 
verwenden. Sondern eben nur std::vector oder std::array. Dann hätten wir 
diesen ganzen Thread nicht.
Und als Anfänger würde ich auch sofort die Variante mit dem Umkopieren 
benutzen. Einfach und vollkommen klar. Ggf. ein ganz klein bisschen 
Overhead.

Aber hier kommen ja dann Leute, die meinen, man müsste das mit 172 
Zeilen Code in kryptischem C-Code erschlagen. Das ist doch das Problem. 
Darauf sage ich nur: vergiss es! Wenn schon optimiert, dann aber doch 
mit C++-Idiomen, und nicht mit so einem C-Gewurstel.

von Mox (Gast)


Angehängte Dateien:

Lesenswert?

Wilhelm M. schrieb:
> std::array hat zero-overhead

Wilhelm M. schrieb:
> Für mich so:
>     const std::array a{"abc"s, "def"s};

Ich bin absolut kein Experte, aber nach zero-overhead sieht das für mich 
irgendwie nicht aus, siehe Anhang. Oder kann ich nur deshalb nicht 
folgen, weil Dem C++er auf dem PC sowieso alles egal ist (siehe Wilhelm 
weiter oben)?

Wilhelm M. schrieb:
> Aber ein C-Array ist und bleibt der falsche Weg in C++.

Ich verwende sie trotzdem.

von Wilhelm M. (wimalopaan)


Lesenswert?

Mox schrieb:
> Wilhelm M. schrieb:
>> std::array hat zero-overhead
>
> Wilhelm M. schrieb:
>> Für mich so:
>>     const std::array a{"abc"s, "def"s};
>
> Ich bin absolut kein Experte, aber nach zero-overhead sieht das für mich
> irgendwie nicht aus, siehe Anhang.

Bitte genau lesen: std::array ist zero-overhead. Von std::string habe 
ich das nicht behauptet. Dann lass doch einfach den UDL-op weg.

von Wilhelm M. (wimalopaan)


Lesenswert?

Mox schrieb:
> Oder kann ich nur deshalb nicht
> folgen, weil Dem C++er auf dem PC sowieso alles egal ist (siehe Wilhelm
> weiter oben)?

Bin sehr fest davon überzeugt, dass das für die Aufgabe unseres TO 
vollkommen egal ist.

von Mox (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Bin sehr fest davon überzeugt, dass das für die Aufgabe unseres TO
> vollkommen egal ist.

Bestimmt, nur hast Du nicht mit dem TO gesprochen sondern mit Yalu. Ihm 
war es offensichtlich nicht so egal, Dir dagegen schon.


Wilhelm M. schrieb:
> Bitte genau lesen: std::array ist zero-overhead. Von std::string habe
> ich das nicht behauptet. Dann lass doch einfach den UDL-op weg.

Alles klar, Danke. Für Strings sind also doch wieder C-Array das Mittel 
der Wahl.

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.