学习C语言标准库,寻找乐趣和裨益!~

Last Updated: 2024-03-27 07:36:29 Wednesday

-- TOC --

学习C语言标准库,寻找乐趣和裨益!~~~

stdio.h

printf系列接口

// Upon successful return, these functions return the number of
// characters printed (excluding the null byte used to end output to strings).
// 如果输出字符串中间含有null,字符串到此就结束!不要出现这种情况...
int printf(const char *restrict format, ...);
int fprintf(FILE *restrict stream,
            const char *restrict format, ...);
int dprintf(int fd,
            const char *restrict format, ...);
// s must be big enough to hold the result (including the last null byte).
// 返回的长度不包括最后的null字节。
int sprintf(char *restrict str,
            const char *restrict format, ...);
// write at most size bytes (including the terminating null byte) to str.
// 此接口返回值表示str空间无限大时的写入长度,如果返回值大于size-1,表示truncated。
int snprintf(char *restrict str, size_t size,
             const char *restrict format, ...);
// sprintf和snprintf都是自动在最后写入null,因为这是string。

在C语言中转换或拼接string,可以使用sprintf或snprintf接口。但是要注意一个Undefined Behavior场景:输出地址和输入地址如果存在overlap的时候。比如:

// Don't Do This..!!! Memory Overlap...
sprintf(buf, "%s some further text", buf);

读写文件接口

// mode: r, r+, w, w+, a, a+. 
// The mode string can also include the letter 'b' either as a last
// character or as a character between the characters in any of the
// two-character strings described above.
FILE *fopen(const char *restrict pathname, const char *restrict mode);
// The function fread() reads nmemb items of data, each size bytes long,
// from the stream pointed to by stream, storing them at the location
// given by ptr.
size_t fread(void *restrict ptr, size_t size, size_t nmemb,
                    FILE *restrict stream);
// The function fwrite() writes nmemb items of data, each size bytes long,
// to the stream pointed to by stream, obtaining them from the location
// given by ptr.
size_t fwrite(const void *restrict ptr, size_t size, size_t nmemb,
                    FILE *restrict stream);
// closed after flush, but only flush user space data provided by C lib.
int fclose(FILE *stream);
// Only flush user space data provided by C lib.
// If the stream argument is NULL, 
// fflush() flushes all open output streams.
int fflush(FILE *stream);
// Set the file position indicator.
// whence: SEEK_SET, SEEK_CUR, SEEK_END
// Offset would be negative value.
int fseek(FILE *stream, long offset, int whence);
// Tp obtains the current value of the file position indicator for
// the stream pointed to by stream.
long ftell(FILE *stream);

// The tmpfile() function opens a unique temporary file
// in binary read/write (w+b) mode. 
// The file will be automatically deleted when it is closed or
// the program terminates.
// 这是个非常nice的功能接口。但在Windows下使用这个接口
// 可能会存在权限的问题,据说默认是在C盘根目录创建文件。
FILE *tmpfile(void);

获取文件大小

FILE *fn = fopen("test.txt", "r");
fseek(fn, 0L, SEEK_END);
long file_size = ftell(fn);
fclose(fn);

put系列接口

// fputc() writes the character c, cast to an unsigned char, to stream.
int fputc(int c, FILE *stream);
// putc() is equivalent to fputc() except that it may be implemented as a macro,
// which evaluates stream more than once.
int putc(int c, FILE *stream);
// putchar(c) is equivalent to putc(c, stdout).
int putchar(int c);
// fputs() writes the string s to stream, without its terminating null byte ('\0').
int fputs(const char *restrict s, FILE *restrict stream);
// puts() writes the string s and a trailing newline to stdout.
// puts固定向stdout输出,自动添加newline,倒是提供了一点便利性。
int puts(const char *s);

perror接口

// 当errno被设置后,用perror接口,打印出系统定义的errno对应的字符串。
// 入参是一个额外的输出字符串,用户自定义,先打印这个。
void perror(const char *s);

// 我常用的Macro,当出错时,提示文件名和行号,方便定位。
#define _PEXIT \
    do {\
        char ebuf[64] = {0};\
        sprintf(ebuf, "%s: %d", __FILE__, __LINE__);\
        perror(ebuf);\
        exit(errno);\
    }while(0)

对比strerror接口,它需要以errno作为输入。而perror直接将对应的string打印出来。

stdlib.h

字符串转数字

#include <stdlib.h>

// 将输入字符串的初始部分转换成整形数据或浮点数。
// 不建议再使用的下面4个接口,原因:
// 1. 发生错误返回0,但0可能是正常值,并且不设置errno
// 2. 没有underflow和overflow的检查
// 3. 只支持10进制
int atoi(const char *nptr);
long atol(const char *nptr);
long long atoll(const char *nptr);
double atof(const char *nptr);

// 下面是建议使用的转换接口!
// 1. 转换nptr指向字符串的初始部分,可以有space,可以有+-符号
// 2. 如果**endptr不为NULL,函数执行结束后,*endptr指向第1个非数字字符
// 3. 可在2到36之间设置进制,0是特殊值
// 4. 出错时会设置errno,但需要用户自己在调用前将errno清零,
// 因为接口可以合法返回0,调用之后,由用户自己判断errno是否为非0。
// 5. manpage有使用case,这组接口还挺复杂的
long strtol(const char *restrict nptr,
            char **restrict endptr, int base);
