C: Day 2 In Class Exercises

Today we have several problems for you to tackle. Parts should look and feel familiar from Day 1, though we will add more features as we go. Those problems are modeled after exercises in Python and, thus, allow you to learn how data structures compare when transitioning from python to C (and later C++).

Problem 1: Reviewing the stress transformation problem

Navigate to /code/c/ExerciseDay2/ex2-1/ to find another solution for the stress transformation problem. The main difference to Frank’s solution is that this one places functions in separate files, as well as adds a header file that contains the definition of the function without its implementation.

Compiling this version requires multiple steps:

gcc stresstransform.c -c
gcc exercise2-1.c stresstransform.o -lm

And you can run the executable as

./a.out

Imagine doing this for many more files, usually tens to hundreds. That would be painstaking and inefficient and very error prone. Software engineers developed several tools to simplify and automate the compile process. One of those tools is cmake, a member of the make family of tools. You find a configuration file names CMakeList.txt in the source folder. The configuration file is a plain text file, so you can and should check out how it is written:

1
2
3
4
5
6
7
cmake_minimum_required (VERSION 2.6)

project (Exercise2-1)

include_directories(${PROJECT_SOURCE_DIR})
add_executable(Exercise2-1 exercise2-1.c stresstransform.c)
target_link_libraries(Exercise2-1 m)

The compile process now becomes

1. a configuration step - done only once or every time you are adding a file to the project. Inside the source folder, execute

$ mkdir build
$ cd build
$ cmake ..

This will check your system for compilers and other development tool and create a Makefile in each source folder.

Note

Placing the compile files into a build folder makes cleanup easier: simply delete the entire build folder when done. It can be regenerated easily using the above procedure.

2. From now on, every time you make changes to any of the files within your project, simply type

$ make

to recompile all portions necessary and link all parts to one executable. That process remains exactly the same regardless of the number of files in your project. Give it a try and see how convenient this is especially for projects provided by somebody else.

Problem 2: Using structures

The implementation of StressTransform() was intentionally done a bit clumsy, just the way a beginner might write it. Your task in this exercise is to create a structure

typedef struct {
        double sigx;
        double sigy;
        double tau;
} STRESS ;

and modify the code from the previous exercise to utilize the much easier to read data structure provided by this struct. Use the code skeleton provided in /code/c/ExerciseDay2/ex2-2 to develop that code. The included CMakeList.txt shall be used to compile your code.

Note

Your modified StressTransform(...) will require a pointer to a STRESS type object. The way to achieve that in an efficient manner is to use a typedef struct {...} STRESS ;.

In addition, inside the function that receives the pointer to a structure, assigning a new value to entries in such a structure requires the syntax

void StressTransform(STRESS stressIn, STRESS *stressOut, ....) {
  ...
  stressIn->sigx = ... ;
}

This replaces the form

*sigx = ... ;

used for scalar-valued arguments.

A working solution is provided by the following set of files.

  • stresstransform.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <stdio.h>
#include <stdlib.h>
#include <math.h>

#include "stresstransform.h"

void StressTransform(STRESS s, STRESS *sp, double theta) {

	double pi = 4.0*atan(1.);
	double th = theta * pi/180.;

	double sn = sin(th);
	double cs = cos(th);

	sp->sigx = s.sigx*cs*cs + s.sigy*sn*sn + 2.*s.tau*sn*cs;
	sp->sigy = s.sigx*sn*sn + s.sigy*cs*cs - 2.*s.tau*sn*cs;
	sp->tau  = (s.sigy - s.sigx)*sn*cs + s.tau*(cs*cs - sn*sn);
}
  • stresstransform.h

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
#ifndef STRESSTRANSFORM_H
#define STRESSTRANSFORM_H

typedef struct {
	double sigx;
	double sigy;
	double tau;
} STRESS ;

void StressTransform(STRESS s, STRESS *sp, double theta);

#endif
  • exercise2-2.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
#include <stdio.h>
#include <stdlib.h>

#include "stresstransform.h"

int main(int argc, char **argv) {

	STRESS S0;
	STRESS Sp;

	S0.sigx = 12.0;
	S0.sigy = -5.5;
	S0.tau  =  3.5;

	StressTransform(S0, &Sp, 0.0);
	printf("sigx' = %12.6f\nsigy' = %12.6f\ntau'  = %12.6f\n\n", Sp.sigx, Sp.sigy, Sp.tau);

	StressTransform(S0, &Sp, 25.0);
	printf("sigx' = %12.6f\nsigy' = %12.6f\ntau'  = %12.6f\n\n", Sp.sigx, Sp.sigy, Sp.tau);
}

