C语言常用库函数大盘点,你想看的都在这

C语言作为一门高效的编程语言,其强大的功能部分归功于其标准库(Standard Library)提供的丰富函数集合。这些库函数帮助开发者轻松完成输入输出、字符串处理、字符处理、数学运算、内存管理、时间日期处理、文件操作以及进程控制等任务。下面,我们将逐一详细介绍这些方面的常用库函数。

1. 输入输出函数

2. scanf()

函数原型

int scanf(const char *format, ...);

功能: scanf() 函数从标准输入(通常是键盘)读取并格式化输入。它根据提供的格式字符串 format 来解析输入,并将结果存储在后续参数指定的变量中。

用法示例

#include <stdio.h>
int main() {
int age;
float height;
printf("Enter your age and height: ");
scanf("%d %f", &age, &height);
printf("You are %d years old and %.2f meters tall.\n", age, height);
return 0;
}

注意

3. puts()

函数原型

int puts(const char *s);

功能: puts() 函数输出一个字符串到标准输出,并在末尾自动添加换行符。它不接受格式字符串,只是简单地输出提供的字符串和一个换行符。

用法示例

#include <stdio.h>
int main() {
puts("Hello, World!");
return 0;
}

注意

4. gets()(不推荐使用)

函数原型(尽管不推荐,但了解其基本形式):

char *gets(char *s);

功能(尽管不推荐): gets() 函数从标准输入读取一行直到换行符,但不包括换行符本身,并将读取的字符串(包括终止的空字符 \0)存储在提供的字符数组中。然而,由于它无法防止缓冲区溢出,因此已被弃用并从许多现代C标准库中移除。

不推荐使用的原因

替代方案

5. putchar()

函数原型

int putchar(int char);

功能: putchar() 函数输出一个字符到标准输出。尽管其参数是 int 类型,但它只输出该整数的低8位(即ASCII码值对应的字符)。

用法示例

#include <stdio.h>
int main() {
putchar('A');
putchar('\n'); // 输出换行符
return 0;
}
6. getchar()

函数原型

int getchar(void);

功能: getchar() 函数从标准输入读取下一个字符(等待用户输入),并将其作为无符号字符(但通常作为 int 返回,以便区分EOF)返回。

用法示例

#include <stdio.h>
int main() {
char c;
c = getchar(); // 读取一个字符
putchar(c); // 输出该字符
putchar('\n'); // 输出换行符
return 0;
}

注意

注意事项

示例:使用 fgets()

#include <stdio.h>
int main() {
char buffer[100];
printf("Enter a line of text: ");
if (fgets(buffer, sizeof(buffer), stdin) != NULL) {
// fgets 会读取换行符(如果缓冲区空间允许)并存储在字符串中
// 可以选择移除字符串末尾的换行符
size_t len = strlen(buffer);
if (len > 0 && buffer[len - 1] == '\n') {
buffer[len - 1] = '\0'; // 移除换行符
}
printf("You entered: %s\n", buffer);
} else {
// fgets 返回 NULL 通常意味着发生了错误或到达了文件末尾(对于 stdin 来说不太可能)
printf("An error occurred or EOF reached.\n");
}
return 0;
}

在这个示例中,fgets() 被用来安全地读取一行文本,并通过检查字符串末尾是否包含换行符来决定是否移除它。这是处理用户输入时的一个常见需求。

  • printf():用于向标准输出(通常是屏幕)打印格式化的字符串。
  • scanf():从标准输入(通常是键盘)读取并格式化输入。
  • puts():输出一个字符串到标准输出,并在末尾自动添加换行符。
  • gets()(不推荐使用,存在安全风险):从标准输入读取一行直到换行符,但不包括换行符本身。
  • putchar():输出一个字符到标准输出。
  • getchar():从标准输入读取下一个字符。

    输入输出函数的详解及用法

    1. printf()

    函数原型

    int printf(const char *format, ...);

    功能: printf() 函数用于向标准输出(通常是屏幕)打印格式化的字符串。它根据提供的格式字符串 format 来格式化后续参数,并将结果输出到标准输出。

    用法示例

    #include <stdio.h>
    int main() {
    int number = 10;
    float pi = 3.14159;
    printf("The number is %d and pi is approximately %.2f.\n", number, pi);
    return 0;
    }

    注意

  • %d 用于整数。
  • %.2f 用于浮点数,保留两位小数。
  • \n 是换行符。
  • 变量前的 & 是取地址符,因为 scanf() 需要变量的地址来存储输入的数据。
  • 输入时,数据之间可以有空格,但格式字符串中的空格用于匹配输入中的空白字符(如空格、制表符或换行符)。
  • 输出的字符串末尾会自动添加一个换行符。
  • 缓冲区溢出风险:如果输入的字符串超过了目标数组的大小,gets() 会继续读取并覆盖数组边界之外的内存,这可能导致程序崩溃或安全漏洞。
  • 使用 fgets(),它允许指定最大读取长度,从而防止缓冲区溢出。
  • 当输入到达文件末尾(EOF)时,getchar() 会返回 EOF,这是一个在 <stdio.h> 中定义的宏,通常等于 -1。但是,因为 getchar() 的返回类型是 int 而不是 char,所以它可以表示所有可能的字符值以及 EOF。
  • EOF:在文件结束或发生读取错误时,getchar() 会返回 EOF。为了区分正常的字符和 EOF,通常将 getchar() 的结果存储在一个 int 类型的变量中,然后检查该变量是否等于 EOF。

  • 缓冲区:标准输入(stdin)是缓冲的,这意味着在读取下一个字符之前,可能会先在内部缓冲区中累积一些输入。这通常对程序是透明的,但在某些情况下(如混合使用 scanf() 和 getchar() 时),可能会遇到意外行为,因为 scanf() 可能会在读取后留下换行符或其他字符在缓冲区中。

  • 替代 gets():如上所述,gets() 函数因为安全原因已被弃用。应使用 fgets() 来替代,fgets() 函数可以从指定的流中读取一行,并将结果存储在字符串中,直到遇到换行符、文件结束符 EOF 或已读取了 n-1 个字符为止(n 是指定的最大读取长度),并在字符串末尾添加一个空字符 \0

