As we did back in Lecture 16, we are now pausing to see how the C language handles arrays. And as before, you will see that many differences are superficial and what you've learned for C++ in this class nicely applies to C.
As a reminder of the differences we've already covered:
Today we will look at arrays. The short description is that C requires you to be more explicit with how much memory you need for an array, but after that, pointers and array usage is the same.
NOTE: you need to now
#include <stdlib.h> for malloc and calloc.
In C++, we've learned that memory is allocated with the keyword
new. For example, to create an array of 5 ints:
int* numbers = new int; // C++ code
In C, we instead use the keywords
calloc. They're almost, but not exactly, the same. The prototype for
The parameter of type
int needs to be the number of bytes that you want for your array from the operating system. So, when requesting space for an array of 5 ints, for example, to arrive at the correct number of bytes to request, you need to know not only the 5, but also the number of bytes required by a single int. Most systems represent an int with 4 bytes, but not always, and embedded systems can be different from platforms. To know how many bytes your type needs, use the
sizeof() function. For example:
malloc( 5*sizeof(int) );
sizeof(int), which returns the number of bytes needed to store a single int. This is then multiplied by 5 (because you need room for 5 of them), the result of which is finally the argument to
malloc just accepts a number, it has no knowledge of the type you're going to store within that space, so it doesn't know whether to return a
int* or a
char* or what. So, it just returns a
void*, which can be cast to any of those types. So, for our array of 5 ints, we finally end up at:
int* intArr = malloc( 5*sizeof(int) );
Say we instead want 12 doubles, then it looks like this:
double* doubles = malloc( 12*sizeof(double) );
You can view this as a simple syntax change, but there is a little more going on. In C++, the
new keyword figures out how many bytes you need based on the primitive type. In C, you have to compute that yourself in the code (with the handy sizeof() function). Here is the side-by-side difference:
Our other option for memory allocation is
calloc, which makes this "I need to know two things, the size of the type and the number of elements I'm story" thing explicit. In
calloc, our "array of 5 ints" and "array of 12 doubles" looks like this:
int* intArr = calloc( 5, sizeof(int) ); double* doubles = calloc( 12, sizeof(double) );
You'll notice that it takes two arguments, while malloc just requires you to multiply them together yourself. The other significant difference is that unlike
calloc not only allocates the memory, it also zeroes it out(!!)! No random garbage in your arrays! But, it's somewhat slower, since it has to iterate over all the memory and zero it out.
The choice between
calloc is largely a personal preference between the two, as long as you're knowledgeable about these differences.
calloc, be sure to
#include <stdlib.h>, or you'll get a warning about the return type of your allocation function.
Remember that scanf requires a pointer to know which memory address will be filled. For example, reading an int:
Nothing changes with arrays, but it might not yet be intuitive to you, so let's be explicit. An array cell is an lvalue which makes it no different from a standard primitive type. If you want to read an array of 10 ints from standard input, it looks like you'd expect:
int N; scanf("%d", &N);
int* nums = malloc( 10*sizeof(int) ); for( int i = 0; i < 10; i++ ) scanf("%d", &nums[i]); // we just need the & to get the address of the array cell
Remember to delete your memory allocation when you're finished with your arrays!
The C equivalent of
delete is the function
Remember how we lost our great
string type from C++?
It's not so bad. Strings in C are instead explicit arrays of chars. There is a small catch, though. The final char of your array must be a null character (0 on your ASCII table, first in your heart). So, if you were to encode the word "hi" as a C string, you would need an array of size three:
char* hiStr = malloc( 3*sizeof(char) ); hiStr = 'h'; hiStr = 'i'; hiStr = 0; //alternatively, the character '\0'
Alternatively and wonderfully, you can also do this:
char* hiStr = "hi";
The null char is still there, just unseen in the code. This second case builds the array in the stack, rather than the heap - this means that this second example does not need to be
freed, while the first does. In general, we're avoiding this in our class (it's called "static allocation"), but for C strings it's too common to step around. You'll also see:
which builds an array of 20 chars in the stack, pointed to by a
char* aString. Again, you can technically build arrays in the stack like this for any type but it's exceedingly common with strings, which tend to be short, and so people put them in the stack, so they don't have to
Once you've allocated space for some chars, your pointer can be used with
scanf to read in strings from the user:
char aString; scanf("%s",aString);
char* aString = malloc( 20*sizeof(char) ); scanf("%s",aString);
cin, the string will be read in until whitespace - here, you have to be careful to have allocated enough space for the string you'll be reading in!
Finally, the above is all about creating and reading strings.
But what about helpful functions? Make sure you
#include <string.h>. This library provides a wide variety of useful string functions.
Again, very superficial differences. First, you make a pointer to a FILE using
fopen, which accepts two C strings as arguments, the first of which is a filename, and the second is a mode (detailed here). After doing this, you can use
fprintf just like you would
printf. So, to open a file and read an integer:
FILE* fin = fopen("someFile.txt","r"); int theInt; fscanf(fin, "%d", &theInt);
$ ./avg How many? 5 5 3 9 2 3 3 2 9 3 5
$ ./fileread Average is 34.850000 Max is 94