栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > C/C++/C#

C语言---指针

C/C++/C# 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

C语言---指针

四要素

内存

指针的初始化与赋值

用对应类型变量的地址

用相同类型的指针

直接用地址

用数组名

字符串

置空

多级指针

指针的加减法

指针偏移

指针的自增自减

通过指针遍历数组

大小端

指针数组

数组指针

typedef

指针函数

函数指针

const

常量指针

指针常量

常量指针常量

内存区划分

代码区

常量区

全局区(静态全局区)

栈区

堆区

void * 指针

动态申请内存

申请和释放

内存泄漏和野指针

动态数组

类似一维数组

类似二维数组

数组扩容

字符串相关函数


  1. 为了方便访问内存中的内容,给每一个内存单元一个编号,我们称这个编号为地址,也就是指针.

  2. 指针也是一种数据类型 所以指针有自己的内存 存储的是地址(编号)

四要素
  1. 指针本身的类型 除去指针名,剩下的就是指针本身的类型

  2. 指针指向的类型 除去指针名和一个*,剩下的就是指针指向的类型

  3. 指针本身的内存 用来存储一个编号(4字节)

  4. 指针指向的内存 可以是各种类型

int num = 0; // 类型:int 
int arr[10] = {}; // 类型:int [10] 
// 去掉名字,剩下的就是类型 

定义

*: 定义时,表示定义的是一个指针 其他的时候表示解析引用(取内容)

&: 取首地址符

#include  
int main() 
{ 
     
    // 类型 变量名; 
    int num = 0; 
     
    // 类型 变量名; 
    // 指针本身的类型 指针名; 
    // 指针指向的类型* 指针名; 
    // ==> 除去指针名,剩下的就是指针本身的类型 
    // ==> 除去指针名和一个*,剩下的就是指针指向的类型 
    // 定义了一个int*类型的指针 取名叫做p 
    // p这个指针,本身的类型:int* 
    // p这个指针,指向的类型:int
    int* p; 
    // 注意: 现在的指针p 没有指向 
    // 所谓的类型,意思是他可以指向 
    // 定义了一个int******类型的指针 取名叫做p1 
    // p这个指针,本身的类型:int****** 
    // p这个指针,指向的类型:int***** 
    int****** p1; 
    return 0; 
}

内存

所有的指针,不论类型,在内存中都占4个字节的内存

#include  
int main() 
{ 
    char* pch; 
    short* psh; 
    int* pn; 
    float* pf; 
    double* pd; 
    printf("%dn", sizeof(pch)); // 4 
    printf("%dn", sizeof(psh)); // 4 
    printf("%dn", sizeof(pn)); // 4 
    printf("%dn", sizeof(pf)); // 4 
    printf("%dn", sizeof(pd)); // 4 
    return 0; 
}

指针的初始化与赋值

用对应类型变量的地址
#include  
int main() 
{ 
    int num = 10; 
    int* pn = # // 初始化 
    float f = 3.14f; 
    float* pf; 
    pf = &f; // 赋值 
    return 0; 
}

用相同类型的指针
#include  
int main() 
{ 
    int num = 10; 
    int* pn = # // 初始化 
    int* p1 = pn; // 初始化
    int* p2; 
    p2 = pn; // 赋值 
    return 0; 
}

直接用地址
#include  
int main() 
{ 
    int* p = (int*)0x36; 
    return 0; 
}

用数组名

一级指针可以接受一维数组的数组名

#include  
int main() 
{ 
    // 数组名就是数组的首地址 
    int arr[5] = { 1, 2, 3, 4, 5 }; 
    int* p = arr; 
    return 0; 
}

字符串
#include  
int main() 
{ 
    // 数组名就是数组的首地址 
    char arr[5] = {'a','b','c' }; 
    char* p = arr; 
    char str[5] = "abcd"; 
    p = str; 
    printf("%sn", str); 
    printf("%sn", p); 
    // ==> char*类型的指针可以直接用来打印整个字符串到''停止 
    p = "asdf"; 
    return 0; 
} 