2. 字符串处理

  • strcpy():复制字符串,包括结束符\0
  • strcat():连接两个字符串。
  • strlen():计算字符串的长度,不包括结束符\0
  • strcmp():比较两个字符串,按字典顺序。
  • strncpy():复制指定长度的字符串,确保目标字符串以\0结尾。
  • strstr():查找子串在字符串中首次出现的位置。
  • sprintf():将格式化的数据写入字符串。

    字符串处理函数的详解及用法

    1. strcpy()

    函数原型

    char *strcpy(char *dest, const char *src);

    功能: strcpy() 函数用于复制字符串 src(包括其结尾的空字符 \0)到字符串 dest 所指向的数组中。注意,dest 必须有足够的空间来存放要复制的字符串。

    用法示例

    #include <stdio.h>
    #include <string.h>
    int main() {
    char src[] = "Hello, World!";
    char dest[50]; // 确保有足够的空间
    strcpy(dest, src);
    printf("Copied string: %s\n", dest);
    return 0;
    }
    2. strcat()

    函数原型

    char *strcat(char *dest, const char *src);

    功能: strcat() 函数将字符串 src 连接到字符串 dest 的末尾,并包括 src 的结尾空字符 \0。同样,dest 必须有足够的空间来存放两个字符串连接后的结果。

    用法示例

    #include <stdio.h>
    #include <string.h>
    int main() {
    char dest[50] = "Hello, ";
    const char *src = "World!";
    strcat(dest, src);
    printf("Concatenated string: %s\n", dest);
    return 0;
    }
    3. strlen()

    函数原型

    size_t strlen(const char *str);

    功能: strlen() 函数计算并返回给定字符串 str 的长度,不包括结尾的空字符 \0

    用法示例

    #include <stdio.h>
    #include <string.h>
    int main() {
    const char *str = "Hello, World!";
    size_t len = strlen(str);
    printf("Length of the string: %zu\n", len);
    return 0;
    }
    4. strcmp()

    函数原型

    int strcmp(const char *str1, const char *str2);

    功能: strcmp() 函数比较两个字符串 str1 和 str2。如果 str1 和 str2 字符串相等,则返回 0;如果 str1 在字典序上小于 str2,则返回负值;如果 str1 在字典序上大于 str2,则返回正值。

    用法示例

    #include <stdio.h>
    #include <string.h>
    int main() {
    const char *str1 = "apple";
    const char *str2 = "banana";
    int result = strcmp(str1, str2);
    if (result < 0) {
    printf("\"%s\" is less than \"%s\"\n", str1, str2);
    } else if (result > 0) {
    printf("\"%s\" is greater than \"%s\"\n", str1, str2);
    } else {
    printf("\"%s\" is equal to \"%s\"\n", str1, str2);
    }
    return 0;
    }
    5. strncpy()

    函数原型

    char *strncpy(char *dest, const char *src, size_t n);

    功能: strncpy() 函数将字符串 src 的前 n 个字符复制到 dest 指向的数组中(不会复制 src 的结尾空字符 \0,除非它是被复制的 n 个字符之一)。如果 src 的长度小于 n,则结果字符串将用空字符 \0 填充直到总共复制了 n 个字符。

    用法示例

    #include <stdio.h>
    #include <string.h>
    int main() {
    char dest[20];
    const char *src = "Hello, World!";
    strncpy(dest, src, 5);
    dest[5] = '\0'; // 确保字符串以 '\0' 结尾
    printf("Copied string with strncpy: %s\n", dest);
    return 0;
    }

    注意:在上面的示例中,我手动添加了 '\0' 来确保 dest 是一个有效的字符串。在使用 strncpy() 时,这是一个常见的做法,因为 strncpy() 不会总是自动添加结尾的空字符 \0(如果源字符串的长度小于指定的 n 个字符,则不会添加额外的 \0)。

    6. strstr()

    函数原型

    char *strstr(const char *str, const char *substr);

    功能: strstr() 函数在字符串 str 中查找第一次出现子字符串 substr 的位置,并返回从该位置开始到 str 结尾的所有字符的指针。如果没有找到子字符串,则返回 NULL

    用法示例

    #include <stdio.h>
    #include <string.h>
    int main() {
    const char *str = "Hello, World!";
    const char *substr = "World";
    char *found = strstr(str, substr);
    if (found) {
    printf("Substring found: %s\n", found);
    } else {
    printf("Substring not found.\n");
    }
    return 0;
    }
    7. sprintf()

    函数原型

    int sprintf(char *str, const char *format, ...);

    功能: sprintf() 函数根据指定的格式 format 来格式化数据,并将结果写入字符串 str 中。它可以接受多个参数,这些参数将根据 format 指定的格式被转换并插入到结果字符串中。

    用法示例

    #include <stdio.h>
    int main() {
    char buffer[50];
    sprintf(buffer, "The answer is %d", 42);
    printf("Formatted string: %s\n", buffer);
    return 0;
    }

    sprintf() 是非常强大的,但使用时需要注意缓冲区溢出的风险,特别是当目标字符串 str 的大小不足以容纳格式化后的字符串时。

