Forum: PC-Programmierung Gegen array[Size]


von Wilhelm M. (wimalopaan)


Lesenswert?

Auch hier schon sehr oft diskutiert, dass
1
void foo(int array[Size]);

einfach falsch (weil: falsch-interpretierbar) ist. Durch Zufall habe ich 
dazu

https://lkml.org/lkml/2015/9/3/428

gefunden.

von Adam P. (adamap)


Angehängte Dateien:

Lesenswert?

Wilhelm M. schrieb:
> https://lkml.org/lkml/2015/9/3/428

"Please people. When I see these kinds of obviously bogus code
problems, that just makes me very upset."

Wilhelm M. schrieb:
> void foo(int array[Size]);

Warum sollte man so etwas machen?


Visual Studio erkennt zumindest was wirklich gemeint ist (Siehe Bild).

von Wilhelm M. (wimalopaan)


Lesenswert?

Adam P. schrieb:
> Warum sollte man so etwas machen?

Das frage ich mich auch.
Wer so etwas macht, hat einfach C nicht verstanden.

Tatsache ist aber, dass es (hier) oft gemacht wird bzw. der Folgefehler 
(sizeof()) gemacht wird.

von Programmierer (Gast)


Lesenswert?

Wilhelm M. schrieb:
> https://lkml.org/lkml/2015/9/3/428

Kann man jemandem Glauben schenken der so eine Gossensprache benutzt?

von Wilhelm M. (wimalopaan)


Lesenswert?

Programmierer schrieb:
> Kann man jemandem Glauben schenken der so eine Gossensprache benutzt?

Ich stimme ja mit vielen seiner Äußerungen nicht(!) überein, und seine 
verbale Ausdrucksweise ist nun allgemein bekannt, aber hier hat er 
wirklich recht.

von vn nn (Gast)


Lesenswert?

Programmierer schrieb:
> Wilhelm M. schrieb:
>> https://lkml.org/lkml/2015/9/3/428
>
> Kann man jemandem Glauben schenken der so eine Gossensprache benutzt?

Kann man jemand Glauben schenken, der keine Argumente bringt?

von Adam P. (adamap)


Lesenswert?

Programmierer schrieb:
> Kann man jemandem Glauben schenken der so eine Gossensprache benutzt?

Nunja, aber es ist nun mal Tatsache, dass man so etwas nicht macht.

Ja das mit dem sizeof() ist natürlich der Oberhammer.
Dann doch lieber KISS und die Welt bleibt in ordnung ;)
1
#define ARRAY_1_SIZE  10
2
3
uint8_t array_1[ARRAY_1_SIZE];
4
5
/**************************************/
6
void foo(uint8_t *src, size_t n)
7
{
8
  /* ... */
9
}
10
11
/**************************************/
12
int main()
13
{
14
  uint8_t array_2[123];
15
16
  /* 1 */
17
  foo(array_1, ARRAY_1_SIZE);
18
19
  /* 2 */
20
  foo(array_2, sizeof(array_2));
21
22
  return 0;
23
}

von Wilhelm M. (wimalopaan)


Lesenswert?

Adam P. schrieb:
> Dann doch lieber KISS und die Welt bleibt in ordnung ;)

Oder man nimmt C++ und templates, denn der Array-Typ ist bei C++ 
vorhanden, an der Stelle zerfällt der Array-Bezeichner natürlich zu 
einem Zeigertyp. Bei templates wird die Arraygröße jedoch richtig 
abgeleitet.

von Adam P. (adamap)


Lesenswert?

Wilhelm M. schrieb:
> Oder man nimmt C++

Ja im ersten Moment sah mir das auch eher nach C++ aus, obwohl selbst 
dafür der Aufruf unvollständig war.

Viel schlimmer wäre es, wenn so etwas gelehrt werden würde :-/
Aber ich hab schon vieles erlebt...

von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Wilhelm M. schrieb:
> Oder man nimmt C++ und templates, denn der Array-Typ ist bei C++
> vorhanden, an der Stelle zerfällt der Array-Bezeichner natürlich zu
> einem Zeigertyp. Bei templates wird die Arraygröße jedoch richtig
> abgeleitet.