置空
int* p = NULL; 
int* p1 = (int*)0X0; 

多级指针
#include  
int main() 
{ 
    int num = 10; 
    printf(" num = %dn", num); 
    printf("&num = %Xn", &num); 
    int* p = # 
    printf("*p = %dn", *p);
    printf(" p = %Xn", p); 
    printf("&p = %Xn", &p); 
    int** pp = &p; // 一个二级指针 
    printf("**pp = %dn", **pp); 
    printf(" *pp = %Xn", *pp); 
    printf(" pp = %Xn", pp); 
    printf(" &pp = %Xn", &pp); 
    int*** ppp = &pp; // 一个三级指针 
    printf("***ppp = %dn", ***ppp); 
    printf(" **ppp = %Xn", **ppp); 
    printf(" *ppp = %Xn", * ppp); 
    printf(" ppp = %Xn", ppp); 
    printf(" &ppp = %Xn", & ppp); 
    return 0; 
}

指针的加减法

指针本身的值(指向) 没有变化

指针偏移
  1. 指针可以加上或者减去一个整数

  2. 指针加上或者减去一个整数后,实际上是进行了偏移

  3. 偏移的范围是加上或减去的整数个单位

单位: 指针指向的类型在内存中所占的字节数

偏移: 指针指向不变,但是可以根据偏移量取内容

#include  
int main() 
{ 
    int num = 10; 
    int* p = # 
    printf("%Xn", p); 
    printf("%Xn", p+1); 
    return 0; 
}

指针的自增自减

自增自减,会改变指针指向

++: 表示指针向后移动一个单位(--: 向前)

单位: 指针指向的类型在内存中所占的字节数

#include  
int main() 
{ 
    int num = 10; 
    num + 10; 
    int* p = # 
    printf("%Xn", p); 
    printf("%Xn", p+1); 
    p + 1; 
    p++; // p = p + 1; 
    return 0; 
}

通过指针遍历数组

遍历一维数组

#include  
int main() 
{ 
    int arr[10] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; 
    for (size_t i = 0; i < 10; i++) 
    { 
        printf("%2d", arr[i]); 
    }
    printf("n"); 
    int* p = arr; 
    // p和arr,除了arr是一个常量之外,其他几乎是一样的 
    for (size_t i = 0; i < 10; i++) 
    { 
        printf("%2d", p[i]); 
    }
    printf("n"); 
    printf("%dn", p[0]); 
    printf("%dn", *(p + 0)); 
    printf("%dn", p[1]); 
    printf("%dn", *(p + 1)); 
    // p[n] <==> *(p+n) 
    return 0; 
}
// p[n]:叫做下标形式
// *(p+n):叫做指针偏移的形式 

遍历二维数组

  1. 二维数组也是数组

  2. 二维数组可以看成元素是一维数组的一维数组

  3. 数组的内存是连续的

#include  
int main() 
{ 
    int arr[3][4] = { 
        {1,2,3,4}, 
        {5,6,7,8}, 
        {9,10,11,12} 
    };
    for (size_t i = 0; i < 3; i++) 
    { 
        for (size_t j = 0; j < 4; j++) 
        { 
            printf("%3d", arr[i][j]); 
        }
        printf("n"); 
    }
    
// 1 
    int* p0 = arr[0]; 
    int* p1 = arr[1]; 
    int* p2 = arr[2]; 
    printf("n"); 
    for (size_t i = 0; i < 4; i++) 
    { 
        printf("%3d", p0[i]); 
    }
    printf("n"); 
    for (int i = -4; i <= 7; i++) 
    { 
        printf("%3d", p1[i]); 
    }
    printf("n"); 
    for (int i = 0; i < 12; i++) 
    { 
        printf("%3d", arr[0][i]); 
    }
    printf("n"); 
    // 下标: 保证数组不越界即可 
    
    // 2 
    int* p = &arr[0][0]; 
    for (int i = 0; i < 12; i++) 
    { 
        printf("%3d", arr[0][i]); 
    }
    printf("n"); 
    for (int i = 0; i < 12; i++) 
    { 
        printf("%3d", *p); 
        p++; 
    }
    printf("n"); 
    return 0; 
}

 