Problem 3: Writing data for use by other programs: CSV

While C is very powerful for numeric computations, it can be impractical to generate graphs or fancy images using the computed values. A more efficient way is to use C to do the analysis, write results to an easily readable file, and use specialized tools for the post-processing. One common and simple format is CSV (comma-separated-values), which van be read easily by MATLAB, python, or Excel.

Your task: modify the code given in /code/c/ExerciseDay2/ex2-3 to

1. Take one argument \(\Delta\theta\) in degrees after the name of the executable, defining the increment at which transformed stress values shall be written:

$ Exercise2-3 5.0

The format of the output shall be for one angle per line, organized as follows:

theta, sigma_x, sigma_y, tau_xy
...

Output shall commence until an angle of \(180^\circ\) has been reached or exceeded.

Once your code outputs the information, run it once more and save the results to a file names list.csv (make sure to add the spaces around the ‘>’)

$ Exercise2-3 5.0 > list.csv

Note

You may want to download the file list.csv to your local computer before trying the next step, for it will require access to your display. That file can be opened in Excel and plotted there. A more efficient way is to prepare some nice plotting code, such as the provided plotter.py. In the same folder where you placed list.csv run

Windows 10

>> python.exe plotter.py

MacOS or Linux

$ python3 plotter.py

Isn’t that nice?

A working solution is provided by the following set of files.

  • stresstransform.c and stresstransform.h are identical to the ones created for Problem 3.

  • exercise2-3.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <stdio.h>
#include <stdlib.h>
#include <math.h>    // need this for the constant pi = M_PI

#include "stresstransform.h"

int main(int argc, char **argv) {

	// get dth from the first argument.  This is given in degrees!

	double dth;

	if (argc>1) { dth = atof(argv[1]) ; }
	else { dth = 22.5; }

	// set the initial stress state

	STRESS S0;

	S0.sigx = 12.0;
	S0.sigy = -5.5;
	S0.tau  =  3.5;

	// define  target container for transformed stress

	STRESS Sp;

	// loop to compute transformed states

	for (double th=0.0; th <= 180.; th+=dth) {

	    StressTransform(S0, &Sp, th);
	    printf("%12.6f, %12.6f, %12.6f, %12.6f\n", th, Sp.sigx, Sp.sigy, Sp.tau);
	}
}

which creates a result like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
    0.000000,    12.000000,    -5.500000,     3.500000
    5.000000,    12.474836,    -5.974836,     1.927406
   10.000000,    12.669381,    -6.169381,     0.296248
   15.000000,    12.577722,    -6.077722,    -1.343911
   20.000000,    12.202646,    -5.702646,    -2.943236
   25.000000,    11.555547,    -5.055547,    -4.453132
   30.000000,    10.656089,    -4.156089,    -5.827722
   35.000000,     9.531600,    -3.031600,    -7.025240
   40.000000,     8.216249,    -1.716249,    -8.009299
   45.000000,     6.750000,    -0.250000,    -8.750000
   50.000000,     5.177406,     1.322594,    -9.224836
   55.000000,     3.546248,     2.953752,    -9.419381
   60.000000,     1.906089,     4.593911,    -9.327722
   65.000000,     0.306764,     6.193236,    -8.952646
   70.000000,    -1.203132,     7.703132,    -8.305547
   75.000000,    -2.577722,     9.077722,    -7.406089
   80.000000,    -3.775240,    10.275240,    -6.281600
   85.000000,    -4.759299,    11.259299,    -4.966249
   90.000000,    -5.500000,    12.000000,    -3.500000
   95.000000,    -5.974836,    12.474836,    -1.927406
  100.000000,    -6.169381,    12.669381,    -0.296248
  105.000000,    -6.077722,    12.577722,     1.343911
  110.000000,    -5.702646,    12.202646,     2.943236
  115.000000,    -5.055547,    11.555547,     4.453132
  120.000000,    -4.156089,    10.656089,     5.827722
  125.000000,    -3.031600,     9.531600,     7.025240
  130.000000,    -1.716249,     8.216249,     8.009299
  135.000000,    -0.250000,     6.750000,     8.750000
  140.000000,     1.322594,     5.177406,     9.224836
  145.000000,     2.953752,     3.546248,     9.419381
  150.000000,     4.593911,     1.906089,     9.327722
  155.000000,     6.193236,     0.306764,     8.952646
  160.000000,     7.703132,    -1.203132,     8.305547
  165.000000,     9.077722,    -2.577722,     7.406089
  170.000000,    10.275240,    -3.775240,     6.281600
  175.000000,    11.259299,    -4.759299,     4.966249
  180.000000,    12.000000,    -5.500000,     3.500000

