Hemuppgifter C-programmering
Maskinorienterad Programmering / Programmering av Inbyggda System

Övningsuppgifter kopplade till C-föreläsningarna

På denna sida finner du grundläggande övningsuppgifter i C. Lösningsförslag finns till alla uppgifterna (se nedan). Dessa övningsuppgifter är mer orienterade mot vad ni behöver i just den här kursen jämfört med andra grundläggande övningar ni kan finna på nätet. Förhoppningsvis är de även lite roligare. De syftar bl a på att förbereda er för er sista laboration där ni får implementera ett valfritt spel (eller annan dylik app) på labdatorn MD407. Tanken där är framförallt att ni på egen hand skall kommunicera med periferienheter och kunna utnyttja interrupts.

Duktiga spelprogrammerare är typiskt mycket attraktiva inom övriga industrin, medan omvänt är mer varierande. Det är svårt att föreställa sig applikationer med mer diversifierade, mångfaceterade och allomfattande problemområden än spel. Dagens avancerade spel är inte sällan som små minioperativsystem, med en realtidskärna, parallellism, multitasking, high-performance computing, filhantering, nätverk, säkerhet, ljud, grafik och artificiell intelligens. Det mesta av detta omfattar färdigheter som ni tillskansar er i senare kurser. Ni har typiskt heller ännu inte läst om avancerade datastrukturer och algoritmer.

Grundläggande i realtidsspel är dock att de hanterar en mängd överlappande händelseförlopp och har inputs och outputs. Vad övningarna på den här sidan syftar till (förutom att ge er grundkunskaper i C, att adressera minne och manipulera bitar) är att även lära er bemästra hyfsat avancerade händelseflöden som inte nödvändigtvis följer en rak sekventiell ordning (något som ofta saknas i andra kurser och tas för givet att ni kan hantera). Men vi ska starta mjukt.

