Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C's aversion to arrays [closed]

In introductory books on C it is often claimed that pointers more or less are arrays. Isn't this a vast simplification, at best?

There is an array type in C and it can behave completely different from pointers, for example:

#include <stdio.h>

int main(int argc, char *argv[]){
  int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
  int *b = a;
  printf("sizeof(a) = %lu\n", sizeof(a));
  printf("sizeof(b) = %lu\n", sizeof(b));
  return 0;
}

gives the output

sizeof(a) = 40 
sizeof(b) = 8 

or as another example a = b would give a compilation error (GCC: "assignment to expression with array type").

Of course there is a close relationship between pointers and arrays, in the sense that yes, the content of an array variable itself is the memory address of the first array element, e.g. int a[10] = {777, 1, 2, 3, 4, 5, 6, 7, 8, 9}; printf("a = %ul\n", a); prints the address containing the 777.

Now, on the one hand, if you 'hide' arrays in structs, you can easily copy large amounts of data (arrays if you ignore the wrapping struct) just by using the = operator (and that's even fast, too):

#include <sys/time.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#define ARRAY_LENGTH 100000000

typedef struct {int arr[ARRAY_LENGTH];} struct_huge_array;

int main(int argc, char *argv[]){
  struct_huge_array *a = malloc(sizeof(struct_huge_array));
  struct_huge_array *b = malloc(sizeof(struct_huge_array));

  int *x = malloc(sizeof(int)*ARRAY_LENGTH);
  int *y = malloc(sizeof(int)*ARRAY_LENGTH);

  struct timeval start, end, diff;

  gettimeofday(&start, NULL);
  *a = *b;
  gettimeofday(&end, NULL);

  timersub(&end, &start, &diff);
  printf("Copying struct_huge_arrays took %d sec, %d µs\n", diff.tv_sec, diff.tv_usec); 

  gettimeofday(&start, NULL);
  memcpy(x, y, ARRAY_LENGTH*sizeof(int));
  gettimeofday(&end, NULL);

  timersub(&end, &start, &diff);
  printf("memcpy took %d sec, %d µs\n", diff.tv_sec, diff.tv_usec); 

  return 0;
}

Output:

Copying struct_huge_arrays took 0 sec, 345581 µs
memcpy took 0 sec, 345912 µs

But you cannot do this with arrays itself. For arrays x, y (of the same size and of the same type) the expression x = y is illegal.

Then, functions can't return arrays. Or if arrays are used as arguments, C collapses them into pointers -- it does not care if the size is explicitly given, so the following program gives the output sizeof(a) = 8:

#include <stdio.h>

void f(int p[10]){
  printf("sizeof(a) = %d\n", sizeof(p));
}

int main(int argc, char *argv[]){
  int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

  f(a);

  return 0;
}

Is there any logic behind this aversion to arrays? Why isn't there a true robust array type in C? What bad would happen if there was one? After all, if an array is hidden in a struct the array does behave as in Go, Rust, ..., i.e. the array is the whole chunk in memory and passing it around will copy its content, not just the memory address of the first element. For example like in Go the following program

package main

import "fmt"

func main() {
    a := [2]int{-777, 777}
    var b [2]int
    b = a
    b[0] = 666

    fmt.Println(a)
    fmt.Println(b)
}

gives the output:

[-777 777]
[666 777]
like image 426
viuser Avatar asked Feb 24 '16 08:02

viuser


People also ask

How does C know when an array ends?

C arrays don't have an end marker. It is your responsibility as the programmer to keep track of the allocated size of the array to make sure you don't try to access element outside the allocated size. If you do access an element outside the allocated size, the result is undefined behaviour.

How do you declare the size of an array?

Declaring Arrays: An array declaration is similar to the form of a normal declaration (typeName variableName), but we add on a size: typeName variableName[size]; This declares an array with the specified size, named variableName, of type typeName. The array is indexed from 0 to size-1.

How do you change a value in an array in Java?

You can use the set() method of java. util. ArrayList class to replace an existing element of ArrayList in Java. The set(int index, E element) method takes two parameters, the first is the index of an element you want to replace, and the second is the new value you want to insert.


2 Answers

The C language was initially designed in the early 1970's on a PDP mini-computer which reportedly just filled up half a room, despite its huge 24 kB memory. (That's kB, not MB, or GB).

Fitting a compiler at all into that memory was the real challenge. So the C language was designed to allow you to write compact programs, and quite a few special operators (like +=, --, and ?:) was added for manual optimizations.

Adding features for copying large arrays as parameters didn't occur to the designers. It wouldn't have been useful anyway.

In C's predecessor, the B language, an array was represented as a pointer to storage allocated separately (see the link in Lars' answer). Ritchie wanted to avoid this extra pointer in C and so got the idea that the array name could be turned into a pointer when used in places not expecting an array:

It eliminated the materialization of the pointer in storage, and instead caused the creation of the pointer when the array name is mentioned in an expression. The rule, which survives in today's C, is that values of array type are converted, when they appear in expressions, into pointers to the first of the objects making up the array.

This invention enabled most existing B code to continue to work, despite the underlying shift in the language's semantics.

And structs didn't get added to the language until later. That you can pass an array inside a struct as a parameter was then a feature that offered another option.

Changing the syntax for arrays was already too late. It would break too many programs. There were already 100s of users...

like image 86
Bo Persson Avatar answered Oct 05 '22 00:10

Bo Persson


This part of the question...

Is there any logic behind this aversion to arrays? Why isn't there a true robust array type in C? What bad would happen if there was one?

... is not really a code question and open to speculation, but I think a short answer might be beneficial: when C was created, it was targeted at machines with very little RAM and slow CPUs (measured in Kilo-Bytes and Megahertz, resp.). It was meant to replace Assembler as systems programming language, but without introducing the overhead that the other existing high-level languages required. For the same reasons, C is still a popular language for micro controllers, due to the control it gives you over the generated program.

Introducing a 'robust' array type would have had under-the-hood performance and complexity penalties for both the compiler and the runtime, which not all systems couldn't afford. At the same time, C offers the capabilities for the programmer to create their own 'robust' array type and use them only in those situations where its use was justified.

I found this article interesting in this context: Dennis Ritchie: Development of the C Language (1993)

like image 22
Lars Avatar answered Oct 05 '22 00:10

Lars