3. 字符处理

用法示例

#include <stdio.h>
#include <ctype.h>
int main() {
char ch1 = 'A';
char ch2 = 'a';
char ch3 = '5';
if (isupper(ch1)) {
printf("%c is an uppercase letter.\n", ch1);
} else {
printf("%c is not an uppercase letter.\n", ch1);
}
if (islower(ch2)) {
printf("%c is a lowercase letter.\n", ch2);
} else {
printf("%c is not a lowercase letter.\n", ch2);
}
// 注意:非字母字符不会返回true
if (isupper(ch3)) {
printf("%c is an uppercase letter.\n", ch3);
} else {
printf("%c is not an uppercase letter.\n", ch3);
}
if (islower(ch3)) {
printf("%c is a lowercase letter.\n", ch3);
} else {
printf("%c is not a lowercase letter.\n", ch3);
}
return 0;
}

注意

  • tolower():将大写字母转换为小写字母。
  • toupper():将小写字母转换为大写字母。
  • isalpha():检查字符是否为字母。
  • isdigit():检查字符是否为数字。
  • isspace():检查字符是否为空白字符(如空格、制表符等)。
  • isupper() 和 islower():分别检查字符是否为大写或小写字母。

    字符处理函数的详解及用法

    1. tolower()

    函数原型

    int tolower(int c);

    功能: 将大写字母转换为小写字母。如果参数c不是大写字母,则不改变其值。

    用法示例

    #include <stdio.h>
    #include <ctype.h>
    int main() {
    char ch = 'A';
    printf("Original: %c, Lowercase: %c\n", ch, tolower(ch));
    // 注意:非字母字符不受影响
    ch = '*';
    printf("Original: %c, Lowercase: %c\n", ch, tolower(ch));
    return 0;
    }
    2. toupper()

    函数原型

    int toupper(int c);

    功能: 将小写字母转换为大写字母。如果参数c不是小写字母,则不改变其值。

    用法示例

    #include <stdio.h>
    #include <ctype.h>
    int main() {
    char ch = 'a';
    printf("Original: %c, Uppercase: %c\n", ch, toupper(ch));
    // 注意:非字母字符不受影响
    ch = '1';
    printf("Original: %c, Uppercase: %c\n", ch, toupper(ch));
    return 0;
    }
    3. isalpha()

    函数原型

    int isalpha(int c);

    功能: 检查参数c是否为字母(大写或小写)。

    用法示例

    #include <stdio.h>
    #include <ctype.h>
    int main() {
    char ch1 = 'a';
    char ch2 = '3';
    if (isalpha(ch1)) {
    printf("%c is an alphabet.\n", ch1);
    } else {
    printf("%c is not an alphabet.\n", ch1);
    }
    if (isalpha(ch2)) {
    printf("%c is an alphabet.\n", ch2);
    } else {
    printf("%c is not an alphabet.\n", ch2);
    }
    return 0;
    }
    4. isdigit()

    函数原型

    int isdigit(int c);

    功能: 检查参数c是否为数字(0-9)。

    用法示例

    #include <stdio.h>
    #include <ctype.h>
    int main() {
    char ch1 = '5';
    char ch2 = 'a';
    if (isdigit(ch1)) {
    printf("%c is a digit.\n", ch1);
    } else {
    printf("%c is not a digit.\n", ch1);
    }
    if (isdigit(ch2)) {
    printf("%c is a digit.\n", ch2);
    } else {
    printf("%c is not a digit.\n", ch2);
    }
    return 0;
    }
    5. isspace()

    函数原型

    int isspace(int c);

    功能: 检查参数c是否为空白字符(如空格、制表符、换行符等)。

    用法示例

    #include <stdio.h>
    #include <ctype.h>
    int main() {
    char ch1 = ' ';
    char ch2 = 'a';
    if (isspace(ch1)) {
    printf("%c is a whitespace character.\n", ch1);
    } else {
    printf("%c is not a whitespace character.\n", ch1);
    }
    if (isspace(ch2)) {
    printf("%c is a whitespace character.\n", ch2);
    } else {
    printf("%c is not a whitespace character.\n", ch2);
    }
    return 0;
    }
    6. isupper() 和 islower()

    函数原型

    int isupper(int c);
    int islower(int c);

    功能

  • isupper():检查参数c是否为大写字母。
  • islower():检查参数c是否为小写字母。
  • 这些函数接受一个int类型的参数,但是实际上这个参数应该是一个字符(即char类型,通常会被自动提升为int)。这是因为函数的实现需要区分EOF(文件结束标志)和有效的字符,而EOF通常被定义为-1,这在char类型中无法直接表示。
  • 如果传递给这些函数的参数不是有效的单字节字符(例如,在多字节字符集环境中使用),则结果可能是未定义的。在处理国际化字符时,应该使用适当的国际化库函数。
  • 这些函数都是针对C语言设计的,但在其他支持类似C库的环境中(如C++,尽管C++有自己的字符串和字符处理方式),这些函数通常也是可用的,或者通过适当的头文件(如<cctype>在C++中)提供。