Und templates sind ja nicht einmal nötig. Im obigen Beispiel was die 
Größe des Arrays ja eine bekannte Konstante:
1
#include <iostream>
2
3
int foo(char (*p)[10])
4
{
5
    std::cout << "size: " << sizeof(*p) << std::endl;
6
}
7
8
int main()
9
{
10
    char c[10];
11
    foo(&c);
12
}

Sollte auch in C funktionieren und funktioniert natürlich auch mit 
Referenzen. In C wäre evtl. das verpacken eines Arrays in eine struct 
noch eine Lösung, bei der ich die Länge des Array in den Typen verpacken 
kann und damit nur zur Compile-Zeit habe? (bin kein C-Experte)
1
#include <stdio.h>
2
3
struct array {
4
    char p[10];
5
};
6
7
void foo(struct array* p)
8
{
9
    printf("size: %zu\n", sizeof(p->p));
10
}
11
12
int main()
13
{
14
    struct array a;
15
    foo(&a);
16
}

von Adam P. (adamap)


Lesenswert?

Torsten R. schrieb:
> In C wäre evtl. das verpacken eines Arrays in eine struct
> noch eine Lösung

Ja das funktioniert,
aber ist das nicht von hinten durch die Brust ins Auge ;) ?

von Wilhelm M. (wimalopaan)


Lesenswert?

Adam P. schrieb:
> aber ist das nicht von hinten durch die Brust ins Auge

Ja: C ist grundsätzlich so. Da man das Typ-System nicht erweitern kann.

von g457 (Gast)


Lesenswert?

> Dann doch lieber KISS und die Welt bleibt in ordnung ;)
[..]
> uint8_t array_2[123];
[..]
>  foo(array_2, sizeof(array_2));

Ersters (KISS) unbedingt. Für array_2 aber nicht sizeof() benutzen (das 
funktioniert hier nur zufällig wie erwartet), generischer ist sowas
1
#define sizeofarray(x)     (sizeof(x)/sizeof((x)[0]))
2
foo(array_2, sizeofarray(array_2));
Bei der Nutzung von array_1 ist es zweckmäßig, das der Codewartbarkeit 
ebenfalls so machen und auf das Größendefine verzichten.

von Adam P. (adamap)


Lesenswert?

g457 schrieb:
> (das
> funktioniert hier nur zufällig wie erwartet)

g457 schrieb:
> #define sizeofarray(x)     (sizeof(x)/sizeof((x)[0]))

Ja hast recht.
Es kommt halt auf den Typ drauf an und was du damit machen möchtest.
Aber ja, verwende ich ebenfalls an vielen Stellen.

Denn wenn du ein uint32_t Array hast und deine Variante verwendest, dann 
erhälst du die Anzahl der "Elemente"...Unpraktisch wenn du in der 
Funktion aber mit einem uint8_t Pointer die Daten verarbeiten 
willst...warum auch immer :-D

Edit:
z.B. eine Funktion die Big-Little-Endian konvertiert.
Auch wenn das mit einer uint32_t + Maske gehen würde.

: Bearbeitet durch User
von Torsten R. (Firma: Torrox.de) (torstenrobitzki)


Lesenswert?

Adam P. schrieb:
> Torsten R. schrieb:
>> In C wäre evtl. das verpacken eines Arrays in eine struct
>> noch eine Lösung
>
> Ja das funktioniert,
> aber ist das nicht von hinten durch die Brust ins Auge ;) ?

Kommt darauf an: Wenn Du Domain-Typen hast, die an vielen Stellen 
verwendet werden und Arrays fester Länge sind, dann kannst Du so den 
Compiler dazu nutzen Fehler zur Compiler-Zeit zu finden.

Was wären die Nachteile? Ich würde davon ausgehen, dass der Code nicht 
wesentlich ineffektiver wäre und bei konsequenter Verwendung im gesamten 
Projekt sollte es auch nicht vorkommen, dass overhead durch Umkopieren 
entstehen muss.

von DPA (Gast)


Lesenswert?

