Let’s explore some more of the weirdness of C.

Part 1: sizeof() is not how you get the length of an array

You don’t have to turn anything in for this part, but please do it. This stuff is important and I’ll expect you to know it in future labs/projects/lectures.

sizeof() is not how you get the length of an array. You cannot “get” the length of an array in C because arrays are not real.

There are two kinds of numerical types in C: integer types and floating-point types.

There are also signed integers (the default) or unsigned integers (which cannot represent negative numbers.) Java doesn’t have these unsigned integers!

Unlike Java, the sizes of the integer types in C are not fixed. They depend on the platform: what CPU and operating system you’re using.

sizeof() is not how you get the length of an array. You cannot “get” the length of an array in C because arrays are not real.

You can find out the size of a type using the sizeof operator. This is NOT a function. It operates at compile time and gives you a constant value saying how many bytes something takes up.

The sizes of the integer types in C follow these inequalities:

sizeof(char) == 1
sizeof(char) <= sizeof(short) <= sizeof(int) <= sizeof(long) <= sizeof(long long)

char is an integer type in C. You can use a char whenever you need a one-byte value or an array of one-byte values. Text characters are really a special case of one-byte value.

Now for practice

Make a copy of lab1.c as a starting point, and name it sizeof.c.

Inside main, put this code:

The (int) in there is to cast the result of sizeof to an int. Otherwise, you’ll get annoying warnings.

	int x = 10;
	printf("sizeof(x) = %d\n", (int)sizeof(x));
	printf("sizeof(int) = %d\n", (int)sizeof(int));

Compile it like gcc -Wall -Werror --std=c99 -o sizeof sizeof.c

Run it like ./sizeof. What does it print?

Now extend it. Print the sizes of the following types:

Make notes of what these print.

Array variables are really weird

Okay: now to blow your mind some. Add this code.

	char a[10];
	int b[10];
	int* c = b;

Now print sizeof(a), sizeof(b), and sizeof(c), sizeof(&a), and sizeof(&b).

What the hell is going on?

Array variables are the ones declared with brackets. C treats them very strangely. They’re kind of pointers but kind of not.

When you use sizeof() on an array variable, it tells you how many bytes it takes up. It does NOT tell you the length, at least not directly.

But when you use sizeof() on a pointer, it gives you the size of the pointer. It never gives you the length of the array that the pointer points to.

sizeof() is not how you get the length of an array. You cannot “get” the length of an array in C because arrays are not real. sizeof() is not how you get the length of an array. You cannot “get” the length of an array in C because arrays are not real. sizeof() is not how you get the length of an array. You cannot “get” the length of an array in C because arrays are not real.

Finally: 32-bit machines

Thoth is a 64-bit machine. But you can compile a 32-bit executable by using the -m32 flag to gcc, like so: gcc -Wall -Werror --std=c99 -m32 -o sizeof sizeof.c

Now run ./sizeof. Which numbers changed? Why do you think that is?


Part 2: Console I/O

Make a new file, lab2.c. Here’s some code to get you started.

Feel free to reuse these functions in future labs/projects.

#include <stdio.h>
#include <string.h>
#include <ctype.h>

void get_line(char* buffer, int size) {
	fgets(buffer, size, stdin);
	int len = strlen(buffer);
	// this is a little more robust than what we saw in class.
	if(len != 0 && buffer[len - 1] == '\n')
		buffer[len - 1] = '\0';
}

// returns 1 if the two strings are equal, and 0 otherwise.
int streq(const char* a, const char* b) {
	return strcmp(a, b) == 0;
}

// returns 1 if the two strings are equal ignoring case, and 0 otherwise.
// so "earth" and "Earth" and "EARTH" will all be equal.
int streq_nocase(const char* a, const char* b) {
	// hohoho aren't I clever
	for(; *a && *b; a++, b++) if(tolower(*a) != tolower(*b)) return 0;
	return *a == 0 && *b == 0;
}

int main() {

	return 0;
}

What your program will do

This program will calculate how much the user weighs on various planets in our solar system. Here’s how your program will work:

  1. Ask them what planet they want to visit.
  2. If they typed exit, use break; to exit the loop.
  3. If they typed earth, call them silly or something.
  4. Otherwise,
    1. Get the scaled weight for that planet using your planet_to_weight function (see below).
    2. If it returned a value less than 0, that means it’s not a planet, so say so.
    3. Otherwise, tell them how much they’d weigh there.
      • "%.2f" would be a nice format.
  5. Go back to step 1.

Here’s how it looks when I interact with my program:

[thoth ~/private/cs0449/lab2]: ./lab2
Uh, how much do you weigh? 250
What planet do you wanna go to ('exit' to exit)? mars
You'd weigh 95.00 there.
What planet do you wanna go to ('exit' to exit)? JUPITER
You'd weigh 635.00 there.
What planet do you wanna go to ('exit' to exit)? pluto
That's not a planet.
What planet do you wanna go to ('exit' to exit)? earth
uh, you're already there, buddy
What planet do you wanna go to ('exit' to exit)? exit
[thoth ~/private/cs0449/lab2]:

Reading a number

First we’ll read a number, the user’s weight (sorry if that’s sensitive info…).

  1. Use the get_line function to ask the user for their weight.

     printf("How much do you weigh? ");
     char input[100];
     get_line(input, sizeof(input)); // notice the sizeof!
    

    We used sizeof(input) so we don’t have to repeat the 100. Also, that avoids mistakes if you change the size of the input array.

  2. Use sscanf to parse the number out of the string. You use it like this:

     int weight;
     sscanf(input, "%d", &weight); // DON'T FORGET THE & or it'll crash.
    

sscanf means string scan formatted. It’s like printf backwards. It can parse values out of a string.

How this works is by handing off a pointer to the weight variable to sscanf. Then it looks in the input string for an integer (the "%d" tells it to do that), and it puts that value into weight indirectly.

Test it out, see if it works. Never write a whole program at once. Compile early, compile often. Print out the weight variable to see if it parsed correctly.


Making a function

The const means “I only want to read the string from this variable, I promise I won’t change it.” We’ll talk about it later in the term.

Before main, make a function with this signature:

float weight_on_planet(const char* planet_name, int user_weight)

This function takes the name of a planet and a weight, and returns:

Here is a table of relative gravity strengths on the seven non-earth planets in our solar system:

Planet Gravity
Mercury 0.38
Venus 0.91
Mars 0.38
Jupiter 2.54
Saturn 1.08
Uranus 0.91
Neptune 1.19

Use streq_nocase to check which planet it is, e.g.

if(streq_nocase(planet, "mars")) {
	// it's mars.
}

The “case-insensitivity” of this function means they can type e.g. “mars”, “Mars”, “MARS” and it’ll all work the same way.

Remember, if the planet is not on this list, return -1.

TEST IT OUT. Call it from main with a few values and see what it returns. See if it behaves how you expect. Test, test, test. Testing your own code BEFORE you use it will save you so much trouble.


Loopydoop

Make an infinite loop. true is not a thing in C by default, so you can write while(1) to make an infinite loop.

In that loop, you’ll be reading a line of input from the user and then using streq_nocase to see what they typed in. You’ll have to check for "exit" and "earth" specifically. Then, if it’s neither of those, use weight_on_planet. Go look at the program description above!


Submission

Remember the instructions you learned last time? Replace lab1 with lab2.