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]
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)
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 struct
s 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...
Arrays are arrays and pointers are pointers, they are not the same.
But to make anything usable of arrays the compiler must use qualified pointers.
By definition an array is a contiguous and homogeneous sequence of elements in memory. So far so good, but how interact with it?
To explain the concept I already used, on other forums, an assembly example:
;int myarray[10] would be defined as
_myarray: .resd 10
;now the pointer p (suppose 64 bit machine)
_p: .resq 1
This is the code emitted by compiler to reserve an array of 10 int
and a pointer to int
in global memory.
Now when referring to the array what you think you can get? Just the address of course (or better the address of the first element). And the address what is? The standard says that it have to be called qualified pointer, but you can really understand now why it is so.
Now look the pointer, when we refer to it the compiler emits code to fetch the contents of the location at address p
, but we can even get p
itself, the address of the pointer variable, using &p
, but we can't do it with an array. Using &myarray
will give back the address of the first element again.
This means that you can assign myarray
address to p
, but not the reverse ;-)
来源:https://stackoverflow.com/questions/35597019/cs-aversion-to-arrays