Dass man in dem fall sizeof nicht nutzen kann, ist natürlich unschön. 
Trotzdem würde ich die Grösse immer hinschreiben, wenn möglich, schon zu 
Dokumentationszwecken. Code analyse tools könnte es eventuell auch 
helfen, Fehler aufzudecken. Wenn andere dass dann falsch verwenden, ist 
das nicht mein Problem. Und mit aktuellem GCC, vernünftigem Warnlevel 
und -Werror geht das sizeof dort ja dann auch nicht mehr.

Bei Mehrdimensionalen Arrays wird es dann sogar richtig nützlich. 
Folgendes gibt 19 aus:
1
void test(int n, int x[][n]){
2
    printf("%d", x[3][1]);
3
}
4
5
int main()
6
{
7
    test(6, (int[][6]){
8
        { 0,  1,  2,  3,  4,  5},
9
        { 6,  7,  8,  9, 10, 11},
10
        {12, 13, 14, 15, 16, 17},
11
        {18, 19, 20, 21, 22, 23}, // Zeile 3
12
    });
13
    return 0;
14
}

von zitter_ned_aso (Gast)


Lesenswert?

DPA schrieb:
> Bei Mehrdimensionalen Arrays wird es dann sogar richtig nützlich.
> Folgendes gibt 19 aus:void test(int n, int x[][n]){
>     printf("%d", x[3][1]);
> }

Und wie gibt man die ganze Matrix aus?

n kann ich ja so ermitteln:
1
int col=sizeof(x[0])/sizeof(int);

aber wie ermittele ich die Anzahl der Zeilen (wenn man sie nicht als 
Parameter mitübergibt)?

von MaWin (Gast)


Lesenswert?

Wilhelm M. schrieb:
> Auch hier schon sehr oft diskutiert, dass
> void foo(int array[Size]);
>
> einfach falsch

Es gibt in C++ aber die richtige Lösung

If you are using C++ there is a better approach, you can pass the array 
as a reference to the function, this way you will keep the size 
information:

 1 // The array is passed as a reference to the function, keeping the 
size information (only for C++)
 2 template<typename T, size_t N>
 3 void func(T (&a)[N]) {
 4   // Here N stores the correct size information for the array
 5   // Basically N is equal to sizeof(my_array)/sizeof(my_array[0]) so 
the next code will work as expected:
 6   for(int i = 0; i < N; ++i) {
 7     a[i] = a[i] + i;
 8   }
 9 }
10
11
12 int main() {
13   // C-style array
14   int my_array[5] = {1, 2, 3, 4, 5};
15
16   // Pass the array to a function
17   func(my_array);
18   return 0;
19 }

Similarly, you can pass a multi-dimensional C-style array to a function 
without losing the size information:

1 template<typename T, size_t M, size_t N>
2 void func(T (&my_array)[M][N]) {
3   // Here M = no. of lines and N = no. of columns
4   // ....
5 }

If you are interested to learn more about modern C++ I would recommend 
reading A Tour of C++ by Bjarne Stroustrup.

von DPA (Gast)


Lesenswert?

zitter_ned_aso schrieb:
> aber wie ermittele ich die Anzahl der Zeilen (wenn man sie nicht als
> Parameter mitübergibt)?

Na gar nicht.

von Wilhelm M. (wimalopaan)


Lesenswert?

MaWin schrieb:
> Es gibt in C++ aber die richtige Lösung

Hatte ich doch oben schon geschrieben:

Beitrag "Re: Gegen array[Size]"

von Yalu X. (yalu) (Moderator)


Lesenswert?

Wilhelm M. schrieb:
> Auch hier schon sehr oft diskutiert, dass
>
> void foo(int array[Size]);
>
> einfach falsch (weil: falsch-interpretierbar) ist.

Meiner Meinung nach ist etwas nicht falsch, nur weil es möglicherweise
von einem blutigen Anfänger falsch interpretiert werden kann, denn wenn
man darauf Rücksicht nehmen wollte, dürfte man 80% der Sprachelemente
von C und C++ nicht verwenden.

Folgende drei Deklarationen sind ja bekanntlich äquivalent:

1
void func(int *arg);          // (1)
2
void func(int arg[]);         // (2)
3
void func(int arg[SIZE]);     // (3)

Bei mehreren Schreibweisen für den gleichen Sachverhalt kann man die
Redundanz nutzen, um dem Leser durch die gezielte Wahl einer bestimmten
Alternative zusätzliche Informationen zu liefern, also eine bestimmte
Absicht zu dokumentieren.

