11

Is it possible to start an array at an index not zero...I.E. you have an array a[35], of 35 elements, now I want to index at say starting 100, so the numbers would be a[100], a[101], ... a[134], is that possible?

I'm attempting to generate a "memory map" for a board and I'll have one array called SRAM[10000] and another called BRAM[5000] for example, but in the "memory" visibility they're contiguous, I.E. BRAM starts right after SRAM, so therefore if I try to point to memory location 11000 I would read it see that it's over 10000 then pass it to BRAM.

While typing this I realized I could I suppose then subtract the 10K from the number and pass that into BRAM, but for the sake of argument, is this possible to index passing 11000 to BRAM?

In the actual architecture I will be implementing, there can/may be a gap between the SRAM and BRAM so for example the address 11008 might not be visible in the memory map, thus writing a giant array full of memory then "partitioning" it will work, but I'll still have to do logic to determine if it's within the ranges of "SRAM and BRAM". Which is what I wanted to avoid in the first place.

TylerH
  • 20,799
  • 66
  • 75
  • 101
onaclov2000
  • 5,741
  • 9
  • 40
  • 54
  • 1
    What kind of sequence is 100, 101...34? :o And no, index's always start from 0. you'll need to do some math. – GManNickG Feb 18 '10 at 15:06
  • I'm guessing a[100], a[101], ... a[34] should be a[100], a[101], ... a[134]. Could someone edit this please? – Hans W Feb 18 '10 at 15:14
  • sorry I missed the 1 in there, I meant 100, 101,... 134...sorry – onaclov2000 Feb 18 '10 at 15:18
  • 1
    You could always define SRAM[10035] and ignore the first 10000 elements, memory is cheap nowadays... :-) – Tiberiu Ana Feb 18 '10 at 15:19
  • 4
    Also, are you trying to do this in C, or in C++? They are two very different things, with two very different approaches to getting what you want. You can do it (almost) transparently in C++ by building your own array class. You can do it in C too, but your code will be clunkier, function calls each time you access the array. – Binary Worrier Feb 18 '10 at 15:34
  • So far I "called" the file a cpp file, but at this point there isn't really a huge differentiation between the two, I think what steers me in the direction of one vs the other will be ease of implementation. This being one of the aspects. – onaclov2000 Feb 18 '10 at 15:35

13 Answers13

21

Is it possible to start an array at an index not zero...I.E. you have an array a[35], of 35 elements, now I want to index at say starting 100, so the numbers would be a[100], a[101], ... a[134], is that possible?

No, you cannot do this in C. Arrays always start at zero. In C++, you could write your own class, say OffsetArray and overload the [] operator to access the underlying array while subtracting an offset from the index.

I'm attempting to generate a "memory map" for a board and I'll have one array called SRAM[10000] and another called BRAM[5000] for example, but in the "memory" visiblity they're contiguous, I.E. BRAM starts right after SRAM, so therefore if I try to point to memory location 11000 I would read it see that it's over 10000 then pass it to bram.

You could try something like this:

char memory[150000];
char *sram = &memory[0];
char *bram = &memory[100000];

Now, when you access sram[110000] you'll be accessing something that's "in bram"

Hans W
  • 3,851
  • 1
  • 22
  • 21
  • 2
    +1, in fact it would make sense using `memory` to access the whole memory map and `sran` and `bram` to access specific subobjects – David Rodríguez - dribeas Feb 18 '10 at 15:18
  • In general I like this idea, but I still wish for there to be that "upper bound" but I suppose that can and will be user defined. – onaclov2000 Feb 18 '10 at 15:38
  • 2
    How about using a macro: `#define ARRAY_OFFSET ((x) - 100)` and accessing the array like `memory[ARRAY_OFFSET(100)]`. The macro has some delusions to it, but it is compatible with both C and C++. – Thomas Matthews Feb 18 '10 at 20:00
15

C++ provides quite a bit more than C in this respect. You can overload operator[] to do the subtraction, and if you want report an error (e.g., throw an exception) if the subscript is out of range.

