Chapter 2¶
Exercise 2-1¶
main.c
#include <stdio.h>
#include <limits.h>
#include <float.h>
float uint2float(unsigned int n);
double uint2double(long unsigned int n);
int main()
{
printf("(In this machine 'char == signed char')\n");
printf(" %24s%24s\n", "standard header", "direct computation");
printf(" %24s%24s\n", "---------------", "------------------");
/* CHAR */
/* unsigned char */
printf("UCHAR_MAX: %24d", UCHAR_MAX);
printf("%24u\n", (unsigned char)~0);
/* signed char */
printf("SCHAR_MIN: %24d", SCHAR_MIN);
printf("%24d\n", (signed char)(1 << (sizeof(signed char)*8 - 1)));
printf("SCHAR_MAX: %24d", SCHAR_MAX);
printf("%24d\n", (signed char)~(1 << (sizeof(signed char)*8 - 1)));
/* char */
printf("CHAR_MIN: %24d", CHAR_MIN);
printf("%24d\n", (char)(1 << (sizeof(char)*8 - 1)));
printf("CHAR_MAX: %24d", CHAR_MAX);
printf("%24d\n", (char)~(1 << (sizeof(char)*8 - 1)));
putchar('\n');
/* INT */
/* unsigned short int */
printf("USHRT_MAX: %24d", USHRT_MAX);
printf("%24u\n", (unsigned short int)~0);
/* signed short int */
printf("SHRT_MIN: %24d", SHRT_MIN);
printf("%24d\n", (short int)(1 << (sizeof(short int)*8 - 1)));
printf("SHRT_MAX: %24d", SHRT_MAX);
printf("%24d\n", (short int)~(1 << (sizeof(short int)*8 - 1)));
/* unsigned int */
printf("UINT_MAX: %24u", UINT_MAX);
printf("%24u\n", (unsigned int)~0);
/* signed int */
printf("INT_MIN: %24d", INT_MIN);
printf("%24d\n", (int)(1 << (sizeof(int)*8 - 1)));
printf("INT_MAX: %24d", INT_MAX);
printf("%24d\n", (int)~(1 << (sizeof(int)*8 - 1)));
/* unsigned long int */
printf("ULONG_MAX: %24lu", ULONG_MAX);
printf("%24lu\n", (long int)~0l);
/* signed long int */
printf("LONG_MIN: %24ld", LONG_MIN);
printf("%24ld\n", (long int)(1l << (sizeof(long int)*8l - 1l)));
printf("LONG_MAX: %24ld", LONG_MAX);
printf("%24ld\n", (long int)~(1l << (sizeof(long int)*8l - 1l)));
putchar('\n');
/* FLOAT */
printf("FLT_MIN: %24e", FLT_MIN);
printf("%24e\n", uint2float(1));
printf("FLT_MAX: %24e", FLT_MAX);
printf("%24e\n", -uint2float(~(1u << 23)));
putchar('\n');
/* DOUBLE */
printf("DBL_MIN: %24e", DBL_MIN);
printf("%24e\n", uint2double(1));
printf("DBL_MAX: %24e", DBL_MAX);
printf("%24e\n", -uint2double(~(1lu << 52)));
return 0;
}
float uint2float(unsigned int n)
{
union utmp{
unsigned int i;
float f;
};
union utmp t;
t.i = n;
return t.f;
}
double uint2double(long unsigned int n)
{
union utmp{
long unsigned int i;
double f;
};
union utmp t;
t.i = n;
return t.f;
}
Compilation and run:
$ gcc main.c
$ ./a.out
(In this machine 'char == signed char')
standard header direct computation
--------------- ------------------
UCHAR_MAX: 255 255
SCHAR_MIN: -128 -128
SCHAR_MAX: 127 127
CHAR_MIN: -128 -128
CHAR_MAX: 127 127
USHRT_MAX: 65535 65535
SHRT_MIN: -32768 -32768
SHRT_MAX: 32767 32767
UINT_MAX: 4294967295 4294967295
INT_MIN: -2147483648 -2147483648
INT_MAX: 2147483647 2147483647
ULONG_MAX: 18446744073709551615 18446744073709551615
LONG_MIN: -9223372036854775808 -9223372036854775808
LONG_MAX: 9223372036854775807 9223372036854775807
FLT_MIN: 1.175494e-38 1.401298e-45
FLT_MAX: 3.402823e+38 3.402823e+38
DBL_MIN: 2.225074e-308 4.940656e-324
DBL_MAX: 1.797693e+308 1.797693e+308
Notes:
Since casting between integer and floats change the bit patterns of the variable, we defined 2 functions that do “special casts” without changing the bit pattern of the variable:
uint2float()
cast fromunsigned int
tofloat
.
uint2double()
cast fromlong unsigned int
todouble
.We are not suppose to know how those 2 functions works until we see
union
in Chapter 6 (another way to do it would be using pointers which are explained in Chapter 5).Notice that the direct computation of the minimum floating-point number of
float
anddouble
give different results than the standard headers. This is because we have computed the smaller floating-point number that we can use before underflowing to 0, while the standard header only defines the smallest normalized floating-point number.
Useful References:
Exercise 2-2¶
main.c
#include <stdio.h>
#define MAXLINE 1000
enum {CONTINUE, BREAK};
int main()
{
char s[MAXLINE];
const int lim = MAXLINE;
int c;
int i;
/*
* for (i = 0; i < lim - 1 && (c = getchar()) != EOF && c != '\n'; ++i) {
* s[i] = c;
* }
*/
/* equivalent loop */
int cont;
for (i = 0, cont = CONTINUE; cont == CONTINUE; ++i){
if (i >= lim - 1){
cont = BREAK;
}else if ((c = getchar()) == EOF ){
cont = BREAK;
}else if (c == '\n'){
cont = BREAK;
}else{
s[i] = c;
}
}
s[i-1] = '\0';
printf("%s\n", s);
return 0;
}
Compilation and run:
$ gcc main.c
$ ./a.out
short-circuiting of && and || is nice.
short-circuiting of && and || is nice.
Notes:
We have used the qualifier
const
for the first time:const int lim = MAXLINE;It simply indicates that the value of
lim
will not be changed. The compiler makes an error if you try to modify a read-only variable. You should initialize read-only variables as it is the only way to assign a useful value to them.
Note
const
can be applied to variables to specify that its value
will not be changed during its lifetime.
When applied to an array, it indicates that its elements will
not be altered:
const int arr[5] = {1, 2, 3, 4, 5};
arr[3] = 2; /* error */
It can be applied to function parameters and it is particulary useful to indicate that a function does not change the elements of some argument array:
long unsigned strnlen(const char s[], long unsigned n);
From this exercise onwards,
we will make use of the const
qualifier when a variable
should remain constant.
Exercise 2-3¶
main.c
#include <stdio.h>
#include <ctype.h>
#define MAXWORD 100
int chtoi(char c);
int htoi(const char s[]);
int getword(char s[], int n);
int main()
{
char s[MAXWORD];
int len;
int c;
while((c = getchar()) != EOF ){
if (getword(s, MAXWORD) > 0){
printf("%d\n", htoi(s));
}
}
return 0;
}
/*
* return the equivalent positive integer value of a single hexadecimal char
* return -1 on error
*/
int chtoi(char c)
{
if (!isxdigit(c)){
return -1;
}
c = tolower(c);
if (isdigit(c)){
return c - '0';
}else{
return c - 'a' + 10;
}
}
/*
* return the equivalent positive integer value of a string of hexadecimal digits
* return -1 on error
*/
int htoi(const char s[])
{
int vc;
int n=0;
int i=0;
if (s[0] == '0' && tolower(s[1]) == 'x') {
i = 2;
}
for( ; s[i] != '\0' ; ++i) {
if ((vc = chtoi(s[i])) == -1){
return -1;
}
n = n * 16 + vc;
}
return n;
}
/*
* A word is any sequence of characters that do not contain a blank, tab or newline.
* At maximum `n` characters are buffered, the rest remain on stdin.
* if 0 characters are readed before encountering a blank, tab or newline; 0 is returned.
* if 0 characters are readed before encountering EOF or there is any error; -1 is returned.
*/
int getword(char s[], int n)
{
int c, i;
for (i = 0; i<n-1 && (c = getchar()) != EOF && !isblank(c) && c != '\n'; ++i) {
s[i] = c;
}
s[i] = '\0';
if (isblank(c) || c == '\n'){
if (ungetc(c, stdin) == EOF){
return -1;
}
}else if(c == EOF && i == 0){
i = -1;
}
return i;
}
Compilation and run:
$ gcc main.c
$ ./a.out
0x1 0X2 0xa 0xFF ff 5 hello 0x11 0xFg
1
2
10
255
255
5
-1
17
-1
Notes:
We make use of
getword
function from Exercise 1-22 for outputing.
Exercise 2-4¶
main.c
#include <stdio.h>
#include <stdlib.h>
#define MAXLINE 100
int cins(const char s[], char c);
void squeeze(char s[], const char e[]);
int main()
{
char *s = NULL;
char *e = NULL;
long unsigned ls = 0;
long unsigned le = 0;
if (getline(&s, &ls, stdin) == -1){
printf("error getline: possible EOF\n");
return 1;
}
if (getline(&e, &le, stdin) == -1){
printf("error getline: possible EOF\n");
return 1;
}
squeeze(s, e);
printf("%s\n", s);
return 0;
}
/* return 1 if string s contains char c and 0 if not */
int cins(const char s[], char c)
{
int i;
for (i = 0; s[i] != '\0'; ++i){
if (s[i] == c) {
return 1;
}
}
return 0;
}
/* delete all character in s that matches any character in e */
void squeeze(char s[], const char e[])
{
int i;
int j;
for (i = 0, j = 0; s[i] != '\0'; ++i){
if (!cins(e, s[i])){
s[j++] = s[i];
}
}
s[j] = '\0';
}
Compilation and run:
$ gcc main.c
$ ./a.out
Confusion is part of programming
aeiou
Cnfsn s prt f prgrmmng
Exercise 2-5¶
main.c
#include <stdio.h>
#define MAXLINE 100
int cins(const char s[], char c);
int any(const char s[], const char e[]);
int main()
{
char *s = NULL;
char *e = NULL;
long unsigned ls = 0;
long unsigned le = 0;
if (getline(&s, &ls, stdin) == -1){
printf("error getline: possible EOF\n");
return 1;
}
if (getline(&e, &le, stdin) == -1){
printf("error getline: possible EOF\n");
return 1;
}
printf("position: %d\n", any(s, e));
return 0;
}
/* return 1 if string s contains char c and 0 if not */
int cins(const char s[], char c)
{
int i;
for (i = 0; s[i] != '\0'; ++i){
if (s[i] == c) {
return 1;
}
}
return 0;
}
/*
* return the first location in the string s1 where
* any character of string s2 occurs (starting from 0)
* return -1 if s1 contains no characters from s2
*/
int any(const char s1[], const char s2[])
{
int i;
for (i = 0; s1[i] != '\0'; ++i) {
if (cins(s2, s1[i])){
return i;
}
}
return -1;
}
Compilation and run:
$ gcc main.c
$ ./a.out
fly slyly in the sky.
aeiou
position: 10
Notes:
We make use of the structure of previous solution; only a singe function is changed. This is an outcome of making solutions code modular (by using functions for each specific job).
Exercise 2-6¶
main.c
#include <stdio.h>
unsigned getmask(unsigned p, unsigned n);
unsigned setbits(unsigned x, unsigned p, unsigned n, unsigned y);
int main()
{
unsigned x, p, n, y;
while(scanf("%x %u %u %x", &x, &p, &n, &y) == 4){
printf("0x%0*X\n", (int) sizeof(unsigned)*2,
setbits(x, p, n, y));
}
return 0;
}
/* a mask to put ON n bits starting at position p */
unsigned getmask(unsigned p, unsigned n)
{
return ~(~0 << n) << (p+1-n);
}
unsigned setbits(unsigned x, unsigned p, unsigned n, unsigned y)
{
unsigned mask = getmask(p, n);
return (x & ~mask) | ((y << (p+1-n)) & mask) ;
}
Compilation and run:
$ gcc main.c
$ ./a.out
0xF 1 1 0x0
0x0000000D
0x0 4 1 0xFF
0x00000010
Notes:
For position
p
we start counting from the right (being the rightmost position0
), as it is done with the code example of Section 2.9 K&R.It is good practice to use
unsigned
when manipulating bits because rightshifting negative values is implementation-defined. For example:a >> n;may have different results with different compilers when
a
is negative.
Exercise 2-7¶
main.c
#include <stdio.h>
unsigned getmask(unsigned p, unsigned n);
unsigned invert(unsigned x, int p, int n);
int main()
{
unsigned x, p, n, y;
while(scanf("%x %u %u", &x, &p, &n) == 3){
printf("0x%0*X\n", (int) sizeof(unsigned)*2, invert(x, p, n));
}
return 0;
}
/* a mask to put ON n bits starting at position p */
unsigned getmask(unsigned p, unsigned n)
{
return ~(~0 << n) << (p+1-n);
}
unsigned invert(unsigned x, int p, int n)
{
return x ^ getmask(p, n);
}
Compilation and run:
$ gcc main.c
$ ./a.out
0x0 1 1
0x00000002
0x0 0 1
0x00000001
0x10 3 4
0x0000001F
Exercise 2-8¶
main.c
#include <stdio.h>
/* position p starts in 0 for this program */
unsigned rightrot(unsigned x, unsigned n);
int main()
{
unsigned x, n;
while(scanf("%x %u", &x, &n) == 2){
printf("0x%0*X\n", (int) sizeof(unsigned)*2, rightrot(x, n));
}
return 0;
}
unsigned rightrot(unsigned x, unsigned n)
{
unsigned leftmost;
n %= sizeof(unsigned) * 8;
leftmost = x & ~(~0 << n);
leftmost <<= sizeof(unsigned)*8 - n;
return x >> n | leftmost;
}
Compilation and run:
$ gcc main.c
$ ./a.out
0x1 1
0x80000000
0x3 2
0xC0000000
0xFF 8
0xFF000000
0xFF 16
0x00FF0000
Exercise 2-9¶
main.c
#include <stdio.h>
unsigned bitcount(unsigned x);
int main()
{
unsigned x;
while(scanf("%x", &x) == 1){
printf("%u\n", bitcount(x));
}
return 0;
}
unsigned bitcount(unsigned x)
{
unsigned n=0;
while (x != 0) {
++n;
x &= x - 1;
}
return n;
}
Compilation and run:
$ gcc main.c
$ ./a.out
0xF
4
0x1
1
0x3
2
0xF0F0
8
Exercise 2-10¶
main.c
#include <stdio.h>
#include <stdlib.h>
#define SOMESIZE 4
int lower(int c);
int main()
{
char *s;
long unsigned n;
int i;
s = NULL;
n = 0;
while(getline(&s, &n, stdin) > 0){
for (i = 0; s[i] != '\0'; ++i){
putchar(lower(s[i]));
}
}
free(s);
return 0;
}
int lower(int c) {
return (c < 'A' || c > 'Z') ? c : c + 'a' - 'A';
}
Compilation and run:
$ gcc main.c
$ ./a.out
EXPLICIT IS BETTER THAN IMPLICIT.
explicit is better than implicit.
Simple Is Better Than Complex.
simple is better than complex.
CoMpLeX Is bEtTeR ThAn cOmPlIcAtEd.
complex is better than complicated.
Notes:
We use for the first time the terniary operator:
return (c < 'A' || c > 'Z') ? c : c + 'a' - 'A';is equivalent to:
if (c < 'A' || c > 'Z'){ return c; }else{ return c + 'a' - 'A'; }