munotes®

Library Functions

Chapter Thirty-Four

Syllabus topic 2, "Basics of functions. User defined and Library functions"

Pages 159 to 163 of 222

In one line

The standard library is a set of functions every C implementation must provide, declared in headers you #include and joined to your program by the linker.

Why they are not part of the language

C the language is small: types, operators, statements, functions. It has no printf, no sqrt and no strlen. Those are functions, written in C, compiled into a library and linked in, and you could write them yourself.

That is not a technicality. It is why the language is small enough to learn in a semester, and it is why printf needs a declaration from a header before you can call it: to the compiler it is an ordinary function that happens to be somebody else's.

The headers you need this semester

HeaderWhat it gives you
<stdio.h>printf, scanf, getchar, putchar, puts, fgets, FILE, EOF, fopen
<stdlib.h>abs, labs, atoi, atof, rand, srand, malloc, free, exit
<string.h>strlen, strcpy, strncpy, strcmp, strcat, strstr, strchr, memset
<math.h>sqrt, pow, fabs, floor, ceil, round, sin, cos, tan, log, exp, fmod
<ctype.h>isdigit, isalpha, isalnum, isspace, isupper, islower, toupper, tolower
<limits.h>INT_MAX, INT_MIN, CHAR_BIT, LONG_MAX
<float.h>DBL_DIG, DBL_MAX, FLT_DIG
<stdbool.h>bool, true, false
<time.h>time, clock, localtime, strftime

The practical: square root and absolute value

MU's Practical 4(c).

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(void)
{
    double x;
    int n;

    printf("Enter a number and a whole number: ");
    if (scanf("%lf %d", &x, &n) != 2) {
        printf("\nThose were not two numbers.\n");
        return 1;
    }

    printf("\n");
    if (x < 0) {
        printf("sqrt(%.4f) is not a real number\n", x);
    } else {
        printf("sqrt(%.4f)  = %.6f\n", x, sqrt(x));
    }
    printf("fabs(%.4f)  = %.4f      <- math.h, takes a double\n", x, fabs(x));
    printf("abs(%d)         = %d           <- stdlib.h, takes an int\n", n, abs(n));
    printf("pow(%.4f, 3) = %.4f\n", x, pow(x, 3.0));
    printf("floor(%.4f) = %.1f, ceil = %.1f, round = %.1f\n",
           x, floor(x), ceil(x), round(x));
    return 0;
}
7.6
-42
Enter a number and a whole number:
sqrt(7.6000)  = 2.756810
fabs(7.6000)  = 7.6000      <- math.h, takes a double
abs(-42)         = 42           <- stdlib.h, takes an int
pow(7.6000, 3) = 438.9760
floor(7.6000) = 7.0, ceil = 8.0, round = 8.0

How to compile that on Linux:

cc -std=c17 -Wall -Wextra prog.c -o prog -lm

The -lm links the maths library, and it must come after the source file. cc -lm prog.c -o prog fails, because the linker works left to right and drops a library nothing has asked for yet. The error is undefined reference to 'sqrt', and it comes from the linker, not the compiler, exactly as chapter 5 said. On macOS the maths functions are in the standard library and no flag is needed, which is why a program that builds on a Mac may not build in the lab.

munotes.in159

Library Functions

abs against fabs, which is a real trap

#include <stdio.h>
#include <stdlib.h>
#include <math.h>

int main(void)
{
    double d = -7.85;

    printf("fabs(-7.85)      = %.4f   <- correct\n", fabs(d));
    printf("abs((int) -7.85) = %d        <- the fraction is gone\n", abs((int) d));
    printf("labs(-3000000000L) = %ld\n", labs(-3000000000L));
    return 0;
}
fabs(-7.85)      = 7.8500   <- correct
abs((int) -7.85) = 7        <- the fraction is gone
labs(-3000000000L) = 3000000000

abs takes an int and returns an int. Passing it a double converts the argument, discarding the fraction before abs ever sees it. Use fabs for a double and labs for a long.

Character classification, which saves writing your own

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

int main(void)
{
    const char *text = "MU B.Sc. IT 2026!";
    int letters = 0, digits = 0, spaces = 0, punct = 0, upper = 0;

    for (int i = 0; text[i] != '\0'; i++) {
        unsigned char c = (unsigned char) text[i];

        if (isalpha(c)) { letters++; }
        if (isdigit(c)) { digits++; }
        if (isspace(c)) { spaces++; }
        if (isupper(c)) { upper++; }
        if (ispunct(c)) { punct++; }
    }
    printf("\"%s\"\n", text);
    printf("letters %d (of which upper case %d), digits %d, spaces %d, punctuation %d\n",
           letters, upper, digits, spaces, punct);

    printf("toupper: ");
    for (int i = 0; text[i] != '\0'; i++) {
        putchar(toupper((unsigned char) text[i]));
    }
    printf("\n");
    return 0;
}
"MU B.Sc. IT 2026!"
letters 7 (of which upper case 6), digits 4, spaces 3, punctuation 3
toupper: MU B.SC. IT 2026!

The (unsigned char) cast is not decoration. Every <ctype.h> function takes an int that must be either a value representable as an unsigned char or EOF. On a machine where plain char is signed, a character above 127 is a negative int, and passing it is undefined behaviour. Casting to unsigned char first is the correct idiom, and it is the reason this book's listings are compiled under both char conventions.

toupper returns an int, and on a character that is not a lower-case letter it returns it unchanged. That is what makes the loop above safe on spaces and punctuation.

Reading the documentation

The most useful skill in this chapter is not a list of functions. It is knowing how to look one up, because nobody remembers argument orders.

