Programming

Features of C99

steloflute 2016. 3. 19. 21:44

http://www.cplusplus.com/articles/iz3hAqkS/


Features of C99

Score: 4.5/5 (43 votes)*****

Introduction

C99 is the 1999 standard of the C programming language. C is a simple, low level language, that is best suited for systems programming.


This article will present a number of C99's features. Some of these features have yet to appear in C++, and therefore might not be familiar to some C++ programmers.


We will start off easy, with minor backports from C++, then move up to C99-only features, to wrap it up with "serious" code, adapted for this article from a small, real life project.


The source code in this article was tested to compile with Pelles C IDE 7, however due to the popularity and age of C99, the code should build fine with many other C compilers. Just be sure to enable C99 support, if needed.


No mandatory return for main()

As in C++, if the return statement is omitted in the main() function, a return 0; is implied.


Booleans

The _Bool data type is introduced, which behaves like an unsigned integer capable of storing only 1 or 0.


The supporting header stdbool.h contains the macros bool, true and false expanding to _Bool, 1 and 0 respectively.


Example: 

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

#include <stdbool.h>

#include <stdio.h>


int main(void)

{

    bool b = false;


    printf("%u\n", b);


    b = 5 > 3;

    printf("%u\n", b);


    b = 0;

    printf("%u\n", b);


    b = -987;

    printf("%u\n", b);

}

Edit & Run



Output: 

0

1

0

1


%zu for size_t

The %zu format specifier was introduced specifically for size_t, so as to clear the confusion of having to choose in between the unsigned integer specifiers %u, %lu, and more recently %llu.


Example: 

1

2

3

4

5

6

7

8

9

10

#include <stddef.h>

#include <stdint.h>

#include <stdio.h>


int main(void)

{

    size_t sz = SIZE_MAX;


    printf("%zu\n", sz);

}

Edit & Run



Possible output: 

4294967295


Functions know their own name

The __func__ identifier behaves like a constant char array containing the name of the function where it is invisibly declared.


Example: 

1

2

3

4

5

6

7

8

9

10

11

12

#include <stdio.h>


void i_know_my_name(void)

{

    printf("%s\n", __func__);

}


int main(void)

{

    i_know_my_name();

    printf("%s\n", __func__);

}

Edit & Run



Output: 

i_know_my_name

main


Variable-length arrays

