数组+指针
数组
1.概念:
具有一定顺序的若干变量的集合
2.定义格式:
存储类型 数据类型 数组名[元素个数]
int arr[5];
数组名:代表数组首元素的首地址,arr是地址常量,不能被赋值,不能为左值
3.访问元素:
数组名[下标]:从0开始
访问第一个元素:arr[0]
访问第n个元素:arr[n-1]
注意数组越界问题
4.特点
- 数组元素的数据类型相同
- 内存连续
- 5.注意:
- 数组的数据类型就是元素的数据类型
- 数组名要符合标识符命名规则
- 在同一个函数中,数组名不要与变量名相同
- 下标从0开始,到n-1结束
-
冒泡排序
两两比较,第j个和j+1个比较
int a[5] = {5 ,4 ,3 ,2 , 1};
第一轮: i = 0 n个数:比较n-1轮,每一轮交换的次数是n-1次开始依次递减的
4 5 3 2 1 j = 0
4 3 5 2 1 j = 1
4 3 2 5 1 j = 2
4 3 2 1 5 j = 3
第二轮: i = 1
3 4 2 1 5
3 2 4 1 5
3 2 1 4 5
第三轮: i = 2
2 3 1 4 5
2 1 3 4 5
第四轮: i = 3
1 2 3 4 5
#include <stdio.h>
#include <string.h>
#define N 5
int main(int argc, char const *argv[])
{
int arr[N] = {5, 4, 3, 2, 1};
int temp; // 用于交换的中间变量
for (int i = 0; i < N - 1; i++)
{
for (int j = 0; j < N - 1 - i; j++)
{
if(arr[j] > arr[j+1])
{
temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
for (int i = 0; i < N; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
选择排序
n个数:下找出最小值下标暂存,选择出最小的值与arr[i]交换
排序过程:
- 首先通过n-1次比较,从n个数中找出最小值的下标,将它与第一个数交换,第一轮选择排序,结果最小值被放在第一个元素的位置上
- 再通过n-2次比较,从剩余的n-1个数中找出最小值的下标做记录,将它与第二个数交换,第二轮排序
- 重复上述过程,共经过n-1轮排序后,排序结束
#include <stdio.h>
#define N 5
int main(int argc, char const *argv[])
{
int arr[N] = {1, 3, 4, 5, 2};
int temp; // 用于交换的中间变量
int k;
for (int i = 0; i < N - 1; i++)
{
k = i;
for (int j = i + 1; j < N; j++)
{
if (arr[j] < arr[k])
{
k = j;
}
}
if (i != k)
{
temp = arr[k];
arr[k] = arr[i];
arr[i] = temp;
}
}
for (int i = 0; i < N; i++)
{
printf("%d ", arr[i]);
}
printf("\n");
return 0;
}
-
字符数组
-
概念
元素的数据类型为字符型的数组
2.形式
char c[] = {'a', 'b', 'c'}; // 逐个字符的赋值,sizeof(c) == 3
// 因为字符串后面会默认补一个\0,所以是6字节
char str[] = {"hello"}; // 用字符串赋值, sizeof(str) == 6
char str[] = "hello"; // 用字符串赋值, sizeof(str) == 6
char str[32] = "hello"; // sizoef(str) == 32
char c[]; // 这样是不可以的,会报一个没有数组大小
char c[] = {}; // 大小是,随便放一个东西就会越界
注意:字符串赋值常常省略数组的长度,需要注意数组越界问题
3.字符数组输入输出
输入
char s[32] = {};
- scanf("%s", s);
*输入字符串不能含有空格*,因为scanf输入字符串遇到空格或者 \n 都会认为字符串输入结束,空格后面就不能再存放到数组里面
如果需要输入空格就按以下格式输入
scanf("%[^\n]", s); // 直到输入\n才结束
4.循环遍历
5.gets
#include <stdio.h>
char *gets(char *s);
功能:从终端获取字符串
参数:s:目标字符数组的首地址
返回值:目标字符数组的首地址
输出
- printf("%s\n", s);
- 循环遍历
- puts
#include <stdio.h>
int puts(const char *s);
功能:向终端输出字符串
参数:要输出字符数组的首地址
返回值:输出字符的个数
指针
指针的优点:
\1. 使程序更简洁、紧凑、高效
\2. 有效的表达更复杂的数据结构
\3. 动态分配内存
\4. 得到多于一个数的函数返回值
1.概念
地址:内存中每个字节单元都有的一个编号(门牌号)
指针:指针就是地址
指针变量:用于存放地址的这种变量就叫做指针
2.格式
存储类型 数据类型 *指针变量名;
3.指针操作符
&:取地址符,取变量的地址
*:取内容,取地址里面的内容
*&a == a // *和&都是单目运算符,互逆运算
4.指针运算
4.1. 算术运算
char str[10] = "hello";
char *p = str;
// 加一,不是加了一个字节,而是一个数据单位
p++; // 指针向高地址方向移动一个数据单位,指针指向发生改变
p--; // 指针向低地址方向移动一个数据单位,指针指向发生改变
例子:
int *p; p++; // 移动4字节
double *p; p++; // 移动8字节
4.2. 关系运算
> < == !=
指针之间的关系原酸比较的是地址的高低
指向高地址的变量大于指向低地址的变量
char str[32] = "hello";
char *p1 = &str[1];
char *p2 = &str[3];
p2 > p1
注意:指向不同类型数组的指针进行关系运算时没有意义,
指向不同区域的指针的关系运算也没有意义
(同一个数组之间进行比较)
总结
- 32位操作系统:指针大小为4字节,64位操作系统,指针大小为8字节
- 内存地址是固定,但是变量的地址不固定(栈区随机分配变量)
- 指针类型根据指针变量指向空间的数据类型(指针变量的数据类型取决于它保存的地址是什么样子的)
指针数组、
本质是数组,里面存放的是指针
格式:存储类型 数据类型 *数组名[元素个数]
例:int a = 10, b = 20, c = 30;
int *arr[3] = {&a, &b, &c};
访问b的地址:
arr[1] *(arr+1)
访问b的值:
*(arr[1]) *(*(arr+1))
数组指针、
定义:本质上是指针,指向的二维数组(行指针)
格式:存储类型 数据类型 (*指针变量名)[列数];
int a[2][3] = {1, 2, 3, 4, 5, 6};
int (*p)[3] = a;
p可以代替a进行元素访问,但是本质不同
访问 a[i][j] 地址:
*(p+i)+j == *(a+i)+j
p[i]+j == a[i]+j
访问a[i][j] 的内容:
*(*(p+i)+j) *(p[i]+j) p[i][j]
数组指针大小:
sizeof(p); // 8字节
段错误、
当程序尝试访问它没有权限访问的内存地址时容易出现段错误
段错误通常是由以下几种情况引起的:
- 访问空指针: 当程序试图访问一个空指针所指向的内存地址时,就会发生段错误。例如,未初始化的指针或者指向已释放的内存的指针。
- 数组越界: 当程序试图访问数组越界的元素时,就会发生段错误。例如,访问数组下标小于 0 或者大于等于数组大小的元素。
- 非法内存访问: 当程序试图访问未分配的内存区域,或者试图访问系统保留的内存区域,就会发生段错误。
- 对只读内存的写操作:当程序试图对只读内存进行写操作时,例如对常量字符串进行修改,就会发生段错误。
要解决段错误问题,可以通过以下几种方式:
方式一:
在程序中加一些打印调试语句,确定段错误的位置,再根据产生错误的原因进行分析具体问题
- 检查空指针: 在使用指针之前,先确保指针不是空指针。可以使用条件语句或者断言来检查指针是否为空。
- 数组边界检查: 在访问数组元素之前,先确保访问的下标在合法的范 围内。
- 动态内存管理: 在动态分配内存(例如使用 malloc、calloc、new m等函数)后,要确保在不再使用该内存时释放它(例如使用 free、delete 等函数)。
- 避免对只读内存进行写操作: 对于常量字符串或者是只读内存区域,避免对其进行写操作,可以使用 const 关键字来声明指向只读内存的 指针。
方式二:
使用工具检查: 可以使用一些工具,如gdb、rdynamic、Valgrind等,在程序运行时检查内存错误,包括段错误、内存泄漏等。
空指针
空指针(Null Pointer)是一个指向不存在内存地址的指针。
野指针
野指针(Wild Pointer): 野指针是指指向未知或者无效内存地址的指针。野指针通常产生于以下几种情况:
- 指针未初始化:指针没有初始化,即没有赋予合法的地址,指针的初始值是随机的。
- 内存释放后未置空:指针指向的内存已经被释放,但指针没有置为 nullptr 或 NULL。
- 指针越界访问:指针指向的内存已经被释放,但程序继续使用该指针。