As a minimal demo, consider the following:

#include <iostream>
#include <stdexcept>

template <class T, int lower, int upper>
class array {
    T data[upper-lower];
public:
    T &operator[](int index) { 
        if (index < lower || index >= upper)
            throw std::range_error("Index out of range");
        return data[index-lower]; 
    }
    T *begin() { return data; }
    T *end() { return data + (upper-lower); }
};

int main() {
    array<int, -3, 5> data;

    for (int i=-3; i<5; i++)
        data[i] = i;

    for (auto const &i : data) 
        std::cout << i << "\t";
    std::cout << "\n";
}
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • 1
    I hope you're just pointing this out as a possible solution and not recommending it. :-) Overloading the [] operator leads to unnecessary complexity. – Steve Lazaridis Feb 18 '10 at 15:13
  • 2
    @Steve: Not really. Overloading operators to do what they normally don't is confusing. Making `a + b` write `a` and `b` to a file is confusing. `[]` is for indexing. That said, It's probably easier just to get a new pointer by adding an offset and using the built-in `operator[]`. For any more complex indexing scheme's, making a wrapper class with `operator[]` is definitely cleanest. – GManNickG Feb 18 '10 at 15:21
  • There's no built-in `operator[]` for classes unless you provide one. – kennytm Feb 18 '10 at 15:38
  • 6
    @Steve:I'd say exactly the opposite: this a case of overloading the operator to do exactly what the user would expect, so it *eliminates* unnecessary complexity instead of adding it. – Jerry Coffin Feb 18 '10 at 16:28
  • Sorry interrupt again! :) How can we generalize this to higher dimensional arrays? – Hosein Rahnama Apr 13 '19 at 17:49
  • 1
    For higher dimensions, you can [overload `operator()`](https://stackoverflow.com/a/41367495/179910), or you can do multiple overloads of `operator[]`, as shown in [another answer](https://stackoverflow.com/a/2216055/179910). – Jerry Coffin Apr 13 '19 at 17:52
10

I remember reading in the book Expert C Programming - Deep C Secrets by Peter Van der Linden discloses a trick to fool the compiler into thinking array offsets starts at 1. Theoretically the trick can be accomplished, I do not have the book with me, but offhand, I recall reading it; it is not portable and may produce undefined behavior.

See here section 6.17 on the C-FAQ. WARNING: Not Portable and Undefined behavior! To quote from the source here.

6.17: Here's a neat trick: if I write

int realarray[10];  
int *array = &realarray[-1];  

I can treat "array" as if it were a 1-based array.

A: Although this technique is attractive (and was used in old editions of the book Numerical Recipes in C), it is not strictly conforming to the C Standard. Pointer arithmetic is defined only as long as the pointer points within the same allocated block of memory, or to the imaginary "terminating" element one past it; otherwise, the behavior is undefined, even if the pointer is not dereferenced. The code above could fail if, while subtracting the offset, an illegal address were generated (perhaps because the address tried to "wrap around" past the beginning of some memory segment).

References: K&R2 Sec. 5.3 p. 100, Sec. 5.4 pp. 102-3, Sec. A7.7 pp. 205-6; ISO Sec. 6.3.6; Rationale Sec. 3.2.2.3.

Read more: http://www.faqs.org/faqs/C-faq/faq/#ixzz0ftyqHOvm

TylerH
  • 20,799
  • 66
  • 75
  • 101
t0mm13b
  • 34,087
  • 8
  • 78
  • 110
  • Even though it is undefined behaviour from an language perspective, it should technically work reliably due to the way modern computers address their memory. The only danger is, that the optimizer figures out that you are invoking undefined behaviour, and optimizes your code away. – cmaster - reinstate monica Jan 11 '14 at 17:53
  • Where does it say `technically work reliably`? It is undefined behaviour for that reason alone, i.e. not to be used everyday, and depends on the C Compiler in question and also the environment itself. There are no guarantees either, regardless if the code optimizes away or not. It may crash, segfault, clobber some other memory part. Using code like that, will raise alarms among code reviewers and be cause of concern as well - i.e. code smells. – t0mm13b Jan 11 '14 at 18:03
  • Well, do some assembler coding, and you'll know why it's technically reliable. Pointers are nothing else than 32 or 64 bit integers under the hood, the hardware does not even distinguish between signed and unsigned (because it does not need to). Anything involved in pointer arithmetic is identical to unsigned integer arithmetic. – cmaster - reinstate monica Jan 11 '14 at 18:19
  • On a system using segmentation, even attempting to *form* an address before the beginning of an array could throw a hardware exception. Segmentation-based virtual memory is no longer *common*, but has been used in real systems *fairly* recently (e.g., OS/2 1.x, 16-bit Windows). On such systems, this code most certainly could fail to the extent of throwing an exception as the program was loading, so none of it would run at all. – Jerry Coffin Sep 24 '14 at 12:51
8

You could cheat with macro:

int myarray[35];

#define a (myarray - 100)

a[100] = 0;

A pointer could also be used.

Richard Pennington
  • 19,673
  • 4
  • 43
  • 72
6

No — as in you can't modify the lower bound in declaration like VB6.

Yes — as in you can do tricks like

int a[35];
int* actual_a = a-100;
printf("%d", actual_a[101]);
...

because x[a] is equivalent to *(x+a). This is highly unrecommended.

kennytm
  • 510,854
  • 105
  • 1,084
  • 1,005
  • 9
    As forming the pointer `a - 100` results in _undefined behaviour_, I would go further than just saying "highly unrecommended". – CB Bailey Feb 18 '10 at 15:35
  • I see how storing the result is UB. But in the expression `(a-100)[101]`, would the sub-expression already cause UB? C has a lot of latitude in reordering evaluation. So, it appears the expressions `*(a-100+101)`, `*(a+101-100)` and `*(a+(101-100))` are all equal, as all can be reordered to the same result. But as the latter is not UB, it seems none should be UB. The reason I wonder is because a macro `#define ACTUAL_A (a-100)` would expand to this form. – MSalters Feb 19 '10 at 09:27
4

You're not very clear on exactly how you want to use these arrays, but its easy enough to set up a single contiguous array that can appear to be two different arrays:

int   ram[15000]; 
int * sram=&ram[0];
int * bram=&ram[10000];

I used the &foo[xxx] notation just to make it explicit what you're doing. Anyway, you can now use ram to index anywhere into the entire array, or sram and bram to index into particular parts.

swestrup
  • 4,079
  • 3
  • 22
  • 33
  • A scary thought: the solution allows access to regions of undefined memory or it provides memory where there isn't any. – Thomas Matthews Feb 18 '10 at 20:04
  • 2
    Well, yes and no. It provides no more unrestricted access to memory than does standard array indexing in C. Provided that one doesn't index out of bounds, one is just as safe as with normal arrays. – swestrup Feb 19 '10 at 20:18
2

As others have noted, you can technically achieve what you want by invoking pointer arithmetic like this:

int* myArray = malloc((upperBound - lowerBound) * sizeof(*myArray));
myArray -= lowerBound;
...
for(int i = lowerBound; i < upperBound; i++) {
    myArray[i];
}

While this will work flawlessly with -O0, it is undefined behaviour from a language point of view. The machine, however, has absolutely no objections to this, to the machine the pointer arithmetic involved is the same as any unsigned integer arithmetic with uintptr_t. Thus, the only danger to this approach is the optimizer.

Now, you can easily defeat the optimizer by splitting the code above into two different compilation units, i. e. have one ".c" file with the function

int* fancyIntArray(size_t lowerBound, size_t upperBound) {
    return intMalloc(upperBound - lowerBound) - lowerBound;
}

and put the function intMalloc() into a different ".c" file:

int* intMalloc(size_t size) {
    return malloc(size_t*sizeof(int));
}

Now, you are safe, because when the optimizer looks at the pointer arithmetic in fancyIntArray(), it does not know that there is no allocated memory in front of the address that intMalloc() returns, as it does not know anything about intMalloc() itself. As such, it is forced to leave your code intact.

And, of course, you should use independent compiler calls to compile the different ".c" files, so that the optimizer really cannot deduce anything about intMalloc().

cmaster - reinstate monica
  • 38,891
  • 9
  • 62
  • 106
  • What exactly could the optimizer do to the code of the first example? – Michael Lehn Apr 18 '14 at 20:08
  • 1
    @MichaelLehn The danger of the optimizer is, that it is allowed to *remove* code of which it can prove that it invokes undefined behavior. I. e. it may replace the undefined behavior with the behavior of doing nothing. Sounds smart, but it creates quite a few traps where people unknowingly invoke undefined behavior - in the first example, it might just remove the entire loop. So, if you really want to use something that is technically undefined behavior, at least make sure that the optimizer is not able to deduce what's going on. – cmaster - reinstate monica Apr 18 '14 at 21:42
2

strictly speaking, this solution does not loet you define an array starting at an index different from 0, but you may declare your memory this way:

typedef union
{
    unsigned char all[15000];
    struct
    {
        unsigned char sram[10000];
        unsigned char bram[5000];
    };
} memory;

this does convey the intent that the memory is contiguous, and that it is split in 2 parts. note that you should beware of the alignment of bram and sram, a #pragma pack(1) may be necessary.

Adrien Plisson
  • 22,486
  • 6
  • 42
  • 73
1

Not in C. You have to do the arithmetic yourself. There are probably bizarre work-arounds that work most of the time, like making a new pointer that is BRAM-11000 and using that instead.

Paul Tomblin
  • 179,021
  • 58
  • 319
  • 408
1

The simplest way to do this:

you have:

int *array = memory ; // starts with 0;
array-= 1000 ; // now array[1000] is 0

In c++ just create class with operator[]

Artyom
  • 31,019
  • 21
  • 127
  • 215
  • 1
    That'll probably work in most C compilers but it's not guaranteed by the standard. Only pointers within an array or one past the end are guaranteed. – paxdiablo Feb 18 '10 at 15:16
  • @paxdiablo: Let's get pedantic and technical here. Does the C standard not guarantee *dereferencing* of pointers outside an array or does it not guarantee *pointer arithmetic* beyond the array boundaries? In my experience, one could define a pointer to the first UART register and adjust the pointer for other contiguous registers, even though an array was never declared. – Thomas Matthews Feb 18 '10 at 20:07
  • Pointer arithmetic is guaranteed up to _and including_ one past the end; derefering is guaranteed for the actual array elements only. The difference is so you can use pointer arithmetic to determine if you have hit the end of an array, before dereferencing the pointer. Pointers to UART registers are always UB. – MSalters Feb 19 '10 at 09:32
0

Pointers and arrays are very similar in C, so you could easilly do something like

element SRAM_MEM[10000];
element BRAM_MEM[5000];

element* SRAM = SRAM_MEM;
element* BRAM = BRAM_MEM-10000;

BRAM[10001] = 0; // sets the first element of BRAM_MEM
Chris Becke
  • 34,244
  • 12
  • 79
  • 148
0

Just have a variable that takes into account the offset. Even if possible, having an array that doesn't start at the conventional 0 would be highly unreadable.

user266117
  • 56
  • 1
  • 5
0

Perhaps you can use union?

#pragma pack(0)
union 
{
  char array[15000];
  struct { char sram[10000]; char bram[5000];  } map;
} combination;

They are physically contiguous. If you access 'array' then it will go into either bram or sram based on the offset

You need the pragma to tell the compiler to NOT align struct members on word boundaries. This prevents a number of bytes of space between the two parts of the map structure.

Jay
  • 13,803
  • 4
  • 42
  • 69