一站式学习C编程

上篇 C语言入门

第1章 程序的基本概念

1.1 程序和编程语言

解释执行的语言:
优点:

跨平台性: 解释器通常是与平台无关的,因此相同的源代码可以在不同的平台上运行,而不需要重新编译。
灵活性: 修改源代码后可以立即运行,无需等待编译过程。
调试方便: 在运行时检查错误,可以逐行调试,更容易发现和修复问题。
学习和使用简单: 对于初学者来说,通常更容易学习和理解。
缺点:

性能相对较低: 解释执行通常比编译执行慢,因为代码在运行时被逐行解释。
依赖解释器: 在目标机器上必须存在相应的解释器,限制了程序的独立性。
源代码需要保护: 因为源代码是直接可见的,所以可能存在安全性和知识产权的问题。
编译执行的语言:
优点:

性能较高: 编译过程将源代码转换为机器代码,执行速度更快。
独立性: 编译后的程序通常不依赖于源代码或编译器,可以在不同的计算机上运行。
代码隐藏: 由于编译后的代码不是直接可读的源代码,可以更好地保护知识产权和程序的安全性。
缺点:

平台依赖性: 编译生成的代码通常与特定平台相关,需要重新编译才能在不同的平台上运行。
编译时间较长: 编译执行需要花费时间将源代码转换为机器代码,这可能会导致开发周期的延长。
调试相对困难: 调试通常需要在源代码级别进行,而不是在生成的机器代码上进行,这可能使调试变得更加复杂。
选择解释执行还是编译执行通常取决于具体的应用场景和需求。有些语言甚至采用了混合模式,结合了两种执行方式的优点。

1.4 第一个程序

#include <stdio.h>
/* main: generate some simple output */
int main(void)
{
    printf(0);
    return 0;
}
gcc -Wall main.c
1.  warning: argument 1 null where non-null expected [-Wnonnull]

这是一个编译器或静态代码分析工具发出的警告信息,提示在某个地方使用了一个被声明为不可为空(non-null)的参数,但实际传入的值是空(null)。

具体来说,这种警告的意思是:

argument 1: 表示在函数或方法的调用中,问题出现在第一个参数。

null: 表示实际传入的值是空(null)。

non-null expected: 表示在该位置,该参数是不可为空的,也就是说,该参数不应该接受空值。

[-Wnonnull]: 这部分通常是指警告的标志或编译选项,这里的“-Wnonnull”可能是告诉编译器要检测非空性的警告。

这样的警告通常是为了帮助开发者在编译阶段发现一些潜在的程序错误,特别是在使用编程语言支持空值和非空值的情况下。在实际代码中,你可能需要检查在这个位置上的参数是否应该为非空,并确保传入的值符合预期。解决这个问题的方式可能包括添加空值检查、调整参数类型或确保传入的参数不为空。

2. warning: null format string [-Wformat-overflow=]

这一部分的警告是告诉你在 main.c 文件的 main 函数的第 5 行调用 printf 时,传递了一个空的格式化字符串。这也是一个潜在的错误,因为 printf 函数需要一个格式化字符串来指定如何输出后续参数。

你应该确保在 printf 中传递一个有效的格式化字符串,例如 printf("%d", 0);,这里 %d 表示将整数 0 以十进制形式输出。

第2章 常量、变量和表达式

2.2 常量

以下是一个简单的C程序演示如何在 printf 中表示 % 字符:

#include <stdio.h>

int main() {
    // 使用 %% 来表示 %
    printf("This is a percent sign: %%\n");

    return 0;
}

在这个例子中,printf(“This is a percent sign: %%\n”); 会输出 “This is a percent sign: %"。在格式化字符串中,%% 被解释为一个普通的 % 字符。

2.5 表达式

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

int main() {
    int x = 17;
    int n = 4;

    int result = (x + n - 1) / n;

    printf("向上取整的结果为: %d\n", result);

    return 0;
}

这个表达式的核心思想是,在除法操作之前,先将被除数 x 加上 n - 1,这样就确保了在进行除法操作时,能够达到向上取整的效果。在这个例子中,结果是 (17 + 4 - 1) / 4,即 20 / 4,结果为 5。

这个方法适用于任何正整数 x 和正整数 n 的情况。

第3章 简单函数

3.1 数学函数

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

int main(void)
{
    double pi = 3.1416;
    printf("sin(pi/2)=%f\nln1=%f\n", sin(pi/2), log(1.0));
    return 0;
}
gcc -Wall main.c -o main

/usr/bin/ld: /tmp/ccuTgF4O.o: in function `main':
main.c:(.text+0x31): undefined reference to `sin'
collect2: error: ld returned 1 exit status

