Well done everyone for such a great effort in the first assignment.
Take a moment to think about how you found the assignment, and what you learned in the process of working on it.
If you could go back in time to the very start of the assignment, and give one piece of advice to your past self, what would you say?
Your tutor has asked a lab pair to present their week 7 work.
Discuss the good, the bad and the ugly aspects of their code.
Please be gentle in any criticism - we are all learning!
They are usually "aimed" at a variable of the particular type.
They're able to read the value of that variable and also are handy because they can be passed into functions, giving the function access to a variable.
Array are homogeneous - formed from a single data type.
Structs can be heterogeneous - formed from a multiple data types.
Array element are accessed with integer array indexes.
Structs fields are accessed by name.
The information should include the pet's name, type of animal, age and weight.
Create a variable of this type and assign information to it to represent an axolotl named "Fluffy" of age 7 that weighs 300grams.
#include <stdio.h>
#include <string.h>
#define MAX_NAME_LENGTH 256
#define MAX_BREED_LENGTH 64
struct pet {
char name[MAX_NAME_LENGTH];
char breed[MAX_BREED_LENGTH];
int age;
int weight;
};
int main(void) {
struct pet my_pet;
strcpy(my_pet.name, "Fluffy");
strcpy(my_pet.breed, "axolotl");
my_pet.age = 7;
my_pet.weight = 300;
return 0;
}
void age_fluffy(struct pet *my_pet);
eg: If fluffy goes from age 7 to age 8, it should end up weighing 8/7 times the amount it weighed before. You can store the weight as an int and ignore any fractions.
Show how this function can be called by passing the address of a struct variable to the function.
#include <stdio.h>
#include <string.h>
#define MAX_NAME_LENGTH 256
#define MAX_BREED_LENGTH 64
struct pet {
char name[MAX_NAME_LENGTH];
char breed[MAX_BREED_LENGTH];
int age;
int weight;
};
void age_fluffy(struct pet *my_pet);
int main(void) {
struct pet my_pet;
strcpy(my_pet.name, "Fluffy");
strcpy(my_pet.breed, "axolotl");
my_pet.age = 7;
my_pet.weight = 300;
age_fluffy(&my_pet);
printf("%d, %d\n", my_pet.age, my_pet.weight);
return 0;
}
void age_fluffy(struct pet *my_pet) {
double old_age = my_pet->age;
my_pet->age = my_pet->age + 1;
double weightMult = my_pet->age / old_age;
my_pet->weight = my_pet->weight * weightMult;
}
//
// Starting code for COMP1511 lab exercises
//
#include <stdio.h>
#include <string.h>
#define MAX_SPECIES_NAME_LENGTH 128
#define MAX_SIGHTINGS 10000
// a struct to represent the date
// a whale pod sighting was made
struct date {
int year;
int month;
int day;
};
// a struct to represent a sighting
// of a pod (group) of whales
struct pod {
struct date when;
int how_many;
char species[MAX_SPECIES_NAME_LENGTH];
};
int read_sightings_file(char filename[], int len, struct pod sightings[len]);
int read_sighting(FILE *f, struct pod *w);
int read_date(FILE *f, struct date *d);
int count_orca_sightings(int n_sightings, struct pod sightings[n_sightings]);
int main(int argc, char *argv[]) {
if (argc != 2) {
fprintf(stderr, "Usage: %s <file>\n", argv[0]);
return 1;
}
struct pod whale_sightings[MAX_SIGHTINGS];
int n_sightings = read_sightings_file(argv[1], MAX_SIGHTINGS, whale_sightings);
if (n_sightings > 0) {
int n_orca_pods = count_orca_sightings(n_sightings, whale_sightings);
printf("%d Orca sightings in %s\n", n_orca_pods, argv[1]);
}
return 0;
}
// return the number of sightings of Orca
int count_orca_sightings(int n_sightings, struct pod sightings[n_sightings]) {
// REPLACE THIS COMMENT WITH YOUR CODE
// THIS FUNCTION SHOULD NOT CALL SCANF OR PRINTF
// IT SHOULD JUST RETURN A VALUE
return 42; // CHANGE ME
}
//
// DO NOT CHANGE THE FUNCTIONS BELOW HERE
//
// return number of sightings read from filename
// -1 is returned if there is an error
int read_sightings_file(char filename[], int len, struct pod sightings[len]) {
FILE *f = fopen(filename, "r");
if (f == NULL) {
fprintf(stderr,"error: file '%s' can not open\n", filename);
return -1;
}
int n_sightings = 0;
while (read_sighting(f, &sightings[n_sightings]) == 1 && n_sightings < len) {
n_sightings = n_sightings + 1;
}
fclose(f);
return n_sightings;
}
// return 1 if a sighting can be read, 0 otherwise
int read_sighting(FILE *f, struct pod *s) {
if (read_date(f, &(s->when)) != 1) {
return 0;
}
if (fscanf(f, "%d", &(s->how_many)) != 1) {
return 0;
}
fgetc(f);
if (fgets(s->species, MAX_SPECIES_NAME_LENGTH, f) == NULL) {
return 0;
}
// finish string at '\n' if there is one
char *newline_ptr = strchr(s->species, '\n');
if (newline_ptr != NULL) {
*newline_ptr = '\0';
}
// also finish string at '\r' if there is one - files from Windows will
newline_ptr = strchr(s->species, '\r');
if (newline_ptr != NULL) {
*newline_ptr = '\0';
}
return 1;
}
// return 1 if a date can be read, 0 otherwise
int read_date(FILE *f, struct date *d) {
int n_scanned = fscanf(f, "%d/%d/%d", &(d->year), &(d->month), &(d->day));
return n_scanned == 3;
}
Note the C library functions like fopen, fscanf,
fclose
which are used to read and write files haven't been covered
lectures.
We'll squeeze them if we have time but they definitely won't be needed for the exam.
Your tutor will explain basically what they do in the supplied code for this week's lab exercise but you don't need to change the code that calls these functions.
Your tutor may still choose to cover some of the questions time permitting.
#include <stdio.h> int main(void) { char str[10]; str[0] = 'H'; str[1] = 'i'; printf("%s", str); return 0; }
printf
, like many C library functions
expects strings to be null-terminated.
In other words printf
, expects the array
str
to contain an element with value '\0'
which marks the end of the sequence of characters to
be printed.
printf
will print
str[0]
('H'), str[1]
then
examine str[2]
.
Code produced by dcc --valgrind
will then
stop with an error because str[2]
is
uninitialized.
The code with gcc will keep executing and printing
element from str
until it encounters one
containing '\0'. Often str[2]
will by
chance contain '\0' and the program will work
correctly.
Another common behaviour will be that the program prints some extra "random" characters.
It is also possible the program will index outside the
array which would result in it stopping with an error
if it was compiled with dcc
.
If the program was compiled with gcc and uses indexes well outside the array it may be terminated by the the operating system because of an illegal memory access.
#include <stdio.h> int main(void) { char str[10]; str[0] = 'H'; str[1] = 'i'; str[2] = '\0'; printf("%s", str); return 0; }
#define MAX_STUDENT_NAME_LENGTH 128 #define N_LABS 12 #define N_TESTS 10 #define N_ASSIGNMENTS 3 struct student { int zid; char name[MAX_STUDENT_NAME_LENGTH]; double lab_marks[N_LABS]; double test_marks[N_TESTS]; double assignment_marks[N_ASSIGNMENTS]; };
int n; int *p, *q;What will happen when each of the following statements is executed (in order)?
p = &n; *p = 5; *q = 17; q = p; *q = 8;
p = &n; // p will point to n *p = 5; // 5 will be stored into n *q = 17; // the program will attempt to store 17 into the variable pointed to by q, // which could cause an error because q has not been initialized. // dcc will give us a warning about this q = p; // q will point to the same variable that p does, ie n *q = 8; // 8 will be stored into n
#include <stdio.h>
#define MAX_PLATE 10000
struct parking_fine {
double amount;
char number_plate[MAX_PLATE];
};
int read_parking_fine(struct parking_fine);
int main(void) {
struct parking_fine f;
if (read_parking_fine(f)) {
printf("%lf %s\n", f.amount, f.number_plate);
}
return 0;
}
// return 1 if a parking fine is successfully was read, 0 otherwise
int read_parking_fine(struct parking_fine fine) {
if (scanf("%lf", &(fine.amount)) != 1) {
return 0;
}
return fgets(fine.number_plate, MAX_PLATE, stdin) != NULL;
}
Fix it.
Any changes made to it do not affect the variable in the main function.
We can instead pass a pointer to the struct:
#include <stdio.h>
#define MAX_PLATE 10000
struct parking_fine {
double amount;
char number_plate[MAX_PLATE];
};
int read_parking_fine(struct parking_fine *fine);
int main(void) {
struct parking_fine f;
if (read_parking_fine(&f)) {
printf("%lf %s\n", f.amount, f.number_plate);
}
return 0;
}
// return 1 if a parking fine was successfully read, 0 otherwise
int read_parking_fine(struct parking_fine *fine) {
if (scanf("%lf", &(fine->amount)) != 1) {
return 0;
}
return fgets(fine->number_plate, MAX_PLATE, stdin) != NULL;
}
strip_comments.c
which reads
lines from its input and prints them after removing any C //
style comments. In another words if the line contains // it
does not print the // or anything after it.
The only functions you can use are fgets
and
printf
.
You can assume lines contain at most 4096 characters.
For example:
./strip_comments x = x + 1; // This means add one to the variable x x = x + 1;Also - is that a good comment to add to a C program?
strip_comments.c
#include <stdio.h>
#define MAX_LINE 4096
int main(void) {
char line[MAX_LINE];
int i;
// reads lines and print them after
// removing // style comments
while (fgets(line, MAX_LINE, stdin) != NULL) {
// iterate through line and see if we encounter a '/' then '/'
// everything after is a comment, so stop the string there
i = 0;
while (line[i] != '\n' && line[i] != '\0') {
// safe to look at line[i+1] because
// we know here line[i] != '\0'
if (line[i] == '/' && line[i + 1] == '/') {
// replace // with a newline and null-terminator
line[i] = '\n';
line[i + 1] = '\0';
// could break here but loop will stop anyway
// because after i = i + 1, line[i] == '\0'
}
i = i + 1;
}
// write possibly-modified line
printf("%s", line);
}
return 0;
}
filter_empty_lines.c
which reads
lines from its input and prints them only if they contain a
non-white-space-character.
In another words remove lines are empty or contain only white-space.
The only functions you can use are fgets
and
printf
.
You can assume lines contain at most 4096 characters.
You can assume there are only 3 white space characters, space, tab & new-line.
For example:
./filter_empty_lines full line full line another no-empty line another no-empty line
filter_empty_lines.c
#include <stdio.h>
#include <stdlib.h>
#define MAX 4096
int main(void) {
char line[MAX];
while (fgets(line, MAX, stdin) != NULL) {
// count non-whitespace characaters on this line
int non_whitespace_count = 0;
int i = 0;
while (line[i] != '\n' && line[i] != '\0') {
// test for non white space
// !isspace(line[i]) would be better (from ctype.h)
if ((line[i] != ' ') && (line[i] != '\t') && (line[i] != '\n')) {
// this is a non-whitespace character
non_whitespace_count = non_whitespace_count + 1;
// could break here
}
i = i + 1;
}
// print the line if it has non-whitespace character
if (non_whitespace_count > 0) {
printf("%s", line);
}
}
return 0;
}
reverse.c
which reads lines and
writes them out with the characters of each line in reverse
order. It should stop when it reaches the end of input.
For example:
./reverse The quick brown fox jumped over the lazy dog. .god yzal eht revo depmuj xof nworb kciuq ehT It was the best of times. It was the worst of times. .semit fo tsrow eht saw tI .semit fo tseb eht saw tI This is the last line. .enil tsal eht si sihT
reverse.c
#include <stdio.h>
#define MAX_LINE 4096
int main(void) {
char line[MAX_LINE];
while (fgets(line, MAX_LINE, stdin) != NULL) {
// find the length of this line
int i = 0;
while (line[i] != '\n' && line[i] != '\0') {
i = i + 1;
}
// when we exit, i is the index of either
// newline character or null terminator
// so go back one position
i = i - 1;
// now print line in reverse
while (i >= 0) {
printf("%c", line[i]);
i = i - 1;
}
printf("\n");
}
return 0;
}
int myStrlen(char *string);
int myStrCmp(char *string1, char *string2);
int beginsWith(char *string1, char *string2);
int isSubstring(char *substring, char *string);