4. 数学运算

用法示例

#include <stdio.h>
#include <math.h>
int main() {
double angleRadians = M_PI / 4;
printf("sin(%.2f radians) = %.2f\n", angleRadians, sin(angleRadians));
printf("cos(%.2f radians) = %.2f\n", angleRadians, cos(angleRadians));
printf("tan(%.2f radians) = %.2f\n", angleRadians, tan(angleRadians));
return 0;
}

asin(), acos(), atan()

函数原型

double asin(double x);
double acos(double x);
double atan(double x);

功能

用法示例

#include <stdio.h>
#include <math.h>
int main() {
double sinValue = 0.5;
double resultRadians = asin(sinValue);
double degrees = resultRadians * (180.0 / M_PI);
printf("asin(%.2f) = %.2f radians (%.2f degrees)\n", sinValue, resultRadians, degrees);
// 类似地,可以使用acos和atan
return 0;
}

ceil() 和 floor()

函数原型

double ceil(double x);
double floor(double x);

功能

ceil()

函数原型

double ceil(double x);

功能: ceil()函数将x向上取整到最接近的整数。如果x是一个整数,则返回其本身;如果x是正数且非整数,则返回大于x的最小整数;如果x是负数,则返回小于或等于x的最大整数(注意,这仍然是向下取整到最接近的整数,但在数轴上方向是向上的,因为整数集是离散的)。

用法示例

#include <stdio.h>
#include <math.h>
int main() {
double num1 = 3.2;
double num2 = -3.8;
double result1 = ceil(num1);
double result2 = ceil(num2);
printf("ceil(%.2f) = %.0f\n", num1, result1);
printf("ceil(%.2f) = %.0f\n", num2, result2);
return 0;
}

输出:

ceil(3.20) = 4
ceil(-3.80) = -3

floor()

函数原型

double floor(double x);

功能: floor()函数将x向下取整到最接近的整数。如果x是一个整数,则返回其本身;如果x是正数且非整数,则返回小于x的最大整数;如果x是负数,则返回小于x的最大整数(这实际上是直接向下取整)。

用法示例

#include <stdio.h>
#include <math.h>
int main() {
double num1 = 3.2;
double num2 = -3.8;
double result1 = floor(num1);
double result2 = floor(num2);
printf("floor(%.2f) = %.0f\n", num1, result1);
printf("floor(%.2f) = %.0f\n", num2, result2);
return 0;
}

输出:

floor(3.20) = 3
floor(-3.80) = -4

round()

函数原型: 虽然C标准库中没有直接命名为round()的函数来执行四舍五入到最接近的整数,但大多数C实现(如GNU C Library, glibc)提供了这样的函数。

double round(double x);

功能: round()函数将x四舍五入到最接近的整数。如果x恰好位于两个整数的中间,则结果将四舍五入到偶数。这是所谓的“银行家舍入法”。

用法示例

#include <stdio.h>
#include <math.h>
int main() {
double num1 = 3.5;
double num2 = 4.5;
double num3 = 2.3;
double num4 = -2.3;
double result1 = round(num1);
double result2 = round(num2);
double result3 = round(num3);
double result4 = round(num4);
printf("round(%.2f) = %.0f\n", num1, result1);
printf("round(%.2f) = %.0f\n", num2, result2);
printf("round(%.2f) = %.0f\n", num3, result3);
printf("round(%.2f) = %.0f\n", num4, result4);
return 0;
}

输出(注意银行家舍入法):

round(3.50) = 4
round(4.50) = 4
round(2.30) = 2
round(-2.30) = -2