long long strtoll(const char *restrict nptr,
                  char **restrict endptr, int base);
unsigned long strtoul(const char *restrict nptr,
                      char **restrict endptr, int base);
unsigned long long strtoull(const char *restrict nptr,
                            char **restrict endptr, int base);
float strtof(const char *restrict nptr, char **restrict endptr);
double strtod(const char *restrict nptr, char **restrict endptr);
long double strtold(const char *restrict nptr, char **restrict endptr);

C++在<string>中也提供了一组功能相似的接口,名为sto*,具体参考:https://en.cppreference.com/w/cpp/string/basic_string/stol,包含一个stoi接口。

排序算法qsort

// base指向内存块的起始位置,
// nmemb表示元素的个数,
// size表示每个元素的大小,
// compar是一个返回int的函数指针,返回值有负,零,正三种情况。
void qsort(void *base, size_t nmemb, size_t size,
           int (*compar)(const void*, const void*));

二分查找bsearch

// key: 要查找对象的值
// base: 序列的起始地址,序列按升序排列!
// nmemb: 序列中元素个数
// size: 每个元素的大小
// compar: 在key与元素比较时调用,返回值有负,零,正三种情况。
void *bsearch(const void *key, const void *base,
              size_t nmemb, size_t size,
              int (*compar)(const void*, const void*));
// 返回NULL表示没有找到,否则返回指向元素的指针。

qsort和bsearch可共用一个compar接口

qsort和bsearch对compar接口的要求是完全一样的,因此可共用,示例如下:

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

static int comp(const void *a, const void *b){
    int aa = *(int*)a;
    int bb = *(int*)b;
    return aa<bb ? -1 : aa>bb ? 1 : 0;
}

int main(){
    int data[] = {123,-234,345,-456,-2,0x80000001,2};
    int size = sizeof(data) / sizeof(int);

    qsort(data, size, sizeof(int), comp);

    printf("* sorted:\n");
    for(int i=0; i<size; ++i)
        printf("%d\n", data[i]);

    int key = 2;
    int *p = bsearch(&key, data, size, sizeof(int), comp);
    if(!p)
        printf("* the value %d is not in the array!\n", key);
    else
        printf("* found! %d, index: %ld\n", *p, p-data);

    return 0;
}

运行输出:

* sorted:
-2147483647
-456
-234
-2
2
123
345
* found! 2, index: 4

内存申请释放接口

// free(0)是合法的,内部有检查!
void free(void *ptr);
// The memory is not initialized.  If size is 0, then malloc()
// returns either NULL, or a unique pointer value that can later be
// successfully passed to free(). 得到的内存未初始化。
void *malloc(size_t size);
// The memory is set to zero.  If nmemb or size is 0, then calloc()
// returns either NULL, or a unique pointer value that can later be
// successfully passed to free(). 得到的内存已被初始化为0。
void *calloc(size_t nmemb, size_t size);
// To change the size of the memory block pointed to by ptr to size bytes.
// The contents will be unchanged in the range from the start of the region
// up to the minimum of the old and new sizes. 伸缩内存大小。
// 如果size比原来的大,新加的部分内存不会被初始化。
// 如果ptr==NULL,等同于调用ptr=malloc(size)。
// 需要避免size==0而ptr!=NULL的情况,这是UB。
// Unless ptr is NULL, it must have been returned by an earlier
// call to malloc(), calloc(), or realloc().
// If the area pointed to was moved, a free(ptr) is done.
// 我理解此接口有可能改变ptr的位置,而原来的位置会自动free。
void *realloc(void *ptr, size_t size);

在C语言中使用这几个接口,不需要强转返回,即不需要强行将void*转换成某种其它类型的指针。但C++不允许这么干,C++的type system比C要严格!

随机数

// 返回一个随机数,范围是 [0,RAND_MAX],测试打印RAND_MAX的值为2147483647。
int rand(void);
// The srand() function sets its argument as the seed for a new
// sequence of pseudo-random integers to be returned by rand().
// These sequences are repeatable by calling srand() with the
// same seed value. If no seed value is provided, the rand()
// function is automatically seeded with a value of 1.
void srand(unsigned int seed);

exit和abort

exit是正常退出,接口可以带一个程序的返回码。

// 设置返回码,结束进程
noreturn void exit(int status);

abort是异常退出,接口不带返回码,执行到abort时,会产生coredump。assert使用abort结束进程。

noreturn void abort(void);

system

// 执行一个shell command,或者可用于探测系统shell是否存在。
// fork and execl("/bin/sh", "sh", "-c", command, (char *) NULL)
// 阻塞执行command。
int system(const char *command);

Linux下exec系列接口

stdint.h

stdint.h是C99标准定义的一个头文件,它定义了各种fixed-sized integer,个人认为应该尽量使用这些定义。

fixed-size int,有助于写出可移植性更好的代码

各种确定size的int,signed或unsigned:

#include <stdio.h>
#include <stdint.h>


int main(void) {
    int8_t i8=1;
    int16_t i16=2;
    int32_t i32=3;
    int64_t i64=4;
    printf("%d %d %d %ld\n", i8,i16,i32,i64);
    uint8_t ui8=1;
    uint16_t ui16=2;
    uint32_t ui32=3;
    uint64_t ui64=4;
    printf("%u %u %u %lu\n", i8,i16,i32,i64);
    return 0;
}

