数组是一种数据结构,用来存储一组相同类型的数据。想象一下,数组就像是一排排整齐的宿舍房间,每个房间都有一个编号(索引),而里面住着的数据就是“室友”。
类型 | 描述 | 示例代码 | 输出 |
---|---|---|---|
一维数组 | 单层宿舍,存储一组相同类型的数据,索引从0开始。 | int dorm[5] = {1, 2, 3, 4, 5}; |
dorm[0] = 1, dorm[1] = 2, ..., dorm[4] = 5 |
多维数组 | 多层宿舍,需要多个索引访问元素。二维数组是最常见形式。 | int building[2][3] = { {1, 2, 3}, {4, 5, 6}}; |
building[0][0] = 1, building[1][2] = 6 |
字符数组与字符串 | 字符数组以\0 结尾表示字符串,用于存储文本数据。 |
char name[] = "Hello"; |
name = "Hello" |
一维数组是最简单的数组形式,就像是一排单层的宿舍。每个房间(元素)都有一个编号,编号从0开始。
int dorm[5] = {1, 2, 3, 4, 5};
这里,dorm
是一个一维数组,可以存储5个整数。dorm[0]
是第一个房间,住着数字1;dorm[4]
是最后一个房间,住着数字5。
示例代码:
#include
int main() {
int dorm[5] = {1, 2, 3, 4, 5}; // 定义一个一维数组
printf("一维数组的内容:\n");
for (int i = 0; i < 5; i++) {
printf("dorm[%d] = %d\n", i, dorm[i]);
}
return 0;
}
输出:
一维数组的内容:
dorm[0] = 1
dorm[1] = 2
dorm[2] = 3
dorm[3] = 4
dorm[4] = 5
多维数组就像是多层的宿舍楼。二维数组是最常见的多维数组,它有两个维度:行和列。每个房间的地址需要两个编号来确定。
int building[2][3] = {
{1, 2, 3}, // 第一层
{4, 5, 6} // 第二层
};
这里,building
是一个二维数组,第一维表示楼层,第二维表示房间。building[0][0]
是第一层的第一个房间,住着数字1;building[1][2]
是第二层的第三个房间,住着数字6。
示例代码:
#include
int main() {
int building[2][3] = {
{1, 2, 3}, // 第一层
{4, 5, 6} // 第二层
};
printf("多维数组的内容:\n");
for (int i = 0; i < 2; i++) { // 遍历楼层
for (int j = 0; j < 3; j++) { // 遍历房间
printf("building[%d][%d] = %d\n", i, j, building[i][j]);
}
}
return 0;
}
输出:
多维数组的内容:
building[0][0] = 1
building[0][1] = 2
building[0][2] = 3
building[1][0] = 4
building[1][1] = 5
building[1][2] = 6
字符数组是用来存储字符的数组,而字符串是字符数组的一种特殊形式,以\0
(空字符)结尾。字符串就像是宿舍门口的名字牌,用来标识每个房间的主人。
char name[6] = {'H', 'e', 'l', 'l', 'o', '\0'};
或者直接写成:
char name[] = "Hello";
这里的name
是一个字符串,存储了“Hello”这个单词,最后一个\0
是字符串的结束标志。
示例代码:
#include
int main() {
char name[6] = {'H', 'e', 'l', 'l', 'o', '\0'}; // 字符数组
char greeting[] = "Hello"; // 字符串
printf("字符数组的内容:%s\n", name);
printf("字符串的内容:%s\n", greeting);
return 0;
}
输出:
字符数组的内容:Hello
字符串的内容:Hello
指针是一种特殊的变量,用来存储另一个变量的地址。想象一下,指针就像是一个导航仪,它不直接存储数据,而是告诉你数据在哪里。
概念 | 描述 | 示例代码 | 输出 |
---|---|---|---|
指针的概念 | 指针存储变量的地址,通过指针可以间接访问和修改变量。 | c |
10 |
指针与数组 | 数组名是数组首元素的地址,指针可以用来遍历数组。 | c |
3 |
指针运算 | 指针可以进行加减运算,用于访问相邻元素。 | c |
2 |
指针与函数 | 指针可以作为函数参数或返回值,用于修改变量或返回地址。 | c |
6 |
指针与字符串 | 字符串是字符数组,可以通过指针逐字符访问。 | c |
e |
字符串操作函数 | 提供字符串复制、比较等功能。 | c |
Hello |
指针是一个变量,用来存储另一个变量的地址。通过指针,我们可以间接访问和修改变量的值。
int value = 10;
int *ptr = &value; // ptr 是一个指针,存储了 value 的地址
&value
是取value
的地址,ptr
是一个指向int
类型的指针。
示例代码:
#include
int main() {
int value = 10; // 定义一个整数变量
int *ptr = &value; // 定义一个指针,存储 value 的地址
printf("value 的值:%d\n", value);
printf("value 的地址:%p\n", &value);
printf("通过指针访问 value:%d\n", *ptr); // 通过指针访问 value 的值
return 0;
}
输出:
value 的值:10
value 的地址:0x7ffccf4f7a4c
通过指针访问 value:10
数组的名字本质上就是一个指向数组首元素的指针。通过指针,我们可以访问数组的元素。
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr; // arr 是数组名,也是首元素的地址
ptr
和 arr
都指向数组的第一个元素。通过指针运算,可以访问数组的其他元素。
示例代码:
#include
int main() {
int arr[5] = {1, 2, 3, 4, 5}; // 定义一个数组
int *ptr = arr; // 指针指向数组的首元素
printf("通过数组名访问元素:\n");
for (int i = 0; i < 5; i++) {
printf("arr[%d] = %d\n", i, arr[i]);
}
printf("通过指针访问元素:\n");
for (int i = 0; i < 5; i++) {
printf("*(ptr + %d) = %d\n", i, *(ptr + i));
}
return 0;
}
输出:
通过数组名访问元素:
arr[0] = 1
arr[1] = 2
arr[2] = 3
arr[3] = 4
arr[4] = 5
通过指针访问元素:
*(ptr + 0) = 1
*(ptr + 1) = 2
*(ptr + 2) = 3
*(ptr + 3) = 4
*(ptr + 4) = 5
指针可以进行加减运算,但不能进行乘除运算。通过指针运算,我们可以遍历数组或访问相邻的元素。
int arr[5] = {1, 2, 3, 4, 5};
int *ptr = arr;
ptr++; // 指针指向下一个元素
printf("%d\n", *ptr); // 输出 2
示例代码:
#include
int main() {
int arr[5] = {1, 2, 3, 4, 5}; // 定义一个数组
int *ptr = arr; // 指针指向数组的首元素
printf("指针初始位置:%p\n", ptr);
printf("指针指向的值:%d\n", *ptr);
ptr++; // 指针指向下一个元素
printf("指针移动后的位置:%p\n", ptr);
printf("指针移动后指向的值:%d\n", *ptr);
return 0;
}
输出:
指针初始位置:0x7ffccf4f7a40
指针指向的值:1
指针移动后的位置:0x7ffccf4f7a44
指针移动后指向的值:2
指针可以作为函数的参数,也可以作为函数的返回值。
指针作为参数:可以修改传入的变量。
void increment(int *p) {
(*p)++;
}
返回指针:函数可以返回一个指针。
int *getPointer() {
int value = 10;
return &value; // 注意:返回局部变量的地址是危险的!
}
示例代码:
#include
void increment(int *p) {
(*p)++; // 通过指针修改变量的值
}
int *getPointer() {
int value = 10;
return &value; // 返回局部变量的地址(不推荐)
}
int main() {
int value = 5;
increment(&value); // 通过指针修改 value
printf("修改后的 value:%d\n", value);
int *ptr = getPointer(); // 获取一个指针
printf("通过指针访问的值:%d\n", *ptr); // 注意:这个值可能已经失效!
return 0;
}
输出:
修改后的 value:6
通过指针访问的值:10
字符串本质上是一个字符数组,以\0
结尾。可以通过指针来操作字符串。
char *str = "Hello";
printf("%c\n", *str); // 输出 'H'
printf("%c\n", *(str + 1)); // 输出 'e'
示例代码:
#include
int main() {
char *str = "Hello"; // 定义一个字符串
printf("字符串的内容:%s\n", str);
printf("通过指针访问字符串的每个字符:\n");
for (int i = 0; str[i] != '\0'; i++) {
printf("str[%d] = %c\n", i, *(str + i));
}
return 0;
}
输出:
字符串的内容:Hello
通过指针访问字符串的每个字符:
str[0] = H
str[1] = e
str[2] = l
str[3] = l
str[4] = o
C语言提供了很多字符串操作函数,比如strcpy
、strcmp
等。
strcpy
:复制字符串。
strcmp
:比较字符串。
示例代码:
#include
#include
int main() {
char src[] = "Hello";
char dest[20];
strcpy(dest, src); // 复制字符串
printf("复制后的字符串:%s\n", dest);
int result = strcmp(src, dest); // 比较字符串
if (result == 0) {
printf("两个字符串相等!\n");
} else {
printf("两个字符串不相等!\n");
}
return 0;
}
输出:
复制后的字符串:Hello
两个字符串相等!
动态内存分配允许我们在程序运行时申请和释放内存。这就像一个灵活的租房服务,可以根据需要随时租用或退房。
函数 | 描述 | 示例代码 | 输出 |
---|---|---|---|
malloc |
分配指定大小的内存,返回指向该内存的指针。 | c |
10 |
calloc |
分配内存并初始化为0。 | c |
0 |
realloc |
调整已分配内存的大小。 | c |
90 |
free |
释放动态分配的内存,避免内存泄漏。 | c |
- |
malloc
用于分配一块指定大小的内存,并返回一个指向这块内存的指针。
int *ptr = (int *)malloc(5 * sizeof(int)); // 分配5个整数的空间
示例代码:
#include
#include
int main() {
int *ptr = (int *)malloc(5 * sizeof(int)); // 分配5个整数的空间
if (ptr == NULL) {
printf("内存分配失败!\n");
return 1;
}
for (int i = 0; i < 5; i++) {
ptr[i] = i * 10; // 初始化数组
}
printf("动态分配的数组内容:\n");
for (int i = 0; i < 5; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]);
}
free(ptr); // 释放内存
return 0;
}
输出:
动态分配的数组内容:
ptr[0] = 0
ptr[1] = 10
ptr[2] = 20
ptr[3] = 30
ptr[4] = 40
calloc
用于分配一块内存,并将其初始化为0。
int *ptr = (int *)calloc(5, sizeof(int)); // 分配5个整数的空间,并初始化为0
示例代码:
#include
#include
int main() {
int *ptr = (int *)calloc(5, sizeof(int)); // 分配5个整数的空间,并初始化为0
if (ptr == NULL) {
printf("内存分配失败!\n");
return 1;
}
printf("动态分配并初始化为0的数组内容:\n");
for (int i = 0; i < 5; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]);
}
free(ptr); // 释放内存
return 0;
}
输出:
动态分配并初始化为0的数组内容:
ptr[0] = 0
ptr[1] = 0
ptr[2] = 0
ptr[3] = 0
ptr[4] = 0
realloc
用于调整已分配内存的大小。
ptr = (int *)realloc(ptr, 10 * sizeof(int)); // 扩展到10个整数的空间
示例代码:
#include
#include
int main() {
int *ptr = (int *)malloc(5 * sizeof(int)); // 初始分配5个整数的空间
if (ptr == NULL) {
printf("内存分配失败!\n");
return 1;
}
for (int i = 0; i < 5; i++) {
ptr[i] = i * 10; // 初始化数组
}
printf("初始动态分配的数组内容:\n");
for (int i = 0; i < 5; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]);
}
ptr = (int *)realloc(ptr, 10 * sizeof(int)); // 扩展到10个整数的空间
if (ptr == NULL) {
printf("内存重新分配失败!\n");
return 1;
}
for (int i = 5; i < 10; i++) {
ptr[i] = i * 10; // 继续初始化数组
}
printf("扩展后的动态分配的数组内容:\n");
for (int i = 0; i < 10; i++) {
printf("ptr[%d] = %d\n", i, ptr[i]);
}
free(ptr); // 释放内存
return 0;
}
输出:
初始动态分配的数组内容:
ptr[0] = 0
ptr[1] = 10
ptr[2] = 20
ptr[3] = 30
ptr[4] = 40
扩展后的动态分配的数组内容:
ptr[0] = 0
ptr[1] = 10
ptr[2] = 20
ptr[3] = 30
ptr[4] = 40
ptr[5] = 50
ptr[6] = 60
ptr[7] = 70
ptr[8] = 80
ptr[9] = 90
free
用于释放动态分配的内存,避免内存泄漏。
free(ptr); // 释放内存
下面是一个综合使用数组、指针和动态内存分配的程序,实现一个动态数组的创建、字符串复制和比较。
#include
#include
#include
// 动态创建一个整数数组,并初始化为指定值
int *createArray(int size, int value) {
int *arr = (int *)malloc(size * sizeof(int));
if (arr == NULL) {
printf("内存分配失败!\n");
return NULL;
}
for (int i = 0; i < size; i++) {
arr[i] = value;
}
return arr;
}
// 打印数组
void printArray(int *arr, int size) {
if (arr == NULL) {
printf("数组为空!\n");
return;
}
for (int i = 0; i < size; i++) {
printf("%d ", arr[i]);
}
printf("\n");
}
// 字符串操作
void stringOperations() {
char src[] = "Hello";
char dest[20];
strcpy(dest, src); // 复制字符串
printf("复制后的字符串:%s\n", dest);
int result = strcmp(src, dest); // 比较字符串
if (result == 0) {
printf("两个字符串相等!\n");
} else {
printf("两个字符串不相等!\n");
}
}
int main() {
// 动态创建数组
int *arr = createArray(5, 10);
printf("动态创建的数组内容:");
printArray(arr, 5);
// 字符串操作
stringOperations();
// 释放动态分配的内存
free(arr);
return 0;
}
输出:
动态创建的数组内容:10 10 10 10 10
复制后的字符串:Hello
两个字符串相等!
数组和指针是C语言中的重要概念,它们就像是数据的“集体宿舍”和“导航仪”。动态内存分配则让我们的程序更加灵活。通过这些知识点,我们可以实现很多强大的功能,比如动态数组和字符串操作。