【C复习】02:指针

指针


一、指针变量

1.定义方式:

方式:基类型 *指针变量,指针变量是基本数据类型派生出来的类型,其不能离开基本类型而独立存在。

  1. 指针类型表示:指向整型数据的指针类型表示为int*,读作指向int的指针 or int指针
  2. 在定义指针变量时必须指定基类型,不同类型的数据在内存中所占的字节数和存放方式是不同的。只有知道该数据类型,才能按存储单元长度和数据的存储形式正确的取出该数据。
2.引用方式:

&取地址符,*间接访问运算符

  1. 给指针变量赋值:p = &a;
  2. 引用指针变量指向的变量:printf("%d", *p);
  3. 引用指针变量的值:printf("%o", p);

注:C语言中实参变量和形参变量之间的数据传递是单向的值传递方式,用指针变量作函数参数同样要遵循这一规则。

  • 不可改变实参指针变量值
  • 但可改变实参指针变量所指向变量的值

二、指针引用数组

1.数组元素指针运算

所谓数组元素指针就是数组元素地址

引用数组元素可以用下标法,也可以用指针法(通过指向数组元素的指针找到所需的元素,占用内存少、运行速度快)。

当指针指向数组元素时,允许对指针(地址)进行加减运算。

  1. 如果指针变量指向数组中一个元素,则p+1指向该元素的下一个元素,p-1指向该元素的上一个元素。
  2. 如果指针变量p1和p2都指向同一个数组,执行p1-p2的结果是(p1-p2)/sizeof(arrElem),即所指元素之间相差元素的个数。
  3. 两个地址不能相加,其结果是无实际意义的。
2.指针引用数组元素

引用数组元素的方法有下标法arr[i]与指针法*(arr + i)

#include<stdio.h>
.
int main() {
    int a[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
    for (int i = 0; i < 10; ++i) printf("%d ", a[i]);//(1)下标法引用数组元素
    printf("\n");
    for (int i = 0; i < 10; ++i) printf("%d ", *(a + i));//(2)数组名计算元素地址 间接访问该元素
    printf("\n");
    for (int *p = a; p < (a + 10); ++p) printf("%d ", *p);//(3)指针变量引用数组元素
    printf("\n");
    return 0;
}

在这里插入图片描述

  1. 方法(1)和方法(2)的执行效率相同,编译器将a[i]转换为*(a+i)再进行处理/计算元素地址,这两种方式寻找数组元素费时较多。
  2. 方法(3)速度较快,用指针变量直接指向元素/不必每次都重新计算元素地址,这种有规律的改变地址值能有效提高执行效率。
  3. 对于方法(3)的执行指针不能直接用数组名arr替代(需要额外定义指针类型),因为数组名是一个指针型常量,其值固定不变。

注:利用指针引用数组元素,比较灵活有不少技巧。

  • *p++:由于++*运算符同优先级,结合方向为自右向左,因此其等价于*(p++)
  • *(p++)*(++p):作用不同,前者是先取*p然后+1,而后者是先+1然后取*p
3.数组名作函数参数

用数组名作为函数实参时,

函数对应的形参可以是指针变量int *arr,也可以是形参数组int arr[],这是因为C语言中下标法和指针法都可以访问到一个数组。

即如果有一个数组arr,则arr[i]*(arr + i)无条件等价。

注:如果用指针变量int *arr作为实参,必须先使指针变量有确定值指向一个已定义的对象,否则报错。

三、指针引用字符串

通过指针引用字符串,可以使字符串的使用更加灵活方便。

1.字符串引用方式

方式1:字符数组arr[i]printf("%s", arr)

方式2:字符指针char *string = "I love china!"printf("%s", string)

  • 对字符指针string初始化,实际上是把字符串的第1个元素的地址赋值给string指针,使string指向字符串第一个字符。

  • %s是输出字符串时所用的格式符,系统会输出string指向的第一个字符,然后自动使string+1输出下一个字符直到遇到字符串结束标志\0为止。

  • 在内存中字符串的最后被自动加上\0

  • 对于数值型数组的元素值只能逐个输出

程序阅读:字符串拷贝函数的进化过程

void copy_string(char *src, char *dest) {
    while((*dest = *src) != '\0') {
        dest++;
        src++;
    }
}
void copy_string(char *src, char *dest) {
    while((*dest++ = *src++) != '\0');
}
void copy_string(char *src, char *dest) {
    while((*dest++ = *src++));
}
2.字符指针和字符数组比较

用字符数组char arr[];与字符指针char *arr;都能实现字符串的存储与运输,但是其二者之间是有区别的:

  1. 字符指针变量中存放的是地址,而字符数组是由若干个元素组成,每个元素中存储一个字符。

  2. 编译时对字符指针变量只分配一个存储单元,而为字符数组分配若干存储单元,以存放各元素的值。

  3. 可以对字符指针赋值,但不能对字符数组名赋值(常量const char *)。

  4. 字符数组中各个元素的值是可以进行修改的,但字符指针指向的字符串不能被赋值(除非直接更换指针指向)

    char a[] = "House";
    char *b = "House";
    a[2] = 'r';//合法
    b[2] = 'r';//非法
    
  5. 若字符指针指向字符,则其对字符数组元素的引用也可以使用下标法和地址法,否则其引用是没有意义的。

注:如果定义了一个字符指针应及时把一个字符变量的地址赋给它,使其指向一个字符型数据。如果未对其赋予一个地址值,此时向其所指对象输入数据,可能会造成严重后果(其所指对象是不可预料的,可能是内存中空白的存储区,也可能是已经存放指令或数据的重要内存段!)。

四、动态内存分配

1.内存动态分配
  • 全局变量是分配在内存中的静态存储区的
  • 局部变量/非静态(包括形参)是分配在内存中的动态存储区的,栈stack
  • 临时数据是是存放在内存动态分配区域,堆heap

内存动态分配区域用于存放一些临时数据,这些数据不必在程序的声明部分定义,不必等到函数结束时才释放,而是随时开辟随时释放。

可以根据需要向系统申请所需大小的空间,由于未在声明部分定义他们为变量或数组,因此不能通过变量或数组名来引用这些数据,只能通过指针来引用。

2.malloc

其作用是在内存的动态存储区中分配一个长度为size的连续空间,

  • 原型:void *malloc(unsigned int size);
  • size:无符号整型(不允许为负数)
  • 返回值为所分配区域的第一个字节的地址

注:返回指针的基类型为void,即不指向任何类型的数据只提供一个地址,若空开辟失败则返回空指针NULL。

3.calloc

其作用是在内存的动态存储区中分配n个长度为size的连续空间,这个空间一般比较大,

用calloc函数可以为一维数组开辟动态存储空间,n为数组元素的个数,每个元素长度为size,这就是动态数组。

  • 原型:void *calloc(unsigned n, unsigned size);
  • 返回值为所分配区域的第一个字节的地址,如果开辟不成功则返回空指针NULL。
4.realloc

如果已经使用malloc或calloc函数获得了动态空间,向改变其大小则可以使用realloc函数进行重新分配。

使用realloc将p所指向的动态空间大小改变为size,p的值不变。

  • 原型:void *realloc(void *p, unsigned int size);
  • 如果分配不成功则返回NULL
5.free

其作用是释放指针p所指向的动态空间,使这部分空间能够被其他变量使用,

  • 原型:void free(void *p);
  • p应是最近一次调用calloc或malloc函数时所得到的函数返回值(开辟的空间的首地址)

在这里插入图片描述

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值