不排除未来还有更高端的芯片出现,int的长度是否会变呢?既然不同的处理器平台有各种区别,那么使用确定size的int,可以避免很多麻烦和bug。

[fastest] minimum-width integer types

stdint.h中还有这样一类我们平常不太会用得到的类型,它们的名称型为[u]int_leastN_t[u]int_fastN_t,它们不是确定size的int,但是从名称上理解,它们提供了一个确定最小size的int

int_leastN_t is required in all C99 implementation for values
 of N of 8, 16, 32, and 64. 
This is the "minimum-width integer types".

int_fastN_t is required in all C99 implementation for values 
of N of 8, 16, 32, and 64. 
This is the "fastest minimum-width integer types".

leastN表示最少有N位的int,而fastN也表示最少有N位的int,但它多了一个约束,fast,要最快的。

如何理解fastN_t?

For example, on a machine with 8 bit bytes and 32 bit fast 
registers, int8_t and int_least8_t are aliased to signed 
char but int_fast8_t is aliased to int32_t. Whereas, 
if the implementation chose to define them, int_least24_t 
and int_fast24_t would both be aliased to int32_t, with 
int24_t left undefined. 

int8_t和int_least8_t对应int8_t;
int_fast8_t对应int32_t;

CPU将一个变量从内存load进register,修改,再write back to memory。不同size的变量,编译之后对应的汇编语句的数量可能都不同,一般32位的CPU,处理int32_t是最快的。定义int_fastN_t,就是这个道理。

下面是/usr/include/stdint.h中的一组定义:

/* Fast types.  */

/* Signed.  */
typedef signed char             int_fast8_t;
#if __WORDSIZE == 64
typedef long int                int_fast16_t;
typedef long int                int_fast32_t;
typedef long int                int_fast64_t;
#else
typedef int                     int_fast16_t;
typedef int                     int_fast32_t;
__extension__
typedef long long int           int_fast64_t;
#endif

/* Unsigned.  */
typedef unsigned char           uint_fast8_t;
#if __WORDSIZE == 64
typedef unsigned long int       uint_fast16_t;
typedef unsigned long int       uint_fast32_t;
typedef unsigned long int       uint_fast64_t;
#else
typedef unsigned int            uint_fast16_t;
typedef unsigned int            uint_fast32_t;
__extension__
typedef unsigned long long int  uint_fast64_t;
#endif

各种size的int的极限值

stdint.h中,还定义了各种size的int的极限值,方便程序代码直接使用,增加代码的可读性。

下面的内容,来自/usr/include/stdint.h文件:

# if __WORDSIZE == 64
#  define __INT64_C(c)  c ## L
#  define __UINT64_C(c) c ## UL
# else
#  define __INT64_C(c)  c ## LL
#  define __UINT64_C(c) c ## ULL
# endif

/* Limits of integral types.  */

/* Minimum of signed integral types.  */
# define INT8_MIN               (-128)
# define INT16_MIN              (-32767-1)
# define INT32_MIN              (-2147483647-1)
# define INT64_MIN              (-__INT64_C(9223372036854775807)-1)
/* Maximum of signed integral types.  */
# define INT8_MAX               (127)
# define INT16_MAX              (32767)
# define INT32_MAX              (2147483647)
# define INT64_MAX              (__INT64_C(9223372036854775807))

/* Maximum of unsigned integral types.  */
# define UINT8_MAX              (255)
# define UINT16_MAX             (65535)
# define UINT32_MAX             (4294967295U)
# define UINT64_MAX             (__UINT64_C(18446744073709551615))

/* Minimum of signed integral types having a minimum size.  */
# define INT_LEAST8_MIN         (-128)
# define INT_LEAST16_MIN        (-32767-1)
# define INT_LEAST32_MIN        (-2147483647-1)
# define INT_LEAST64_MIN        (-__INT64_C(9223372036854775807)-1)
/* Maximum of signed integral types having a minimum size.  */
# define INT_LEAST8_MAX         (127)
# define INT_LEAST16_MAX        (32767)
# define INT_LEAST32_MAX        (2147483647)
# define INT_LEAST64_MAX        (__INT64_C(9223372036854775807))

/* Maximum of unsigned integral types having a minimum size.  */
# define UINT_LEAST8_MAX        (255)
# define UINT_LEAST16_MAX       (65535)
# define UINT_LEAST32_MAX       (4294967295U)
# define UINT_LEAST64_MAX       (__UINT64_C(18446744073709551615))

/* Minimum of fast signed integral types having a minimum size.  */
# define INT_FAST8_MIN          (-128)
# if __WORDSIZE == 64
#  define INT_FAST16_MIN        (-9223372036854775807L-1)
#  define INT_FAST32_MIN        (-9223372036854775807L-1)
# else
#  define INT_FAST16_MIN        (-2147483647-1)
#  define INT_FAST32_MIN        (-2147483647-1)
# endif
# define INT_FAST64_MIN         (-__INT64_C(9223372036854775807)-1)
/* Maximum of fast signed integral types having a minimum size.  */
# define INT_FAST8_MAX          (127)
# if __WORDSIZE == 64
#  define INT_FAST16_MAX        (9223372036854775807L)
#  define INT_FAST32_MAX        (9223372036854775807L)
# else
#  define INT_FAST16_MAX        (2147483647)
#  define INT_FAST32_MAX        (2147483647)
# endif
# define INT_FAST64_MAX         (__INT64_C(9223372036854775807))

