C语言教程——第1轮
第一个C语言程序
#include <stdio.h>
int main()
{
/*这是一段注释*/
printf("hello world");
return 0;
}
整数的声明、运算和输出
#include <stdio.h>
int main(){
int a,b;
a=1;
b=2;
int c=4;
printf("C的值是:%d\n",c);
c=a+b;
printf("更新后,C的值是:%d",c);
}
小数的声明、运算和输出
#include <stdio.h>
int main(){
float a,b;
a=1;
b=1.2;
printf("输出小数a:%f\n输出小数b:%f",a,b);
double c,d;
c=2;
d=2.3;
printf("输出小数c:%f\n输出小数d:%f",c,d);
}
输出结果:
输出小数a:1.000000
输出小数b:1.200000
输出小数c:2.000000
输出小数d:2.300000
--------------------
进程退出, 总耗时 0.1958 s. 返回值: 0; CPU 时间: 0.0000 ms; 内存: 564 KB.
按任意键退出...
宏定义
#include <stdio.h>
#define PI 3.14159265
int main(){
float pai=PI;
printf("%f",pai);
}
输出结果:
3.141593
--------------------
进程退出, 总耗时 0.1756 s. 返回值: 0; CPU 时间: 0.0000 ms; 内存: 568 KB.
按任意键退出...
另一个示例:
#include<stdio.h>
#define MULTIPLY(a,b) a*b
int main(){
int a=MULTIPLY(1+2,2+3);
printf("计算结果是:%d",a);
}
常量
#include<stdio.h>
int main(){
const int LENGTH = 10;
printf("value of length:%d",LENGTH);
}
运算符
#include<stdio.h>
int main(){
int a = 233;
int b = 12;
int c;
float d; // 提前声明float变量,使代码更规范
// 加法运算
c = a + b;
printf("a + b 的值是 %d\n", c );
// 减法运算
c = a - b;
printf("a - b 的值是 %d\n", c );
// 乘法运算
c = a * b;
printf("a * b 的值是 %d\n", c );
// 除法运算(整数除法)
c = a / b;
printf("a / b 的整数结果是 %d\n", c );
// 除法运算(浮点数结果,注意整数除法的局限性)
d = (float)a / b; // 强制类型转换,得到正确的浮点数结果
printf("a / b 的浮点数结果是 %f\n", d );
// 取模运算(求余数)
c = a % b;
printf("a %% b 的值是 %d\n", c ); // %需要用%%转义
// 自增运算(a++:先赋值后自增)
c = a++;
printf("a++ 赋值给c后,c的值是 %d\n", c );
// 自减运算(a--:先赋值后自减)
c = a--;
printf("a-- 赋值给c后,c的值是 %d\n", c );
return 0;
}
关于++和--的位置
++放在变量名之前,意味着先进行自加运算,再参与其它操作,放在变量名后面,意味着先进行操作,再进行自加运算,自减亦然。
#include <stdio.h>
int main()
{
int c;
int a = 10;
c = a++;
printf("先赋值后运算:\n");
printf("Line 1 - c 的值是 %d\n", c );
printf("Line 2 - a 的值是 %d\n", a );
a = 10;
c = a--;
printf("Line 3 - c 的值是 %d\n", c );
printf("Line 4 - a 的值是 %d\n", a );
printf("先运算后赋值:\n");
a = 10;
c = ++a;
printf("Line 5 - c 的值是 %d\n", c );
printf("Line 6 - a 的值是 %d\n", a );
a = 10;
c = --a;
printf("Line 7 - c 的值是 %d\n", c );
printf("Line 8 - a 的值是 %d\n", a );
}
if判断
#include <stdio.h>
int main(){
int a=1,b=2;
if(a<b){
printf("a比b小");
}else{
printf("a比b大");
}
return 0;
}
#include <stdio.h>
int main(){
int a=1,b=2,c=3;
if(a+b==c){
printf("a+b等于c");
}else if(a+b>c){
printf("a+b大于c");
}else{
printf("a+b小于c");
}
return 0;
}
for循环
#include <stdio.h>
int main() {
printf("使用for循环打印1到5:\n");
for (int i = 1; i <= 5; i++) {
printf("%d ", i);
}
printf("\n");
return 0;
}
while循环
#include <stdio.h>
int main() {
int sum = 0;
int num = 1;
printf("使用while循环计算1到10的和:\n");
while (num <= 10) {
sum += num;
num++;
}
printf("1到10的和是:%d\n", sum);
return 0;
}
返回值的函数
#include <stdio.h>
// 定义一个返回int类型的函数,计算两个整数的和
int addNumbers(int a, int b) {
int result;
result = a + b;
return result; // 返回计算结果
}
int main() {
int num1 = 10, num2 = 20;
int sum;
// 调用函数并接收返回值
sum = addNumbers(num1, num2);
printf("%d + %d = %d\n", num1, num2, sum);
return 0;
}
#include <stdio.h>
// 定义一个返回float类型的函数,计算两个数的平均值
float calculateAverage(int a, int b) {
float average;
average = (a + b) / 2.0f; // 使用2.0f确保浮点数除法
return average; // 返回计算得到的平均值
}
int main() {
int num1 = 7, num2 = 10;
float avg;
// 调用函数并接收返回值
avg = calculateAverage(num1, num2);
printf("%d 和 %d 的平均值是: %.2f\n", num1, num2, avg);
return 0;
}
不返回值的函数
#include <stdio.h>
// 简单的void函数,打印信息
void showMessage() {
printf("这是一个void函数,没有返回值\n");
}
int main() {
showMessage(); // 直接调用,无需处理返回值
return 0;
}
#include <stdio.h>
// void函数尝试修改参数值
void modifyValue(int x) {
x = 100; // 这里修改的是函数内部的局部变量x
printf("函数内部修改后的值: %d\n", x);
}
int main() {
int num = 10;
printf("调用函数前的值: %d\n", num);
modifyValue(num); // 传递num的值给函数
printf("调用函数后的值: %d\n", num); // num的值未改变
return 0;
}
数组
#include <stdio.h>
int main() {
// 定义一个包含3个整数的数组并初始化
int arr[] = {10, 20, 30};
// 访问数组元素(通过索引,从0开始)
printf("%d\n", arr[0]); // 访问第一个元素
printf("%d\n", arr[1]); // 访问第二个元素
printf("%d\n", arr[2]); // 访问第三个元素
return 0;
}
#include <stdio.h>
int main ()
{
int n[ 10 ]; /* n 是一个包含 10 个整数的数组 */
int i,j;
/* 初始化数组元素 */
for ( i = 0; i < 10; i++ )
{
n[ i ] = i + 100; /* 设置元素 i 为 i + 100 */
}
/* 输出数组中每个元素的值 */
for (j = 0; j < 10; j++ )
{
printf("Element[%d] = %d\n", j, n[j] );
}
return 0;
}
获取数组长度:
#include <stdio.h>
int main() {
int array[] = {1, 2, 3, 4, 5};
int length = sizeof(array) / sizeof(array[0]);
printf("数组长度为: %d\n", length);
return 0;
}
enum枚举
#include <stdio.h>
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};
int main()
{
enum DAY day;
day = WED;
printf("%d",day);
return 0;
}
基本指针
#include <stdio.h>
int main ()
{
int var = 20; /* 实际变量的声明 */
int *ip; /* 指针变量的声明 */
ip = &var; /* 在指针变量中存储 var 的地址 */
printf("var 变量的地址: %p\n", &var );
/* 在指针变量中存储的地址 */
printf("ip 变量存储的地址: %p\n", ip );
/* 使用指针访问值 */
printf("*ip 变量的值: %d\n", *ip );
return 0;
}
空指针:
#include <stdio.h>
int main ()
{
int *ptr = NULL;
printf("ptr 的地址是 %p\n", ptr );
return 0;
}
数组与指针:
#include <stdio.h>
int main() {
int arr[] = {10, 20, 30};
int *p = arr; // 指针指向数组首地址
// 通过数组名访问
printf("数组访问: %d\n", arr[1]);
// 通过指针访问(两种方式)
printf("指针访问1: %d\n", *(p + 1));
printf("指针访问2: %d\n", p[1]); // 指针也可像数组一样使用
return 0;
}
函数指针
#include <stdio.h>
int max(int x, int y)
{
return x > y ? x : y;
}
int main(void)
{
/* p 是函数指针 */
int (* p)(int, int) = & max; // &可以省略
int a, b, c, d;
printf("请输入三个数字:");
scanf("%d %d %d", & a, & b, & c);
/* 与直接调用函数等价,d = max(max(a, b), c) */
d = p(p(a, b), c);
printf("最大的数字是: %d\n", d);
return 0;
}
这段代码演示了C语言中函数指针的用法,主要功能是找出三个数字中的最大值。以下是详细解释:
函数定义部分:
int max(int x, int y)
{
return x > y ? x : y;
}
定义了一个名为 max 的函数,接收两个 int 类型参数 x 和 y。使用三元运算符 x > y ? x : y 返回两个数中较大的那个:如果 x 大于 y,返回 x;否则返回 y。
函数指针的定义与赋值:
int (*p)(int, int) = &max;
声明一个函数指针 p,它指向的函数必须满足:返回值为 int 类型,且接收两个 int 类型参数。= &max 将函数 max 的地址赋给指针 p。这里的 & 是可选的,因为在C语言中,函数名本身即代表其入口地址,所以也可以直接写成 p = max。
主函数逻辑:
printf("请输入三个数字:");
scanf("%d %d %d", &a, &b, &c);
使用 printf 提示用户输入三个整数。使用 scanf 从标准输入读取三个整数值,并分别存入变量 a、b、c 中(注意变量需提前声明并取地址)。
使用函数指针调用函数:
d = p(p(a, b), c);
此语句等价于:d = max(max(a, b), c)。执行步骤:首先调用 p(a, b),即 max(a, b),得到 a 和 b 中的较大值;然后将该结果与 c 再次传入 p(...),即 max(上一步结果, c),从而得到三个数中的最大值;最终结果赋值给变量 d。函数指针调用方式和普通函数调用完全一致:指针名(实参列表)。
输出结果:
printf("最大的数字是: %d\n", d);
将最终计算出的最大值 d 输出到控制台。
核心要点总结:
函数指针是一种特殊的指针类型,用于存储函数的地址,允许通过指针间接调用函数。语法格式:返回类型 (*指针名)(参数列表)。调用方式:指针名(参数),与直接调用函数无异。主要应用场景:实现回调机制(如 qsort 中传入比较函数);实现策略模式或动态绑定函数;构建函数表、状态机等高级结构。
示例运行:
输入:
请输入三个数字: 10 25 15
输出:
最大的数字是: 25
通过本例,你可以掌握函数指针的基本定义、赋值与调用方法,并理解其在程序设计中的灵活性与实用性。
回调函数
#include <stdlib.h>
#include <stdio.h>
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
for (size_t i=0; i<arraySize; i++)
array[i] = getNextValue();
}
// 获取随机值
int getNextRandomValue(void)
{
return rand();
}
int main(void)
{
int myarray[10];
/* getNextRandomValue 不能加括号,否则无法编译,因为加上括号之后相当于传入此参数时传入了 int , 而不是函数指针*/
populate_array(myarray, 10, getNextRandomValue);
for(int i = 0; i < 10; i++) {
printf("%d ", myarray[i]);
}
printf("\n");
return 0;
}
字符串
#include <stdio.h>
#include <string.h> // 提供字符串操作函数
int main() {
// 字符串的三种定义方式
char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // 手动添加结束符
char str2[] = "World"; // 自动添加结束符,长度由编译器计算
char *str3 = "Hello, C!"; // 字符串常量,指针指向首地址
// 直接打印字符串
printf("字符串1: %s\n", str1);
printf("字符串2: %s\n", str2);
printf("字符串3: %s\n", str3);
// 字符串拼接
char greeting[20];
strcpy(greeting, str1); // 复制str1到greeting
strcat(greeting, " "); // 拼接空格
strcat(greeting, str2); // 拼接str2
printf("拼接结果: %s\n", greeting);
// 获取字符串长度(不含结束符)
printf("str1长度: %d\n", strlen(str1));
printf("str2长度: %d\n", strlen(str2));
return 0;
}
这段内容解释了C语言中字符串的定义方式和基本操作,以下是详细说明:
字符串的本质:
C语言中没有专门的字符串类型,字符串是通过字符数组实现的,以特殊字符 '\0'(空字符)作为结束标志,表示字符串的终止。
三种定义方式:
char str1[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
手动定义字符数组,逐个初始化字符并显式添加 '\0'。数组长度必须至少为字符数加一,以预留结束符位置。
char str2[] = "World";
简化写法,使用双引号直接赋值。编译器会自动在字符串末尾添加 '\0',且无需手动指定数组长度,系统根据字符串内容自动计算。
char *str3 = "Hello, C!";
通过字符指针指向字符串常量。该字符串存储在程序的常量区,节省内存空间,是推荐的常用方式,尤其适用于只读字符串。
字符串操作:
使用 %s 格式符可打印整个字符串,printf 会从首地址开始输出字符,直到遇到 '\0' 为止。
借助 string.h 头文件中的标准库函数可进行常见字符串操作:
strcpy(dst, src);
将源字符串 src 复制到目标数组 dst。要求 dst 有足够空间容纳 src 的内容(包括 '\0')。
strcat(dst, src);
将源字符串 src 拼接到目标字符串 dst 的末尾。dst 必须有足够空间容纳拼接后的完整字符串。
strlen(str);
计算字符串 str 的长度,返回 '\0' 之前的有效字符个数,不包括结束符本身。
运行结果示例:
字符串1: Hello
字符串2: World
字符串3: Hello, C!
拼接结果: Hello World
str1长度: 5
str2长度: 5
这个例子展示了字符串的基本定义方法和常用操作,体现了C语言中字符串以 '\0' 结尾的核心特性,以及如何通过标准库函数便捷地处理字符串。掌握这些基础对后续学习字符串处理、内存管理及指针操作至关重要。
字符串操作指令
#include <stdio.h>
#include <string.h>
int main() {
char a[20] = "Hi";
char b[20] = "There";
char c[20];
strcpy(c, a);
printf("复制: %s\n", c);
strcat(a, b);
printf("拼接: %s\n", a);
printf("长度: %d\n", strlen(a));
printf("比较: %d\n", strcmp("abc", "abd"));
return 0;
}
结构体
#include <stdio.h>
#include <string.h>
// 定义通用结构体:存储个人信息
struct Person {
char name[20]; // 姓名
int age; // 年龄
char city[20]; // 城市
};
int main() {
// 声明结构体变量
struct Person p1, p2;
// 初始化第一个人信息
strcpy(p1.name, "Zhang San");
p1.age = 25;
strcpy(p1.city, "Beijing");
// 初始化第二个人信息
strcpy(p2.name, "Li Si");
p2.age = 30;
strcpy(p2.city, "Shanghai");
// 输出信息
printf("%s, %d, %s\n", p1.name, p1.age, p1.city);
printf("%s, %d, %s\n", p2.name, p2.age, p2.city);
return 0;
}
typedef struct {
char name[20];
int age;
} Student; // 现在可以直接用 Student,不用写 struct
Student s3 = {"王五", 19};
printf("姓名:%s,年龄:%d\n", s3.name, s3.age);
共用体
#include <stdio.h>
// 定义一个简单的共用体
union Value {
int num; // 整数
char ch; // 字符
};
int main() {
union Value v;
// 存储整数
v.num = 65;
printf("整数值: %d\n", v.num);
printf("对应字符: %c\n", v.ch); // 65对应ASCII码的'A'
// 存储字符(会覆盖之前的整数)
v.ch = 'B';
printf("字符值: %c\n", v.ch);
printf("对应整数: %d\n", v.num); // 'B'的ASCII码是66
return 0;
}
#include <stdio.h>
// 数据类型标识
enum Type { TEMP, COUNT, STATUS };
// 联合体存储不同类型数据
union Data {
float temp; // 温度
int count; // 计数
char status[10];// 状态
};
// 传感器数据结构
struct Sensor {
enum Type type;
union Data data;
};
int main() {
// 初始化三个不同类型的传感器数据
struct Sensor s1 = {TEMP, .data.temp = 26.3f};
struct Sensor s2 = {COUNT, .data.count = 1200};
struct Sensor s3 = {STATUS, .data.status = "正常"};
// 输出数据
printf("温度: %.1f℃\n", s1.data.temp);
printf("计数: %d次\n", s2.data.count);
printf("状态: %s\n", s3.data.status);
return 0;
}
结构体作为函数参数
#include <stdio.h>
#include <string.h>
// 定义学生结构体
struct Student {
char name[20];
int age;
};
// 结构体作为值传递的函数
void printStudent(struct Student s) {
printf("学生:%s,%d岁\n", s.name, s.age);
}
int main() {
// 初始化结构体变量
struct Student s3;
strcpy(s3.name, "王小明");
s3.age = 16;
// 调用函数,传递结构体(值传递)
printStudent(s3);
return 0;
}
结构体指针
(*结构体指针).成员
= 结构体指针->成员
。C 语言提供了两种通过结构体指针访问成员的语法,这两种语法完全等价,只是写法不同(->
是 (*).
的语法糖,更简洁)。
#include <stdio.h>
#include <string.h>
// 定义学生结构体
struct Student {
char name[20]; // 姓名
int id; // 学号
float score; // 成绩
};
// 函数声明:打印学生信息
void printStudent(struct Student *s);
int main() {
// 初始化两个学生
struct Student s1, s2;
strcpy(s1.name, "张三");
s1.id = 101;
s1.score = 92.5f;
strcpy(s2.name, "李四");
s2.id = 102;
s2.score = 88.0f;
// 调用函数打印信息(传递地址)
printStudent(&s1);
printStudent(&s2);
return 0;
}
// 函数定义:通过指针访问结构体成员
void printStudent(struct Student *s) {
printf("姓名: %s, 学号: %d, 成绩: %.1f\n",
s->name, s->id, s->score);
}
文件读写
#include <stdio.h>
#include <stdlib.h>
int main() {
// 打开当前目录下的文件(假设文件名为test.txt)
FILE *file = fopen("test.txt", "r");
// 检查文件是否打开成功
if (file == NULL) {
printf("无法打开文件,请确保test.txt存在于当前目录\n");
return 1;
}
// 读取文件内容并打印
char ch;
printf("文件内容:\n");
while ((ch = fgetc(file)) != EOF) {
putchar(ch);
}
// 获取并打印当前工作目录
char cwd[1024];
if (getcwd(cwd, sizeof(cwd)) != NULL) {
printf("\n\n当前工作目录:%s\n", cwd);
} else {
printf("\n\n无法获取当前工作目录\n");
}
// 关闭文件
fclose(file);
return 0;
}
程序会尝试打开当前目录下的test.txt
文件,并用 fgetc
逐个字符读取内容并打印。
通过getcwd
函数获取当前工作目录(程序运行时所在的目录)并显示。
在程序的工作目录下创建一个test.txt
文件,并写入一些内容,编译并运行程序,会显示文件内容和当前工作目录。
如果 test.txt
不存在,程序会提示"无法打开文件"。
运行示例(假设test.txt
内容为"Hello, File!"
):
文件内容:
Hello, File!
当前工作目录:/home/user/documents
C错误处理
C 语言没有内置的异常处理机制,因此错误通常通过函数返回值和全局变量 errno
来表示。以下是常见的错误处理方式:
- 返回值判断:许多函数在失败时会返回特定的值,如
NULL
(用于指针函数)或-1
(用于整数返回函数)。 - errno 全局变量:定义在
errno.h
头文件中,用于记录最近一次系统或库函数调用的错误码。程序开始时可以将errno
设置为0
,表示初始无错误状态。 - 错误输出建议:所有错误信息都应输出到标准错误流
stderr
,而不是标准输出stdout
,以确保错误消息与正常输出分离。
常用错误处理函数
函数 | 作用 |
---|---|
perror(str) | 输出自定义字符串 str + ": " + errno 对应的错误文本到 stderr |
strerror(errno) | 返回 errno 对应的错误文本字符串的指针 |
代码示例 1:使用 errno
、perror
和 strerror
#include <stdio.h>
#include <errno.h>
#include <string.h>
int main() {
FILE *fp = fopen("nonexistent.txt", "r");
if (!fp) {
fprintf(stderr, "错误码: %d\n", errno);
perror("perror 输出");
fprintf(stderr, "strerror: %s\n", strerror(errno));
return 1;
}
fclose(fp);
return 0;
}
输出示例:
错误码: 2
perror 输出: No such file or directory
strerror: No such file or directory
代码示例 2:自定义错误处理并退出
#include <stdio.h>
#include <stdlib.h>
int main() {
int a = 10, b = 0;
if (b == 0) {
fprintf(stderr, "错误:除数不能为零\n");
exit(EXIT_FAILURE);
}
printf("结果: %d\n", a / b);
return 0;
}
输出:
错误:除数不能为零
退出状态宏
使用 exit()
或 return
时,推荐使用以下宏来增强代码的可读性和可移植性:
宏 | 值 | 含义 |
---|---|---|
EXIT_SUCCESS | 0 | 表示成功退出 |
EXIT_FAILURE | -1 | 表示异常退出 |
注意:
EXIT_FAILURE
的值是实现定义的,在某些系统中可能为-1
,但标准推荐使用宏而不是硬编码值,以确保跨平台兼容性。
如果您有更多问题或需要进一步解释,请随时告知!
单向链表
#include <stdio.h>
#include <stdlib.h>
// 定义链表节点结构
struct Node {
int data; // 数据域
struct Node* next; // 指针域(指向下一个节点)
};
// 创建并返回一个包含3个节点的简单链表
struct Node* createSimpleList() {
// 创建3个节点
struct Node* head = NULL;
struct Node* second = NULL;
struct Node* third = NULL;
head = (struct Node*)malloc(sizeof(struct Node));
second = (struct Node*)malloc(sizeof(struct Node));
third = (struct Node*)malloc(sizeof(struct Node));
// 分配数据并连接节点
head->data = 1;
head->next = second;
second->data = 2;
second->next = third;
third->data = 3;
third->next = NULL; // 最后一个节点指向NULL
return head;
}
// 打印链表内容
void printList(struct Node* n) {
while (n != NULL) {
printf("%d ", n->data);
n = n->next;
}
printf("\n");
}
int main() {
struct Node* list = createSimpleList();
printList(list);
return 0;
}
链表类别:这是一个单向链表(Singly Linked List)
简单解释:
-
每个节点包含两部分:
数据域
(如示例中的data
存储整数值)指针域
(next
指向下一个节点的地址)
-
特点:
- 节点通过指针线性连接
- 从头节点开始,每个节点指向下一个节点
- 最后一个节点指向
NULL
表示链表结束 - 内存非连续分配,支持动态扩展
-
基础操作:
- 创建节点(使用
malloc
分配内存) - 连接节点(通过指针链接)
- 遍历访问(通过
next
指针顺序访问)
- 创建节点(使用
这种链表是C语言中最基础的数据结构之一,常用于需要动态管理数据的场景(如不确定数据量的存储)。
多文件项目
extern_demo/ # 项目根目录
├── main.c # 主程序文件,定义全局变量并调用函数
├── support.c # 辅助功能文件,实现共享函数
└── support.h # 头文件,声明共享函数(规范做法)
1.support.h
// support.h
#ifndef SUPPORT_H // 防止头文件重复包含
#define SUPPORT_H
// 声明函数原型,供其他文件调用
void write_extern(void);
#endif
2.main.c
// main.c
#include <stdio.h>
#include "support.h" // 包含头文件,获取 write_extern 函数声明
// 定义全局变量(仅在此处定义一次)
int count;
int main() {
// 给全局变量赋值
count = 5;
// 调用 support.c 中实现的函数
write_extern();
return 0;
}
3.support.c
// support.c
#include <stdio.h>
#include "support.h" // 包含头文件,确保函数声明与实现一致
// 声明要使用的全局变量(来自其他文件,不分配新空间)
extern int count;
// 实现 write_extern 函数,使用全局变量 count
void write_extern(void) {
printf("count 的值是:%d\n", count);
}