C语言中的指针

本文详细介绍了C语言中的指针概念,包括指针的定义、使用、存储、初始化、运算以及在数组、字符串、二维数组中的应用。同时讲解了常量指针、指针与二维数组的关系,并举例说明了指针在字符串处理和数组操作中的常见函数。
摘要由CSDN通过智能技术生成

1 什么是指针

  • 指针就是地址
  • 指针变量是用来存储地址的

2 如何定义指针

int num = 100;
int * p = #
  • 定义了一个存储有整型变量num地址的指针变量p,通常称p指向num
  • p可称为整形指针
  • p这个指针变量的类型为int *

3 指针变量的基本使用

  • &:取地址符
  • *:寻址符,通过一个指针找到它所对应的内存空间
#include<stdio.h>
int main(void){
    int num = 100;
    int * p = &num;
    
    printf("&num = %p\n", &num);// 打印num的地址
    printf("p = %p\n", p);      // 打印p的值 p中存储的就是num的地址
    printf("num = %d\n", num);  // 打印num的值
    printf("*p = %d\n", *p);    // 打印*p的值 等同于 num的值
    printf("&p = %p\n", &p);    // 打印指针变量p的地址

    printf("%d\n", sizeof(p));  // 求的是指针变量p所占用的内存空间
    printf("%d\n", sizeof(*p)); // 求的事num所占用的内存空间
    printf("%d\n", sizeof(num));// 求的事num所占用的内存空间

    return 0;
}

运行结果:

4 指针的存储

  • 32位机,指针占用4个字节内存空间
  • 64位机,指针占用8个字节内存空间
  • 通常开发环境中,无论32位机还是64位机,指针都占4个字节

5 指针的初始化

  • 没有进行初始化的指针称为野指针,指向哪是不确定的
  • 对于指针初始化可以赋值为某个变量的地址
  • 如果指针在定义时指向哪不确定,需要在程序运行过程中进行赋值,那么它的初始值可赋值为NULL,称这种指针为空指针

6 指针所支持的运算

  • *:寻址符
  • &:取地址符
  • =:赋值运算符,可将一个地址赋值给指针变量
  • ==、!=、>、<:关系运算符
    • ==、!=:判断两个指针指向的是否是同一块内存
    • >、<:判断指针存储地址的大小关系
  • +:指针+整数
    • 指针每+1,实际上是往后移动一个所指向变量类型单位的长度
  • -:指针-整数
    • 指针每-1,实际上是往前移动一个所指向变量类型单位的长度
  • -:指针-指针
    • 求的是两个指针地址之间,单位元素的个数
    • 不存在指针+指针的运算
#include<stdio.h>
int main(void){
    int arr[4] = {1,2,3,4};
    int *p1 = &arr[0];
    int *p2 = &arr[1];
    int *p3 = &arr[2];
    int *p4 = &arr[3];
    printf("p1 = %p\n", p1); 
    printf("p2 = %p\n", p2); 
    printf("p3 = %p\n", p3); 
    printf("p4 = %p\n", p4); 
    printf("p4-p1 = %d\n", p4-p1); 
    return 0;
}

 运行结果:

 7 多重指针

  • 若一个指针存储的内容是另一个指针的地址,则称这个指针为多重指针
  • 二级指针:指向一级指针的指针
  • 三级指针:指向二级指针的指针
  • 注意:
    • 允许连续寻址
    • 不允许连续取地址
#include<stdio.h>
int main(void){
    int num = 10;
    int * p1 = &num;   //   &num   ->  指针(地址),带类型 int *
    int ** p2 = &p1;  // p2 二级指针,指向一级指针的指针
 
    printf("num = %d\n", num);      // num这块内存,10
    printf("*p1 = %d\n", *p1);      // num这块内存,10
    printf("**p2 = %d\n", **p2);    // num这块内存,10

    printf("&num = %p\n", &num);    // p1这块内存,num的地址
    printf("p1 = %p\n", p1);        // p1这块内存,num的地址
    printf("*p2 = %p\n", *p2);      // p1这块内存,num的地址

    printf("&p1 = %p\n", &p1);      // p1的地址
    printf("p2 = %p\n", p2);        // p1的地址
    return 0;
}

