Skip to main content

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:使用 errnoperrorstrerror

#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_SUCCESS0表示成功退出
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)

简单解释

  1. 每个节点包含两部分:

    • 数据域(如示例中的data存储整数值)
    • 指针域next指向下一个节点的地址)
  2. 特点:

    • 节点通过指针线性连接
    • 从头节点开始,每个节点指向下一个节点
    • 最后一个节点指向NULL表示链表结束
    • 内存非连续分配,支持动态扩展
  3. 基础操作:

    • 创建节点(使用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);
}