Class introduction (for everyone, starting with the tutor):
Please turn on your camera for the intro, if you can and you are comfortable with this. Having your webcam on is optional in online COMP1521 tut-labs, unless you have a cute pet in which case its required, but you only need show the pet.
What is your prefered name (what should we call you?)
What other courses are you doing this term
What parts of C from COMP1511/COMP1911 were the hardest to understand?
Do you know any good resources to help students who havve forgotten their C? For example:
Where is each variable located in memory?
Where are the strings located?
The s1 variable is a global variable
and would be accessible from
any function in this .c file.
It would also be accessible from
other .c files that referenced it
as an extern'd variable.
C implementations typically store global variables
in the data segment (region of memory).
The s2 variable is a local variable,
and is only accessible within
the main() function.
C implementations typically store
local variables on the stack,
in a stack frame created for function —
in this case, for main().
C implementations typically place
string literals such as "abc"
in the text segment with the program's code.
C's sizeof operator is a prefix unary operator (precedes its 1 operand) - what are examples of other C unary operators?
name
operator
C
note
unary minus
-
int i = -5;
unary plus
+
int j = +5;
Decrement
--
int l = --i;
prefix or postfix
Increment
++
int k = ++j;
prefix or postfix
Logical negation
!
if (true == ! false)
Bitwise negation
~
int m = ~0;
Address of
&
int *n = &m;
Indirection
*
int o = *n;
Why is C's sizeof operator different to other C unary & binary operators?
sizeof can be given a variable, value or a type as argument.
The syntax to distinguish this is weird,
the type is surrounded by brackets.
malloc(8) might be correct (depending on what struct ndoe is) but is definitely non-portable,
struct node might be 8 bytes on a 32-bit OS and 12 or 16 bytes on a 64-bit OS
d.data is incorrect as d is not a struct its a pointer to a struct
c->data is illegal as c will be NULL when its executed
What is a pointer? How do pointers relate to other variables? Pointers are variables that refer (point) to another variable.
Typically they implement this by storing a memory address of the variable they refer to.
Given a pointer to a variable you can get its value and also assign to it.
Consider the following small C program:
#include<stdio.h>intmain(void){intn[4]={42,23,11,7};int*p;p=&n[0];printf("%p\n",p);// prints 0x7fff00000000printf("%lu\n",sizeof(int));// prints 4// what do these statements print ?n[0]++;printf("%d\n",*p);p++;printf("%p\n",p);printf("%d\n",*p);return0;}
Assume the variable n has address 0x7fff00000000.
Assume sizeof (int) == 4.
What does the program print?
Program output:
0x7fff00000000
4
43
0x7fff00000004
23
The n[0]++ changes the value by one,
because n is an int variable.
The p++ changes the value by four,
because p is a pointer to an int,
and addition of one to a pointer
changes it to the point to the next element of the array.
Each array element is four bytes, because sizeof (int) == 4
Consider the following pair of variables
intx;// a variable located at address 1000 with initial value 0int*p;// a variable located at address 2000 with initial value 0
If each of the following statements
is executed in turn,
starting from the above state,
show the value of both variables
after each statement:
p = &x;
x = 5;
*p = 3;
x = (int)p;
x = (int)&p;
p = NULL;
*p = 1;
If any of the statements would trigger an error,
state what the error would be.
Starting with x == 0 and p == 0:
a. p = &x; # x == 0, p == 1000
b. x = 5; # x == 5, p == 1000
c. *p = 3; # x == 3, p == 1000
d. x = (int)p; # x == 1000, p = 1000
e. x = (int)&p; # x == 2000, p = 1000
f. p = NULL; # x = 2000, p = NULL
g. *p = 1; # error, dereference NULL pointer
Note that NULL is generally represented by a zero value.
Note also that statements (d) and (e) are things that you are
extremely unlikely to do.
Write a function that increases the age of fluffy by one and then increases
its weight by the fraction of its age that has increased. The function is defined like this:
void age_fluffy(struct pet *my_pet);
e.g.: 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.
Write a main function that takes command line input that fills out the fields
of the pet struct. Remember that command line arguments are given to our main
function as an array of strings, which means we'll need something to convert
strings to numbers.
#include<stdio.h>#include<string.h>#include<stdlib.h>#define MAX_NAME_LENGTH 256#define MAX_BREED_LENGTH 64structpet{charname[MAX_NAME_LENGTH];charbreed[MAX_BREED_LENGTH];intage;intweight;};intmain(intargc,char*argv[]){if(argc<5){printf("%s needs 4 arguments to populate a pet.\n",argv[0]);return1;}else{structpetmy_pet;strcpy(my_pet.name,argv[1]);strcpy(my_pet.breed,argv[2]);my_pet.age=strtol(argv[3],NULL,10);my_pet.weight=strtol(argv[4],NULL,10);}return0;}
What will happen when the above program is compiled and executed? The above program will compile without errors . 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 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 operating system because of an illegal memory access.
For each of the following commands,
describe what kind of output would be produced:
gcc -E x.c
gcc -S x.c
gcc -c x.c
gcc x.c
Use the following simple C code as an example:
#include<stdio.h>#define N 10intmain(void){charstr[N]={'H','i','\0'};printf("%s\n",str);return0;}
gcc -E x.c
Executes the C pre-processor,
and writes modified C code to stdout
containing the contents of all #include'd files
and replacing all #define'd symbols.
gcc -S x.c
Produces a file x.s
containing the assembly code
generated by the compiler
for the C code in x.c.
Clearly, architecture dependent.
gcc -c x.c
Produces a file x.o
containing relocatable machine code
for the C code in x.c.
Also architecture dependent.
This is not a complete program,
even if it has a main() function:
it needs to be combined with
the code for the library functions
(by the linker ld(1)).
gcc x.c
Produces an executable file called a.out,
containing all of the machine code needed
to run the code from x.c
on the target machine architecture.
The name a.out can be overridden
by specifying a flag -o filename.
Consider the following (working) C code
to trim whitespace from both ends of a string:
// COMP1521 21T2 GDB debugging example#include<stdio.h>#include<stdlib.h>#include<string.h>#include<ctype.h>#include<assert.h>voidtrim(char*str);char**tokenise(char*str,char*sep);voidfreeTokens(char**toks);intmain(intargc,char**argv){if(argc!=2)exit(1);char*string=strdup(argv[1]);printf("Input: \"%s\"\n",string);trim(string);printf("Trimmed: \"%s\"\n",string);char**tokens=tokenise(string," ");for(inti=0;tokens[i]!=NULL;i++)printf("tok[%d] = \"%s\"\n",i,tokens[i]);freeTokens(tokens);return0;}// trim: remove leading/trailing spaces from a stringvoidtrim(char*str){intfirst,last;first=0;while(isspace(str[first]))first++;last=strlen(str)-1;while(isspace(str[last]))last--;inti,j=0;for(i=first;i<=last;i++)str[j++]=str[i];str[j]='\0';}// tokenise: split a string around a set of separators// create an array of separate strings// final array element contains NULLchar**tokenise(char*str,char*sep){// temp copy of string, because strtok() mangles itchar*tmp;// count tokenstmp=strdup(str);intn=0;strtok(tmp,sep);n++;while(strtok(NULL,sep)!=NULL)n++;free(tmp);// allocate array for argv stringschar**strings=malloc((n+1)*sizeof(char*));assert(strings!=NULL);// now tokenise and fill arraytmp=strdup(str);char*next;inti=0;next=strtok(tmp,sep);strings[i++]=strdup(next);while((next=strtok(NULL,sep))!=NULL)strings[i++]=strdup(next);strings[i]=NULL;free(tmp);returnstrings;}// freeTokens: free memory associated with array of tokensvoidfreeTokens(char**toks){for(inti=0;toks[i]!=NULL;i++)free(toks[i]);free(toks);}
You can grab a copy of this code as trim.c.
The part that you are required to write
(i.e., would not be part of the supplied code)
is highlighted in the code.
Change the code to make it incorrect.
Run the code, to see what errors it produces, using this command:
gcc -std=gnu99 -Wall -Werror -g -o trim trim.c
./trim " a string "
Then use GDB to identify the location
where the code "goes wrong".