/* Maximum of fast unsigned integral types having a minimum size.  */
# define UINT_FAST8_MAX         (255)
# if __WORDSIZE == 64
#  define UINT_FAST16_MAX       (18446744073709551615UL)
#  define UINT_FAST32_MAX       (18446744073709551615UL)
# else
#  define UINT_FAST16_MAX       (4294967295U)
#  define UINT_FAST32_MAX       (4294967295U)
# endif
# define UINT_FAST64_MAX        (__UINT64_C(18446744073709551615))

stdint.h中的主要内容,就是上面这些了,还有一些其它内容,有兴趣的同学,请自行阅读源码。

limits.h

头文件limits.h提供了各种size的int的最大最小值。stdint.h中也有最大最小值定义,但他们都是针对stdint.h中定义的类型。下面是经过整理的limits.h内容:

/* Number of bits in a `char'.  */
#  define CHAR_BIT  8

/* Minimum and maximum values a `signed char' can hold.  */
#  define SCHAR_MIN (-128)
#  define SCHAR_MAX 127

/* Maximum value an `unsigned char' can hold.  (Minimum is 0.)  */
#  define UCHAR_MAX 255

/* Minimum and maximum values a `signed short int' can hold.  */
#  define SHRT_MIN  (-32768)
#  define SHRT_MAX  32767

/* Maximum value an `unsigned short int' can hold.  (Minimum is 0.)  */
#  define USHRT_MAX 65535

/* Minimum and maximum values a `signed int' can hold.  */
#  define INT_MIN   (-INT_MAX - 1)
#  define INT_MAX   2147483647

/* Maximum value an `unsigned int' can hold.  (Minimum is 0.)  */
#  define UINT_MAX  4294967295U

/* Minimum and maximum values a `signed long int' can hold.  */
#  if __WORDSIZE == 64
#   define LONG_MAX 9223372036854775807L
#  else
#   define LONG_MAX 2147483647L
#  endif
#  define LONG_MIN  (-LONG_MAX - 1L)

/* Maximum value an `unsigned long int' can hold.  (Minimum is 0.)  */
#  if __WORDSIZE == 64
#   define ULONG_MAX    18446744073709551615UL
#  else
#   define ULONG_MAX    4294967295UL
#  endif

/* Minimum and maximum values a `signed long long int' can hold.  */
#   define LLONG_MAX    9223372036854775807LL
#   define LLONG_MIN    (-LLONG_MAX - 1LL)

/* Maximum value an `unsigned long long int' can hold.  (Minimum is 0.)  */
#   define ULLONG_MAX   18446744073709551615ULL

/* integer width */
#  define CHAR_WIDTH 8
#  define SCHAR_WIDTH 8
#  define UCHAR_WIDTH 8
#  define SHRT_WIDTH 16
#  define USHRT_WIDTH 16
#  define INT_WIDTH 32
#  define UINT_WIDTH 32
#  define LLONG_WIDTH 64
#  define ULLONG_WIDTH 64

inttypes.h

这个头文件中定义了很多printf接口所用到的类型specifier。

为了写出更好更安全的可移植代码,<stdint.h><limits.h><inttypes.h>这三个头文件非常重要!我们要尽量使用这三个头文件提供的各种定义,避免直接在代码中使用直接使用int,long...等原始类型。

inttypes.h文件内容如下:

/* Copyright (C) 1997-2022 Free Software Foundation, Inc.
   This file is part of the GNU C Library.

   The GNU C Library is free software; you can redistribute it and/or
   modify it under the terms of the GNU Lesser General Public
   License as published by the Free Software Foundation; either
   version 2.1 of the License, or (at your option) any later version.

   The GNU C Library is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
   Lesser General Public License for more details.

   You should have received a copy of the GNU Lesser General Public
   License along with the GNU C Library; if not, see
   <https://www.gnu.org/licenses/>.  */

/*
 *      ISO C99: 7.8 Format conversion of integer types <inttypes.h>
 */

#ifndef _INTTYPES_H
#define _INTTYPES_H     1

#include <features.h>
/* Get the type definitions.  */
#include <stdint.h>

/* Get a definition for wchar_t.  But we must not define wchar_t itself.  */
#ifndef ____gwchar_t_defined
# ifdef __cplusplus
#  define __gwchar_t wchar_t
# elif defined __WCHAR_TYPE__
typedef __WCHAR_TYPE__ __gwchar_t;
# else
#  define __need_wchar_t
#  include <stddef.h>
typedef wchar_t __gwchar_t;
# endif
# define ____gwchar_t_defined   1
#endif

# if __WORDSIZE == 64
#  define __PRI64_PREFIX        "l"
#  define __PRIPTR_PREFIX       "l"
# else
#  define __PRI64_PREFIX        "ll"
#  define __PRIPTR_PREFIX
# endif

/* Macros for printing format specifiers.  */

/* Decimal notation.  */
# define PRId8          "d"
# define PRId16         "d"
# define PRId32         "d"
# define PRId64         __PRI64_PREFIX "d"

# define PRIdLEAST8     "d"
# define PRIdLEAST16    "d"
# define PRIdLEAST32    "d"
# define PRIdLEAST64    __PRI64_PREFIX "d"

# define PRIdFAST8      "d"
# define PRIdFAST16     __PRIPTR_PREFIX "d"
# define PRIdFAST32     __PRIPTR_PREFIX "d"
# define PRIdFAST64     __PRI64_PREFIX "d"


