C语言编程之旅:从入门到实战

第一节课:C 语言基础知识

第一个 C 程序

1. Hello World 代码展示

#include <stdio.h>  // 预处理指令,包含标准输入输出头文件

int main() {  // 主函数,程序入口

   printf("Hello, World!\n");  // 输出函数,将双引号内的内容输出到控制台,\n表示换行

   return 0;  // 返回值,0表示程序正常结束

}
  • #include <stdio.h>:这是预处理指令,<stdio.h>是标准输入输出头文件,包含了输入输出函数(如printf)的声明。在程序编译前,预处理器会将该头文件的内容插入到此处,使得程序可以使用这些函数。

  • int main()main函数是 C 程序的入口点,每个 C 程序都必须有一个main函数。int表示main函数的返回值类型是整数。程序从main函数开始执行。

  • printf("Hello, World!\n");printf是标准库中的输出函数,用于将双引号内的字符串输出到控制台。\n是转义字符,表示换行,即输出完 “Hello, World!” 后光标会移动到下一行。

  • return 0;return语句用于结束函数,并返回一个值。这里返回 0,通常表示程序正常执行结束。在 C 语言中,main函数返回值为 0 表示程序成功完成,非 0 值表示程序执行过程中出现了错误。

2. 程序结构解析

  • 函数的组成:一个函数由函数头和函数体组成。
    int main()是函数头,其中int是返回值类型,main是函数名,括号内可以包含参数列表(这里为空,用void或空括号表示)。{}之间的部分是函数体,包含了函数要执行的具体语句。

  • 头文件的作用:头文件包含了函数声明、类型定义、宏定义等内容。通过#include指令将头文件包含进来,程序就能使用头文件中定义的函数和符号。除了标准库头文件(如stdio.h),我们还可以自定义头文件,用于组织和复用代码。

  • 注释的使用:注释是对代码的解释说明,不会被编译器执行。C 语言中有两种注释方式,一种是单行注释,以//开头,如// 这是单行注释;另一种是多行注释,以/*开头,以*/结尾,如/* 这是多行注释,可以跨越多行 */。合理使用注释可以提高代码的可读性。

变量与数据类型

1. 基本数据类型

  • 整型(int):用于存储整数,在 32 位系统中,通常占用 4 个字节(32 位),取值范围是 - 2147483648 到 2147483647 。

    int num = 10;
    

    定义了一个整型变量num并初始化为 10。

  • 浮点型(float、double)

    • float:单精度浮点型,用于存储小数,占用 4 个字节,能精确表示大约 6 - 7 位有效数字。

      float f = 3.14f;   //注意这里的`f`后缀表示这是一个单精度浮点数。
      
    • double:双精度浮点型,占用 8 个字节,能精确表示大约 15 - 16 位有效数字。

      double d = 3.141592653589793;
      
  • 字符型(char):用于存储单个字符,占用 1 个字节。字符型在内存中存储的是字符对应的 ASCII 码值。

    char ch = 'A';
    

    定义了一个字符型变量ch并初始化为字符'A',其对应的 ASCII 码值是 65。

2. 变量命名规则

  • 变量名只能由字母、数字和下划线组成,且不能以数字开头。例如,age_name是合法的变量名,而123num是非法的。

  • 变量名不能是 C 语言的关键字,如intreturnif等。

  • 变量名应具有描述性,能清晰地表达变量的用途,如用studentAge表示学生的年龄,而不是用无意义的a``b``c

  • 保持命名风格一致性,如统一使用驼峰命名法(如StudentName)或下划线命名法(如student_name)。

3. 变量的声明与初始化

  • 先声明后赋值:先声明变量类型和名称,然后再给变量赋值。

    int num;  // 声明一个整型变量num
    
    num = 20;  // 给num赋值为20
    
  • 一次声明多个变量:可以在同一行声明多个相同类型的变量,用逗号分隔。

    int a, b, c;  // 声明三个整型变量a、b、c
    
    a = 1; b = 2; c = 3;  // 分别给a、b、c赋值
    
  • 初始化:在声明变量的同时给变量赋初值。

    float f = 3.14f;  // 声明并初始化一个单精度浮点型变量f
    
    char ch = 'B';  // 声明并初始化一个字符型变量ch
    
  • 修改变量值:可以在变量声明后随时修改变量的值。

    int num = 10;
    num = num + 5;  // 将num的值修改为15
    

运算符与表达式