请注意,round()函数返回的是double类型,但通常四舍五入的结果是一个整数,因此可以直接赋值给int类型的变量(如果需要的话,并且不介意潜在的类型转换)。然而,在某些情况下(如恰好位于两个整数中间时),结果可能仍然是一个double类型的浮点数。

  • sqrt():计算平方根。
  • pow():计算x的y次幂。
  • fabs():计算浮点数的绝对值。
  • sin()cos()tan():分别计算正弦、余弦、正切值。
  • asin()acos()atan():分别计算反正弦、反余弦、反正切值。
  • ceil() 和 floor():分别向上或向下取整到最接近的整数。
  • round():四舍五入到最接近的整数。

    sqrt()

    函数原型

    double sqrt(double x);

    功能:计算并返回参数x的平方根。如果x是负数,则结果是一个NaN(Not a Number)值。

    用法示例

    #include <stdio.h>
    #include <math.h>
    int main() {
    double num = 16.0;
    double result = sqrt(num);
    printf("The square root of %.2f is %.2f\n", num, result);
    return 0;
    }

    pow()

    函数原型

    double pow(double x, double y);

    功能:计算并返回xy次幂(x^y)。

    用法示例

    #include <stdio.h>
    #include <math.h>
    int main() {
    double base = 2.0;
    double exponent = 3.0;
    double result = pow(base, exponent);
    printf("%.0f to the power of %.0f is %.0f\n", base, exponent, result);
    return 0;
    }

    fabs()

    函数原型

    double fabs(double x);

    功能:计算并返回浮点数x的绝对值。

    用法示例

    #include <stdio.h>
    #include <math.h>
    int main() {
    double num = -123.45;
    double absNum = fabs(num);
    printf("The absolute value of %.2f is %.2f\n", num, absNum);
    return 0;
    }

    sin(), cos(), tan()

    函数原型

    double sin(double x);
    double cos(double x);
    double tan(double x);

    功能

  • sin(x):计算x(以弧度为单位)的正弦值。
  • cos(x):计算x(以弧度为单位)的余弦值。
  • tan(x):计算x(以弧度为单位)的正切值。
  • asin(x):计算x的反正弦值,返回的角度在[-π/2, π/2]范围内,以弧度为单位。
  • acos(x):计算x的反余弦值,返回的角度在[0, π]范围内,以弧度为单位。
  • atan(x):计算x的反正切值,返回的角度在[-π/2, π/2]范围内,以弧度为单位。
  • ceil(x):将x向上取整到最接近的整数。当然,以下是对ceil()floor()函数以及round()函数的进一步详解及用法:

5. 内存管理

  • malloc():动态分配指定大小的内存块。
  • calloc():分配内存并清零,常用于分配数组。
  • realloc():调整之前通过malloccalloc分配的内存块大小。
  • free():释放之前分配的内存块。z

    malloc()

    函数原型

    void* malloc(size_t size);

    功能:动态分配指定大小的内存块,并返回一个指向该内存块的指针。如果分配失败,则返回NULL

    用法示例

    #include <stdio.h>
    #include <stdlib.h>
    int main() {
    int *ptr;
    int n = 5; // 假设我们需要5个整数的空间
    ptr = (int*)malloc(n * sizeof(int)); // 分配内存
    if (ptr == NULL) {
    printf("Memory allocation failed!\n");
    return 1;
    }
    // 使用ptr指向的内存
    for(int i = 0; i < n; i++) {
    ptr[i] = i * i;
    }
    // 打印分配的内存中的值
    for(int i = 0; i < n; i++) {
    printf("%d ", ptr[i]);
    }
    // 释放内存
    free(ptr);
    return 0;
    }

    calloc()

    函数原型

    void* calloc(size_t num, size_t size);

    功能:分配一块足够存储numsize大小的元素的内存,并将所有位初始化为0。返回一个指向分配的内存的指针;如果分配失败,则返回NULL

    用法示例

    #include <stdio.h>
    #include <stdlib.h>
    int main() {
    int *ptr;
    int n = 5; // 假设我们需要5个整数的空间
    ptr = (int*)calloc(n, sizeof(int)); // 分配并初始化内存
    if (ptr == NULL) {
    printf("Memory allocation failed!\n");
    return 1;
    }
    // 使用ptr指向的内存(注意:calloc已经初始化为0)
    for(int i = 0; i < n; i++) {
    ptr[i] = i + 100; // 示例赋值
    }
    // 打印分配的内存中的值
    for(int i = 0; i < n; i++) {
    printf("%d ", ptr[i]);
    }
    // 释放内存
    free(ptr);
    return 0;
    }

    realloc()

    函数原型

    void* realloc(void* ptr, size_t size);

    功能:调整之前通过malloccallocrealloc分配的内存块的大小。如果ptrNULL,则realloc的行为与malloc相同。如果调整成功,返回指向新内存块的指针(可能与原指针相同,也可能不同)。如果失败,则返回NULL,但原内存块不会被释放。

    用法示例

    #include <stdio.h>
    #include <stdlib.h>
    int main() {
    int *ptr;
    int n = 5;
    ptr = (int*)malloc(n * sizeof(int));
    if (ptr == NULL) {
    printf("Initial memory allocation failed!\n");
    return 1;
    }
    // 假设我们需要更多空间
    n = 10;
    ptr = (int*)realloc(ptr, n * sizeof(int));
    if (ptr == NULL) {
    printf("Memory reallocation failed!\n");
    free(ptr); // 注意:这里的free调用在逻辑上是多余的,因为ptr已经是NULL了
    return 1;
    }
    // 使用ptr指向的新内存...
    // 释放内存
    free(ptr);
    return 0;
    }

    free()

    函数原型

    void free(void* ptr);

    功能:释放之前通过malloccallocrealloc分配的内存块。如果ptrNULL,则free函数什么也不做。

    注意:释放内存后,应将指向该内存的指针设置为NULL,以避免野指针(dangling pointer)的问题。野指针是指向已经被释放的内存的指针,如果尝试通过野指针访问或修改内存,将导致未定义行为,可能引发程序崩溃或数据损坏。

    用法示例(接上面realloc的示例):

    #include <stdio.h>
    #include <stdlib.h>
    int main() {
    int *ptr;
    int n = 5;
    ptr = (int*)malloc(n * sizeof(int));
    if (ptr == NULL) {
    printf("Initial memory allocation failed!\n");
    return 1;
    }
    // 假设我们需要更多空间
    n = 10;
    ptr = (int*)realloc(ptr, n * sizeof(int));
    if (ptr == NULL) {
    printf("Memory reallocation failed!\n");
    // 注意:如果realloc失败,这里的原始ptr可能仍然是有效的(如果realloc内部决定不移动内存块),
    // 但因为我们不知道这一点,所以最好总是检查realloc的返回值。
    // 不过,如果我们确定要退出,可以安全地跳过这里的free调用(因为程序即将退出),
    // 或者使用原始的ptr调用free(如果确信realloc没有移动内存块)。
    // 这里为了演示,我们假设退出。
    return 1;
    }
    // 使用ptr指向的新内存...
    // 释放内存
    free(ptr);
    // 将指针设置为NULL,避免野指针
    ptr = NULL;
    // 现在ptr是安全的,不会指向任何已分配的内存
    return 0;
    }

    在这个示例中,我们在释放内存后立即将ptr设置为NULL。这是一个好习惯,可以帮助避免在程序的其他部分意外使用到这个已经被释放的指针。记住,free()函数只是释放了内存,让操作系统知道这块内存可以被再次使用,但它不会自动将指针设置为NULL。因此,程序员需要负责将不再使用的指针设置为NULL