Hur som helst finns här andra grundläggande övningar för den som skulle vilja ha:
  • http://www.learn-c.org
  • http://www.tutorialspoint.com/cprogramming/
  • http://www.cprogramming.com/tutorial.html
  • Installera utvecklingsmiljön CodeLite









    Skapa nytt workspace och project


    Slå på C99 för ditt projekt

  • Installera CodeLite från sidan Resurser på kurshemsidan. (Installera inte någon de senare versioner som finns på CodeLites hemsida, ty vi har patchat CodeLite för bättre debuggstöd mot MD407-plattformen som används i era laborationer längre fram.)
  • Du kan med fördel även installera ETERM, SimServer, GCC (samt CoolTerm och Drivrutiner FTDI-krets om du använder Mac), så detta är avklarat inför uppgifterna i Arbetsboken samt Labbarna. Just nu behöver vi dock endast CodeLite. För hemuppgifterna kommer vi nämligen att till en början endast kompilera C-program mot din dators inbyggda processor (Intel / AMD). Senare kommer vi använda en korskompilator mot ARM-processorn på MD407-datorn.
  • Skapa ett tomt workspace och ett nytt projekt. Projektet skall vara av typen "Console -> simple executable (gcc)". Försök helst utan hjälp, men behöver du instruktioner så finns det här.
  • Vi vill gärna använda C99. Slå på detta genom att högerklicka på projektet och välja settings->compiler->gå in i project->settings->Compiler->"C Compiler Options" och klicka i "Enable ANSI C99 features". Försök helst utan hjälp, men behöver du instruktioner så finns det här. Observera att du behöver slå på C99 för varje projekt du skapar.
  • Kompilera och testkör programmet. Behöver du instruktioner finns det här.
  •  

    C - Föreläsning 1.

    Uppg. 1
    Gör en for-loop som skriver ut "I am an awesome C programmer" 10 gånger.
    #include <stdio.h> int main() { for(int i=0; i<10; i++) { printf("I am an awesome C programmer\n"); } return 0; }
    Uppg. 2
    Skapa en sträng och skriv ut den baklänges.
    #include <stdio.h> #include <string.h> int main() { char str[] = "I am a great programmer"; for(int i=strlen(str)-1; i>=0; i--) { printf("%c", str[i]); } return 0; }
    Tips: Googla på "length of a string in c" så finner du att funktionen strlen() returnerar längden av en sträng. Googla även på "strlen header" så ser du att strlen() är definierad i string.h vilken behöver inkluderas och är en systemheaderfil.

    Kort om #include-direktivet:
    #include <file>
    This variant is used for system header files. It searches for a file named file in a standard list of system directories.
    #include "file" This variant is used for header files of your own program. It searches for a file named file first in the directory containing the current file, then in the quote directories and then the same directories used for .





    Nu ska vi ta en titt på vilka funktioner som faktiskt finns fördefinierade i C...

    I den här kursen använder vi ANSI C99 och GNU-kompilatorn (gcc). Andra kompilatorer som finns är t ex LLVM, clang, (Min-GW), Visual C++ och GCC-ARM-kompilatorn som vi använder senare som korskompilator för labdatorn.

    C Quick Reference Guide

    En översikt över C-språket samt de funktioner som ingår i standardbiblioteken (och resp. headerfil som behöver inkluderas). C Reference Card (ANSI)
    Den som ändå skulle vilja ha en fullständig GNU C referensmanual finner detta t ex här: GNU C Reference Manual.

    C standard libraries

    Noggrannare beskrivning av funktionerna i standardbiblioteken finner ni här: http://en.cppreference.com/w/c/header
    Tanken är att dessa funktioner skall vara identiska för alla ANSI-C-kompilatorer och operativsystem. Skillnader kan dock ändå finnas, som t ex för ARM-kompilatorn. T ex så saknas operativsystem för MD407 och därmed faktiskt alla standardbibliotek.

    C system include files

    Observera att förutom standardbiblioteken finns även systembibliotek. Dessa skiljer ofta markant mellan operativsystem. Var ligger systemheaderfilerna?
    • MacOS X Yosemite: se här
    • Windows: högerklicka på #include <filename.h> och välj goto declaration.
    • MD407 + ARM-korskompilatorn: Eftersom operativsystem saknas för MD407 så saknas även standardheaderfilerna för C.
    För mer komplexa program måste man därför tyvärr anpassa programkoden för varje plattform som man kompilerar mot. Av denna anledning existerar det bibliotek som abstraherar bort skillnaderna (som t ex wxWidgets, SDL (som bl a används av Valve) mfl.).

    C - Föreläsning 1. forts...

    Uppg. 3
    Bitwise OR används typiskt för att ett-ställa bitar. Bitwise AND används typiskt för att nollställa bitar. Bitwise XOR används typiskt för att invertera bitar. Du måste vara bekväm med att översätta fram och tillbaks mellan decimala, hexadecimala och binära talsystemet. (Oktala talsystemet används sällan nu för tiden.) Du måste behärska AND, OR, XOR, <<, >> och ~ (not).
    Ni måste kunna tända o släcka givna bitar i bytes, shorts och ints. Allt detta kommer på tentan.
    1. Skapa en funktion som tar en byte som input och printar ut dess bitarna med 1:or och 0:or. T ex 00001001 för byte = 9. Gör även en funktion för shorts (16-bits) och en för ints (32-bits). Testa.
      #include <stdio.h> #include <string.h> void printBinary(unsigned char byte) { for(int i=7; i>=0; i--) { int bit = (byte >> i) & 0x01; printf("%d", bit); } } int main() { unsigned char byte = 128; printBinary(byte); return 0; }

    2. Gör en funktion som tar en 8 tecken lång sträng av 1:or och 0:or och printar ut den som ett decimaltal. Ex: char str[] = "00001001" ska ge utskriften 9 om funktionen anropas med strängen.
      #include <stdio.h> #include <string.h> void printDecimal(char str[]) { unsigned char byte = 0; for(int i=0; i<8; i++) { if(str[i] == '1') byte |= (1<<(7-i)); } printf("%d", byte); } int main() { char str[] = "00001001"; printDecimal(str); return 0; }
    Använd dina två funktioner i de föregående uppgifterna för att verifiera dina lösningar nedan.
    1. Skapa en unsigned char c = 128;. Ettställ bit 1, 3 och 5. Antag bitarna numreras 0-7 (1-8 är också vanligt). Verifiera och skriv ut decimaltalet.
      #include <stdio.h> int main() { unsigned char byte = 128; printBinary(byte); printf("\n"); byte |= (1<<1 | 1<<3 | 1<<5); printBinary(byte); printf("\n"); printf("%d\n", byte); return 0; }
    2. För att skriva ut ett tal hexadecimalt kan du använda printf("%x\n", tal);. Hexadecimaltal anges i C med prefixet 0x. Konvertera följande tal, med penna och papper, till respektive talsystem (decimalt, hexadecimalt samt binärt). Använd dina funktioner för att verifiera svaren:
            97, 0x97, 10010010
    3. Skapa en variabel signed char x = 27;. Invertera alla bitarna och addera 1. Testkör. Vad hände? Varför?
      x ^= 0xff; x++;
    4. Skapa ett program som ger följande utskrift - se videon nedan.
      Gör det genom att skifta bitar. Skapa en variabel unsigned short a = 0x8000; där du skiftar ner bitarna varje iteration. Skapa på motsvarande vis en variabel där du skiftar upp bitarna varje iteration. OR:a ihop de två variablerna inför varje utskrift.
            För att vid varje iteration få en blank terminal med texten längst ned, skapa en funktion som varje iteration rensar terminalfönstret alldeles innan du skriver ut texten. Detta kan göras genom att t ex skriva ut c:a 80 st "\n".
            En sak till... För att dina iterationer skall komma i ett lagom tempo så kan du använda operativsystemets sleep-funktion efter varje iteration. Har du skapat en yttre evig loop så gör anropet till sleep (som återför kontrollen till operativsystemet) även att programmet kan vara enklare att avbryta när du stänger console-fönstret.
      Windows: Använd Sleep(millisekunder) tillsammans med #include <windows.h>

      Mac OS: Använd usleep(mikrosekunder) tillsammans med #include <unistd.h>
      #include <stdio.h> #include <string.h> #include <unistd.h> //(eller <windows.h>) void printBinary(unsigned short tal) { for(int i=15; i>=0; i--) { int bit = (tal >> i) & 0x01; printf("%d", bit); } } void clearscreen() { for(int i=0; i<80; i++) printf("\n"); } int main() { while (1) { unsigned short a = 0x8000, b = 0x0001; for(int i=0; i<15; i++) { clearscreen(); a >>= 1; b <<= 1; printBinary(a | b); printf("\n"); // without the sleep, the eternal loop may // also cause the program to be harder to // terminate. usleep(250000); // för Windows: Sleep(250); } } return 0; }
    5. Gör nu ditt eget animerade favoritbitmönster
    6. Lägg till så att texten scrollar omväxlande till höger/vänster så att den ser ut att studsa mot terminalkanterna. (Om du kan, lägg även till så att bitmönstret animeras hälften så fort som texten scrollar). Se videon nedan.
    #include <stdio.h> #include <string.h> #include <stdlib.h> // för abs() #include <unistd.h> //(eller <windows.h>) ... void printSpaces(int n) { for(int i=0; i<n; i++) printf(" "); } int main() { unsigned short a = 1, b = 0x8000; int scrollWidth = 50; int loopIter = 0; while (1) { clearscreen(); // Hantera horisontell bouncing scroll // låt scrolliter loopa runt från 0 t.om. 99; int scrollIter = loopIter % (2*scrollWidth); int pos = 35 + scrollWidth - abs(scrollWidth - scrollIter); printSpaces(pos); // hantera bit-scroll if((loopIter % 2) != 0) { // uppdatera bitmönstret endast varannan iteration. a = (a <= 1) ? 0x8000 : a >> 1; b = (b == 0x8000) ? 0x0001 : b << 1; } printBinary(a | b); printf("\n"); usleep(33333); loopIter++; } return 0; }

    Uppg. 4
    1. Om du tittar på lösningen ovan så ser du principen för att lägga till sinsemellan oberoende händelseförlopp. Gör nu så att texten även studsar i y-led. Se videon nedan:
      ... #include <math.h> //för fabs(), sin() ... // Hantera horisontell bouncing scroll ... // hantera bit-scroll ... // Hantera vertikal studs float t = (loopIter % 41) / 40.0f; // t: 0 -> 40 float y = 10.3f*( fabs(2*(3*t*t - 2*t*t*t) -1)); // ser bättre ut än en t^2-kurva for(int i=0; i<y; i++) printf("\n");
    Uppg. 4
    1. Bitwise OR används typiskt för att ett-ställa bitar.
    Varför, när, vad/hur. Lägg till studsande scroll. Titta gärna på lösningsförslaget för föregående uppgift om du inte redan gjort det, så inser du hur du lätt lägger till detta. Vi har nu tittat på några enkla programexempel. Globala variabler bör man använda sparsmakat. Skapa en struct som håller de globala variablerna. Skapa en instans av structen. Skapa framebuffer. Skapa en ny struct som innehåller { en array av din struct samt en variabel för hur lång den är }. Initiera en instans av den nya struct:en och se till att din programkod loopar över samtliga instanser.
    YouTube-filmer
    Scrolla in/ut en text från vänster/höger.
    ... Tips: skapa en funktion som rensar skärmen.
    Rita en cirkel med ascii-tecken.
    Rita en sinuskurva.
    Rita en cirkel/sinuskurva som rör sig.
    För flera separata objekt - lämpligt med en s k framebuffer
    ... Skapa flera objekt som rör sig i fönstret.
    Skapa fyrverkeri.
    Skapa objekt som studsar mot skärmkanten och mot varandra.
    ... Tips - game loop. Motsvarar t ex en event-loop eller realtidsloop i andra typer av problem.
    Lägg till att olika objekt skapas och tas bort vid olika tidpunkter, t ex 0,5 och 10s efter start. States: Skapa animerade objekt (som upprepande växlar ascii-tecken).
    User input - gå igenom getch, scanf, samt känna av keystates.
    - Keystates - motsvarighet finns för resp operativsystem, men ingår ej i C' standarsystembibliotek.
    - Windows: GetAsyncKeyState()
    - Mac: CGEventSourceKeyState(), svårt att accessa från C utan att också använda C++ eller Objective C.
    - Man kan använda färdiga libraries som SDL eller wxWidgets som båda har C-interface.

    fönsterhanteringssystem - score i ett fönster, action i ett annat.
    matematiska funktioner, fraktaler, blixtar, Förklara dubbelbuffring
    På labdatorn MD407 kommer ni att kommunicera direkt med grafikdisplayen. På operativsystem tillåts detta typiskt inte (t ex Windows, MacOS, Linux), utan man måste accessa funktioner i operativsystemet. Eftersom funktionerna varierar mellan operativsystem så kommunicerar man typiskt hellre med API:er (som abstraherar bort OS-beroendet), som kommunicerar med operativsystemet, som kommunicerar med drivrutiner som kommunicerar med hårdvaran. Vi har valt SDL som API.

    A Sample C Program

    This is your first C program. Paste this example into a plain text editor and name the file test1.c
     
    test1.c
    #include <stdio.h> main () { printf ("I'm a C program\n"); }
    The #include <stdio.h> statement includes the C code that deals with input and output. We need this for the printf function.

    The next line defines the main function that all C programs have.

    Finally, I call the printf function with some text to display on the command line. The \n inserts a new line after the text.

    Use GCC to Compile

    Open Terminal and change to the directory that contains the test.c file. Enter the command gcc test1.c -o test1. To run the program, enter ./test1 on the command line.

    If see errors, go back and make sure you pasted the sample source code correctly.
     
    host% gcc test1.c -o test1 host% ./test1 I'm a C program host%
    The -o parameter for gcc sets the name of the program file. If you leave this out, the program will just be called "a.out".

    About the Compiler

    You need to use a compiler to convert your C source code into a binary program before you can run it. A compiled program often runs much faster than a script. Much of Mac OS X is written in C.

    In this tutorial, we're using gcc on the command line to compile, but Xcode provides a graphical front end to the same tools.
     

    Portability

    A C program can usually only run on the same type of computer it was compiled on. If you compile your C source on Mac OS X, you can't run the program on something like Linux. You have to recompile the same code on Linux.

    In more complex C programs, you have to change the code for each platform you compile for. This is called porting the code.

    C Differences

    It's easier to learn C if you can compare it to a language you already know. Below is a list of concepts that are new to you if you're coming from a scripting language like PHP or Perl:
     
    Basic C Concepts
    compiler creates useable programs from C source
    typed variables kind of data that a variable contains
    typed functions the kind of data returned from a function
    header files (.h) declare functions and variables in a separate file
    structs groups of related values
    enums lists of predefined values
    pointers aliases to other variables

    These are all pretty simple concepts. The C language is not that complicated, it's just that all the different pieces can be put together in a way that makes things seem confusing.

    Without pointers, C would look almost identical to PHP. Pointers are tricky conceptually, but Cocoa abstracts you from the most of the those details for basic programming.

    You're not going to learn about pointers in this tutorial. The focus here is getting you up and running quickly. You can go back and revisit pointers when you want to get into more serious programming.

    Typed Variables

    In scripting languages, you can use variables freely. You're allowed to change the contents of a variable from an integer number, to a decimal number, and finally to text:
     
    $variable = 2; $variable = 1.618; $variable = 'A';
    In C, the rules are more strict. You have to declare the type of data a variable will hold, and the type can't change later. Here's the C equivalent of the snippet above:
     
    int variable1 = 2; float variable2 = 1.618; char variable3 = 'A';
    Notice that I had to define three separate variables in the C version, one for each type of data. You only have to declare the variable type once, though:
     
    float variable2 = 1.618; variable2 = 3.921; variable2 = 4.212;

    Available Types

    For our purposes, these are the built-in C types you need to know about:
     
    Type Description Examples
    int integer numbers, including negatives 0, 78, -1400
    unsigned int integer numbers (no negatives) 0, 46, 900
    float floating point decimal numbers 0.0, 1.618, -1.4
    char single text character or symbol 'a', 'D', '?'

    Although you won't see them as often, be aware of the double type, which is a version of float that can hold larger decimal point numbers, and long, which is a larger version of int.

    C also allows you to create your own variable types.

    Typed Functions

    In C, you have to declare the type of data you plan to return from a function. The return type can be any C variable type, and is placed to the left of the function name.
     
    int numberOfPeople () { return 3; } float dollarsAndCents () { return 10.33; } char firstLetter () { return 'A'; }
    You can also specify the return type as void. For now, you can just think of this as saying that no value will be returned:
     
    void printHello () { printf ("Hello\n"); }

    Types for Parameters

    It's also necessary to define types for values passed into a function. Unlike scripting languages, though, you can't set default values.
     
    int difference (int value1, int value2) { return value1 - value2; } float changeDue (float amountPaid, float costOfItem) { return amountPaid - costOfItem; }

    Declaring Functions

    In C, a function has to be declared before any other code can call it. You can put all the functions before main(), but this quickly becomes a lot of work.

    The solution is a function prototype. It looks like a function definition, but it doesn't have curly braces and ends in a semicolon:
     
    int difference ( int value1, int value2 ); float changeDue ( float amountPaid, float costOfItem );
    In a function prototype, you specify the type of data to return, the function name, and a type for each value passed into the function.

    Here's an example. Paste the contents into a file called test2.c
     
    test2.c
    #include <stdio.h> int sum ( int x, int y ); main () { int theSum = sum (10, 11); printf ( "Sum: %i\n", theSum ); } int sum ( int x, int y ) { return x + y; }
    I include stdio.h so I can use printf. After that, I declare the sum function using a prototype. Inside main, I call the sum function, and store the result in a variable called theSum.

    Because the sum function specifies a return type of int, I need to declare theSum as an int too. The variable type should match the function return type.

    I display the contents of theSum using printf. At the bottom is the sum function itself, which just adds the two number together. This is called the function implementation.
     

    Compile the Example

    Open Terminal and change to the directory which contains test2.c. Then, type "gcc test2.c -o test2" to compile.

    Type "./test2" to run the program.
     
    host% gcc test2.c -o test2 host% ./test2 Sum: 21 host%

    Format Strings

    You might be wondering what that %i in the above example means. In some scripting languages like PHP, you can freely embed variables inside of double quotes:
     
    $var1 = 3; $var2 = 8; print ("First value: $var1 second value: $var2");
    In C, you can't embed variables directly inside text. You have to use a format string with markers for the variables:
     
    int var1 = 3; int var2 = 8; printf ("First value: %i second value: %i", var1, var2);
    Format Markers
    int %i / %d
    unsigned int %u
    float %f
    char %c
    The format string is everything inside the double quotes. Put a % marker anywhere you want the contents of a variable to appear.

    The letter you put next to the percent symbol depends on the type of value you want to display. In this case, I use %i because I have int variables.

    The format string is followed by a comma, and the name of one variable for each marker you used. It's critical that the variable names come after the closing quote and a single comma.

    Cocoa programs use NSLog instead of printf, but the formatting rules are almost identical.

    Type Casting

    Sometimes you need to convert a variable from one type to another. For example, you might have a float that you need to use in a function that requires an int.

    Sometimes you can just use an int variable in place of a float without any errors, but it's better to do the conversion yourself. This conversion is called casting.
     
    int multiply (int x, int y) { return x * y; } int trips = 6; float distance = 4.874; int approxDistance = (int)distance; int total = multiply ( trips, approxDistance );
    So casting is basically putting the desired type in parantheses, just to the left of the value.

    The results of casting vary on what you're converting to and from. Converting a float to an int will simply chop off the digits after the decimal point. Casting does not round numbers up.

    Casting temporarily converts the contents only, not the variable itself. In the above example, distance is a still a float variable after the cast, so you have to use the cast again every time you want it treated as an int.
     

    More Casting Examples

    Here's a way to call the function without using the approxDistance variable. I can just do the cast inside the function call instead:
     
    int result = multiply (trips, (int)distance);
    Sometimes you need to convert the data returned from a function into a different type. You use the same form for that:
     
    int multiply (int x, int y) { return x * y; } float result; result = (float) multiply (3, 6);
    From the function definition, I can see that multiply returns an int. I want to store the result in a float, so when I call the function, I use a (float) cast before the function name. The result variable will have a value of 18.0.

    Header Files

    Since you need to declare all functions in C before you can use them, it would be helpful to group related function declarations and manage them in a single place. This is what header files do.

    Header files are vital in large projects because they give you an overview of source code without having to look through every line of code.

    Create a Header File

    Here's a sample header file. Copy the contents into a file named math_functions.h
     
    math_functions.h
    int sum (int x, int y); float average (float x, float y, float z);
    Here are the function implementations. Paste the contents into a file called math_functions.c
     
    math_functions.c
    int sum (int x, int y) { return (x + y); } float average (float x, float y, float z) { return (x + y + z) / 3; }
    In the average function, I separated the addition from the division using parenthesis. If I didn't, only the last number would be divided. It's always good practice to group all the values used in a single math operation.

    Using Header Files

    Here's a program that uses the functions declared in the header file. Copy the contents into a file called test3.c
     
    test3.c
    #include <stdio.h> #include "math_functions.h" main () { int theSum = sum (8, 12); float theAverage = average (16.9, 7.86, 3.4); printf ("the sum is: %i ", theSum); printf ("and the average is: %f \n", theAverage); printf ("average casted to an int is: %i \n", (int)theAverage); }
    After the standard stdio.h, I include math_functions.h. This allows the program call the sum and average functions.

    Inside main, I call the sum function and store the result in an int variable. Then I call the average function and store the result in a float variable.

    The program uses printf three times. Once using the %i symbol for the int variable theSum, once using %f for the float variable theAverage, and once with %i by casting theAverage to int.

    The stdio.h include is in angle brackets because it's from a C library (more on that later). The math_functions.h file is specific to this program, so it's in double quotes.

    Compile the Example

    You should now have three files, all in the same directory:

    math_functions.h - math function declarations
    math_functions.c - math function implementations
    test3.c - the actual program

    Switch back to Terminal, and change to the directory which contains the three files. To compile the program, type "gcc test3.c math_functions.c -o test3".

    Run the program by typing "./test3"
     
    host% gcc test3.c math_functions.c -o test3 host% ./test3 the sum is: 20 and the average is: 9.386666 average casted to an int is: 9 host%
    This time we gave gcc two input files: test3.c and math_functions.c. The gcc command combine the contents the two .c files into a single program.

    I don't need to list the header file math_functions.h on the command line. I just use the #include statement instead.

    Structs

    Structs are structured groups of variables. Here's an example of a struct designed to store song information.
     
    typedef struct { int lengthInSeconds; int yearRecorded; } Song;
    Struct Diagram It looks like I'm declaring two int variables, but I'm actually creating a new type of variable. The typedef statement assigns a name to the struct. In this case, the name is Song.

    Every Song variable I declare will hold two values: lengthInSeconds and yearRecorded. In this case, both fields hold int values, but a field in a struct can be any type (even another struct).

    Once you define a struct, you can use it the same way you'd use int, float or char. You can make as many Song variables as you want, each with their own length and year.

    You assign a value to field in a struct using the dot syntax:
     
    Song song1; song1.lengthInSeconds = 213; song1.yearRecorded = 1994; Song song2; song2.lengthInSeconds = 248; song2.yearRecorded = 1998;
    I created a variable called song1 and gave it a type of Song. I then set the length and year using the dot syntax. The variable song2 is also a Song, but it has different values for its fields.

    Structs in Functions

    Functions can specify structs as input or output. These function declarations and the struct itself can be put in a header file.

    Paste the following code into a file called song.h
     
    song.h
    typedef struct { int lengthInSeconds; int yearRecorded; } Song; Song make_song (int seconds, int year); void display_song (Song theSong);
    In this header file, I defined the Song struct. I also declared two functions, make_song and display_song. Now we need to create the method implementations.

    Paste the following code into a file called song.c
     
    song.c
    #include <stdio.h> #include "song.h" Song make_song (int seconds, int year) { Song newSong; newSong.lengthInSeconds = seconds; newSong.yearRecorded = year; display_song (newSong); return newSong; } void display_song (Song theSong) { printf ("the song is %i seconds long ", theSong.lengthInSeconds); printf ("and was made in %i\n", theSong.yearRecorded); }
    The make_song function takes two int values and returns a Song struct. The display_song function takes any Song struct as input, and displays its values. Note that the make_song function calls display_song when a new Song is created.

    I had to include song.h in song.c because the functions use the Song type. You have to include the song.h header in any file which uses Song in any way.

    Structs in Use

    Now we need a program that uses the song.h and song.c files. Paste the following code into a file called test4.c
     
    test4.c
    #include <stdio.h> #include "song.h" main () { Song firstSong = make_song (210, 2004); Song secondSong = make_song (256, 1992); Song thirdSong = { 223, 1997 }; display_song ( thirdSong ); Song fourthSong = { 199, 2003 }; }
    In this program, I create two Songs using the make_song function. Rather than using make_song for the third song, I use a special syntax that involves putting comma-separated values between curly brackets.

    Note that when I don't call make_song to create a song, the display_song function doesn't get automatically called. So on the next line, I call it manually. The fourth song is created in the same way, but since I never call display_song for it, it's never displayed in Terminal.

    It's better to use a function to create a new struct instance so you can control the process more precisely. In this case, I can automatically display information about each Song that's created.

    Compile the Example

    Now that you have song.h, song.c and test4.c, you can compile them and see the output.

    Switch to Terminal, change to the directory that contains the three files, and enter the command "gcc test4.c song.c -o test4".

    Type "./test4" to run the program.
     
    host% gcc test4.c song.c -o test4 host% ./test4 the song is 210 seconds long and was made in 2004 the song is 256 seconds long and was made in 1992 the song is 223 seconds long and was made in 1997 host%

    Constants

    The value of a variable can change as a program runs. By contrast, a constant is assigned a value once in the declaration, and that value can't change until the program is restarted.
     
    const float goldenRatio = 1.618; const int daysInWeek = 7;
    You can have a constant version of any of the standard C variable types. The only trick is that you have to supply a value when you create the variable.

    Enums

    We could spend a lot of time on enums, but in the spirit of focusing on the relevance to Cocoa, we're going to make this really, really simple.

    Here's an enum from Cocoa's NSString class which defines text search options:
     
    enum { NSCaseInsensitiveSearch = 1, NSLiteralSearch = 2, NSBackwardsSearch = 4, NSAnchoredSearch = 8, NSNumericSearch = 64 };
    Apple uses enums in Cocoa to group a series of related constants. This is often used as a way to set a "mode" for a particular function. The values of the constants don't really matter, just the name.

    Here's an example of an enum in use. Don't worry about understanding what this code does, just focus on how the enum is being used:
     
    [string compare:anotherString options:NSCaseInsensitiveSearch]; [string compare:anotherString options:NSLiteralSearch];

    Libraries

    The stdio.h file included in every example is a header file for part of the standard C library. A library is a collection of reusable code. It can define things like functions, structs, constants and enums.

    There are thousands of third party C libraries available. Some are free and come with .c source files. Others require a fee and provide header files and documentation but no source code.

    Library Diagram

    To use a third party library, you have to compile and "link" to it. For example, if I wanted to write a C program that could talk to a MySQL database, I could link to the libmysql library and include its headers in my program.

    When you use code created by others, it's important to know what the terms of use are. Some licenses, for example, may require you that share all of the source code for your application.

    If you're feeling adventurous, take a look in /usr/include, which is a standard place for C header files.

    Final Example

    Here's a final program that uses almost all of the concepts from this tutorial. You need to have the following files from before:

    math_functions.h - math function declaration
    math_functions.c - math function implementations
    song.h - Song struct and related function declarations
    song.c - implementation of the Song functions

    Paste the contents of this example into a file called final.c
     
    final.c
    #include <stdio.h> #include "math_functions.h" #include "song.h" main () { const int numberOfSongs = 3; printf ("total number of songs will be: %i\n", numberOfSongs); int i; for (i = 0; i < numberOfSongs; i++) { printf ("loop trip %i ", i); } printf ("\n"); Song song1 = make_song (223, 1998); Song song2 = make_song (303, 2004); Song song3 = { 315, 1992 }; display_song (song3); int combinedLength = sum (song1.lengthInSeconds, song2.lengthInSeconds); printf ("combined length of song1 and song2 is %i\n", combinedLength); float x = (float) song1.lengthInSeconds; float y = (float) song2.lengthInSeconds; float z = (float) song3.lengthInSeconds; float averageLength; averageLength = average (x, y, z); printf ("average length is: %f as a float ", averageLength); printf ("and %i as an int\n", (int) averageLength); }
    This example may look complex, but it's just repetition of the same concepts. If there's anything here that doesn't make sense, go back and read the previous sections.
     

    Compile the Example

    Make sure you have the four files previously mentioned plus final.c, all in the same directory. Switch to Terminal and change to the directory which contains the files. Enter the command gcc final.c song.c math_functions.c -o final.

    Type ./final to run the program
     
    host% gcc final.c song.c math_functions.c -o final host% ./final total number of songs will be: 3 loop trip 0 loop trip 1 loop trip 2 the song is 223 seconds long and was made in 1998 the song is 303 seconds long and was made in 2004 the song is 315 seconds long and was made in 1992 combined length of song1 and song2 is 526 average length is: 280.333344 as a float and 280 as an int

    Wrap Up

    We've covered a lot of ground in a relatively small amount of space. If you understand the code in the final example, you should be ready to start learning the basics of Objective-C and Cocoa.

    As you do more programming, you'll need to look into some more advanced topics such as pointers and memory management. Some starting points for those are listed below.

    Theocacao: C Memory and Arrays
    Theocacao: C Pointers and Dynamic Memory

    Love it? Have suggestions? Send feedback on this tutorial.
     
    Copyright © 2004-2015 Scott Stevenson
    Made with TextMate
    Feedback Form
    Cocoa Dev Central is a servicemark of Tree House Ideas
    Site design © 2004-2015 Scott Stevenson