1. 算术运算符

  • 加(+):用于两个数相加。例如int sum = 3 + 5;sum的值为 8。

  • 减(-):用于两个数相减。例如int diff = 8 - 3;diff的值为 5。

  • 乘(*):用于两个数相乘。例如int product = 4 * 6;product的值为 24。

  • 除(/):用于两个数相除。注意,整数相除结果为整数,会舍去小数部分。例如int result = 7 / 2;result的值为 3。
    若要得到小数结果,则至少有一个操作数为浮点数,如float fResult = 7.0f / 2;fResult的值为 3.5。

  • 取余(%):用于求两个整数相除的余数,操作数必须为整数。例如int remainder = 7 % 2;remainder的值为 1。

2. 运算符优先级:运算符优先级决定了表达式中各运算符的计算顺序。

在表达式3 + 4 * 2中,乘法的优先级高于加法,所以先计算4 * 2 = 8,再计算3 + 8 = 11
常见的运算符优先级口诀:“括号成员第一;全体单目第二;乘除余三,加减四;移位五,关系六;等于(与)不等排第七;位与异或和位或,“三分天下” 八九十;逻辑或跟与,十一和十二;条件高于赋值,逗号运算级最低”。
通过加括号可以改变运算顺序,如(3 + 4) * 2,先计算括号内的3 + 4 = 7,再计算7 * 2 = 14

输入输出函数

1. printf 函数

  • 基本格式化输出printf函数用于将格式化的数据输出到控制台。

    printf("The number is %d\n", num);
    

    其中%d是格式控制符,表示输出一个十进制整数,num是要输出的变量。

  • 控制输出格式

    • 限定宽度:可以使用%nd来限定输出整数的宽度,n表示宽度。

      int num = 123;
      printf("%5d", num);
      

      会输出 " 123"(前面有两个空格,使总宽度为 5)。

    • 左对齐:默认是右对齐,使用%-nd可实现左对齐。

      printf("%-5d", num);
      

      会输出 "123 "(后面有两个空格,使总宽度为 5)。

    • 显示正负号:使用%+d可显示整数的正负号。

      int num = -10;
      printf("%+d", num);
      

      会输出 “-10”。

    • 限定小数位数:对于浮点数,使用%.nf来限定小数位数,n表示小数位数。

      float f = 3.14159;
      printf("%.2f", f);
      

      会输出 “3.14”。

2. scanf 函数scanf函数用于从标准输入设备(通常是键盘)读取数据。

int num;
scanf("%d", &num);

%d是格式控制符,表示读取一个十进制整数。
&num表示取num的地址,将读取到的数据存储到num变量中。
注意,使用scanf函数时要确保输入的数据类型与格式控制符匹配。

分支结构与循环结构

1. 分支结构

  • if - else 语句:根据条件判断执行不同的代码块。
int num = 10;

if (num > 5) {

   printf("The number is greater than 5\n");

} else {

   printf("The number is less than or equal to 5\n");

}

首先判断num > 5这个条件是否成立,如果成立,执行if后面花括号内的语句,即输出 “The number is greater than 5”;
如果不成立,执行else后面花括号内的语句,即输出 “The number is less than or equal to 5”。

  • if - else if - else 语句:用于多条件判断。
int score = 85;

if (score >= 90) {

   printf("Grade: A\n");

} else if (score >= 80) {

   printf("Grade: B\n");

} else if (score >= 70) {

   printf("Grade: C\n");

} else {

   printf("Grade: D\n");

}

依次判断条件,当score >= 90成立时,输出 “Grade: A”;
不成立则判断score >= 80,成立输出 “Grade: B”;
以此类推,若前面条件都不成立,执行else后面的语句,输出 “Grade: D”。

2. 循环结构

  • for 循环:常用于已知循环次数的情况。例如计算 1 到 10 的累加和:
int sum = 0;

for (int i = 1; i <= 10; i++) {

   sum = sum + i;

}

printf("The sum from 1 to 10 is %d\n", sum);

for循环有三个表达式:
int i = 1是初始化表达式,只在循环开始时执行一次,用于初始化循环变量i为 1;
i <= 10是条件表达式,每次循环开始时判断,若条件成立则执行循环体,否则结束循环;
i++是更新表达式,每次循环结束后执行,用于更新循环变量i,使i自增 1。在循环体中,将i累加到sum中。

  • while 循环:先判断条件,条件成立再执行循环体。例如输出 1 到 5 的数字:
int num = 1;

while (num <= 5) {

   printf("%d ", num);

   num++;

}

首先判断num <= 5是否成立,成立则执行循环体,输出num的值并将num自增 1,然后再次判断条件,直到条件不成立时结束循环。

  • do - while 循环:先执行一次循环体,再判断条件。
int num = 1;

do {

   printf("%d ", num);

   num++;

} while (num <= 5);

先执行循环体,输出num的值并将num自增 1,然后判断num <= 5是否成立,成立则继续执行循环体,否则结束循环。
while循环不同,do - while循环至少会执行一次循环体。