大小端

如何通过代码确定当前使用的机器是大端还是小端?(使用指针)

小端存储(VS):

存储:从低字节(低地址)存储数据的低位

读取:从高向低读取

大端存储:

存储:从高字节(高地址)存储数据的高位

读取:从低向高读取

指针数组

是数组: 数组元素的类型是指针

  1. 关于数组元素访问

  2. 关于指针取内容

  3. 组合到一起使用

#include  
int main() 
{ 
     
    // 1 定义几个int类型的变量 
    int a, b, c; 
    a = 1; 
    b = 2; 
    c = 3; 
    // 2 定义几个int*类型的指针 
    int *pa, *pb, *pc; 
    pa = &a; 
    pb = &b; 
    pc = &c; 
    // 3 定义一个数组 数组元素是int*类型的指针 长度为3 
    // 元素类型 数组名[长度] = {}; 
    int* arr[3] = { pa, pb, pc }; 
    // 访问数组元素(通过数组名arr修改b的值) 
    // arr[0] <===> pa *pa <==> a 
    // arr[1] <===> pb *pb <==> b 
    // arr[2] <===> pc *pc <==> c 
    printf("b = %dn", b); 
    b = 20; 
    printf("b = %dn", b); 
    *pb = 30; 
    printf("b = %dn", b); 
    *arr[1] = 40; 
    printf("b = %dn", b); 
    return 0; 
}

数组指针

是指针: 指向数组的指针

#include  
int main() 
{ 
     
    // 1 定义一个数组 
    int arr[5] = { 1, 2, 3, 4, 5 }; 
    // 2 定义一个指针指向数组arr 
    // 指针指向的类型* 指针名 ; 
    // int [5] ===> 怎么加*? ==>() 
    // (int [5])* *(int [5]) 
    // int [5]* int* [5] 
    // int (*p) [5] 
    int(*p)[5] = &arr; 
    // 定义了一个指针p 
    // p指向的类型:int [5] (是一个数组) 
    // p本身的类型:int(*)[5] 
    // 通过数组指针p 访问数组元素arr[2] 
    // p <====> &arr 
    // *p <====> arr 
    // (*p)[2] <====> arr[2]
    printf("(*p)[2] = %dn", (*p)[2]); 
    // 结论: p[n] <==> *(p+n) 
    printf("*(*p+2) = %dn", *(*p + 2)); 
    printf("p[0][2] = %dn", p[0][2]); 
    printf("*(p[0] + 2) = %dn", *(p[0] + 2)); 
    return 0; 
}

(一维数组)数组名:

  1. 数组的名字

  2. 整个数组的首地址

  3. 数组第0个元素的首地址

注意

数组指针的定义(类型的推导)

数组元素类型 (*) 数组长度

typedef
#include  

typedef int I;
// 给数组类型取别名 
typedef int ArrType[5]; 
// 给数组指针类型取别名 
typedef int(*PArrType)[5]; 
​
int main() 
{ 
    int a = 10; 
    printf("%dn", a); 
    I num = 999; 
    printf("%dn", num); 
    int arr[5] = { 1, 2, 3, 4, 5 }; 
    int arrr[6]; 
    int(*p)[5] = &arr; 
    PArrType p0 = &arr; 
    ArrType arr1 = { 1, 2, 3, 4, 5 }; 
    ArrType* p1 = &arr1; 
    
    return 0; 
}

指针函数

是函数: 返回值类型是指针

#include  
int* function(); 
int main() 
{ 
    int* p = function(); 
    printf("%dn", *p); 
    printf("%dn", *p); 
    return 0; 
}
​
int* function()
{ 
    int num = 0; 
    int* pn = # 
    return pn; 
}

注意:

使用指针函数,不能返回指向栈区的指针

函数指针

是指针: 指向函数的指针