在链接阶段“对 sin’” error typically occurs when the linker is unable to find the implementation of the sin` 函数的未定义引用。此问题通常与未链接的数学库有关。

gcc your_program.c -o your_program -lm

-lm 在链接阶段添加数学库

使用math.h中声明的库函数还有一点特殊之 处,gcc命令行必须加-lm选项,因为数学函数位于 libm.so库文件中(这些库文件通常位于/lib目录下),-lm选项告诉编译器,我们程序中用到的数学函数要到这个库文件里找。

第4章 分支语句

4.2 if else

#include <stdio.h>

void printDigits(int x) {
    // 获取十位和个位数字
    int tenDigit = (x / 10) % 10;
    int unitDigit = x % 10;

    // 打印十位和个位数字
    printf("十位数字:%d,个位数字:%d\n", tenDigit, unitDigit);
}

int main() {
    // 示例:调用函数并传递整数值
    int number = 42;
    printDigits(number);

    return 0;
}

第5章 深入理解函数

#include <stdio.h>

// 布尔函数,判断是否为闰年
int is_leap_year(int year) {
    // 如果能被400整除,或者能被4整除但不能被100整除,则是闰年
    if ((year % 400 == 0) || ((year % 4 == 0) && (year % 100 != 0))) {
        return 1; // 是闰年
    } else {
        return 0; // 不是闰年
    }
}

int main() {
    // 示例:调用函数并传递年份值
    int year = 2024;

    if (is_leap_year(year)) {
        printf("%d年是闰年\n", year);
    } else {
        printf("%d年不是闰年\n", year);
    }

    return 0;
}
#include <math.h>

// 四舍五入函数
double myround(double x) {
    return (x >= 0.0) ? floor(x + 0.5) : ceil(x - 0.5);
}

int main() {
    // 示例:调用函数并传递小数值
    double num1 = -3.51;
    double num2 = 4.49;

    printf("myround(%g) = %g\n", num1, myround(num1));
    printf("myround(%g) = %g\n", num2, myround(num2));

    return 0;
}

第6章 循环语句

6.4 break和continue语句

void diamond(int x, char m)
{
	if(x%2 != 0){
        int n = x/2 +1;
        for(int y=1; y <= n; y++) {
            for (int i=1;i<=(n-y);i++) printf(" ");
            for (int i=1;i<=(y*2-1);i++) printf("%c",m);
            for (int i=1;i<=(n-y);i++) printf(" ");
            printf("\n");
        }
        for(int y=n-1; y >= 1; y--) {
            for (int i=1;i<=(n-y);i++) printf(" ");
            for (int i=1;i<=(y*2-1);i++) printf("%c",m);
            for (int i=1;i<=(n-y);i++) printf(" ");
            printf("\n");
	    }
    }
    else printf("Error!\n");
}

第7章 结构体

7.2 数据抽象

实现一个用分子分母的格式来表示有理数的 结构体rational以及相关的函数,rational结构体之间可以做加减乘除运算,运算的结果仍然是 rational。测试代码如下: 注意要约分为最简分数,例如1/8和-1/8相减的 打印结果应该是1/4而不是2/8,可以利用第5.3节 习题中的Euclid算法来约分。在动手编程之前先思 考一下这个问题实现了什么样的数据抽象,抽象层 应该由哪些函数组成。

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

// 递归函数实现 Euclid 算法求最大公约数
int gcd(int a, int b)
{
    if (abs(b) == 0) {
        return abs(a); // 当b为0时,a即为最大公约数
    } else {
        return gcd(b, a % b); // 否则,递归调用gcd函数
    }
}
//最小公倍数
int cm(int a, int b)
{
    return a*b/gcd(a,b);
}
//定义分数 结构体
struct rational
{
    int x, y;
};

struct rational make_rational(int a, int b)
{
    struct rational z;
    if(b < 0)
    {
        a = -a;
        b = -b;
    }

    int gv = gcd(a,b);
    z.x = a / gv;
    z.y = b / gv;
    return z;
}
struct rational add_rational(struct rational a, struct rational b)
{
    struct rational z;
    int cmv = cm(a.y,b.y);
    z.y = cmv;
    z.x = cmv / a.y * a.x + cmv / b.y * b.x;
    z = make_rational(z.x,z.y);
    return z;
}
struct rational sub_rational(struct rational a, struct rational b)
{
    struct rational z;
    int cmv = cm(a.y,b.y);
    z.y = cmv;
    z.x = cmv / a.y * a.x - cmv / b.y * b.x;
    z = make_rational(z.x,z.y);
    return z;
}

struct rational mul_rational(struct rational a, struct rational b)
{
    struct rational z;
    z.y = a.y * b.y;
    z.x = a.x * b.x;
    z = make_rational(z.x,z.y);
    return z;
}
struct rational div_rational(struct rational a, struct rational b)
{
    struct rational z;
    z.y = a.y * b.x;
    z.x = a.x * b.y;
    z = make_rational(z.x,z.y);
    return z;
}

void print_rational(struct rational c)
{
    printf("%d/%d\n",c.x,c.y);
}


int main(void)
{
    struct rational a = make_rational(1,8);
    struct rational b = make_rational(-1,8);
    print_rational(add_rational(a,b));
    print_rational(sub_rational(a,b));
    print_rational(mul_rational(a,b));
    print_rational(div_rational(a,b));
    return 0;
}

专业英语

文档信息