The plotter.py script that displays the data as Mohr’s circle is given here to show how simple it is to generate nice plots automagically.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
import sys
import matplotlib.pyplot as plt
import pandas as pd

try:
    df = pd.read_csv('list.csv', header=None)
except:
    print("Could not find file: 'list.csv'")
    sys.exit(-1)

df.plot(x=1, y=3, label="Mohr's circle")

plt.xlabel('$\sigma_x$')
plt.ylabel('$\\tau_{xy}$')
plt.grid(True)
plt.axis('equal')
plt.show()
../_images/Figure_2-3.png

Mohr’s circle created from list.csv and plotter.py

Problem 4: Writing to a binary file

Modify the code generated in the previous exercise to write a binary file names mohrcircle.dta instead of the formatted ASCII data. The data shall be exported in clocks composed of double theta followed by a block of STRESS (or the three components of stress as double).

You may be working of your code or use the provided code skeleton in /code/c/ExerciseDay2/ex2-4.

This time, your code should be totally silent on execution. The only sign of success will be the creation of the data file. For the next steps, run your program with the following parameters:

$ Exercise2-4 5.0

Note

How large do you expect the binary file to be? Discuss, predicts, and check using

$ ls -l mohrcircle.dta

You should be able to predict the exact number (to the byte!).

Note

This problem comes with validation code, something worth developing every time you are working on software that will be modified over an extended period of time and/or by multiple people.

The validation consists of (1) a C code parse.c which reads the binary file and outputs its contents to a CSV file, and (2) a shell script validate.sh that attempts to run the validation code and compares the output generated from your binary file to an output generated by a correct code.

Run the validation script as

$ sh ./validate.sh

and check its feedback. (That script may not run on all platforms.)

A working solution is provided by the following set of files.

  • stresstransform.c and stresstransform.h are identical to the ones created for Problem 3.

  • exercise2-4.c

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <stdio.h>
#include <stdlib.h>

#include "stresstransform.h"

/* ************************************************************************
 * 
 *      writing to a binary file
 *
 * ************************************************************************/

int main(int argc, char **argv) {

	// get dth from the first argument.  This is given in degrees!

	double dth;

	if (argc>1) {
		dth = atof(argv[1]) ;
	}
	else {
		dth = 22.5;
	}

	// open the file for writing 

	FILE *f_ptr;

	f_ptr = fopen("mohrcircle.dta","wb");

	// define a data structure for a single entry in the file

	struct RESULT {
		double angle;
		STRESS thestress;
	} result;

	// set the initial stress state

	STRESS S0;

	S0.sigx = 12.0;
	S0.sigy = -5.5;
	S0.tau  =  3.5;

	// define  target container for transformed stress

	STRESS Sp;

	// loop to compute transformed states

	for (double th=0.0; th <= 180.; th+=dth) {

	    StressTransform(S0, &Sp, th);

	    //
	    // note:
	    //
	    // ... result is a structure. fwrite needs a reference to that structure
	    // ... sizeof(result) returns the number of bytes occupied by the ONE copy of result
	    // ... 1 is the number of blocks to write
	    // ... f_ptr is a pointer to the FILE handle

            result.angle = th;
	    result.thestress = Sp;

	    fwrite(&result, sizeof(result), 1, f_ptr);

	}

	// done writing data -- close the file
	fclose(f_ptr);
}


Executing the generated executable as

$ Exercise2-4 5.0

creates a binary file mohrcircle.dta which has a size of (STRESS has 3 double entires)

(37 blocks) of (1 double + 1 STRESS) * ( 8 bytes/double ) = 1184 bytes

Note

If your system has gcc installed, you may run the validation script to check your solution. Running it as

$ sh ./validate.sh

should generate the friendly answer:

**
** Your code passed with flying colors!
**

Note

Binary files are not readable by traditional ASCII editors (text editors). Doings so, usually shows some unintelligible scramble of characters, sometimes leaving your terminal in an unusable state.

However, you may view binary files using a hex-dump utility. That approach may help you understand and recover the structure of a binary file (though it still requires some practice and skill and luck). You may try such a tool on your binary file using

$ xxd mohrcircle.dta | less