# define PRIi8          "i"
# define PRIi16         "i"
# define PRIi32         "i"
# define PRIi64         __PRI64_PREFIX "i"

# define PRIiLEAST8     "i"
# define PRIiLEAST16    "i"
# define PRIiLEAST32    "i"
# define PRIiLEAST64    __PRI64_PREFIX "i"

# define PRIiFAST8      "i"
# define PRIiFAST16     __PRIPTR_PREFIX "i"
# define PRIiFAST32     __PRIPTR_PREFIX "i"
# define PRIiFAST64     __PRI64_PREFIX "i"

/* Octal notation.  */
# define PRIo8          "o"
# define PRIo16         "o"
# define PRIo32         "o"
# define PRIo64         __PRI64_PREFIX "o"

# define PRIoLEAST8     "o"
# define PRIoLEAST16    "o"
# define PRIoLEAST32    "o"
# define PRIoLEAST64    __PRI64_PREFIX "o"

# define PRIoFAST8      "o"
# define PRIoFAST16     __PRIPTR_PREFIX "o"
# define PRIoFAST32     __PRIPTR_PREFIX "o"
# define PRIoFAST64     __PRI64_PREFIX "o"

/* Unsigned integers.  */
# define PRIu8          "u"
# define PRIu16         "u"
# define PRIu32         "u"
# define PRIu64         __PRI64_PREFIX "u"

# define PRIuLEAST8     "u"
# define PRIuLEAST16    "u"
# define PRIuLEAST32    "u"
# define PRIuLEAST64    __PRI64_PREFIX "u"

# define PRIuFAST8      "u"
# define PRIuFAST16     __PRIPTR_PREFIX "u"
# define PRIuFAST32     __PRIPTR_PREFIX "u"
# define PRIuFAST64     __PRI64_PREFIX "u"

/* lowercase hexadecimal notation.  */
# define PRIx8          "x"
# define PRIx16         "x"
# define PRIx32         "x"
# define PRIx64         __PRI64_PREFIX "x"

# define PRIxLEAST8     "x"
# define PRIxLEAST16    "x"
# define PRIxLEAST32    "x"
# define PRIxLEAST64    __PRI64_PREFIX "x"

# define PRIxFAST8      "x"
# define PRIxFAST16     __PRIPTR_PREFIX "x"
# define PRIxFAST32     __PRIPTR_PREFIX "x"
# define PRIxFAST64     __PRI64_PREFIX "x"

/* UPPERCASE hexadecimal notation.  */
# define PRIX8          "X"
# define PRIX16         "X"
# define PRIX32         "X"
# define PRIX64         __PRI64_PREFIX "X"

# define PRIXLEAST8     "X"
# define PRIXLEAST16    "X"
# define PRIXLEAST32    "X"
# define PRIXLEAST64    __PRI64_PREFIX "X"

# define PRIXFAST8      "X"
# define PRIXFAST16     __PRIPTR_PREFIX "X"
# define PRIXFAST32     __PRIPTR_PREFIX "X"
# define PRIXFAST64     __PRI64_PREFIX "X"


/* Macros for printing `intmax_t' and `uintmax_t'.  */
# define PRIdMAX        __PRI64_PREFIX "d"
# define PRIiMAX        __PRI64_PREFIX "i"
# define PRIoMAX        __PRI64_PREFIX "o"
# define PRIuMAX        __PRI64_PREFIX "u"
# define PRIxMAX        __PRI64_PREFIX "x"
# define PRIXMAX        __PRI64_PREFIX "X"


/* Macros for printing `intptr_t' and `uintptr_t'.  */
# define PRIdPTR        __PRIPTR_PREFIX "d"
# define PRIiPTR        __PRIPTR_PREFIX "i"
# define PRIoPTR        __PRIPTR_PREFIX "o"
# define PRIuPTR        __PRIPTR_PREFIX "u"
# define PRIxPTR        __PRIPTR_PREFIX "x"
# define PRIXPTR        __PRIPTR_PREFIX "X"


/* Macros for scanning format specifiers.  */

/* Signed decimal notation.  */
# define SCNd8          "hhd"
# define SCNd16         "hd"
# define SCNd32         "d"
# define SCNd64         __PRI64_PREFIX "d"

# define SCNdLEAST8     "hhd"
# define SCNdLEAST16    "hd"
# define SCNdLEAST32    "d"
# define SCNdLEAST64    __PRI64_PREFIX "d"

# define SCNdFAST8      "hhd"
# define SCNdFAST16     __PRIPTR_PREFIX "d"
# define SCNdFAST32     __PRIPTR_PREFIX "d"
# define SCNdFAST64     __PRI64_PREFIX "d"

/* Signed decimal notation.  */
# define SCNi8          "hhi"
# define SCNi16         "hi"
# define SCNi32         "i"
# define SCNi64         __PRI64_PREFIX "i"

# define SCNiLEAST8     "hhi"
# define SCNiLEAST16    "hi"
# define SCNiLEAST32    "i"
# define SCNiLEAST64    __PRI64_PREFIX "i"

# define SCNiFAST8      "hhi"
# define SCNiFAST16     __PRIPTR_PREFIX "i"
# define SCNiFAST32     __PRIPTR_PREFIX "i"
# define SCNiFAST64     __PRI64_PREFIX "i"