6. 时间日期

  • time():获取当前时间(自1970年1月1日以来的秒数)。
  • localtime() 和 gmtime():将time_t值转换为本地时间或格林威治时间。
  • strftime():将时间格式化为字符串。
  • difftime():计算两个时间点之间的差异(以秒为单位)。

    time()

    函数原型

    #include <time.h>
    time_t time(time_t *tloc);

    功能:获取当前时间(自1970年1月1日(称为Unix纪元或Epoch)以来的秒数),并可选地将其存储在提供的time_t类型的变量中。

    用法示例

    #include <stdio.h>
    #include <time.h>
    int main() {
    time_t rawtime;
    time(&rawtime);
    printf("Current time since Epoch: %ld\n", rawtime);
    return 0;
    }

    localtime() 和 gmtime()

    函数原型

    #include <time.h>
    struct tm *localtime(const time_t *timep);
    struct tm *gmtime(const time_t *timep);

    功能:这两个函数都将time_t值(自Epoch以来的秒数)转换为struct tm类型的结构体,其中包含了详细的日期和时间信息(如年、月、日、小时、分钟、秒等)。localtime()将时间转换为本地时区的时间,而gmtime()则将其转换为UTC(格林威治时间)。

    用法示例

    #include <stdio.h>
    #include <time.h>
    int main() {
    time_t rawtime;
    struct tm * timeinfo;
    time(&rawtime);
    timeinfo = localtime(&rawtime);
    printf("Local time and date: %s", asctime(timeinfo));
    timeinfo = gmtime(&rawtime);
    printf("UTC time and date: %s", asctime(timeinfo));
    return 0;
    }

    注意:这里使用了asctime()函数来将struct tm结构体转换为易读的字符串形式。

    strftime()

    函数原型

    #include <time.h>
    size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);

    功能:根据提供的格式字符串format,将struct tm类型的时间信息格式化为字符串。

    用法示例

    #include <stdio.h>
    #include <time.h>
    int main() {
    time_t rawtime;
    struct tm * timeinfo;
    char buffer[80];
    time(&rawtime);
    timeinfo = localtime(&rawtime);
    strftime(buffer, 80, "%Y-%m-%d %H:%M:%S", timeinfo);
    printf("Formatted date and time: %s\n", buffer);
    return 0;
    }

    difftime()

    函数原型

    #include <time.h>
    double difftime(time_t time2, time_t time1);

    功能:计算两个时间点(time1time2)之间的差异,以秒为单位。返回值是time2 - time1的结果,类型为double

    用法示例

    #include <stdio.h>
    #include <time.h>
    int main() {
    time_t start, end;
    double elapsed_time;
    time(&start);
    // 假设这里有一些耗时的操作
    // ...
    time(&end);
    elapsed_time = difftime(end, start);
    printf("Elapsed time: %.2f seconds\n", elapsed_time);
    return 0;
    }

    这些函数共同提供了在C程序中处理和格式化时间的基本能力。

7. 文件操作

示例:使用rename()函数

以下是一个简单的示例,展示了如何使用rename()函数在C语言中重命名文件:

#include <stdio.h>
int main() {
const char *oldname = "oldfile.txt";
const char *newname = "newfile.txt";
if (rename(oldname, newname) == 0) {
printf("File successfully renamed\n");
} else {
perror("Error renaming file");
return 1;
}
return 0;
}

请注意,如果oldname指定的文件不存在,或者由于某些原因(如权限问题)无法重命名该文件,rename()函数将失败,并设置errno以指示错误原因。您可以使用perror()函数打印出与errno相关联的错误消息。