8 指针与一维数组

  • 当一个指针指向数组某元素时,可以通过这个指针加减来访问数组中的每一个元素。

  • 下标访问元素本质上就是指针加减寻址

#include <stdio.h>

int main(void)
{ 
    int i;
    int arr[5] = {11, 22, 33, 44, 55};
    int * p = &arr[0];

    for (i=0; i<5; i++) {
        // 下标访问元素 和  指针寻址访问元素 本质上是等价的
        // arr[i] => *(arr+i)  下标本质上就是指针寻址
        printf("%d[arr] = %d\n", i, i[arr]);
        printf("arr[%d] = %d\n", i, arr[i]);
        printf("*(arr+%d) = %d\n", i, *(arr+i));
        printf("*(p+%d) = %d\n", i, *(p+i));
        printf("p[%d] = %d\n", i, p[i]);
    }

    return 0;
}
  • arr在数值层面:表示arr数组首元素的地址(指针)
  • arr在变量层面:表示一个长度为5、元素类型为int的数组
  • 数组名,可以理解成它是一个特殊的指针,类似于指针而又和指针有区别
#include <stdio.h>

int main(void)
{ 
    int i;
    int arr[5] = {11, 22, 33, 44, 55};
    int * p = arr; 

    // 相同点
    for (i=0; i<5; i++) {
        printf("%d\n", arr[i]); // *(arr+i)
        printf("%d\n", p[i]);   // *(p+i)
    }

    // 不同点
    printf("%p\n", arr);    // 数组首元素(arr[0])的地址
    printf("%p\n", p);      // 数组首元素(arr[0])的地址
    printf("%p\n", &arr);   // 整个数组(int arr[5])的地址
    printf("%p\n", &p);     // 指针变量p的地址

    printf("arr  + 1 = %p\n", arr + 1);  // 移动一个int单位
    printf("&arr + 1 = %p\n", &arr + 1); // 移动一个数组(int [5])单位

    printf("sizeof(arr) = %d\n", sizeof(arr));  // 20 求的是整个数组(int arr[5])的大小
    printf("sizeof(p) = %d\n", sizeof(p));      // 4  求的是指针变量p的内存大小

    return 0;
}
  • 数组指针
    • 本质上是一个指针,指向数组的指针
  • 指针数组
    • 本质上是一个数组,存储指针的数组
#include<stdio.h>
int main(void){
    int arr[5] = {11, 22, 33, 44, 55};
    //p就是数组指针
    int (*p)[5] = &arr;
    // brr 就是一个指针数组,每一个元素都是一个指针
    int * brr[5];

    printf("%p\n", p);//p为数组arr的地址,输出数组arr的地址
    printf("%p\n", &arr);//输出数组arr的地址

    printf("%p\n", arr);//数组名,一般用一个数组的首元素地址来标识一个数组,即输出数组首元素的地址
    printf("%p\n", *p);//数组首元素的地址
    printf("%p\n", p[0]);//数组首元素的地址
    printf("%p\n", &arr[0]);//数组首元素的地址
    return 0;
}

运行结果:

 总结:p[1] => *(p+1) => arr[1] = >*(arr+1)

            p[0] = >*p => arr[0] = >*arr

            p+1 = >&arr[1] =>&(*(arr+1))

            p = >&arr[0] = >&(*(arr+0)) => &(*arr) => arr

9 指针与字符串

  • 字符串
    • 存储有字符串的字符数组
    • 字符串字面值
      • 本质上是一个指针,指针指向内存的只读区。
      • 字符串字面值,实际上会在只读去存储一个字符数组,数组中的元素就是字符串字面值里面的字符+\0,然后字符串字面值就是指向这个数组首元素的地址。
#include <stdio.h>