#include  
void fun() 
{ 
    printf("fun is been used!n"); 
}
​
int function(char a,int n) 
{ 
    printf("fun1 is been used!n"); 
    return n; 
}
​
int main() 
{ 
    // 返回值类型(*函数指针名)(形参类型) 
    int(*pfunction)(char,int) = function; 
    char ch = 0; 
    int num = 0; 
    pfunction(ch ,num); 
    fun(); 
    // 指针指向的类型* 指针名; 
    // void () ==> void (*) () 
    void(*pfun1) () = &fun; // pfun1 &fun ==> *pfun1 fun 
    void(*pfun2) () = fun; // pfun2 fun 
    (*pfun1)(); 
    pfun1(); 
    (*pfun2)(); 
    pfun2(); 
    return 0; 
}

const

声明一个常量

关键字const用来告诉编译器一个一旦被初始化过的变量就不能再修改

int a; // 碗 
const int n; // 一个又大又圆的碗

常量指针

指针指向一个常量

修饰指针指向

#include  
int main() 
{ 
    // 常量指针 
    const int num = 10; 
    // num = 100; 
    int* p1 = # 
    *p1 = 100; 
    printf("%dn", num); // 可以通过p1修改num 
    // 不可以通过p2,p3修改num 
    const int* p2 = # 
    / 
     
}

静态全局区: 从程序开始系统自动分配内存,到程序结束系统自动回收内存

栈区

存储 普通局部变量

栈区: 从定义开始系统自动分配内存,出了函数系统自动回收临时内存

堆区

由程序员手动申请,手动释放

void * 指针

也是指针: 空类型的指针

#include  
int main() 
{ 
    void* p = NULL; 
    // p + 1; 
    // p++; 
    // ==> p不能进行+-也不能++-- 
    int* pn = NULL; 
    // pn = p; 
    p = pn;
    short* psh = NULL; 
    // psh = p; 
    p = psh; 
    return 0; 
}
  1. 不能偏移

  2. 不能自增自减

  3. 可以接受其他类型的指针给值

  4. 不能直接给其他类型的指针值(可以强转)

  5. 不能直接取内容

==> void*类型的指针不知道自己的长度

动态申请内存

申请和释放
#include  
#include  
int main() 
{ 
     
    
     
    
    return 0; 
}

注意:申请的内存,必须释放。释放之后指针必须置空

内存泄漏和野指针

内存泄漏: 申请的内存没有进行释放

野指针: 指针指向了不该指向的地方

举例

#include  
#include  
int main() 
{ 
    // 1.1 使用malloc申请一个int类型大小的内存(4字节) 
    
    // 1 申请 
    int* p = (int*)malloc(sizeof(int)); // (int*)malloc(4); 
    
    // 2 使用 
    *p = 100; 
    
    // 3 释放 
    free(p); 
    
    // 4 置空. 
    p = NULL; 
    
    // 1.2 使用malloc申请十个int类型大小的内存(4*10=40字节) 
    
    // 1 申请 
    int* p1 = (int*)malloc(sizeof(int)* 10); // (int*)malloc(40); 
    
    // 2 使用 
    p1[0] = 1; 
    p1[2] = 10; 
    
    // 3 释放 
    free(p1); 
    
    // 4 置空 
    p1 = NULL; 
    
    // 2 使用calloc申请一个int类型大小的内存(4字节) 
    
    // 1 申请 
    int* p2 = (int*)calloc(sizeof(int), 1); 
    
    // 2 使用 
    *p2 = 100; 
    
    // 3 释放 
    free(p2); 
    
    // 4 置空 
    p2 = NULL; 
    return 0; 
}

动态数组

类似一维数组
#include  
#include  
int main() 
{ 
    // 使用malloc申请十个int类型大小的内存(4*10=40字节) 
    // 1 申请
    int len = 5; 
    int* arr = (int*)calloc(sizeof(int), len); 
    // 2 使用 
    for (size_t i = 0; i < len; i++) 
        { 
            arr[i] = i; 
        }
    for (size_t i = 0; i < len; i++) 
        { 
            printf("%2d", arr[i]); 
        }
    printf("n"); 
    // 3 释放 
    free(arr); 
    // 4 置空 
    arr = NULL; 
    return 0; 
}

