New to programming, don't get 2D/3D arrays - c

Hey everyone, I'm basically new to programming. I've decided to try and get started with C (not C++ or C#) and so far I've been doing pretty well. I managed to get far as two-dimensional arrays before I started to falter. While I think I broadly understand 2D integer arrays, I certainly don't understand 3D string arrays.
I'm learning by taking the techniques and applying them in an actual program I've created, an exchange rate "calculator" that basically takes asks the user to select a base currency then prints its value in USD. There's no maths involved, I simply googled stuff like EUR/USD and set the values manually in the array which I discuss below.
But here's where I'm getting stuck. I figure the best way to learn multi-dimensional arrays is to practically apply the theory, so here's what I've typed so far (I've omitted the other functions of my program (including the code which calls this function) for brevity):
char currencies[5][3][4] = {
{'1','2','3','4','5'},
{'GBP','EUR','JPY','CAD','AUD'},
{'1.5','1.23','0.11','0.96','0.87'}
};
int point, symbol, value;
displayarraycontents()
{
for(point=1;point<5;point++){
for(symbol=1;symbol<5;symbol++){
for(value=1;symbol<5;symbol++)
printf("%s ", currencies[point][symbol][value]);
printf("\n");
}}
}
Because C doesn't feature a string data type, building string arrays completely messes with my head.
Why currencies[5][3][4]? Because I'm storing a total of 5 currencies, each marked by a 3-letter symbol (eg EUR, CAD), which have a value of up to 4 digits, including the decimal point.
I'm trying to display this list:
1 GBP 1.5
2 EUR 1.23
3 JPY 0.11
4 CAD 0.96
5 AUD 0.87
When I click build, the line where I specify the values in the array is highlighted with several instances of this warning:
warning: overflow in implicit constant conversion
...and the line where I print the contents of the array is highlighted with this warning:
warning: format '%s' expects type 'char *', but argument 2 has type 'int'
Upon running the code, the rest of the program works fine except this function, which produces a "segmentation error" or somesuch.
Could somebody give me a hand here? Any help would be greatly appreciated, as well as any links to simple C 2D/3D string array initialisation tutorials! (my two books, the K&R and Teach Yourself C only provide vague examples that aren't relevant)
Thanks in advance!
-Ryan
EDIT: updated code using struct:
struct currency {
char symbol[4];
float value[5];
};
void displayarraycontents(){
int index;
struct currency currencies[] {
{"GBP", 1.50},
{"EUR", 1.23},
{"JPY", 0.11},
{"CAD", 0.96},
{"AUD", 0.87},};
}
I get the following errors:
main.c:99: error: nested functions are disabled, use -fnested-functions to re-enable
main.c:99: error: expected '=', ',', ';', 'asm' or 'attribute' before '{' token
main.c:100: error: expected ';' before '}' token
main.c:100: error: expected expression before ',' token
In the actual code window itself, every symbol is flagged as an "unexpected token".

In this case, you don't actually want a 3D array. In fact, since you have a table of values, all you need is a 1D array.
The tricky part is that each element of the array needs to store two things: the currency symbol, and the associated exchange rate. C has a way of building a type that stores two things - it's the struct mechanism. We can define a struct to hold a single currency:
struct currency {
char symbol[4];
char value[5];
};
(Note that this does not create a variable; it creates a type. struct currency is analagous to char, except that we defined the meaning of the former ourselves).
...and we can now create an array of 5 of these:
struct currency currencies[5] = {
{"GBP", "1.5" },
{"EUR", "1.23" },
{"JPY", "0.11" },
{"CAD", "0.96" },
{"AUD", "0.87" } };
To iterate over them and print them out, the code would look like:
void displayarraycontents(void)
{
int point;
for(point = 0; point < 5; point++)
{
printf("%d %s %s\n", point + 1, currencies[point].symbol, currencies[point].value);
}
}

You need a to correct your array dimensions, and you also need to declare your strings as strings, not as multibyte character constants:
char currencies[3][5][5] = {
{"1","2","3","4","5"},
{"GBP","EUR","JPY","CAD","AUD"},
{"1.5","1.23","0.11","0.96","0.87"}
};
Your logic for the array dimensions is wrong - what you want is 3 columns, each with 5 entries, each of which is a string 5 bytes long.
Your for loop should index from 0, not from 1.

There is also a oops in for statements:
for(point=1;point<5;point++)
First item in an array is in 0 position, so for statements should be like this:
for(point=0;point<5;point++)

It would make more sense to use structs here rather than a multi-dimensional array.
#include <stdio.h>
typedef struct Currency {
const char* symbol;
double value;
} Currency;
Currency CURRENCIES[] = {
{"GBP", 1.5},
{"EUR", 1.23},
{"JPY", 0.11},
{"CAD", 0.96},
{"AUD", 0.87},
};
size_t NUM_CURRENCIES = sizeof(CURRENCIES) / sizeof(Currency);
int main()
{
size_t index;
for (index = 0; index < NUM_CURRENCIES; index++)
{
printf("%zu %s %.2f\n",
index + 1, CURRENCIES[index].symbol, CURRENCIES[index].value);
}
return 0;
}

It should be
char currencies[3][5][5] = {
because it contains 3 lists containing 5 strings each.
Each string has a max of 4 characters, but you need the additional NUL character, so 5 at the end.
-- EDIT
You have the array access confused. Using your array definition (fixed as above) it would be currencies[data_type][index] to get a string.
data_type = 0 -> the index
data_type = 1 -> the symbol
data_type = 2 -> the value
the first line
{'1','2','3','4','5'},
is redundant.
Fixed code:
char currencies[2][5][5] = {
{"GBP","EUR","JPY","CAD","AUD"},
{"1.5","1.23","0.11","0.96","0.87"}
};
void displayarraycontents()
{
int index;
for(index = 0;index < 5;index++) {
printf("%i %s %s\n", index, currencies[0][index], currencies[1][index]);
}
}

In C/C++ you would normally read your array dimentions from right to left to get a good idea of how the compiler will see it. In this case, you need to store strings of 4 characters each which requires storage for 5 chars (to include the trailing \0) therefore [5] will be the array size. Next you are storing groups of 5 items, therefore the middle value will be [5] and finally, you are storing a total of 3 groups of these items, therefore [3]. The final result of all of this is char currencies[3][5][5] = . . .;
Of course, as replied elsewhere, you need to use the double quotes for string values.

If you want to solve this with multi-dimensional arrays, as #Forrest says, you need [3][5][5]. Look at it this way: in the initializer, find the outermost braces: inside that, on the top level, how many elements are there? 3. Now, each of these elements (one level in), how many elements? 5. Drilling further down, inside each of those, you have a string of 4 elements, plus one for the terminator, again 5.
Second error: you can only ever have one character in single quotes, like 'a'; that's char type, and equivalent to ASCII code (97 in this case). For strings, you have to use double quotes ("abc", which is equivalent to {97, 98, 99, 0}).
Third error: loops. You are not actually iterating over all three loops while printing a string at a time (since printf will actually do one of the loops for you) - so you should only have 2 loops (or, less efficiently, you can keep all three loops, but then print only a character at a time). Also, you need to be aware of the loop limits; you are going up to 5 in each case, but this will give you runtime garbage (in the best case) or runtime crash (in the worst case) when you go out of your [3] dimension. Thus, something like this:
Then again, your innermost loop is inconsistent in your variable usage (copy-paste error).
However, there will almost never be need to write code like this. You mainly use 2D arrays for matrix operations. Something like this should only have a one-dimensional array, storing record elements.
struct currency {
int id;
char[4] symbol;
float value;
} currencies[5];

You don't need to store the indices (1-5) as you can access the array (0-4) and thus know the indices. You can encapsulate the other values in a struct or two seperate arrays which gets your array(s) down to one dimension as it should be... In that way the items have proper types and you don't misuse two-dimensional arrays.
A 2D or 3D area shouldn't be filled with items that should be of a different type, it is needed when you have items that are of the same type and have a logic 2D or 3D structure. The pixels on your screen are a good example of something that needs a 2D structure, coordinates in a 3D graph are a good example of something that needs a 3D structure.

Related

Basic initialization of a function using arrays

Currently setting up a function to declare the cases for a 4-part seven-segment display (a digital alarm clock basically) but keep getting errors when trying to confirm that it is properly set (Specifically from the core opening line)
Have tried looking around, but nothing is specific enough to the arrays I need.
Just a note on the code. I have not properly set up a code block before, so I am unsure as to how to highlight syntaxes. If it is properly set up, just ignore this message
Originally had it set as int SegmentWrite(int...[]) but would not produce results.
Other tests I have tried is including a size [10][7] before the opening braces, to declare count and size, changing type from byte to int, char, and float (although only byte produced any sort of result, not being what I wanted)
void SegmentWrite (int SegmentDigits[]){
byte SegmentDigits = zero[7] {1,1,1,1,1,1,0};//0
one[7] = {0,1,1,0,0,0,0};//1
two[7] = {1,1,0,1,1,0,1};//2
three[7] = {1,1,1,1,0,0,1};//3
four[7] = {0,1,1,0,0,1,1};//4
five[7] = {1,0,1,1,0,1,1};//5
six[7] = {1,0,1,1,1,1,1};//6
seven[7] = {1,1,1,0,0,0,0};//7
eight[7] = {1,1,1,1,1,1,1};//8
nine[7] = {1,1,1,1,0,1,1}; //9
This particular snippet of code isn't supposed to do anything - not on it's own, at least. When verifying, to confirm proper formatting, I only got the error report "declaration of 'byte SegmentDigits' shadows a parameter' with the first line highlighted.
-Just a note, found another error, specifically with the opening line - "declaration of 'SegmentWrite' as array of void" or "'SegmentWrite' declared as function returning an array"
You need to use a different name for the parameter array and the local array in the function.
Also, the function should take another parameter, the digit that you're trying to display.
You should then declare a 2-dimensional array, and you can use the digit as an index into it.
void SegmentWrite (int SegmentDigits[7], int digit){
int allSegmentDigits[][7] = {
{1,1,1,1,1,1,0},//0
{0,1,1,0,0,0,0},//1
{1,1,0,1,1,0,1},//2
{1,1,1,1,0,0,1},//3
{0,1,1,0,0,1,1},//4
{1,0,1,1,0,1,1},//5
{1,0,1,1,1,1,1},//6
{1,1,1,0,0,0,0},//7
{1,1,1,1,1,1,1},//8
{1,1,1,1,0,1,1}} //9
memcpy(SegmentDigits, allSegmentDigits[digit], sizeof allSegmentDigits[0]);
}

Is an initialised 2d array fine for mapping consecutive integers to strings in C?

I have to map consecutive integer codes from 1 to 100 to strings in C. Normally, for a mapping of number to string, I would have something like this:
#define code1 1
#define code2 2
.
.
#define code100 100
struct map
{
int code;
char *msg;
}objs[100];
I would then loop over the objs and if the number matches, I would use the corresponding string of the obj array. Since I know that the numbers to be mapped are consecutive, I can just do this:
const char *arr[100] = { "abc", "def", ....... "100th msg"};
I can then forget the looping and just print arr[code]. Is this a bad approach? The only disadvantage I see is that when somebody else adds a code in the middle, they have to be careful about it. The advantage is obviously that I don't need to loop over the struct array.
Using a direct indexed array is a commonly used approach that works fine if the data never (rarely) changes, and there are not too many gaps, because you spend a record for every gap. At some point the management or the storage cost of the gaps may become an issue.
If you need to cope with more dynamic compile-time updates to the data then the next best thing is a sorted array. If you can guarantee that your entries are always in order but perhaps there are gaps, or new entries added to the end, then you can binary chop your ordered array to quickly find the entry you want. You may want to do a start-up pass that checks the array is correctly ordered, but you only have to do that once.
If you need to worry about runtime updates, then you seriously need to consider higher-level container abstractions such as mapping trees or hashmaps.
Suppose the array has error messages. Then a common approach is to define constants fo each error and print the message associated with it, for example:
#define ERR_NONE 0
#define ERR_NOMEM 1
#define ERR_BADNUM 2
// etc
and define the array as:
const char *msgs[] = {
"No error",
"Out of memory",
"Bad number",
// etc
};
and have a function to print the message, for example:
void printmsg(int code)
{
printf("%s\n",msgs[code]);
}
which can be called as
printmsg(ERR_NOMEM);
For modularity, the #defines can be in e.g. errors.h, together with the prototype of printmsg, and the array can be in errors.c.
The only problem with your approach is that the codes can never change. You can't add intermediate codes without changing the entire code. But it should work. Also the first code should be zero or you'll have to either pad the array or shift the codes when accessing.
Essentially what you have is an immutable hash table.
#define BASE_CODE 5
#define CODE_BLUE 5
#define CODE_GREEN 6
const char *responses[] = {'blue', 'green'};
printf("%s\n", responses[code - BASE_CODE]);
If you want to be able to change the codes (add, remove, insert codes in the middle of the sequence, verify if a code was properly referenced), then you should stick with the first approach, but add a hash function so you don't need to loop sequentially over the array.

Swift String.Index vs transforming the String to an Array

In the swift doc, they say they use String.Index to index strings, as different characters can take a different amount of memory.
But I saw a lot of people transforming a String into an array var a = Array(s) so they can index by int instead of String.Index (which is definitely easier)
So I wanted to test by myself if it's exactly the same for all unicode character:
let cafeA = "caf\u{E9}" // eAcute
let cafeB = "caf\u{65}\u{301}" // combinedEAcute
let arrayCafeA = Array(cafeA)
let arrayCafeB = Array(cafeB)
print("\(cafeA) is \(cafeA.count) character \(arrayCafeA.count)")
print("\(cafeB) is \(cafeB.count) character \(arrayCafeB.count)")
print(cafeA == cafeB)
print("- A scalar")
for scalar in cafeA.unicodeScalars {
print(scalar.value)
}
print("- B scalar")
for scalar in cafeB.unicodeScalars {
print(scalar.value)
}
And here is the output :
café is 4 character 4
café is 4 character 4
true
- A scalar
99
97
102
233
- B scalar
99
97
102
101
769
And sure enough, as mentioned in the doc strings are just an array of Character, and then the grapheme cluster is down within the Character object, so why don't they indexed it by int ? what's the point of creating/using String.Index actually ?
In a String, the byte representation is packed, so there's no way to know where the character boundaries are without traversing the whole string from the start.
When converting to an array, this is traversal is done once, and the result is an array of characters that are equidistantly spaced out in memory, which is what allows constant time subscripting by an Int index. Importantly, the array is preserved, so many subscripting operations can be done upon the same array, requiring only one traversal of the String's bytes, for the initial unpacking.
It is possible extend String with a subscript that indexes it by an Int, and you see it often come up on SO, but that's ill advised. The standard library programmers could have added it, but they purposely chose not to, because it obscures the fact that every indexing operation requires a separate traversal of the String's bytes, which is O(string.count). All of a sudden, innocuous code like this:
for i in string.indices {
print(string[i]) // Looks O(1), but is actually O(string.count)!
}
becomes quadratic.

Number sequences length, element first and last indexes in array

Im beginner in programming. My question is how to count number sequences in input array? For example:
input array = [0,0,1,1,1,1,1,1,0,1,0,1,1,1]
output integer = 3 (count one-sequences)
And how to calculate number sequences first and last indexes in input array? For example:
input array = [0,0,1,1,1,1,1,1,0,1,0,1,1,1]
output array = [3-8,10-10,12-14] (one first and last place in a sequence)
I tried to solve this problem in C with arrays. Thank you!
Your task is a good exercise to familiarize you with the 0-based array indexes used in C, iterating arrays, and adjusting the array indexes to 1-based when the output requires.
Taking the first two together, 0-based arrays in C, and iterating over the elements, you must first determine how many elements are in your array. This is something that gives new C programmers trouble. The reason being is for general arrays (as opposed to null-terminated strings), you must either know the number of elements in the array, or determine the number of elements within the scope where the array was declared.
What does that mean? It means, the only time you can use the sizeof operator to determine the size of an array is inside the same scope (i.e. inside the same block of code {...} where the array is declared. If the array is passed to a function, the parameter passing the array is converted (you may see it referred to as decays) to a pointer. When that occurs, the sizeof operator simply returns the size of a pointer (generally 8-bytes on x86_64 and 4-bytes on x86), not the size of the array.
So now you know the first part of your task. (1) declare the array; and (2) save the size of the array to use in iterating over the elements. The first you can do with int array[] = {0,0,1,1,1,1,1,1,0,1,0,1,1,1}; and the second with sizeof array;
Your next job is to iterate over each element in the array and test whether it is '0' or '1' and respond appropriately. To iterate over each element in the array (as opposed to a string), you will typically use a for loop coupled with an index variable ( 'i' below) that will allow you to access each element of the array. You may have something similar to:
size_t i = 0;
...
for (i = 0; i< sizeof array; i++) {
... /* elements accessed as array[i] */
}
(note: you are free to use int as the type for 'i' as well, but for your choice of type, you generally want to ask can 'i' ever be negative here? If not, a choice of a type that handles only positive number will help the compiler warn if you are misusing the variable later in your code)
To build the complete logic you will need to test for all changes from '0' to '1' you may have to use nested if ... else ... statements. (You may have to check if you are dealing with array[0] specifically as part of your test logic) You have 2 tasks here. (1) determine if the last element was '0' and the current element '1', then update your sequence_count++; and (2) test if the current element is '1', then store the adjusted index in a second array and update the count or index for the second array so you can keep track of where to store the next adjusted index value. I will let you work on the test logic and will help if you get stuck.
Finally, you need only print out your final sequence_count and then iterate over your second array (where you stored the adjusted index values for each time array was '1'.
This will get you started. Edit your question and add your current code when you get stuck and people can help further.

"Direction" of bidimensional arrays in C

as a C newcomer I'm a bit confused about bidimensional arrays.
If I want to represent a matrix of 3 rows and 5 columns, I guess the correct declaration is:
char a[3][5];
So, is this an array of 3 pointers to 5 arrays of chars or what?
How come whenever I try to cycle through it like the following it seems to read the wrong results?
int x, y;
for( x=0; x<3; x++ ){
for( y=0; y<3; y++ ){
printf( "%c", a[x][y] );
}
}
Are the following equivalent and correct ways to initialize it?
char a[3][5] = {
{1,0,0,0,1},
{1,0,0,0,1},
{1,0,0,0,1},
};
char a[3][5] = {1,0,0,0,1,
1,0,0,0,1,
1,0,0,0,1};
Thanks for any eventual upcoming explanation.
EDIT
Sorry the typos, the code is not copied. By the way, I keep on having them read like they where read in a vertical way, not in a horizontal one.
Also in the example in this tutorial http://www.cplusplus.com/doc/tutorial/arrays/ it reads the array in a way that is not streight-forward to me as it seems to work on a 5x3, HeightWidth, yx, colsrows structure instead of a 3x5, WidthHeight, xy. rowscols one:
#define WIDTH 5
#define HEIGHT 3
int jimmy [HEIGHT][WIDTH];
int n,m;
int main ()
{
for (n=0;n<HEIGHT;n++)
for (m=0;m<WIDTH;m++)
{
jimmy[n][m]=(n+1)*(m+1);
}
return 0;
}
Just for what it is and what not.
char a[3][5];
There are no pointers involved. A multi dimensional array like that is an array of arrays of .... and so on. In your case, you have an array of 3 arrays of 5 characters. It becomes clearer when you do it with typedefs.
typedef char T[5];
T a[3];
No pointers are involved whatsoever. If you want to access the first array of those 3 ones, you can do so:
a[0];
And it will give you back an object of type char[5]. Normally, you don't notice that because normally you index all dimensions. So the array that's returned by a[0] is subscript by the next index, for example a[0][1]. The [1] will be applied to the array that was returned by a[0], which as we have figured out earlier has type char[5].
So, is this an array of 3 pointers to 5 arrays of chars or what?
Let's create that type and see how it's different to the above. Creating it is simple, once you get the basic declarators:
Creating a pointer: *D
Creating an array: D[N]
D is just an existing another declarator. So now let's go on. First you say array of 3 pointers to 5 arrays of chars.... I think you meant array of 3 pointers to arrays of 5 chars. First, array of 5 is created like
D1[5]
Now, let's replace D1 by a pointer to declarator:
(*D2)[5]
We had to insert parentheses, because the subscript operator [N] binds better than the dereference operator *, and it would otherwise be read as *(D2[5]) which isn't what we want. Now we have pointer to array of 5.
Now let's do the array of 3. Replacing D2 by D3[3] yields this:
(*D3[3])[5]
Great, now we have got a array of 3 pointer to array of 5. Just putting the base type that that declarator appertains to yields the complete declaration:
char (*D3[3])[5];
That's of course a complete different thing :) You could use it to store pointers to your other array which was of type array of 3 arrays of 5 char. Let's store a pointer to the first sub-array of a into D3[0]. We figured out earlier that a[0] has type char[5]. We can store a pointer to that array into D3[0], because D3 is an array of pointers to char[5], what a coincidence!
D3[0] = &a[0]; // works!
I hope this little exercise has shown you some of the relations between pointers and arrays. Cheers!
I see a couple of problems with your code. First (copied from above):
int x, y;
for( x=0; x<3; x++ ){
for( x=0; x<3; x++ ){
printf( a[x][y] );
}
}
In your inner-loop, it looks like you want to use y instead of x, and you want y to go from 0..5. Currently, you are repeating the variable x. Also, you have a problem with your printf() statement. Here's some corrected code:
int x, y;
for( x=0; x<3; x++ ){
for( y=0; y<5; y++ ){
printf("%d\n", a[x][y] );
}
}
Second, when you initialize your array, your code is almost correct. Here's the corrected version:
char a[3][5] = {
{1,0,0,0,1},
{1,0,0,0,1},
{1,0,0,0,1}
};
(I removed the , after the very last "row" of data - that was a syntax error.)
The 2nd syntax you posted is incorrect (the one with no curly braces).
You've got wrong results because you're using x twice (looks like a copy/paste error). Try
int x, y;
for (x = 0; x < 3; x++) {
for (y = 0; y < 5; y++) {
printf("%c", a[x][y]); // Emil H says "%d" might be more useful
}
}
Edit: I'm not sure what's confusing about that tutorial. It's precisely equivalent to your code, except instead of printing the array, it's setting each element to (row*column) (where both row and column are one-based, hence the +1's).
The image that is in the tutorial is a great representation of the data:
alt text http://www.cplusplus.com/doc/tutorial/arrays/bidimensional_arrays3.gif
From the example, the nested for loops traverses across (left -> right) the array row by row and fills in a value for each column.
The small errors like using "%c" when you probably want "%d" and such are not what you really want to get answered I humbly think. You say you are a bit confused about arrays and also mentions that you expect to see pointers as the array-elements of the array containing arrays. You
The last comma in your definition of the array-of-arrays is NOT a syntax error. It is permitted as of C99.
What really will clear up things is to know that arrays are not pointers. It is true that an array name may be used as a constant pointer (to the first element) and that pointers can be indexed like they were arrays. This does not mean, however, that arrays and pointers are the same. They are not.
You ask about what the symbol "a" in your program actually is. It is an array of arrays. The memory layout may be visualized like a long line cut in three parts but still on a continuous line. Then cut each of these in five parts in the same manner. When addressing one element you must use two indexes; first for which five-element-array you want and then for which fifth of this element you want.
The memory layout is not like a grid of rows and columns. The memory is addressed by one scalar, so its linear. Pointer arithmetic may be the next thing you could look into, and see how an increment on a pointer works.
int x, y;
for( x=0; x<3; x++ ){
for( x=0; x<3; x++ ){
printf( a[x][y] );
}
}
You need to change the 2nd for loop to refer to 'y' instead of 'x'.
hope that helps.
You're using printf in the wrong way. Try:
printf("%d", a[x][y]);
I'm not sure whether you'll want to use %c or %d. If you want to print the number, use %d. If you want to print an actual character, use %c.
char a[3][5];
So, is this an array of 3 pointers to
5 arrays of chars or what?
Yes, that's what it is. Although you could also manipulate it as a consecutive set of 15 chars.
By convention, most people would think of it as representing a matrix with 3 rows and 5 columns (I think), but there's nothing about the data structure itself that requires that. You could just as easily use it to represent 5 rows and 3 columns. Or 3 sets that each include 5 elements but have no meaningful relationship to each other at all.
A particular library for doing matrix manipulation would have a convention for this.

Resources