C语言复习笔记 11

C语言数组指针 (指向数组的指针)

数组(Array)是一系列具有相同类型的数据的集合,每一份数据叫做一个数组元素(Element)。数组中的所有元素在内存中是连续排列的,整个数组占用的是一块内存。

#include <stdio.h>
int main (void) 
{
    int arr[4] = {1,2,3,4};
    printf("arr=%p %p %p \n", arr, &arr[0], &arr[1]);

    return 0;
}
//运行结果——明白连续地址的概念了
0018FF38
0018FF38
0018FF3C
Press any key to continue

数组名的本意是表示整个数组,也就是表示多份数据的集合,但在使用过程中经常会转换为指向数组第 0 个元素的指针,所以我们说数组名可以认为是一个指针,但并不就是。也就表示了数组名和数组首地址并不总是等价。

下面的例子演示了如何以指针的方式遍历数组元素:

#include <stdio.h>
int main (void) 
{
    int arr[] = {1,2,3,4};
    int len = sizeof(arr) / sizeof(int);
    int *p = arr;
    for (int i = 0; i < 4; i++) {
        printf("%d ", *(p+i));
    }
    printf("\n");

    return 0;
}

如果一个指针指向了一个数组,我们就称它为数组指针(Array Pointer)。

需要注意的是:
sizeof(arr) / sizeof(int)不能改成 sizeof(p) / sizeof(int)
数组在内存中只是数组元素的简单排列,没有开始和结束标志,在求数组的长度时不能使用sizeof(p) / sizeof(int),因为 p 只是一个指向 int 类型的指针,编译器并不知道它指向的到底是一个整数还是一系列整数(数组),所以 sizeof(p) 求得的是 p 这个指针变量本身所占用的字节数,而不是整个数组占用的字节数。

也就是说,根据数组指针不能逆推出整个数组元素的个数,以及数组从哪里开始、到哪里结束等信息。不像字符串,数组本身也没有特定的结束标志,如果不知道数组的长度,那么就无法遍历整个数组。

总结

引入数组指针后,我们就有两种方案来访问数组元素了,一种是使用下标,另外一种是使用指针。
1) 使用下标

也就是采用 arr[i] 的形式访问数组元素。如果 p 是指向数组 arr 的指针,那么也可以使用 p[i] 来访问数组元素,它等价于 arr[i]。

2) 使用指针

也就是使用 (p+i) 的形式访问数组元素。另外数组名本身也是指针,也可以使用 (arr+i) 来访问数组元素,它等价于 *(p+i)。

不管是数组名还是数组指针,都可以使用上面的两种方式来访问数组元素。不同的是,数组名是常量,它的值不能改变,而数组指针是变量(除非特别指明它是常量),它的值可以任意改变。也就是说,数组名只能指向数组的开头,而数组指针可以先指向数组开头,再指向其他元素。

更改上面的代码,借助自增运算符来遍历数组元素:

#include <stdio.h>

int main(){
    int arr[] = { 99, 15, 100, 888, 252 };
    int i, *p = arr, len = sizeof(arr) / sizeof(int);

    for(i=0; i<len; i++){
        printf("%d  ", *p++ );
    }
    printf("\n");
    return 0;
}

第 8 行代码中,p++ 应该理解为 (p++),每次循环都会改变 p 的值(p++ 使得 p 自身的值增加),以使 p 指向下一个数组元素。该语句不能写为 *arr++,因为 arr 是常量,而 arr++ 会改变它的值,这显然是错误的。

对指针变量的运算,实际上是对变量中存储的地址的运算。int a = 1;内存给a分配的地址编号是0018FF44, int *p = &a; 故指针变量p中保存的是0018FF44,这个地址在内存中对应的数据是1(a); 语句p + 1,代表的是 对指针变量中存放的地址 + 4(int 内存4字节);就是现在指针变量中存放的地址是00FF48,至于这个地址对应的数据是多少,就不清了,如果没有定义的话。

 指针指向的数据运算符 * 和 自增++ 运算符优先级问题
获取指针指向的数据运算符 * 是低于 自增运算符++的 
     这里写图片描述


思考
假设 p 是指向数组 arr 中第 n 个元素的指针,那么 p++、++p、(*p)++ 分别是什么意思呢?
*p++