/* Unsigned decimal notation.  */
# define SCNu8          "hhu"
# define SCNu16         "hu"
# define SCNu32         "u"
# define SCNu64         __PRI64_PREFIX "u"

# define SCNuLEAST8     "hhu"
# define SCNuLEAST16    "hu"
# define SCNuLEAST32    "u"
# define SCNuLEAST64    __PRI64_PREFIX "u"

# define SCNuFAST8      "hhu"
# define SCNuFAST16     __PRIPTR_PREFIX "u"
# define SCNuFAST32     __PRIPTR_PREFIX "u"
# define SCNuFAST64     __PRI64_PREFIX "u"

/* Octal notation.  */
# define SCNo8          "hho"
# define SCNo16         "ho"
# define SCNo32         "o"
# define SCNo64         __PRI64_PREFIX "o"

# define SCNoLEAST8     "hho"
# define SCNoLEAST16    "ho"
# define SCNoLEAST32    "o"
# define SCNoLEAST64    __PRI64_PREFIX "o"

# define SCNoFAST8      "hho"
# define SCNoFAST16     __PRIPTR_PREFIX "o"
# define SCNoFAST32     __PRIPTR_PREFIX "o"
# define SCNoFAST64     __PRI64_PREFIX "o"

/* Hexadecimal notation.  */
# define SCNx8          "hhx"
# define SCNx16         "hx"
# define SCNx32         "x"
# define SCNx64         __PRI64_PREFIX "x"

# define SCNxLEAST8     "hhx"
# define SCNxLEAST16    "hx"
# define SCNxLEAST32    "x"
# define SCNxLEAST64    __PRI64_PREFIX "x"

# define SCNxFAST8      "hhx"
# define SCNxFAST16     __PRIPTR_PREFIX "x"
# define SCNxFAST32     __PRIPTR_PREFIX "x"
# define SCNxFAST64     __PRI64_PREFIX "x"


/* Macros for scanning `intmax_t' and `uintmax_t'.  */
# define SCNdMAX        __PRI64_PREFIX "d"
# define SCNiMAX        __PRI64_PREFIX "i"
# define SCNoMAX        __PRI64_PREFIX "o"
# define SCNuMAX        __PRI64_PREFIX "u"
# define SCNxMAX        __PRI64_PREFIX "x"

/* Macros for scaning `intptr_t' and `uintptr_t'.  */
# define SCNdPTR        __PRIPTR_PREFIX "d"
# define SCNiPTR        __PRIPTR_PREFIX "i"
# define SCNoPTR        __PRIPTR_PREFIX "o"
# define SCNuPTR        __PRIPTR_PREFIX "u"
# define SCNxPTR        __PRIPTR_PREFIX "x"


__BEGIN_DECLS

#if __WORDSIZE == 64

/* We have to define the `uintmax_t' type using `ldiv_t'.  */
typedef struct
  {
    long int quot;              /* Quotient.  */
    long int rem;               /* Remainder.  */
  } imaxdiv_t;

#else

/* We have to define the `uintmax_t' type using `lldiv_t'.  */
typedef struct
  {
    __extension__ long long int quot;   /* Quotient.  */
    __extension__ long long int rem;    /* Remainder.  */
  } imaxdiv_t;

#endif


/* Compute absolute value of N.  */
extern intmax_t imaxabs (intmax_t __n) __THROW __attribute__ ((__const__));

/* Return the `imaxdiv_t' representation of the value of NUMER over DENOM. */
extern imaxdiv_t imaxdiv (intmax_t __numer, intmax_t __denom)
      __THROW __attribute__ ((__const__));

/* Like `strtol' but convert to `intmax_t'.  */
extern intmax_t strtoimax (const char *__restrict __nptr,
                           char **__restrict __endptr, int __base) __THROW;

/* Like `strtoul' but convert to `uintmax_t'.  */
extern uintmax_t strtoumax (const char *__restrict __nptr,
                            char ** __restrict __endptr, int __base) __THROW;

/* Like `wcstol' but convert to `intmax_t'.  */
extern intmax_t wcstoimax (const __gwchar_t *__restrict __nptr,
                           __gwchar_t **__restrict __endptr, int __base)
     __THROW;

/* Like `wcstoul' but convert to `uintmax_t'.  */
extern uintmax_t wcstoumax (const __gwchar_t *__restrict __nptr,
                            __gwchar_t ** __restrict __endptr, int __base)
     __THROW;

__END_DECLS

#endif /* inttypes.h */

float.h

float.h定义了浮点数类型 float、double、long double 的一些宏,规定了这些类型的范围和精度。

FLT_ROUNDS

宏FLT_ROUNDS表示当前浮点数加法的舍入方式。

它有以下可能的值:

FLT_RADIX

宏FLT_RADIX表示科学计数法的指数部分的底(base),一般总是2

浮点数类型的最大最小值

(5)两个同类型浮点数之间可表示的最小差值(最小精度)

FLT_EPSILON
DBL_EPSILON
LDBL_EPSILON

(6)DECIMAL_DIG

宏DECIMAL_DIG表示十进制有效位数。

(7)FLT_EVAL_METHOD

宏FLT_EVAL_METHOD表示浮点数运算时的类型转换。

它可能有以下值。

-1:不确定。
0:在当前类型中运算。
1:float 和 double 类型的运算使用 double 类型的范围和精度求值。
2:所有浮点数类型的运算使用 long double 类型的范围和精度求值。

(8)浮点数尾数部分的个数