int main(void)
{ 
    char arr[] = "ABCD";    // arr是一个字符数组,数组里面保存着 ABCD\0
                            // 字符串的数据保存在自己的内存中
    char * p = "ABCD";      // p是一个字符指针,指向只读区保存 ABCD\0 的位置
                            // 字符串的数据保存在只读区

    printf("%s\n", arr);    // 传入的是arr数组首元素的地址
    printf("%s\n", p);      // 传入的是指针p保存的地址,只读区存放ABCD\0数组首元素地址
    printf("%s\n", "ABCD"); // 只读区存放ABCD\0数组首元素地址


    arr[0] = 'B';   // 修改数组arr首元素的值,允许!栈区
    // *p = 'B';       // 尝试往只读区写入数据,不允许!

    // arr = "1234";   // arr不允许指向其它位置
    p = "1234";     // p指向只读区存放 1234\0的位置

    return 0;
}
  • 字符串在输出时,需要传入一个指针,然后从这个地址开始一次取字符,如果当前字符不是'\0'则输出,并且指针+1,继续上述操作,直到遇到'\0'未知。
#include <stdio.h>

int main(void)
{ 
    char arr[] = "ABCD1234"; 
    char * p = "ABCD1234"; 

    // 字符串输出函数需要是一个指向存储有字符串内容
    // 的字符数组首元素的地址
    printf("%s\n", arr+4);
    printf("%s\n", p+4);
    printf("%s\n", "ABCD1234"+4);

    printf("%s\n", arr);
    printf("%s\n", p);
    printf("%s\n", "ABCD1234");

    return 0;
}

9.1 字符串函数

1、strcmp(str1, str2):字符串比较

str1 = str2     返回0

str1 < str2     返回负数

str1 > str2     返回正数

2、strlen(str) :求字符串长度

 3、strcat(str1, str2):字符串拼接

将字符串str2拼接到str1的后面

4、strcpy(str1, str2):字符串复制

将字符串str2复制给str1

9.2 字符指针数组

// 字符指针数组
// 数组里面每一个元素都是一个字符指针
char * arr1[5] = {"111", "222", "333", "444", "555"};
char * temp;
int i;
for (i=0; i<5; i++) {
    puts(arr1[i]);
}

 10 集中特殊的指针

  • const指针

#include <stdio.h>

int main(void)
{   
    int num = 10;
    // 常量指针,指向常量的指针
    const int * p1 = &num; // 表示 *p1 不允许修改
    int const * p2 = &num; // 表示 *p2 不允许修改
    // 指针常量,指针本身是常量
    int * const p3 = &num; // 表示 p3 不允许被修改
    // 常量常指针
    int const * const p4 = &num; // 表示p4 不允许被修改,*p4也不允许被修改

    return 0;
}

11 指针与二维数组

int a[3][4] = {{1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23}};

  •  a是数组名,a数组包含3行元素,即a[0],a[1],a[2]。每个行元素都可以看成含有4个元素的一维数组。则a[0],a[1],a[2]分别是这三个一维数组的数组名。
  • a[0],a[1],a[2]分别是这三个一维数组的数组名,而一维数组名表示的是数组第一个元素的地址,所以a[0]表示元素a[0][0]的地址。

a[0] => &a[0][0]         a[0]+1 => &a[0][1]

a[1] => &a[1][0]         a[1]+1 => &a[1][1]

a[2] => &a[2][0]         a[2]+1 => &a[2][1]

a为数组名,即为一维数组首元素的地址,而此一维数组的首元素是一维数组a[0]

a => &a[0] => &(&a[0][0])  二维数组名a是地址的地址

a+1 => &a[1]

a+2 => &a[2]

int a[3][4] = {{1, 3, 5, 7}, {9, 11, 13, 15}, {17, 19, 21, 23}};
//两种定义方式
int **p = a;
int *p[4] = a;
  • p => &a[0]                  *p => a[0] => &a[0][0]                  *p+1 => &a[0][1]
  • p+1 => &a[1]              *(p+1) => a[1] => &a[1][0]           *(p+1)+1 => &a[1][1]
  • p+2 => &a[2]              *(p+2) => a[2] => &a[2][0]           *(p+2)+1 => &a[2][1]
  • *(p+i)+j => &a[i][j]
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值