类似二维数组
  1. 分段申请

  2. 分段释放

#include  
#include  
int main() 
{ 
    // 申请类似于int arr[3][4]; 
    // 1 申请 
    
    // 1.1 申请3个指针 
    int** pp = (int**)calloc(sizeof(int*), 3); 
    
    // 1.2 分3次申请4个int大小的内存 
    for (size_t i = 0; i < 3; i++) 
    { 
    pp[i] = (int*)calloc(sizeof(int), 4); 
    }
    // ==> 不一定像真正的二维数组一样内存连续 
    
    // 2 使用 
    for (size_t i = 0; i < 3; i++) 
    { 
    for (size_t j = 0; j < 4; j++) 
    { 
    pp[i][j] = i * 10 + j; 
    printf("%3d", pp[i][j]); 
    }
    printf("n"); 
    }
    printf("n"); 
    
    // 3 释放 
    for (size_t i = 0; i < 3; i++) 
    { 
    free(pp[i]); 
    pp[i] = NULL;
    }
    free(pp); 
    
    // 4 置空 
    pp = NULL; 
    return 0; 
}

数组扩容
#include  
#include  
int main() 
{ 
    // 1 给定默认长度 
    // 2 可以添加数据 
    // 3 如果装满了,就扩容 
    // 默认初始长度:10 
    int len = 10; 
    // 申请长度为len(10)个int大小的内存 
    int* p = (int*)calloc(sizeof(int), len); 
    int* ptemp = p; 
    int i = 0; // 下标 
    int num = 0; // 用来接收每一次的输入 
    // 添加数据(重复输入) 
    while (scanf("%d",&num),num!=666) 
    { 
        // 1 长度足够 
        if (i < len) 
        { 
            ptemp[i++] = num; 
        }
        // 2 长度不够,需要扩容 
        else 
        { 
            // 扩容规则 
            len *= 2; 
            p = (int*)calloc(sizeof(int), len); // 重新申请一段内存 
            // 拷贝之前内存中的数据 注意:i 
            for (size_t j = 0; j < i; j++) 
                { 
                    p[j] = ptemp[j]; 
                }
            p[i++] = num; // 添加当前需要添加的数据 
            free(ptemp); 
            ptemp = p; // ptemp重新指向新内存 
        } 
    }
    // 写一套输出 检查一下 注意:i 
    for (size_t j = 0; j < i; j++) 
    {
        printf("%2d", ptemp[j]); 
    }
    printf("n当前内存长度:%dn", len); 
    printf("当前数据个数:%dn", i); 
    free(p); // 释放 
    // free(ptemp); // 不要重复释放 
    p = ptemp = NULL; 
    return 0; 
}

字符串相关函数
#include  // 断言 
// assert(条件) 如果条件为真 继续程序 为假 中断 
// char *strcpy( char *to, const char *from ); 
char* Mystrcpy(char* pstr, const char* psrc) 
{ 
    assert(*psrc != ''); 
    char* ptemp = pstr; 
    while ((*ptemp++ = *psrc++) != ''); 
    return pstr; 
}
// 思考: 
// 1 为什么第二个参数用 const 
// 防止psrc被修改 
// 2 为什么要返回一个指针? 
// 为了实现链式操作(套娃) 
// size_t strlen(char *str); 
​
size_t Mystrlen(char *str) 
{ 
    size_t len = 0; 
    // 从str处向后移动指针 
    char* ptemp = str; 
    while (*ptemp++ != '') 
    len++; 
    return len; 
}
// char *strcat( char *str1, const char *str2 ); 
​
char* Mystrcat(char *str1, const char *str2) 
{ 
    // 判断str2是否为空
    if ('' == *str2) 
    return str1; 
    char* ptemp = str1; 
    // 找到str1(ptemp)中的'' 
    while (*ptemp != '') 
    ptemp++; 
    // 将str2在ptemp处开始拷贝 
    while ((*ptemp++ = *str2++) != ''); 
    return str1; 
}

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/847626.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号