数组

1. 数组的定义:数组是一组相同类型数据的集合,能让我们更方便地管理多个同类型变量。例如,定义一个包含 5 个整数的数组:

int numbers[5];  // 定义一个名为numbers的整型数组,数组大小为5,可存储5个int类型数据

2. 数组的初始化:在定义数组时给元素赋值,避免使用未初始化的“脏数据”。

  • 部分初始化:只初始化数组的部分元素,未初始化的元素会被自动初始化为 0(数值型数组)或空字符(字符型数组)。
int numbers[5] = {1, 2, 3};  // 数组前三个元素为1、2、3,后两个元素自动设为0
  • 完全初始化:初始化数组的所有元素,元素个数需与数组大小一致。例如:
int numbers[5] = {1, 2, 3, 4, 5};  // 5个元素分别对应1、2、3、4、5
  • 省略数组大小初始化:初始化时不写数组大小,编译器会根据初始化值的个数自动计算大小。例如:
int numbers[] = {1, 2, 3, 4, 5};  // 编译器自动确定数组大小为5,无需手动计算

3. 数组元素的访问:通过“数组名[下标]”访问,下标从 0 开始(即第一个元素下标为 0,最后一个为“数组大小-1”)。

int numbers[5] = {1, 2, 3, 4, 5};
// 访问并打印数组元素
printf("第一个元素:%d\n", numbers[0]);  // 输出1(下标0对应第一个元素)
printf("第三个元素:%d\n", numbers[2]);  // 输出3(下标2对应第三个元素)
// 修改数组元素
numbers[1] = 10;  // 将第二个元素(下标1)的值从2改为10
printf("修改后第二个元素:%d\n", numbers[1]);  // 输出10

4. 数组的遍历:用循环(常用for循环)依次访问数组的所有元素,适合批量处理数据。

int numbers[5] = {1, 2, 3, 4, 5};
// i从0到4(数组大小-1),依次遍历每个元素
for (int i = 0; i < 5; i++) {
    printf("numbers[%d] = %d\n", i, numbers[i]);
}
// 输出结果:依次打印1、2、3、4、5

函数

1. 函数的定义:函数是一段实现特定功能的代码块,可重复调用,避免代码重复编写。

定义格式为:返回值类型 函数名(参数列表) { 函数体 }
例如,定义一个计算两数之和的函数:

// 返回值类型为int,函数名add,参数列表为两个int类型变量a和b
int add(int a, int b) {
    int sum = a + b;  // 函数体:计算a和b的和
    return sum;       // 返回计算结果sum
}

2. 函数的调用:使用“函数名(参数)”的格式调用函数,获取函数的返回结果或执行函数功能。

例如,调用上述add函数:

#include <stdio.h>  // 需包含stdio.h才能使用printf

// 先定义add函数(函数调用前必须先定义或声明)
int add(int a, int b) {
    return a + b;
}

int main() {
    int num1 = 3, num2 = 5;
    // 调用add函数,传入num1和num2作为参数,将返回值赋给result
    int result = add(num1, num2);
    printf("3 + 5 = %d\n", result);  // 输出结果:3 + 5 = 8
    return 0;
}

3. 函数的声明:若函数定义在调用之后(如main函数之后),需先声明函数,告诉编译器函数的“样子”。

声明格式为:返回值类型 函数名(参数列表);

#include <stdio.h>

// 函数声明:说明有一个int类型返回值、名为add、参数为两个int的函数
int add(int a, int b);

int main() {
    int result = add(4, 6);  // 此时可正常调用,无需担心函数未定义
    printf("4 + 6 = %d\n", result);  // 输出10
    return 0;
}

// 函数定义(在调用之后,需先声明)
int add(int a, int b) {
    return a + b;
}

4. 无返回值与无参数函数

  • 无返回值函数:返回值类型用void,函数体中无需return(或仅写return;),常用于执行打印、修改变量等操作。
void printHello() {  // 无参数、无返回值的函数
    printf("Hello, C language!\n");  // 仅执行打印功能
}

int main() {
    printHello();  // 调用函数,直接执行打印,无需接收返回值
    return 0;
}
  • 无参数函数:参数列表用void表示,说明函数不需要接收参数。
    例如上述printHello()函数,参数列表可写为void,即void printHello(void)

指针

1. 指针的定义:指针是存储变量“内存地址”的变量,相当于变量的“门牌号”。

定义格式为:数据类型 *指针名;,其中“*”表示该变量是指针。
例如,定义一个指向int类型变量的指针:

int num = 10;    // 定义普通int变量num,值为10
int *p;          // 定义int类型指针p,专门存储int变量的地址
p = &num;        // 将num的地址(&是取地址符)赋值给指针p
// 此时p中存储的是num的地址,通过p可找到num

