CS 202 - Notes 2018-09-19
More on Arrays
Passing arrays to functions
max.c
/**
In this example, we are looking at the different ways we can pass arrays to functions.
To compile: gcc -o max max.c -Wall
To run: ./max
C. Andrews
*/
#include <stdio.h>
#include <stdlib.h>
#define ARRAY_LENGTH 5
/*
The first approach is to put a sized array in. This is not great unless you want to communicate in your code that this only deals
with one array size. As we showed in class, the compiler typically doesn't enforce this, and you can get strange answers when you
pass in arrays that are either longer or shorter than this number.
*/
int max1(int a[ARRAY_LENGTH]){
int max = a[0];
for (int i = 1; i < ARRAY_LENGTH; i++){
if (max < a[i]){
max = a[i];
}
}
return max;
}
/*
The second approach is to declare it as an array, but pass the length in a separate variable.
*/
int max2(int a[], int length){
int max = a[0];
for (int i = 1; i < length; i++){
if (max < a[i]){
max = a[i];
}
}
return max;
}
/*
It is also perfectly reasonable to pass a pointer and the length. This is functionally the same, but not as communicative.
*/
int max3(int * a, int length){
int max = a[0];
for (int i = 1; i < length; i++){
if (max < a[i]){
max = a[i];
}
}
return max;
}
/*
This generates a random array and then calls the three max functions on it.
*/
int main(int argc, char * argv[]){
int length = 25;
int a[length];
for (int i = 0; i < length; i++){
a[i] = rand() % 25;
printf("%d\n", a[i]);
}
printf("max1: %d\n", max1(a));
printf("max2: %d\n", max2(a, length));
printf("max3: %d\n", max3(a, length));
return 0;
}
Returning arrays from functions
Here is an example function that will not work
int * makeRandomArray(int length){
int a[length];
for (int i = 0; i < length; i++){
a[i] = rand() % 25;
printf("%d: %d\n", i, a[i]);
}
return a;
}
The core problem is that the variable is allocated locally, and locally allocated values are released and eventually overwritten. This normally isn't a problem because we don't have access to the variable name any more. However, if we try to return a pointer to that memory (as we do here), we can still access those values after we were supposed to be done with them. This would give us an unpredictable array, whose values could change at some unknown point in the future when the system needs those memory locations for something else.
Fortunately, the compiler shouldn't even let you get away with this.
Strings
Strings are just arrays of characters (char[]).
To indicate their end, the last character is always a NULL character ('\0', which just has the integer value of 0). String functions rely on this. For example, strlen
just counts the characters up until it reaches the '\0'.
string literals, strings that we write explicitly in our code surrounded by quotes, can be assigned to variables in a variety of ways, each way has different consequences.
strings.h
/*
In this example, we are looking at the myriad ways that we can create a variable that holds a string and how their behavior differs.
In all cases we are making use of "string literals" (text explicitly represented between "").
To compile: gcc -o strings trings.c -Wall
To run: ./strings
C. Andrews
*/
#include <stdio.h>
int main(int argc, char * argv[]){
// These three create pointers that point to the string stored in read-only memory
const char * str1 = "string1";
char * const str2 = "string2";
char * str3 = "string3";
// these two populate arrays in the "data" section of the executable, and are read-write
char str4[] = "string4";
char str5[8] = "string5";
printf("str1: %s\n", str1);
printf("str2: %s\n", str2);
printf("str3: %s\n", str3);
printf("str4: %s\n", str4);
printf("str5: %s\n", str5);
printf("\n");
// The first difference we find comes when we try to update a character in the string
// The pointers are pointing to read only memory, so the strings can't be updated
// Note that the use of const in str1 is saying that str1 points to a constant char *, so the compiler knows the string can't be changed
// For the other two, the compiler doesn't know, so the program crashes when we try to access that memory
//str1[0] = '*'; //assignment to read only memory
//str2[0] = '*'; // segfault
//str3[0] = '*'; // segfault
str4[0] = '*';
str5[0] = '*';
printf("str1: %s\n", str1);
printf("str2: %s\n", str2);
printf("str3: %s\n", str3);
printf("str4: %s\n", str4);
printf("str5: %s\n", str5);
printf("\n");
// The other thing we can try to do is point to a different string
// str2 can't be changed because we declared it as const, so the compiler won't let us change its value (the address it points to)
// here the const keyword is modifying the variable name itself, saying the value stored in this variable can't be changed,
// as opposed to str1, which is a dynamic variable that points at a static string
str1 = "dalek1";
// str2 = "dalek2"; //read only variable
str3 = "dalek3";
// str4 and str5 are arrays and we can't update the contents of an array this way
//str4 = "dalek4"; // can't assign to array
//str5 = "dalek5"; // can't assign to array
printf("str1: %s\n", str1);
printf("str2: %s\n", str2);
printf("str3: %s\n", str3);
printf("str4: %s\n", str4);
printf("str5: %s\n", str5);
printf("\n");
return 0;
}