• 11_C++_《指针》_笔记整理


    空指针和野指针

    空指针

    不允许向NULL和非法地址拷贝内存

    void test(){
    	char *p = NULL;
    	//给p指向的内存区域拷贝内容
    	strcpy(p, "1111"); //error
    	char *q = 0x1122;
    	//给q指向的内存区域拷贝内容
    	strcpy(q, "2222"); //error	
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    野指针的三种情况

    1. 未初始化指针 → \rightarrow 只定义了指针,没有赋给它地址
    2. malloc后也free了,但是指针没有置空 → \rightarrow 相当于没有给指针赋变量地址
    3. 指针操作超越变量作用域 → \rightarrow 一个函数的指针是局部变量,函数生命周期结束之后,这个局部变量消失

    空指针和野指针的释放问题

    空指针可以释放 → \rightarrow 可以多次free操作
    野指针不可以释放 → \rightarrow 不可以

    指针的步长含义

    1. 指针变量+1之后,跳跃的字节数量 → \rightarrow int指针+1之后跳4个字节;char指针+1跳一个
    2. 解引用的时候,取的字节数 → \rightarrow int指针取当前位置之后的4个字节;char指针取一个

    获取自定义数据格式的偏移

    可以获得结构体某成员的首地址距离当前结构体首地址的偏移量

    #include
    
    offsetof(结构体,属性)
    
    • 1
    • 2
    • 3

    指针做函数参数

    输入特性

    输入特性 → \rightarrow 主调函数中分配内存,将内存的指针赋给被调函数,可以运行

    1. 主调函数在区分配内存,可以跑
    void fun(char *p)
    {
    	// 给p指向的内存区域拷贝内容
    	strcpy(p, "abcddsgsd");
    }
    void test(void)
    {
    	// 输入
    	// 主调函数分配内存
    	char buf[100] = { 0 };
    	fun(buf);
    	printf("buf  = %s\n", buf);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. 主调函数在区分配内存,也可以跑,也就是malloc生成的内存

    输出特性

    输出特性 → \rightarrow 被调函数中分配内存,此时主调函数的指针应该比被调函数的形参指针低一级

    void fun(char **p, int *len)
    {
    	// 被调函数的形参指针高一级
    	char *tmp = (char *)malloc(100);
    	if (tmp == NULL)
    	{
    		return;
    	}
    	strcpy(tmp, "adlsgjldsk");
    	// 间接赋值
    	*p = tmp;
    	*len = strlen(tmp);
    }
    void test(void)
    {
    	// 输出
    	// 被调用函数分配内存,地址传递
    	char *p = NULL;
    	int len = 0;
    	fun(&p, &len);
    	if (p != NULL)
    	{
    		printf("p = %s, len = %d\n", p, len);
    	}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    字符串

    案例1——字符串长度

    char str6[] = "hello\012world";
    printf("%s\n", str6);
    printf("sizeof str6:%d\n", sizeof(str6));
    printf("strlen str6:%d\n", strlen(str6));
    
    • 1
    • 2
    • 3
    • 4

    输出的结果是:

    hello
    world
    12
    11
    
    • 1
    • 2
    • 3
    • 4

    原因:\012对应ASCII码是换行符,是具有实际意义的字符,因此在显示字符串的时候换行了;字符串长度算他一个。

    案例2——字符串拷贝

    // 拷贝方法1————采用下标索引的方式
    void copy_string01(char* dest, char* source ){
    	for (int i = 0; source[i] != '\0';i++){
    		dest[i] = source[i];
    	}
    }
    // 拷贝方法2————采用指针解引用的方式
    void copy_string02(char* dest, char* source){
    	while (*source != '\0'){
    		*dest = *source;
    		source++;
    		dest++;
    	}
    }
    // 拷贝方法3————采用运算符优先级顺序,本质和拷贝方法2相同
    void copy_string03(char* dest, char* source){
    	//判断*dest是否为0,0则退出循环
    	while (*dest++ = *source++){}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    案例3——字符串翻转

    void reverse_string(char* str){
    	if (str == NULL){
    		return;
    	}
    	int begin = 0;
    	// 字符串长度减1才是我们的结尾
    	int end = strlen(str) - 1;
    	while (begin < end){
    		//交换两个字符元素
    		char temp = str[begin];
    		str[begin] = str[end];
    		str[end] = temp;
    		begin++;
    		end--;
    	}
    }
    // 也可以用指针解引用
    // while循环的条件是对指针进行比较
    void test(){
    	char str[] = "abcdefghijklmn";
    	printf("str:%s\n", str);
    	reverse_string(str);
    	printf("str:%s\n", str);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24

    案例4——格式化字符串

    可以利用sprintf对字符串进行格式化

    sprintf(目标字符串(char数组), "格式",占位参数(不止一个));
    
    • 1

    calloc和realloc

    callocmalloc一样都是在堆区分配内存
    不同点:

    1. calloc会将分配的内存初始化为0
    2. realloc重新在堆区分配内存
    3. realloc机制:原有空间后序有足够大的空闲空间,那么直接在原有空间后继续开辟内存,返回原有空间的首地址;没有足够大空闲空间,重新分配一个足够大的空间,并且将原有空间的内容拷贝到新空间下,释放原有空间,将新空间的首地址返回

    sscanf使用

    将已知的字符串通过格式化匹配出有效信息
    贪婪性指的是能匹配到就多匹配一些
    贪婪性的具体案例

    格式作用
    %*s或%*d跳过数据
    %[width]s读指定宽度的数据
    %[a-z]匹配a到z中任意字符(尽可能多的匹配)
    %[aBc]匹配a、B、c中一员,贪婪性
    %[^a]匹配非a的任意字符,贪婪性
    %[^a-z]表示读取除a-z以外的所有字符

    const使用场景

    值传递形式调用函数需要拷贝数据,可能会占据更多的内存
    通过地址传递,占用的内存更少一些(对于结构体而言)
    const用来修饰函数中的形参,防止误操作,在函数内部再更改形参的数据时,会报错

    一级指针

    指针越界

    void test(){
    	char buf[3] = "abc";
    	// 没有存'\0'
    	printf("buf:%s\n",buf);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    指针叠加会不断改变指针指向

    void test(){
    	char *p = (char *)malloc(50);
    	char buf[] = "abcdef";
    	int n = strlen(buf);
    	int i = 0;
    	for (i = 0; i < n; i++)
    	{
    		*p = buf[i];
    		p++; 
    		// 修改原指针指向
    	}
    	free(p);
    	// error
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    二级指针

    二级指针做形参输出特性

    //被调函数,由参数n确定分配多少个元素内存
    void allocate_space(int **arr,int n){
    	//堆上分配n个int类型元素内存
    	int *temp = (int *)malloc(sizeof(int)* n);
    	if (NULL == temp){
    		return;
    	}
    	//给内存初始化值
    	int *pTemp = temp;
    	for (int i = 0; i < n;i ++){
    		//temp[i] = i + 100;
    		*pTemp = i + 100;
    		pTemp++;
    	}
    	//指针间接赋值
    	*arr = temp;
    }
    //打印数组
    void print_array(int *arr,int n){
    	for (int i = 0; i < n;i ++){
    		printf("%d ",arr[i]);
    	}
    	printf("\n");
    }
    //二级指针输出特性(由被调函数分配内存)
    void test(){
    	int *arr = NULL;
    	int n = 10;
    	//给arr指针间接赋值
    	allocate_space(&arr,n);
    	//输出arr指向数组的内存
    	print_array(arr, n);
    	//释放arr所指向内存空间的值
    	if (arr != NULL){
    		free(arr);
    		arr = NULL;
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38

    二级指针做形参输入特性

    //打印数组
    void print_array(int **arr,int n){
    	for (int i = 0; i < n;i ++){
    		printf("%d ",*(arr[i]));
    	}
    	printf("\n");
    }
    //二级指针输入特性(由主调函数分配内存)
    void test(){
    	int a1 = 10;
    	int a2 = 20;
    	int a3 = 30;
    	int a4 = 40;
    	int a5 = 50;
    	int n = 5;
    	int** arr = (int **)malloc(sizeof(int *) * n);
    	arr[0] = &a1;
    	arr[1] = &a2;
    	arr[2] = &a3;
    	arr[3] = &a4;
    	arr[4] = &a5;
    	print_array(arr,n);
    	free(arr);
    	arr = NULL;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    与或非运算

    1. 与运算可以检测奇偶性
    2. 或运算可以设定某位“点亮”
    3. 异或运算可以实现数据互换

    左移和右移

    每左移/右移一位,相当于乘以/除以2的位次方
    右移运算符>>将其左侧的操作数的值每位向右移动,移动的位数由其右侧的操作数指定。
    丢弃移出左侧操作数有段的位
    对于unsigned类型,使用0填充左端空出的位。
    对于有符号类型,结果依赖于机器。空出的位可能用0填充,或者使用符号(最左端)位的副本填充。

    多维数组

    一维数组

    一维数组本质并不是一个指针
    有两种特殊情况:(1)对数组名称进行sizeof ;(2)对数组名称取地址 ,获取的指针步长是整个数组长度
    除了两种特殊情况外,都是指向数组中首元素的地址的指针

    指针常量

    指针的指向不可以修改

    传入到函数参数

    提高可读性,可写成void test(int arr[]),退化

    数组指针的定义方式

    1. 先定义出数组的类型,再通过类型创建数组指针
    typedef  int(ARRAY_TYPE)[5];
    
    • 1
    1. 先定义数组指针的类型,再创建数组指针变量
    typedef  int(*ARRAY_TYPE)[5];
    
    • 1
    1. 直接创建数组指针变量
    int(*pArr)[5] = &arr;
    
    • 1

    二维数组

    1. 除了两种特殊情况外,都是指向第一个一维数组的指针(第一行
    2. 两种特殊情况:
      (1)sizeof 统计整个二维数组长度;
      (2)对数组名称取地址 int(*p2)[3][3] = &arr;
    3. 二维数组做函数参数传递方式
    void printArray( int p[][3] , int row, int col)
    
    • 1
    void printArray(int p[3][3], int row, int col)
    // 可读性高
    
    • 1
    • 2
    void printArray( int(*p)[3]  , int row ,int col)
    
    • 1

    数组指针和指针数组的区别

    1. 数组指针是一个指向数组的指针 int (*p)[10];
    2. 指针数组是一个存放指针的数组 int * p[10];

    排序算法

    选择排序算法

    选择排序算法 - 超链接

  • 相关阅读:
    CentOS7中原生Python2.7.5和Python3共存
    2594. 修车的最少时间(Java)
    均匀光源积分球的应用领域有哪些
    vim相关命令讲解!
    自制手机app的51蓝牙+循迹+OLED显示速度小车
    FPGA-时钟管理单元
    Hive简介及安装配置
    Webpack 复习小结
    Go语言学习笔记——Golang 1.18新特性工作区workspace
    极智开发 | 阿里云ECS本地开发环境搭建
  • 原文地址:https://blog.csdn.net/m0_48948682/article/details/126010551