另外,请注意,在某些实现中,如果newname已经存在,并且它是一个文件而不是目录,则rename()函数可能会覆盖它,而不会给出错误。但是,这种行为并不是所有系统都保证的,因此在跨平台编程时应该谨慎处理这种情况。如果可能的话,最好在尝试重命名之前检查目标名称是否已经存在

  • fopen():打开文件。
  • fclose():关闭文件。
  • fread() 和 fwrite():从文件读取或向文件写入数据块。
  • fseek():移动文件指针到指定位置。
  • ftell():返回文件指针的当前位置。
  • remove():删除文件。
  • rename():重命名文件或目录。

    fopen()

    函数原型

    #include <stdio.h>
    FILE *fopen(const char *filename, const char *mode);

    功能:打开文件,并返回一个指向FILE对象的指针,该对象用于后续的文件操作。filename是文件路径和名称,mode是打开文件的模式(如只读、只写、追加等)。

    用法示例

    FILE *fp = fopen("example.txt", "r"); // 以只读模式打开文件
    if (fp == NULL) {
    perror("Error opening file");
    return(-1);
    }
    // 后续文件操作...
    fclose(fp); // 操作完成后关闭文件

    fclose()

    函数原型

    #include <stdio.h>
    int fclose(FILE *stream);

    功能:关闭一个打开的文件流。成功时返回0,失败时返回EOF,并设置errno以指示错误。

    用法示例

    fclose(fp); // 关闭之前打开的文件

    fread() 和 fwrite()

    函数原型

    #include <stdio.h>
    size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream);
    size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream);

    功能fread()用于从指定的文件流中读取数据,fwrite()用于向文件流中写入数据。ptr是指向数据缓冲区的指针,size是每个数据项的大小(以字节为单位),nmemb是要读取或写入的数据项的数量,stream是文件流的指针。

    用法示例

    char buffer[100];
    size_t numRead = fread(buffer, sizeof(char), 100, fp); // 从文件读取数据
    size_t numWritten = fwrite("Hello, world!", sizeof(char), 13, fp); // 向文件写入数据

    fseek()

    函数原型

    #include <stdio.h>
    int fseek(FILE *stream, long offset, int whence);

    功能:将文件指针移动到指定的位置。offset是相对于whence参数的偏移量,whence可以是SEEK_SET(文件开头)、SEEK_CUR(当前位置)、或SEEK_END(文件末尾)。

    用法示例

    fseek(fp, 0, SEEK_SET); // 将文件指针移动到文件开头

    ftell()

    函数原型

    #include <stdio.h>
    long ftell(FILE *stream);

    功能:返回当前文件指针的位置。如果出错,则返回-1L。

    用法示例

    long currentPosition = ftell(fp); // 获取当前文件指针的位置

    remove()

    函数原型

    #include <stdio.h>
    int remove(const char *filename);

    功能:删除指定的文件。成功时返回0,失败时返回非0值。

    用法示例

    if (remove("example.txt") != 0) {
    perror("Error deleting file");
    return(-1);
    }

    rename()

    函数原型(注意:在C标准库中并不直接提供rename函数,但它通常在POSIX系统中可用,并包含在stdio.hunistd.h中):

    #include <stdio.h> // 某些环境可能需要#include <unistd.h>
    int rename(const char *oldname, const char *newname);

    功能:重命名文件或目录。成功时返回0,失败时返回-1,并设置errno以指示错误。

    用法示例

    if (rename("oldfile.txt", "newfile.txt") != 0) {
    perror("Error renaming file");
    return(-1);
    }

    请注意,rename()函数的行为(特别是在跨不同文件系统或不同目录间重命名文件时)可能因操作系统而异。在大多数Unix-like系统(包括Linux和macOS)中,rename()函数能够原子性地重命名或移动文件(如果新名称位于不同的目录中)。这意味着在重命名过程中,文件不会因为系统的其他部分同时访问它而处于不一致的状态。

    然而,在Windows系统中,rename()(如果通过C标准库或类似的接口访问)的行为可能略有不同,特别是在尝试将文件移动到不同的卷(驱动器)上时。在Windows中,这通常需要一个先复制后删除的过程,这可能会引入额外的复杂性,比如文件访问权限问题或需要处理文件名冲突等。

    跨平台注意事项

  • 文件路径:在不同操作系统中,文件路径的表示方式可能不同。Unix-like系统使用正斜杠(/)作为目录分隔符,而Windows则通常使用反斜杠(\),尽管Windows也支持正斜杠。

  • 换行符:文本文件的换行符也可能不同。Unix-like系统使用\n作为换行符,而Windows则使用\r\n。在跨平台编程时,需要注意这一点,尤其是在读写文本文件时。

  • 文件访问权限:在某些操作系统中,文件的访问权限可能受到更严格的控制。在尝试打开、删除或重命名文件之前,请确保您的程序具有适当的权限。

8. 进程控制(通常涉及POSIX或特定操作系统的API)