where the | less pipes the output in a pager utility that allows you to search the output, jump pages forward and backward, or move to any specific line. Press q to exit this utility.

You should get something like this output:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
00000000: 0000 0000 0000 0000 0000 0000 0000 2840  ..............(@
00000010: 0000 0000 0000 16c0 0000 0000 0000 0c40  ...............@
00000020: 0000 0000 0000 1440 ae3b bbc3 1df3 2840  .......@.;....(@
00000030: 5d77 7687 3be6 17c0 cc6a 053c a7d6 fe3f  ]wv.;....j.<...?
00000040: 0000 0000 0000 2440 fa09 0419 b956 2940  ......$@.....V)@
00000050: f413 0832 72ad 18c0 2053 87d4 b9f5 d23f  ...2r... S.....?
00000060: 0000 0000 0000 2e40 ed49 1037 cb27 2940  .......@.I.7.')@
00000070: da93 206e 964f 18c0 d479 65e9 a880 f5bf  .. n.O...ye.....
00000080: 0000 0000 0000 3440 b684 0627 c167 2840  ......4@...'.g(@
00000090: 6c09 0d4e 82cf 16c0 2b0a 6e55 bf8b 07c0  l..N....+.nU....
000000a0: 0000 0000 0000 3940 a435 96ac 701c 2740  ......9@.5..p.'@
000000b0: 486b 2c59 e138 14c0 1376 18e6 01d0 11c0  Hk,Y.8...v......
000000c0: 0000 0000 0000 3e40 c550 d3e2 ea4f 2540  ......>@.P...O%@
000000d0: 8ca1 a6c5 d59f 10c0 d893 206e 964f 17c0  .......... n.O..
000000e0: 0000 0000 0080 4140 23b9 5fee 2d10 2340  ......A@#._.-.#@
000000f0: 8fe4 7eb9 b740 08c0 b8ae 0b7f d819 1cc0  ..~..@..........
00000100: 0000 0000 0000 4440 f2ce f725 b86e 2040  ......D@...%.n @
00000110: 9077 be2f c175 fbbf 0bfb f1dd c204 20c0  .w./.u........ .
00000120: 0000 0000 0080 4640 0100 0000 0000 1b40  ......F@.......@
00000130: 1000 0000 0000 d0bf 0000 0000 0080 21c0  ..............!.
00000140: 0000 0000 0000 4940 b45a 01cf a9b5 1440  ......I@.Z.....@
00000150: 3695 fac3 5829 f53f ae3b bbc3 1d73 22c0  6...X).?.;...s".
00000160: 0000 0000 0080 4b40 69ea 903a b75e 0c40  ......K@i..:.^.@
00000170: 9415 6fc5 48a1 0740 f909 0419 b9d6 22c0  ..o.H..@......".
00000180: 0000 0000 0000 4e40 3686 9a16 577f fe3f  ......N@6...W..?
00000190: 745e 593a 2a60 1240 ee49 1037 cba7 22c0  t^Y:*`.@.I.7..".
000001a0: 0000 0000 0040 5040 98ae 8f54 05a2 d33f  .....@P@...T...?
000001b0: 1505 b7aa dfc5 1840 b684 0627 c1e7 21c0  .......@...'..!.
000001c0: 0000 0000 0080 5140 40d8 6198 0740 f3bf  ......Q@@.a..@..
000001d0: 0f76 18e6 01d0 1e40 a435 96ac 709c 20c0  .v.....@.5..p. .
000001e0: 0000 0000 00c0 5240 b427 41dc 2c9f 04c0  ......R@.'A.,...
000001f0: ed49 1037 cb27 2240 8aa1 a6c5 d59f 1dc0  .I.7.'"@........
00000200: 0000 0000 0000 5440 6e5d 17fe b033 0ec0  ......T@n]...3..
00000210: 5cd7 853f ec8c 2440 4872 bfdc 5b20 19c0  \..?..$@Hr..[ ..
00000220: 0000 0000 0040 5540 17f6 e3bb 8509 13c0  .....@U@........
00000230: 0cfb f1dd c284 2640 e19d ef4b 70dd 13c0  ......&@...Kp...
00000240: 0000 0000 0080 5640 0000 0000 0000 16c0  ......V@........
00000250: 0000 0000 0000 2840 0200 0000 0000 0cc0  ......(@........
00000260: 0000 0000 00c0 5740 5d77 7687 3be6 17c0  ......W@]wv.;...
00000270: af3b bbc3 1df3 2840 c66a 053c a7d6 febf  .;....(@.j.<....
00000280: 0000 0000 0000 5940 f313 0832 72ad 18c0  ......Y@...2r...
00000290: fa09 0419 b956 2940 2853 87d4 b9f5 d2bf  .....V)@(S......
000002a0: 0000 0000 0040 5a40 da93 206e 964f 18c0  .....@Z@.. n.O..
000002b0: ed49 1037 cb27 2940 d879 65e9 a880 f53f  .I.7.')@.ye....?
000002c0: 0000 0000 0080 5b40 6c09 0d4e 82cf 16c0  ......[@l..N....
000002d0: b684 0627 c167 2840 2b0a 6e55 bf8b 0740  ...'.g(@+.nU...@
000002e0: 0000 0000 00c0 5c40 4a6b 2c59 e138 14c0  ......\@Jk,Y.8..
000002f0: a535 96ac 701c 2740 1076 18e6 01d0 1140  .5..p.'@.v.....@
00000300: 0000 0000 0000 5e40 8ea1 a6c5 d59f 10c0  ......^@........
00000310: c850 d3e2 ea4f 2540 d693 206e 964f 1740  .P...O%@.. n.O.@
00000320: 0000 0000 0040 5f40 98e4 7eb9 b740 08c0  .....@_@..~..@..
00000330: 27b9 5fee 2d10 2340 b4ae 0b7f d819 1c40  '._.-.#@.......@
00000340: 0000 0000 0040 6040 9277 be2f c175 fbbf  .....@`@.w./.u..
00000350: f2ce f725 b86e 2040 0afb f1dd c204 2040  ...%.n @...... @
00000360: 0000 0000 00e0 6040 1000 0000 0000 d0bf  ......`@........
00000370: 0100 0000 0000 1b40 0000 0000 0080 2140  .......@......!@
00000380: 0000 0000 0080 6140 2895 fac3 5829 f53f  ......a@(...X).?
00000390: b75a 01cf a9b5 1440 ae3b bbc3 1d73 2240  .Z.....@.;...s"@
000003a0: 0000 0000 0020 6240 8c15 6fc5 48a1 0740  ..... b@..o.H..@
000003b0: 72ea 903a b75e 0c40 fb09 0419 b9d6 2240  r..:.^.@......"@
000003c0: 0000 0000 00c0 6240 765e 593a 2a60 1240  ......b@v^Y:*`.@
000003d0: 2286 9a16 577f fe3f ed49 1037 cba7 2240  "...W..?.I.7.."@
000003e0: 0000 0000 0060 6340 1405 b7aa dfc5 1840  .....`c@.......@
000003f0: a8ae 8f54 05a2 d33f b684 0627 c1e7 2140  ...T...?...'..!@
00000400: 0000 0000 0000 6440 0e76 18e6 01d0 1e40  ......d@.v.....@
00000410: 3cd8 6198 0740 f3bf a435 96ac 709c 2040  <.a..@...5..p. @
00000420: 0000 0000 00a0 6440 eb49 1037 cb27 2240  ......d@.I.7.'"@
00000430: ac27 41dc 2c9f 04c0 8ea1 a6c5 d59f 1d40  .'A.,..........@
00000440: 0000 0000 0040 6540 5ed7 853f ec8c 2440  .....@e@^..?..$@
00000450: 745d 17fe b033 0ec0 4572 bfdc 5b20 1940  t]...3..Er..[ .@
00000460: 0000 0000 00e0 6540 0afb f1dd c284 2640  ......e@......&@
00000470: 11f6 e3bb 8509 13c0 eb9d ef4b 70dd 1340  ...........Kp..@
00000480: 0000 0000 0080 6640 0000 0000 0000 2840  ......f@......(@
00000490: ffff ffff ffff 15c0 0500 0000 0000 0c40  ...............@

Problem 5: Reading From a binary file

Reading of data from files and placing them into containers such as Vectors is easy if you know the size of the data you are reading. If this is unknown the problem becomes more tricky. The solution presented on slide 22 worked for a small number of nputs, but failed with a segmentation fault for larger problems. You are to fix the problem. A copy of the offending files has been placed in the directory ex2-5 along with two files. The program can handle the first small.txt, it will fail with the second big.txt. Can you make the program work. The solution will test your understanding of file I/O, memory management and pointers.

Note

No cmake or Makefile has been provided. You can compile the file with icc or whatever compiler you are using. The program takes a single input, the file to read. To compile and test the program, issue the following at the terminal prompt.

icc file2.c -o file2
./file2 small.txt
./file2 big.txt