The variable-length arrays (or VLA's) are arrays that can be declared by using a variable, instead of a compile-time constant, for their size. They do not have variable length as in being able to resize.


VLA's are infamous because they're allocated on the stack and not the heap. The stack area is used for local variables, and is more limited in size than the heap. If the size of the VLA is too big, a stack overflow will occur, resulting in a crash.


Still, the VLA is a very useful tool when the programmer wants to use small arrays, while avoiding the tedious malloc() + free() business.


Example: 

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

// This program will construct and display an n*n identity matrix.


#include <stddef.h>

#include <stdio.h>


int main(void)

{

    size_t n=0;


    printf("Please input `n': ");

    scanf("%zu", &n);


    int matrix[n][n];


    for (size_t i=0; i < n; ++i)

        for (size_t j=0; j < n; ++j)

            if (i == j)

                matrix[i][j] = 1;

            else

                matrix[i][j] = 0;


    for (size_t i=0; i < n; ++i)

    {

        for (size_t j=0; j < n; ++j)

            printf("%d ", matrix[i][j]);


        printf("\n");

    }

}

Edit & Run



Sample output: 

Please input `n': 10

1 0 0 0 0 0 0 0 0 0 

0 1 0 0 0 0 0 0 0 0 

0 0 1 0 0 0 0 0 0 0 

0 0 0 1 0 0 0 0 0 0 

0 0 0 0 1 0 0 0 0 0 

0 0 0 0 0 1 0 0 0 0 

0 0 0 0 0 0 1 0 0 0 

0 0 0 0 0 0 0 1 0 0 

0 0 0 0 0 0 0 0 1 0 

0 0 0 0 0 0 0 0 0 1 


Variadic macros

Functions can accept a variable number of parameters by using the ellipsis (...). Starting from C99, so too can macros. In the macro's definition, __VA_ARGS__ will be used to expand the parameters.


Example: 

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

#include <stdbool.h>

#include <stddef.h>

#include <stdint.h>

#include <stdio.h>

#include <stdlib.h>

#include <string.h>

#include <time.h>


#define TIME_PRINTF(format, ...)    do {                        \

    time_t t = time(NULL);                                      \

    const char *prefix = "%s -> ";                              \

    char time_format_vla[strlen(prefix) + strlen(format) + 1];  \

    strcpy(time_format_vla, prefix);                            \

    strcat(time_format_vla, format);                            \

    printf(time_format_vla, ctime(&t), __VA_ARGS__);            \

} while (false)


int main(void)

{

    srand(time(NULL));

    TIME_PRINTF("Hello %s, your number is %d! Please wait...\n\n", "User", rand() % 100);


    // waste some time

    for (size_t n=0; n < SIZE_MAX; ++n);


    // unfortunately, we need to pass at least two parameters    

    TIME_PRINTF("%s", "So how's it going?");

}

Edit & Run



Sample output: 

Wed Apr  3 12:33:23 2013

 -> Hello User, your number is 75! Please wait...


Wed Apr  3 12:33:33 2013

 -> So how's it going?


Designated initializers

C99 offers a way to control which member in a structure, or which element in an array, to initialize and to what value.


It's easier to just jump into the example for this one.


Example: 

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

#include <ctype.h>

#include <stddef.h>

#include <stdio.h>


int main(void)

{

    char ca[10] = {[4] = 'e', [0] = 'a', [2] = 'c', [1] = 'b', [3] = 'd', [9] = 'z'};


    //         0    1    2    3    4   . . . . . .  9

    // ca == {'a', 'b', 'c', 'd', 'e', 0, 0, 0, 0, 'z'}


    printf("Contents of ca:\n  ");


    // the zeros are not printable, because they aren't the '0' character,

    // so we need to cast them to int so as to print their numeric value

    for (size_t i=0; i < sizeof ca; ++i)

        if (isprint(ca[i]))

            printf("%c ", ca[i]);

        else

            printf("%d ", (int)ca[i]);


    printf("\n\n");


    struct Test

    {

        char    c;

        int     i;

        float   f;

    };


    struct Test t = {.f = 3.14f, .c = 'Z', .i = 10};


    printf("Contents of t:\n  c == %c\n  i == %d\n  f == %f\n", t.c, t.i, t.f);

}

Edit & Run



Output: 

Contents of ca:

  a b c d e 0 0 0 0 z 


Contents of t:

  c == Z

  i == 10

  f == 3.140000


Compound literals

A compound literal is basically a nameless variables, and looks very similar to a cast. It works together beautifully with variadic macros and designated initializers to produce clean, high-level looking code.


In the simplest usage scenario, compound literals take the place of temporary variables, which we don't care to have around.


Example: 

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

#include <ctype.h>

#include <stddef.h>

#include <stdint.h>

#include <stdio.h>

#include <string.h>

#include <time.h>


// this function will change the case of all letters in the message array,

// lowercase letters will become uppercase, and vice versa

void flip_case(char *message)

{

    printf("flip_case()\n");

    printf("Before:   %s\n", message);


    for (size_t i=0, ml = strlen(message); i < ml; ++i)

    {

        const char temp = message[i];


        if (isupper(temp))

            message[i] = tolower(temp);

        else

        if (islower(temp))

            message[i] = toupper(temp);

    }


    printf("After:    %s\n\n", message);

}


// this function will add 10 to an integer i

void add_ten(int *i)

{

    printf("add_ten()\n");

    printf("Before:   %d\n", *i);

    *i += 10;

    printf("After:    %d\n\n", *i);

}


// this function will add 1 to even numbers in the numbers array,

// only the first n numbers are operated on

void kill_evens(int *numbers, size_t n)

{

    printf("kill_evens()\n");

    printf("Before:   ");


    for (size_t i=0; i < n; ++i)

        printf("%d ", numbers[i]);


    printf("\n");


    for (size_t i=0; i < n; ++i)

        if (numbers[i] % 2 == 0)

            numbers[i] += 1;


    printf("After:    ");


    for (size_t i=0; i < n; ++i)

        printf("%d ", numbers[i]);


    printf("\n\n");

}


int main(void)

{

    flip_case((char[]){"Hello C99 World!"});


    add_ten(&(int){5});


    kill_evens((int[]){2, 3, 29, 90, 5, 6, 8, 0}, 8);


    printf("Current time: %s\n", ctime(&(time_t){time(NULL)}));

}

Edit & Run



Output: 

flip_case()

Before:   Hello C99 World!

After:    hELLO c99 wORLD!


add_ten()

Before:   5

After:    15


kill_evens()

Before:   2 3 29 90 5 6 8 0 

After:    3 3 29 91 5 7 9 1 


Current time: Wed Apr  3 12:44:55 2013


For a more advanced example demonstrating the value of compound literals, consider this scenario: we've written our own strscat() function, which is basically a strcat() with an extra parameter for maximum length, and we want to test to see if it works correctly.


Now, I'll let the code talk.


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

76

77

78

79

80

81

82

83

84

85

86

87

88

89

90

91

92

93

94

95

96

97

98

99

100

101

102

103

104

105

106

107

108

109

110

111

112

113

114

115

116

#include <stddef.h>

#include <stdio.h>


///

/// @brief Appends contents of array `from` to array `to`.

/// @pre `limit` != `0`

/// @note No operation is performed for a `limit` of `0`.

/// @remarks Resulting array is NUL-terminated.

/// @param [out] to      String to be written to.

/// @param limit         Maximum number of bytes that string `to` can store, including NUL.

/// @param [in] from     String to be copied from.

/// @returns Size of resulting string (NUL not counted).

///

size_t strscat(char *to, size_t limit, const char *from)

{

    size_t s=0;


    if (limit != 0)

    {

        while (to[s] != '\0')

            ++s;


        for (size_t i=0; from[i] != '\0' && s < limit - 1; ++i, ++s)

            to[s] = from[i];


        to[s] = '\0';

    }


    return s;

}


typedef struct

{

    char        *to;

    size_t      limit;

    const char  *from;

    const char  *result;

    size_t      retval;

} test_t;


static size_t tests_failed;


static void run_test(test_t *t)

{

    size_t i=0;


    if (t->retval != strscat(t->to, t->limit, t->from))

    {

        ++tests_failed;

        return;

    }


    while (t->result[i] != '\0' || t->to[i] != '\0')

        if (t->result[i] != t->to[i])

        {

            ++tests_failed;

            break;

        }

        else

            ++i;

}


#define RUN_TEST(...)   run_test(&(test_t){__VA_ARGS__})


int main(void)

{

    RUN_TEST(

        .to     = (char[15]){"The Cutty"},

        .limit  = 15,

        .from   = " Sark is a ship dry-docked in London.",

        .result = "The Cutty Sark",

        .retval = 14

    );


    RUN_TEST(

        .to     = (char[15]){"The Cutty"},

        .limit  = 0,

        .from   = "this won't get appended",

        .result = "The Cutty",

        .retval = 0

    );


    RUN_TEST(

        .to     = (char[15]){"The Cutty"},

        .limit  = 15,

        .from   = "!",

        .result = "The Cutty!",

        .retval = 10

    );


    RUN_TEST(

        .to     = (char[]){"The Cutty Sark"},

        .limit  = 3,

        .from   = "this shouldn't get appended",

        .result = "The Cutty Sark",

        .retval = 14

    );


    RUN_TEST(

        .to     = (char[]){"The Cutty Sark"},

        .limit  = 1,

        .from   = "this shouldn't get appended, either",

        .result = "The Cutty Sark",

        .retval = 14

    );


    RUN_TEST(

        .to     = (char[]){""},

        .limit  = 1,

        .from   = "this had better not get appended!",

        .result = "",

        .retval = 0

    );


    (void)fprintf(stderr, "Number of tests failed: %zu.\n", tests_failed);

}

Edit & Run



Ending notes

I hope you enjoyed reading this article, and as always, contact me via PM if you have suggestions for improving it.


Useful links

C99 articles

http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=215

http://gcc.gnu.org/onlinedocs/gcc/Function-Names.html

http://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html

http://gcc.gnu.org/onlinedocs/gcc/Variable-Length.html

http://gcc.gnu.org/onlinedocs/gcc/Designated-Inits.html

http://gcc.gnu.org/onlinedocs/gcc/Compound-Literals.html


Software

http://www.smorgasbordet.com/pellesc/

http://nuwen.net/mingw.html

'Programming' 카테고리의 다른 글

Comparison of programming languages  (0) 2016.03.29
ARIA 소스코드 보급  (0) 2016.03.28
Go 언어의 장단점  (0) 2016.03.09
C/C++ macros  (0) 2016.02.21
C++에서 UTF8, Unicode, Ansi 문자열 변환..  (0) 2016.02.08