2. 指针的解引用:用“*指针名”的格式访问指针指向的变量(即通过“门牌号”找到对应的变量),称为“解引用”。

#include <stdio.h>

int main() {
    int num = 10;
    int *p = &num;  // 定义指针p并直接赋值为num的地址(一步完成)
    
    printf("num的值:%d\n", num);      // 输出10(直接访问num)
    printf("num的地址:%p\n", &num);  // 输出num的地址(如0x7ffeefbff4c4)
    printf("p存储的地址:%p\n", p);   // 输出与&num相同的地址(p指向num)
    printf("p指向的值:%d\n", *p);   // 解引用p,输出10(等同于num的值)
    
    *p = 20;  // 解引用p并修改值,相当于修改num的值
    printf("修改后num的值:%d\n", num);  // 输出20(num被指针修改)
    return 0;
}

3. 指针与数组的关系:数组名本质是数组第一个元素的地址(即数组名 == &数组[0]),可通过指针遍历数组。

#include <stdio.h>

int main() {
    int numbers[5] = {1, 2, 3, 4, 5};
    int *p = numbers;  // 数组名numbers是&numbers[0],指针p指向数组第一个元素
    
    // 通过指针遍历数组(p++表示指针向后移动一个int类型的大小)
    for (int i = 0; i < 5; i++) {
        printf("numbers[%d] = %d\n", i, *(p + i));  // 等同于numbers[i]
    }
    // 输出结果:依次打印1、2、3、4、5
    return 0;
}

4. 空指针与野指针

  • 空指针:指向“空地址”(用NULL表示)的指针,说明指针暂时不指向任何变量,避免乱指。
    int *p = NULL;
    
  • 野指针:未初始化或指向已释放内存的指针,使用野指针会导致程序崩溃,需避免。
    int *p;  //(未初始化,p的值随机,是野指针)
    

结构体

1. 结构体的定义:结构体是自定义的数据类型,可将不同类型的数据(如int、char、float)组合在一起,描述一个“复杂对象”。

定义格式为:struct 结构体名 { 成员列表 };
例如,定义一个描述“学生”的结构体:

// 定义名为Student的结构体,包含学号、姓名、年龄三个成员
struct Student {
    int id;        // 整型成员:学号
    char name[20]; // 字符数组成员:姓名(最多存19个字符,最后一个为\0)
    int age;       // 整型成员:年龄
};

2. 结构体变量的定义与初始化:定义结构体变量后,可像普通变量一样使用,初始化时需给每个成员赋值。

#include <stdio.h>

// 先定义结构体
struct Student {
    int id;
    char name[20];
    int age;
};

int main() {
    // 方式1:定义时初始化(按成员顺序赋值)
    struct Student stu1 = {101, "Zhang San", 18};
    
    // 方式2:指定成员名初始化(顺序可任意)
    struct Student stu2 = {
        .id = 102,
        .name = "Li Si",
        .age = 19
    };
    
    printf("stu1:学号=%d,姓名=%s,年龄=%d\n", stu1.id, stu1.name, stu1.age);
    printf("stu2:学号=%d,姓名=%s,年龄=%d\n", stu2.id, stu2.name, stu2.age);
    return 0;
}

3. 结构体成员的访问

  • 普通结构体变量:用“变量名.成员名”访问。例如stu1.idstu1.name
  • 结构体指针:用“指针名->成员名”访问(或(*指针名).成员名,前者更简洁)。
#include <stdio.h>

struct Student {
    int id;
    char name[20];
};

int main() {
    struct Student stu = {103, "Wang Wu"};
    struct Student *p = &stu;  // 定义结构体指针p,指向stu
    
    // 两种访问方式等价
    printf("学号:%d\n", p->id);       // 指针用->访问成员,输出103
    printf("姓名:%s\n", (*p).name);   // 解引用后用.访问,输出Wang Wu
    return 0;
}

4. 结构体数组:存储结构体变量的数组,可批量管理多个复杂对象(如多个学生)。

#include <stdio.h>

struct Student {
    int id;
    char name[20];
    int age;
};

int main() {
    // 定义结构体数组,存储3个学生信息
    struct Student students[3] = {
        {101, "Zhang San", 18},
        {102, "Li Si", 19},
        {103, "Wang Wu", 18}
    };
    
    // 遍历结构体数组,打印每个学生信息
    for (int i = 0; i < 3; i++) {
        printf("第%d个学生:学号=%d,姓名=%s,年龄=%d\n", 
               i+1, students[i].id, students[i].name, students[i].age);
    }
    return 0;
}
posted @ 2025-10-18 16:54  我今天真的要早睡了  阅读(44)  评论(0)    收藏  举报