#include <stdio.h>
int main (void) 
{
    int arr[] = {3,8,11,33};
    int len = sizeof(arr) / sizeof(int);
    int *p = &arr[1];

    printf("%d\n", *p++);-------------- 3
    // 根据运算符优先级 *(p++)
    // * 取指针所存储的地址的数据
    // ++ 也就是 += 1
    // 对指针变量的运算,就是对指针变量存储的地址的运算,
    // p++ 也就是 p += 1; 也就是 p=p+1; 也就是指针变量p所存储的地             
    //址+4 (1个int占4字节);
    //数组中变量在内存中排列是连续的,也就是a[0] 占4个字节,,内存中
    //下4个字节是a[1],依次排列,
    // ++ 后自增运算符 表示先用变量的值,再之后对变量+1
    // 所以这里 *(p++) 表达式的值就是 *p的值,也就是arr[1],即3
    // 但是指针 p 指向了下一个数,也就是说 现在的p=&a[2]; *p即8
    printf("%d\n", *p);------------------- 8

    printf("\n");

    return 0;
}

*++p

#include <stdio.h>
int main (void) 
{
    int arr[] = {3,8,11,33};
    int len = sizeof(arr) / sizeof(int);
    int *p = &arr[1];


    printf("%d\n", *++p);  ------ 8 
    printf("%d\n", *p);    ------ 8
    //优先级 *(++p)
    // ++p 取之后的值
    // 也就是表达式的值现在为 *(p+1) 也就是 a[2]
    // 所以 *p 也是a[2]

    printf("\n");

    return 0;
}

(*p) ++

#include <stdio.h>
int main (void) 
{
    int arr[] = {3,8,11,33};
    int len = sizeof(arr) / sizeof(int);
    int *p = &arr[1];

    printf("%d\n", (*p)++);  --- 8
    printf("%d\n", *p);     ---- 9
    printf("%d\n", arr[1]); ---- 9
    // 优先级 括号优先级最高,所以先处理(*p) 也就是 a[1]的值
    // (*p)++ 也就是 a[1]++,表达式先取a[1]的值,然后对a[1]+=1
    // 也就是说现在 a[1]由原来的 8 变成了 9printf("\n");

    return 0;
}

C语言字符串指针(指向字符串的指针)

C语言中没有特定的字符串类型,我们通常是将字符串放在一个字符数组中
char str[] = "hello world";

除了字符数组,C语言还支持另外一种表示字符串的方法,就是直接使用一个指针指向字符串,例如:
char *str = "hello world";

字符串中的所有字符在内存中是连续排列的,str 指向的是字符串的第 0 个字符;我们通常将第 0 个字符的地址称为字符串的首地址。字符串中每个字符的类型都是char,所以 str 的类型也必须是char *。

strlen 和 sizeof 的区别

sizeof —— 内存
strlen——字符个数

sizeof() C语言中判断数据类型或者表达式长度符;不是一个函数,字节数的计算在程序编译时进行,而不是在程序执行的过程中才计算出来。

用法
sizeof(类型说明符,数组名或表达式);
或sizeof (变量名);

定义
sizeof是C/C++中的一个操作符(operator),简单的说其作用就是返回一个对象或者类型所占的内存字节数。
深入理解sizeof操作符

strlen (C语言函数)
strlen所作的仅仅是一个计数器的工作,它从内存的某个位置(可以是字符串开头,中间某个位置,甚至是某个不确定的内存区域)开始扫描,直到碰到第一个字符串结束符’\0’为止,然后返回计数器值(长度不包含’\0’)。

strlen只能用char*做参数,且必须是以”\0”结尾的。

char aa[10]=”jun”; cout<

两种表示字符串方式的区别

区别1

#include <stdio.h>
#include <string.h>
int main (void) {

    char *str1 = "hello world";
    char str2[] = "hello world";
    int len = sizeof(str1);
    int len1 = strlen(str1);
    int len2 = sizeof(str2);
    printf("%s \n%d\n%d\n%d\n", str1, len, len1, len2);


    return 0;
}

这里写图片描述

区别2
它们最根本的区别是在内存中的存储区域不一样,字符数组存储在全局数据区或栈区第二种形式的字符串存储在常量区。全局数据区和栈区的字符串(也包括其他数据)有读取和写入的权限,而常量区的字符串(也包括其他数据)只有读取权限,没有写入权限。

内存权限的不同导致的一个明显结果就是,字符数组在定义后可以读取和修改每个字符,而对于第二种形式的字符串,一旦被定义后就只能读取不能修改,任何对它的赋值都是错误的。

#include <stdio.h>
int main(){
    char *str = "Hello World!";
    str = "I love C!";  //正确
    str[3] = 'P';  //错误

    return 0;
}

到底使用字符数组还是字符串常量

在编程过程中如果只涉及到对字符串的读取,那么字符数组和字符串常量都能够满足要求;如果有写入(修改)操作,那么只能使用字符数组,不能使用字符串常量。

最后总结一下,C语言有两种表示字符串的方法,一种是字符数组,另一种是字符串常量,它们在内存中的存储位置不同,使得字符数组可以读取和修改,而字符串常量只能读取不能修改。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值