On a Linux machine, man 3 sqrt gives the header, the prototype, what it returns and what -lm is needed. The 3 is the section for library functions; without it you may get a shell command of the same name.

munotes.in160

Library Functions

For each function, four things are worth reading before you use it:

  1. Which header declares it.
  2. The prototype: parameter types and the return type.
  3. What it returns on failure. sqrt of a negative gives a not-a-number value; atoi of text that is not a number gives 0, which is indistinguishable from the text "0".
  4. Whether it changes anything you passed it. strcpy writes into its first argument; strlen writes nothing.

Why not to write your own

#include <stdio.h>
#include <math.h>

double my_sqrt(double x)
{
    if (x < 0) {
        return -1.0;
    }
    double guess = x / 2.0;
    for (int i = 0; i < 40; i++) {
        if (guess == 0.0) {
            break;
        }
        guess = (guess + x / guess) / 2.0;
    }
    return guess;
}

int main(void)
{
    double values[] = {2.0, 9.0, 1e10};

    printf("%12s %18s %18s\n", "x", "my_sqrt", "sqrt");
    for (int i = 0; i < 3; i++) {
        printf("%12g %18.10f %18.10f\n",
               values[i], my_sqrt(values[i]), sqrt(values[i]));
    }
    return 0;
}
           x            my_sqrt               sqrt
           2       1.4142135624       1.4142135624
           9       3.0000000000       3.0000000000
       1e+10  100000.0000000000  100000.0000000000

Forty rounds of Newton's method agree with the library to ten decimal places, which is a decent result for twelve lines. The library version is still the one to use: it is correct for every input including the awkward ones, it is as accurate as the hardware allows, and on most machines it is a single processor instruction. Writing your own is an excellent exercise and a poor habit.

What this does NOT mean

Library functions are not part of the language. They are ordinary functions in a compiled library, which is why they need a header and a link step.

A header is not the library. The header declares; the library defines. Chapter 5.

#include <math.h> is not enough on Linux. You also need -lm, after the source file.

abs is not for floating-point values. fabs is.

sqrt of a negative number is not an error you can see. It returns a not-a-number value and sets errno. Test the argument first.

atoi does not report failure. It returns 0 for text that is not a number. strtol reports properly, and is the one to use when the input matters.

A <ctype.h> function does not take a char. It takes an int, and a plain char should be cast to unsigned char first.

Quick revision

  • The library is functions, not language. Header declares, library defines, linker joins.
  • <stdio.h> input and output, <stdlib.h> general utilities, <string.h> strings, <math.h> maths, <ctype.h> characters, <limits.h> and <float.h> limits.
  • Linux: cc prog.c -o prog -lm for <math.h>, and -lm after the source.
  • abs is <stdlib.h> and int; fabs is <math.h> and double; labs for long.
  • floor rounds down, ceil rounds up, round to nearest, a cast to int truncates towards zero.
  • <ctype.h> takes an int: cast a char to unsigned char first.
  • toupper returns non-letters unchanged.
  • man 3 name gives the header, the prototype and the link flag.
  • Check what a function returns on failure before trusting it.
munotes.in161

Library Functions

Test yourself

1. What is the difference between a library function and a user-defined function?

A library function is supplied with the implementation, already compiled, and is declared in a standard header. A user-defined function is written by the programmer in their own source. Both are called the same way.

2. Which header declares sqrt, and what else does a Linux build need?

<math.h>, and the link flag -lm placed after the source file.

3. What is wrong with abs(-7.85)?

abs takes an int, so the argument is converted and the fraction is discarded before abs runs, giving 7 rather than 7.85. Use fabs.

4. Give the four functions that turn 7.6 into a whole number, and their answers.

floor gives 7.0, ceil gives 8.0, round gives 8.0, and (int) gives 7. For -7.6 they give -8.0, -7.0, -8.0 and -7.

5. Why cast to unsigned char before calling isdigit?

Because the function takes an int that must be representable as an unsigned char or be EOF, and on a machine where plain char is signed a character above 127 would be a negative value, which is undefined behaviour.

6. atoi("hello") returns 0. Why is that a problem?

Because atoi("0") also returns 0, so a failure cannot be told from a valid zero. Use strtol, which reports where it stopped parsing.

7. Which header gives INT_MAX?

<limits.h>.

What can be asked on this, and how to answer it

"What are library functions? Name any five with their headers." Define them as functions supplied by the implementation, declared in standard headers and linked in. Then five with headers and one line each: printf from <stdio.h>, strlen from <string.h>, sqrt from <math.h>, abs from <stdlib.h>, toupper from <ctype.h>.

"Write a program to find the square root and the absolute value of a number using library functions." Give this chapter's program, and mention -lm. The abs against fabs distinction is the most likely follow-up.

"Distinguish between library and user-defined functions." Library functions come with the compiler, are already compiled, are declared in standard headers and cannot be changed. User-defined functions are written by the programmer, compiled with the program, and declared by the programmer. Both are called identically, which is the point worth adding.

munotes.in162

Library Functions

"What is the difference between floor, ceil, round and a cast to int?" floor gives the largest integer value not greater than the argument, ceil the smallest not less, round the nearest with halves away from zero, and a cast truncates towards zero. Give 7.6 and -7.6 through all four, because the negative case is where they separate.

"Why does a program using sqrt fail to link with undefined reference to sqrt?" Because <math.h> only declares the function; its compiled code is in the maths library, which must be named to the linker with -lm after the source file. Add that the error comes from the linker rather than the compiler.

munotes.in163

The rest of this subject

These notes are cut from the University's printed syllabus. Open the syllabus itself for the same subject.

Report or request
Done!