FLT_MANT_DIG
DBL_MANT_DIG
LDBL_MANT_DIG

(9)浮点数指数部分有效数字的个数(十进制)

FLT_DIG
DBL_DIG
LDBL_DIG

(10)科学计数法的指数部分的最小次幂(负数)

FLT_MIN_EXP
DBL_MIN_EXP
LDBL_MIN_EXP

(11)科学计数法的指数部分的十进制最小次幂(负数)

FLT_MIN_10_EXP
DBL_MIN_10_EXP
LDBL_MIN_10_EXP

(12)科学计数法的指数部分的最大次幂

FLT_MAX_EXP
DBL_MAX_EXP
LDBL_MAX_EXP

(13)科学计数法的指数部分的十进制最大次幂

FLT_MAX_10_EXP
DBL_MAX_10_EXP
LDBL_MAX_10_EXP

math.h

头文件math.h提供了一些常用的数学计算函数接口。


assert.h

assert

// #define NDEBUG
#include <assert.h>

assert(expression);

由于assert被触发后,会调用abort结束程序的执行,有时这反而会不利于程序的调试。自定义类似assert机制的Marco一定也不难,比如下面就是我常用的HOPE系列:

#define HOPE_EQ(a,b) do{\
      if(a != b)\
          std::cerr<<"HOPE_EQ FAILED "<<__FILE__<<": "<<__LINE__<<std::endl;\
    }while(0)

#define HOPE_LE(a,b) do{\
      if(a > b)\
          std::cerr<<"HOPE_LE FAILED "<<__FILE__<<": "<<__LINE__<<std::endl;\
    }while(0)

static_assert(C11)

C11引入了静态断言static_assert,用于在编译阶段进行断言判断。由于只在编译阶段起作用,因此它不能用对变量使用。

static_assert(constant-expression, string-literal);

当第一个参数的值为false时,会产生一条编译错误,第二个参数就是预先定义好的错误提示信息。

C++大量使用static_assert来判断模板的参数是否符合需要。

使用范围:static_assert可以用在全局作用域中,命名空间中,类作用域中,函数作用域中,几乎可以不受限制的使用。

time.h

// time() returns the time as the number of seconds
// since the Epoch, 1970-01-01 00:00:00 +0000 (UTC).
time_t time(time_t *tloc);
// 返回时间字符串,带\n\0,输入为time_t类型的指针
char *ctime(const time_t *timep);
// 返回时间字符串,带\n\0,输入为struct tm类型的指针
char *asctime(const struct tm *tm);
// 将time_t类型转换成struct tm类型,并且转换为UTC时间
struct tm *gmtime(const time_t *timep);
// 将time_t类型转换成struct tm类型,并且转换为本地当前时间
struct tm *localtime(const time_t *timep);
// 时间字符串格式化接口,format部分参数丰富,请参考manpage
size_t strftime(char *restrict s, size_t max,
                const char *restrict format,
                const struct tm *restrict tm);

代码示例,请参考:C语言时间计算

ctype.h

头文件ctype.h定义了一批C语言字符类别判断函数(C character classification functions),用于测试字符是否属于特定的字符类别,如字母字符、控制字符等等。

int isalnum(int c) // isalpha(c) or isdigit(c) is true
int isalpha(int c) // isupper(c) or islower(c) is true
int isblank(int c) // space ' ' and horizontal tab \t
int iscntrl(int c) // control character
int isdigit(int c) // decimal digit
int isgraph(int c) // printing character except space
int islower(int c) // lower-case letter
int isprint(int c) // printing character including space
int ispunct(int c) // printing character except sapce or letter or digit
int isspace(int c) // 6 whitespaces
int isupper(int c) // upper-case letter
int isxdigit(int c) // hexadecimal digit
int tolower(int c)
int toupper(int c)

6个whitespaces

参数类型为int,返回类型也为int,0 is false,1 is true。

在ASCII字符集中,printing characters is from 0x02 to 0x7E。control characters is from 0x00 to 0x1F, and 0x7F。

stddef.h

size_t和ssize_t

ptrdiff_t

C99定义了ptrdiff_t,在stddef.h头文件中。

ptrdiff_t是C/C++标准库中定义的一个与机器相关的数据类型,ptrdiff_t类型变量通常用来保存两个指针减法操作的结果。标准库类型ptrdiff_t与size_t概念一样,ptrdiff_t也是一种与机器相关的类型。size_t是unsigned类型,而ptrdiff_t则是signed整型,通常被定义为long int

errno.h

man 3 errno

The <errno.h> header file defines the integer variable errno, which is set by system calls and some library functions in the event of an error to indicate what went wrong.

Linux系统调用,或部分库函数(比如sqrt)在失败的时候,会设置这个变量errno。

errno is defined by the ISO C standard to be a modifiable lvalue of type int, and must not be explicitly declared; errno may be a macro. errno is thread-local; setting it in one thread does not affect its value in any other thread.

线程中直接使用errno是安全的。

string.h

str系列接口

#include <string.h>

// The strlen() function calculates the length of the string pointed to
// by s, excluding the terminating null byte ('\0').
size_t strlen(const char *s);
// The  strcpy() function copies the string pointed to by src, including 
// the terminating null byte ('\0'), to the buffer pointed to by dest.
// The strings may not overlap, and the destination string dest must be
// large enough to receive the copy.  Beware of buffer overruns! 
char *strcpy(char *restrict dest, const char *src);
// The strncpy() function is similar, except that at most n bytes of src
// are copied.  Warning: If there is no null byte among the first n bytes
// of src, the string placed in dest will not be null-terminated.
char *strncpy(char *restrict dest, const char *restrict src, size_t n);
// The strcpy() and strncpy() functions return a pointer to the 
// destination string dest.

