C基础语法
变量
在C语言中,变量是指用于存储数据值的一种命名的内存位置。在程序执行期间,可以通过变量名来访问和操作这些存储的数据。
在C语言中,变量必须在使用之前声明,声明变量时需要指定变量的数据类型。
-
声明变量: 在C语言中,变量需要在使用之前声明。变量声明告诉编译器变量的名称和类型。
int a; // 声明一个整型变量a float b; // 声明一个浮点型变量b char c; // 声明一个字符型变量c
-
变量命名规则: 变量的名称可以由字母、数字和下划线组成,但必须以字母或下划线开头。此外,变量名称是区分大小写的。
-
赋值: 赋值运算符(=)用于将值赋给变量。
a = 10; // 将整数10赋值给a变量 b = 3.14; // 将浮点数3.14赋值给b c = 'A'; // 将字符'A'赋值给变量c
-
数据类型: 变量在声明时需要指定数据类型,C语言中常见的数据类型包括整型(int)、浮点型(float、double)、字符型(char)等。
int age; // 声明一个整型变量age float height; // 声明一个浮点型变量height char initial; // 声明一个字符型变量initial
-
作用域: 变量的作用域决定了它在程序中可见的范围。在C语言中,变量可以是全局的(在整个程序中可见)或局部的(只在特定的函数或代码块中可见)。
-
存储类别: 变量的存储类别决定了变量的存储位置和生命周期。常见的存储类别包括自动变量(auto)、静态变量(static)、寄存器变量(register)、外部变量(extern)等。
-
常量变量: 在程序执行过程中,其值不会改变的变量称为常量变量。在C语言中,可以使用关键字
const
声明常量变量。const int MAX_VALUE = 100; // 声明一个整型变量MAX_VALUE, 并初始化为100
常量
- 整型常量: 整数常量是不带小数点或指数部分的数值。
10 // 十进制整数常量
0xFF // 十六进制整数常量
045 // 八进制整数常量
- 浮点型常量: 浮点常量包括小数部分或指数部分。
3.14 // 浮点型常量
6.022e23 // 科学计数法表示的浮点型常量
- 字符常量: 字符常量是单个字符,用单引号括起来。
'A' // 字符常量
'\n' // 转义序列,表示换行
- 字符串常量: 字符串常量是由字符组成的序列,用双引号括起来。
"Hello, world!" // 字符串常量
"This is a string constant."
- 符号常量(宏常量): 通过
#define
预处理指令定义的常量称为符号常量或宏常量。
#define PI 3.14159
#define MAX_SIZE 100
- 枚举常量: 使用
enum
关键字定义的常量。
enum Weekday {Monday, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday};
- 空指针常量: 用
NULL
表示空指针。
int *ptr = NULL; // ptr是一个空指针
- 布尔常量: 在C语言中,布尔常量没有专门的关键字表示,通常用0表示假(false),非零值表示真(true)。
int isTrue = 1; // 布尔常量,1表示真
int isFalse = 0; // 布尔常量,0表示假
数据类型
-
整型(Integer): 用于表示整数值,可以是有符号或无符号的。
-
int
:标准整数类型,通常占用4个字节(取决于系统)。 -
short
:短整型,通常占用2个字节。 -
long
:长整型,通常占用4个字节。 -
long long
:长长整型,通常占用8个字节。
-
有符号整型能够表示正数、负数和零,而无符号整型只能表示非负数。
-
浮点型(Floating-point): 用于表示带有小数部分的数值。
-
float
:单精度浮点数,通常占用4个字节。 -
double
:双精度浮点数,通常占用8个字节。 -
long double
:扩展精度浮点数,占用字节数通常大于double类型。
-
-
字符型(Character): 用于表示单个字符,存储在内存中的ASCII编码中。
char
:通常占用1个字节。
-
空类型(Void): 表示没有值的数据类型,通常用于函数返回类型。
void
:通常用于函数无返回值或指针类型不明确的情况。
-
复合数据类型: 可以通过组合基本数据类型来创建更复杂的数据结构。
-
数组(Array)
:一组相同类型的数据元素的集合。 -
结构体(Struct)
:不同类型的数据组合在一起形成的自定义数据类型。 -
联合(Union)
:不同类型的数据共享同一块内存空间,但只能存储其中一个类型的数据。
-
C语言还提供了类型修饰符,用于修改基本数据类型的属性,如signed
、unsigned
、short
、long
等。
运算符
- 算术运算符: 用于执行基本的算术操作,如加法、减法、乘法、除法和取模。
+
:加法-
:减法*
:乘法/
:除法%
:取模(求余)
- 赋值运算符: 用于将值赋给变量。
=
:赋值+=
:加法赋值-=
:减法赋值*=
:乘法赋值/=
:除法赋值%=
:取模赋值<<=
:左移赋值>>=
:右移赋值&=
:按位与赋值|=
:按位或赋值^=
:按位异或赋值
- 递增和递减运算符: 用于增加或减少变量的值。
++
:递增--
:递减
- 关系运算符: 用于比较两个值之间的关系,并返回布尔值(1表示真,0表示假)。
==
:等于!=
:不等于>
:大于<
:小于>=
:大于等于<=
:小于等于
- 逻辑运算符: 用于组合多个条件,并返回布尔值。
&&
:逻辑与(AND)||
:逻辑或(OR)!
:逻辑非(NOT)
- 位运算符: 用于对整数类型的数据进行位操作。
&
:按位与|
:按位或^
:按位异或~
:按位取反<<
:左移>>
:右移
- 条件运算符(三元运算符): 用于根据条件选择执行不同的操作。
? :
:条件表达式
- 逗号运算符: 用于在同一行中分隔多个表达式,并按顺序依次执行这些表达式。
,
:逗号运算符
流程控制语句
在C语言中,流程控制语句用于控制程序的执行流程,使程序可以根据条件执行不同的操作。以下是C语言中常见的流程控制语句:
-
条件语句:
-
if语句: 根据条件执行不同的代码块。
if (condition) { // 如果条件为真,执行这里的代码 } else { // 如果条件为假,执行这里的代码 }
-
if-else if-else语句: 多个条件的选择结构。
if (condition1) { // 如果条件1为真,执行这里的代码 } else if (condition2) { // 如果条件2为真,执行这里的代码 } else { // 如果以上条件都不满足,执行这里的代码 }
-
-
循环语句:
-
while循环: 在条件为真的情况下重复执行一段代码块。
while (condition) { // 只要条件为真,就会重复执行这里的代码 }
-
do-while循环: 先执行一次循环体,然后在条件为真的情况下重复执行。
do { // 先执行一次,然后根据条件重复执行这里的代码 } while (condition);
-
for循环: 在指定的条件为真的情况下重复执行一段代码块,通常用于已知循环次数的情况。
for (initialization; condition; update) { // 在每次循环之前初始化变量,然后根据条件重复执行这里的代码,并更新变量 }
-
-
跳转语句:
-
break语句: 跳出当前循环或switch语句。
-
continue语句: 结束当前循环的当前迭代,继续下一次迭代。
-
return语句: 从函数中返回值。
-
-
switch语句: 根据表达式的值选择执行不同的分支。
switch (expression) { case constant1: // 如果表达式的值等于常量1,执行这里的代码 break; case constant2: // 如果表达式的值等于常量2,执行这里的代码 break; default: // 如果表达式的值不等于任何一个常量,执行这里的代码 break; }
这些流程控制语句可以帮助程序员根据不同的条件控制程序的执行流程,使程序更加灵活和高效。
数组
在C语言中,数组是一种用于存储相同类型数据元素的集合,这些元素通过索引来访问。数组在C语言中是非常重要的数据结构,可以按照顺序存储数据,并通过索引快速访问。
以下是关于C语言数组的一些重要概念:
-
声明数组: 在C语言中,数组的声明包括数组类型和数组名,以及数组的大小。
int numbers[5]; // 声明一个包含5个整数的数组
-
初始化数组: 可以在声明数组的同时对其进行初始化。
int numbers[5] = {1, 2, 3, 4, 5}; // 声明并初始化一个数组
-
访问数组元素: 可以通过索引来访问数组中的元素,索引从0开始。
int value = numbers[2]; // 访问数组numbers的第3个元素,即索引为2的元素
-
多维数组: C语言支持多维数组,可以是二维、三维甚至更高维度的数组。
int matrix[3][3]; // 声明一个3x3的二维数组
-
数组作为函数参数: 可以将数组作为函数的参数传递。
void printArray(int arr[], int size) { for (int i = 0; i < size; i++) { printf("%d ", arr[i]); } } int main() { int numbers[] = {1, 2, 3, 4, 5}; printArray(numbers, 5); return 0; }
-
动态数组: 在C语言中,可以使用动态内存分配函数(如
malloc()
和calloc()
)来创建动态数组。int *numbers; int size = 5; numbers = (int*)malloc(size * sizeof(int)); // 创建一个包含5个整数的动态数组
-
数组名和指针: 在C语言中,数组名本身就是指向数组首元素的指针。
int numbers[] = {1, 2, 3, 4, 5}; int *ptr = numbers; // 数组名numbers就是指向数组首元素的指针
数组在C语言中是非常常见和重要的数据结构,可以用于存储和处理大量数据。了解如何声明、初始化、访问和操作数组对于编写高效的C程序至关重要。
指针
在C语言中,指针是一种特殊的变量,用于存储内存地址。通过指针,可以直接访问内存中的数据,可以实现对内存的动态分配和释放,以及实现数据的传递和操作。以下是关于C语言中指针的一些重要概念:
-
声明指针: 在声明指针时,需要指定指针所指向的数据类型。
int *ptr; // 声明一个指向整型数据的指针
-
初始化指针: 可以将指针初始化为一个变量的地址,也可以初始化为NULL(表示空指针)。
int num = 10; int *ptr = # // 将ptr指向num的地址 int *ptr = NULL; // 初始化一个空指针
-
访问指针所指向的数据: 使用解引用操作符
*
来访问指针所指向的数据。int num = 10; int *ptr = # printf("%d", *ptr); // 输出ptr所指向的整数值,即输出10
-
指针算术: 可以对指针进行算术运算,如指针加法、减法等。
int array[5] = {1, 2, 3, 4, 5}; int *ptr = array; // 将ptr指向数组的第一个元素 ptr++; // 将ptr移动到数组的下一个元素
-
指针和数组: 数组名本身就是指向数组首元素的指针,可以通过指针来访问数组元素。
int array[5] = {1, 2, 3, 4, 5}; int *ptr = array; // 数组名array就是指向数组首元素的指针 printf("%d", *(ptr + 2)); // 输出数组的第三个元素,即输出3
-
指针和函数: 可以将指针作为函数的参数传递,以实现对函数外部变量的修改。
void modifyValue(int *ptr) { *ptr = 20; // 修改ptr所指向的变量的值为20 } int main() { int num = 10; modifyValue(&num); // 将num的地址传递给函数 printf("%d", num); // 输出20 return 0; }
-
动态内存分配: 使用
malloc()
、calloc()
等函数可以在运行时动态地分配内存,并返回指向分配内存的指针。int *ptr = (int*)malloc(sizeof(int)); // 动态分配一个整型变量的内存空间
指针是C语言中非常重要的概念,它提供了对内存的灵活访问和操作,可以帮助实现复杂的数据结构和算法。但同时也需要谨慎使用指针,避免出现内存泄漏和悬挂指针等问题。
拓展(malloc与calloc的异同)
malloc()
和calloc()
是C语言中用于动态分配内存的函数,它们有相似之处,也有一些不同之处。
相同点:
-
动态内存分配: 两者都用于在程序运行时动态地分配内存空间,而不是在编译时静态地分配内存。
-
返回类型: 两者的返回类型都是
void*
,即指向分配内存的指针。
不同点:
-
参数不同:
malloc()
函数接受一个参数,表示要分配的内存字节数。calloc()
函数接受两个参数,第一个参数表示要分配的内存块的数量,第二个参数表示每个内存块的大小。
-
初始化:
malloc()
分配的内存区域的内容是未初始化的,即其中的值是不确定的,可能是任意的。calloc()
分配的内存区域会被初始化为零,即其中的每个字节都被设置为0。
-
性能:
- 在某些系统上,
calloc()
可能比malloc()
稍慢,因为它在分配内存后还需要初始化为零,而malloc()
只需简单地分配内存而无需初始化。
- 在某些系统上,
-
使用场景:
- 如果对内存的初始化并不重要,或者可以通过其他方式初始化,那么通常建议使用
malloc()
,因为它的性能可能更高。 - 如果需要确保分配的内存被初始化为零,可以使用
calloc()
。
- 如果对内存的初始化并不重要,或者可以通过其他方式初始化,那么通常建议使用
示例:
使用malloc()
:
int *ptr;
ptr = (int*)malloc(5 * sizeof(int));
使用calloc()
:
int *ptr;
ptr = (int*)calloc(5, sizeof(int));
总的来说,malloc()
和calloc()
都是动态内存分配的重要函数,选择使用哪一个取决于具体的需求,以及对内存内容初始化的要求。
宏定义
在C语言中,宏定义是一种预处理指令,用于在程序代码中定义常量、函数、条件编译等。宏定义由#define
关键字开头,后面跟着宏名称和宏的替换文本。当程序中出现宏名称时,预处理器会将其替换为宏的替换文本。
以下是一些关于宏定义的常见用法:
-
定义常量: 可以使用宏定义来定义常量,方便在程序中使用。
#define PI 3.14159 #define MAX_SIZE 100
-
定义函数宏: 可以使用宏定义来定义简单的函数,它们在编译时进行文本替换。
#define SQUARE(x) ((x) * (x)) #define MAX(a, b) ((a) > (b) ? (a) : (b))
-
条件编译: 可以使用宏定义来控制程序的编译过程,根据条件编译不同的代码段。
#define DEBUG 1 #if DEBUG printf("Debug mode is enabled.\n"); #else printf("Debug mode is disabled.\n"); #endif
-
条件定义: 可以使用宏定义来定义某些特定条件下的代码。
#define WINDOWS_PLATFORM #ifdef WINDOWS_PLATFORM // Windows平台下的代码 #else // 非Windows平台下的代码 #endif
-
宏与函数的区别:
- 宏是在预处理阶段进行文本替换,而函数是在编译阶段进行调用。
- 宏的替换文本不会进行类型检查,而函数会进行类型检查。
- 宏可以接受任意数量的参数,而函数的参数数量是固定的。
- 宏定义可以节省函数调用的开销,但可能会导致代码的可读性下降。
总的来说,宏定义是C语言中一个强大的工具,可以用于定义常量、函数、条件编译等,提高程序的灵活性和可维护性。但在使用时需要注意宏定义可能带来的潜在问题,如替换过程中的副作用、代码可读性下降等。
结构体
在C语言中,结构体(Structures)是一种用户自定义的数据类型,用于将不同类型的数据组合成一个单独的数据单元。结构体允许将多个相关的变量组织在一起,以便更方便地操作和管理这些变量。以下是关于C语言结构体的一些重要概念:
-
声明结构体: 使用
struct
关键字来声明一个结构体类型,然后在大括号内列出结构体的成员。struct Person { char name[50]; int age; float height; };
-
结构体变量: 可以使用声明的结构体类型来定义结构体变量。
struct Person person1;
-
结构体成员访问: 使用点操作符(
.
)来访问结构体的成员。strcpy(person1.name, "John"); person1.age = 25; person1.height = 175.5;
-
初始化结构体: 可以在声明结构体变量的同时初始化结构体的成员。
struct Person person2 = {"Alice", 30, 160.0};
-
结构体数组: 可以声明结构体数组,其中每个元素都是一个结构体变量。
struct Person people[10];
-
结构体指针: 可以使用结构体指针来操作结构体变量。
struct Person *ptr = &person1; ptr->age = 35;
-
嵌套结构体: 结构体可以嵌套在另一个结构体中。
struct Address { char street[100]; char city[50]; int zip_code; }; struct Employee { char name[50]; struct Address address; };
结构体在C语言中用于组织和管理复杂的数据结构,可以很好地表达现实世界中的各种概念和实体。结构体提供了一种便捷的方式来封装和操作数据,使得程序更具有可读性、可维护性和可扩展性。
IO操作
在C语言中,I/O(输入/输出)操作是通过标准库函数来实现的。主要涉及到标准输入、标准输出和文件操作。以下是C语言中常见的I/O操作:
-
标准输入/输出:
-
printf(): 用于向标准输出(通常是终端)打印格式化的输出。
printf("Hello, world!\n");
-
scanf(): 用于从标准输入(通常是键盘)读取输入数据。
int num; scanf("%d", &num);
-
-
文件操作:
-
打开文件: 使用
fopen()
函数打开文件,并返回文件指针。FILE *file = fopen("example.txt", "r"); // 以只读方式打开文件
-
关闭文件: 使用
fclose()
函数关闭文件。fclose(file);
-
读取文件内容: 使用
fscanf()
或fgets()
函数从文件中读取内容。char buffer[256]; fscanf(file, "%s", buffer); // 从文件中读取一个字符串 fgets(buffer, sizeof(buffer), file); // 从文件中读取一行
-
写入文件内容: 使用
fprintf()
或fputs()
函数向文件中写入内容。fprintf(file, "This is a line of text.\n"); // 写入格式化的字符串 fputs("Another line of text.\n", file); // 写入字符串
-
-
错误处理:
-
feof(): 检查文件是否到达文件末尾。
if (feof(file)) { printf("End of file reached.\n"); }
-
ferror(): 检查文件读写错误。
if (ferror(file)) { printf("Error reading from file.\n"); }
-
这些是C语言中常见的I/O操作。通过这些函数,你可以从标准输入读取用户输入,向标准输出打印输出,以及对文件进行读写操作。在进行文件操作时,一定要记得在使用完文件后关闭文件,以释放资源并确保数据完整性。