I.Allg. verwende ich die drei Alternativen deswegen wie folgt:

(1) für Zeiger auf einzelne Objekte. Der Sinn dahinter ist, ein Out-
oder ein In/Out-Argument zu deklarieren, das in der Funktion beschrieben
bzw. überschrieben wird. Innerhalb der Funktion wird auf das Objekt
ausschließlich mit *arg und nicht etwa mit dem äquivalenten arg[0]
zugegriffen.

(2) für Zeiger auf eine Sequenz mit einer unbestimmten Anzahl von
Einzelobjekten, also insbesondere auch für Arrays, wohl wissend, dass
sich die Operatoren & und sizeof, auf den dem Zeiger angewandt, anders
verhalten als bei einem Array. Innerhalb der Funktion wird auf die
einzelnen Objekte mit arg[i] und nicht etwa mit dem äquivalenten
*(arg+i) zugegriffen.

(3) für Zeiger auf eine zusammenhängende Sequenz mit einer festen Anzahl
SIZE von Einzelobjekten, also insbesondere auch Arrays mit genau SIZE
Elementen, wohl wissend, dass der Compiler dies nicht überprüft.
Innerhalb der Funktion wird auf die einzelnen Objekte wie bei (2) mit
arg[i] zugegriffen.

Der Anwender der Funktion erkennt damit allein aus der Deklaration (ohne
die gesamte Softwaredokumentation lesen zu müssen) sofort, dass bei (2)
die Funktion nicht einen Zeiger auf eine Einzelvariable, sondern ein
Array o.ä. erwartet. Bei (3) erkennt er zusätzlich, dass nicht irgendein
Array, sondern eins mit exakt SIZE Elementen erwartet wird.


Ich schrieb oben "i.Allg.", denn ich muss zugeben, dass ich aus Faulheit
oft für alle drei Anwendungsfälle auf die Schreibweise (1) zurückfalle,
was immerhin den Vorteil hat, den Linus nicht zu upsetten, falls mein
Code einmal in seine Hände fallen sollte ;-)

von Nils (Gast)


Lesenswert?

Tja..

Wer im C Standard nicht stehen geblieben ist schreibt neuerdings auch 
gerne mal:

void foo (int arg[static SIZE]);


Der Compiler kann dann prüfen, ob das Argument, welches übergeben wird 
mindestens die Größe SIZE hat. Falls der Compiler in der Lage ist raus 
zu finden, das man diese Regel bricht gibt zumindest clang eine Warnung 
raus. GCC wird sicher bald nachziehen (oder hat es schon getan, bin da 
nicht auf dem neuesten Stand).

Zudem impliziert man, das für das Argument "arg" NULL ein ungültiger 
Wert ist.

Sehr hilfreich in der Praxis.

von zitter_ned_aso (Gast)


Lesenswert?

Wilhelm M. schrieb:
> https://lkml.org/lkml/2015/9/3/428

Was schreibt Linus da eigentlich über die "warnings"?  Muss man da was 
aktivieren um diese zu bekommen?  Ich bekomme in so einem Fall nämlich 
keine.

von zitter_ned_aso (Gast)


Lesenswert?

ähm doch, ich bekomme die Warnung.

aber nur hier:

1
void print_arr(int arr[]){                                               
2
                                                                        
3
    for(size_t i=0; i<sizeof(arr); i++)                                 
4
        printf("%d\n", arr[i]);                                         
5
}



mache ich aus dem Parameter einen Zeiger dann gibt es auch keine 
Warnung:

1
void print_arr(int* arr){                                               
2
                                                                        
3
    for(size_t i=0; i<sizeof(arr); i++)                                 
4
        printf("%d\n", arr[i]);                                         
5
}

Aber das ist doch das gleiche (als Parameter).

von zitter_ned_aso (Gast)


Lesenswert?

Nils schrieb:
> Zudem impliziert man, das für das Argument "arg" NULL ein ungültiger
> Wert ist.
>
> Sehr hilfreich in der Praxis.

Ja, das ist interessant! Man wird da tatsächlich gewarnt. Eine gute 
Sache ;-)

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.