// The strcmp() function compares the two strings s1 and s2. 
// The locale is not taken into account (for a locale-aware comparison, see strcoll(3)).  The comparison is done using unsigned characters.
// strcmp() returns an integer indicating the result of the comparison,
// as follows:
// 0, if the s1 and s2 are equal;
// a negative value if s1 is less than s2;
// a positive value if s1 is greater than s2.
int strcmp(const char *s1, const char *s2);
// The strncmp() function is similar, except it compares only the first
// (at most) n bytes of s1 and s2.
int strncmp(const char *s1, const char *s2, size_t n);

// The strcat() function appends the src string to the dest string,
// overwriting the terminating null byte ('\0') at the end of dest,
// and then adds a terminating null byte. 
// The strings may not overlap, and the dest string must have enough
// space for the result. 
// If dest is not large enough, program behavior is unpredictable;
// buffer overruns are a favorite avenue for attacking secure programs.
// As with strcat(), the resulting string in dest is
// always null-terminated.
char *strcat(char *restrict dest, const char *restrict src);
// The strncat() function is similar, except that:
// it will use at most n bytes from src; and
// src does not need to be null-terminated if it contains n or more bytes.
// If src contains n or more bytes, strncat() writes n+1 bytes to dest
// (n from src plus the terminating null byte).
// Therefore, the size of dest must be at least strlen(dest)+n+1.
char *strncat(char *restrict dest, const char *restrict src, size_t n);

// The strchr() function returns a pointer to the first occurrence of
// the character c in the string s.
// If not found, return NULL.
char *strchr(const char *s, int c);
// The strrchr() function returns a pointer to the last occurrence of
// the character c in the string s. (reverse)
char *strrchr(const char *s, int c);

// The strstr() function finds the first occurrence of the substring
// needle in the string haystack.
// The terminating null bytes ('\0') are not compared.
// 如果没有在haystack中找到needle,返回NULL。
char *strstr(const char *haystack, const char *needle);
// 输出errnum对应的字符串的指针
char *strerror(int errnum);

// 测试:
#include <stdio.h>
#include <string.h>

int main(){
    for(int i=0; i<255; ++i)
        printf("%d: %s\n", i, strerror(i));
    return 0;
}
// 部分输出:
...
132: Operation not possible due to RF-kill
133: Memory page has hardware error
134: Unknown error 134
135: Unknown error 135
...

mem系列接口

关于memcpy和memmove的异同

#include <string.h>

// The memset() function fills the first n bytes of the memory area
// pointed to by s with the constant byte c.
void *memset(void *s, int c, size_t n);

// The memcpy() function copies n bytes from memory area src to memory
// area dest.  The memory areas must not overlap. 
// Use memmove(3) if the memory areas do overlap.
void *memcpy(void *restrict dest, const void *restrict src, size_t n);

// The memmove() function copies n bytes from memory area src to memory
// area dest.  The memory areas may overlap: copying takes place as
// though the bytes in src are first copied into a temporary array
// that does not overlap src or dest, and the bytes are then copied
// from the temporary array to dest.
void *memmove(void *dest, const void *src, size_t n);

// The memchr() function scans the initial n bytes of the memory area
// pointed to by s for the first instance of c. Both c and the bytes
// of the memory area pointed to by s are interpreted as unsigned char.
// A pointer is returned, which points to the matching byte 
// or NULL if the character does not occur in the given memory area.
void *memchr(const void *s, int c, size_t n);

// The memcmp() function compares the first n bytes
// (each interpreted as unsigned char) of the memory areas s1 and s2.
int memcmp(const void *s1, const void *s2, size_t n);

关于memcmp的性能测试

memcmp的实现,应该不会是单个个的char做比较。

stdbool.h

引用这个头,就可以方便的使用booltruefalse这几个关键词。

stdarg.h

#incldue <stdarg.h>

void va_start(va_list ap, last);
type va_arg(va_list ap, type);
void va_end(va_list ap);
void va_copy(va_list dest, va_list src);

int vprintf(const char *restrict format, va_list ap);
int vfprintf(FILE *restrict stream,
            const char *restrict format, va_list ap);
int vdprintf(int fd,
            const char *restrict format, va_list ap);
int vsprintf(char *restrict str,
            const char *restrict format, va_list ap);
int vsnprintf(char *restrict str, size_t size,
            const char *restrict format, va_list ap);

这几个接口的使用,请参考:实现参数数量不定的函数接口

iso646.h

包含此头文件后,C代码就可以使用如下定义,也可以可以提高代码的可读性,这些定义可以在C++环境下直接使用。下面的定义,也是iso646.h文件的全部内容:

#ifndef _ISO646_H
#define _ISO646_H

#ifndef __cplusplus
#define and     &&
#define and_eq  &=
#define bitand  &
#define bitor   |
#define compl   ~
#define not     !
#define not_eq  !=
#define or      ||
#define or_eq   |=
#define xor     ^
#define xor_eq  ^=
#endif

#endif

本文链接:https://cs.pynote.net/sf/c/202208052/

-- EOF --

-- MORE --