C标准库本身不直接提供跨平台的进程控制功能,但POSIX标准定义了一系列用于进程控制的函数,如:

  • fork():创建子进程。

  • exec() 系列函数(如execl()execp()execvp()等):在子进程中执行一个新程序。

  • wait() 和 waitpid():等待子进程结束,并回收资源。

  • getpid() 和 getppid():分别获取当前进程的ID和父进程的ID。

  • signal() 和更现代的 sigaction():用于设置信号处理程序,以捕获和响应进程接收到的信号(如SIGINT表示中断信号)。

  • kill() 和 raise()kill() 函数用于向另一个进程发送信号,而 raise() 函数用于向当前进程发送信号。

  • pause():使当前进程暂停执行,直到接收到一个信号。

注意:进程控制函数(如 fork()exec()wait()kill() 等)主要基于 UNIX 和类 UNIX 系统(如 Linux)的 POSIX 标准。在 Windows 系统中,这些函数不直接可用,而需要使用 Windows 特定的 API,如 CreateProcess()TerminateProcess()WaitForSingleObject() 等,来进行进程控制。

进程控制函数的详解及用法

1. fork()

函数原型

#include <unistd.h>
pid_t fork(void);

功能:创建一个新的进程(称为子进程),它是调用进程(称为父进程)的一个副本。

返回值

  • 在父进程中,fork()返回新创建的子进程的PID。
  • 在子进程中,fork()返回0。
  • 如果出现错误,则返回-1。

用法示例

pid_t pid = fork();
if (pid == -1) {
// 错误处理
} else if (pid == 0) {
// 子进程代码
} else {
// 父进程代码
}
2. exec() 系列函数

函数原型(示例为execl()):

#include <unistd.h>
int execl(const char *path, const char *arg, ..., /* (char *) NULL */);

功能:在子进程中加载并执行指定的程序,替换子进程的映像和数据,但进程ID不变。

返回值execl()exec函数在成功时不返回;在出错时返回-1,但实际上由于它们替换了进程映像,出错的情况很少见。

用法示例(使用execl()):

if (fork() == 0) {
execl("/bin/ls", "ls", "-l", (char *)NULL);
// 如果execl执行成功,则不会执行到这里;如果失败,则会执行到这里
perror("execl failed");
exit(EXIT_FAILURE);
}
3. wait() 和 waitpid()

函数原型

#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options);

功能:等待子进程结束,并回收子进程的资源。wait()等待任一子进程,而waitpid()可以等待特定的子进程或一组子进程。

返回值

  • 成功时返回结束的子进程的PID。
  • 如果设置了WNOHANG且没有子进程结束,则返回0。
  • 如果出错,则返回-1。

用法示例(使用wait()):

int status;
pid_t pid = wait(&status);
if (pid == -1) {
// 错误处理
}
// 处理子进程结束
4. getpid() 和 getppid()

函数原型

#include <unistd.h>
pid_t getpid(void);
pid_t getppid(void);

功能getpid()返回当前进程的PID,getppid()返回当前进程的父进程的PID。

用法示例

pid_t pid = getpid();
pid_t ppid = getppid();
printf("PID: %d, PPID: %d\n", pid, ppid);
5. signal() 和 sigaction()

函数原型signal()):

#include <signal.h>
void (*signal(int sig, void (*func)(int)))(int);

函数原型sigaction()):

#include <signal.h>
int sigaction(int sig, const struct sigaction *act, struct sigaction *oact);

功能signal()用于设置信号处理函数,但其行为在不同系统上可能不一致。sigaction()提供了一个更可靠的方式来设置信号处理程序,并支持更复杂的信号处理选项。

用法示例sigaction()):

#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include <unistd.h>
void signal_handler(int signum) {
printf("Caught signal %d\n", signum);
// 清理并退出
exit(signum);
}
int main() {
struct sigaction sa;
sa.sa_handler = signal_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = 0;
sigaction(SIGINT, &sa, NULL);
while (1) {
pause(); // 等待信号
}
return 0;
}
6. kill() 和 raise()

函数原型

#include <signal.h>
int kill(pid_t pid, int sig);
int raise(int sig);

功能kill()用于向另一个进程发送信号,raise()用于向当前进程发送信号。

返回值:成功时返回0,出错时返回-1。

用法示例kill()):

pid_t pid = getpid(); // 仅为示例,实际中应使用要发送信号的进程ID
if (kill(pid, SIGINT) == -1) {
perror("kill failed");
}

用法示例raise()):

if (raise(SIGINT) == -1) {
perror("raise failed");
}
7. pause()

函数原型

#include <unistd.h>
int pause(void);

功能:使调用进程挂起(暂停执行),直到接收到一个信号。

返回值pause()函数总是返回-1,并设置errnoEINTR,表示它被信号中断。

用法示例(已在sigaction()示例中展示):

pause(); // 等待信号

这些函数提供了进程控制的核心功能,是Unix和类Unix系统编程中不可或缺的一部分。在实际应用中,应根据具体需求合理选择和使用这些函数。

总结

C语言的标准库为开发者提供了丰富的函数集,覆盖了从基本输入输出、字符串与字符处理、数学运算到高级的内存管理、时间日期处理、文件操作及进程控制等多个方面。掌握这些常用库函数的使用,对于高效地编写C语言程序至关重要。然而,对于进程控制等特定于操作系统的功能,开发者需要了解并适应不同操作系统平台上的API差异。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值