C语言数组与指针备忘录

目录

数组

一维数组

多维数组(二维数组举例)

数组注释:

指针

一级指针

多级指针

指针万能拆解法

野指针

空指针

指针运算

数组指针

指针数组

附录


数组

一维数组

类型说明符 数组名[整型常量表达式] (申请的是连续空间)

int a[4] = {0};//整型数组a[0]~a[3],清空数组(未赋值的,自动赋值为零)
char b[4] = {'h','e','l','l','o'};//字符数组
// 初始化时局部变量为随机数,全局变量为0

初始化只能在定义的时候执行

// 正常初始化
int a[5] = {100,200,300,400,500};
int a[5] = {100,200,300,400,500,600}; // 错误,越界了
int a[ ] = {100,200,300}; //自动根据初始化列表分配数组元素个数
int a[10] = {1,2,3,4,5};//部分初始化,未初始化的自动初始化为0
//下面是错误初始化
int a[5];
a[5] = {1,2,3,4};//a[5]未知空间且只是单个空间,只能赋值。
a = {1,2,3,4,5};//不能对数组名a赋值。

数组偏移

数组的下标地址与首地址的偏移量有关,与数组的大小无关

注释:可以理解为和数据类型说明符无关,无论int、char还是其他类型,

// 比如, a[3]等价于a的基地址+偏移量2个(int单位)
printf("a[2] addr :%p,%p,%p\n",&a[2],a+2,&a[0]+2);

变长数组(不是真的变长,只是定义数组前,变量所赋初值为可变值)

int size = 10;
// 不能在使用变长数组的情况下初始化数组
int a[size] = {1,2,3};//编译出错
// 变长数组只能先定义再使用
int a[size]; // 正确的
a[0] = 10;

获取数组长度

int numbers[] = {1, 2, 3, 4, 5};
int length = sizeof(numbers) / sizeof(numbers[0]);

多维数组(二维数组举例)

二维数组的取地址及内容输出。

[]的作用就是解引用array[0]==*(array+0)

#include<stdio.h>
int main()
{
    int a[3][5]={0,1,2,3,4,5,6,7,8,9,10,11,12,13,14};
    printf("%d  %p\n",a[2][3],&a[2][3]);                  //方法一
    printf("%d  %p  %p  %p\n",a[2][0],&a[2][0],*(a+2),*(&a[2]+0));                                                /**(a+2)==a[0+2][0]*/
    printf("%d %p %p %p\n",a[0][0],&a[0][0],*(a+0),*(&a[0]+0));
}                                      
//结果
// 13  0x7ffffcc04
// 10  0x7ffffcbf8  0x7ffffcbf8  0x7ffffcbf8
// 0  0x7ffffcbd0  0x7ffffcbd0  0x7ffffcbd0

数组注释:

为什么数组下标从0开始

因为数组名就是这个数组的首元素的地址,所以从0开始是为了不让首元素地址产生偏移从而导致数据丢失因为*(Array+0)其实就是Array[O],在c编译的时候所有的[]都会被转换成*()同时数组名也可以代表整个数组

// * 表示将地址里面的内容取出,我们把它称为解引用
//&(buf+1)如果带取地址符&表示整个数组的地址,如果buf为二维数组则表示&取整个二维数组的地址,否则为取首元素地址
char Array[5] = {'j','a','c','k'};//一维数组
printf("%c%c%c%c%c\n",Array[0],&Array[0],*(Array+0),*(&Array[0]),0+[array]);

解引用数组名取出内容,而数组名里的内容是地址,对数组名取地址取得数组名的地址而不是内容

int a[2][3]={{1,2,3}, {4,5,6}};//非线性,{}后会自动补上'\0'。
//int a[2][3]={1,2,3,4,5,6};线性关系
//a[2] 是数组的定义,表示该数组拥有两个元素
//int [3]是元素的类型,表示该数组元素是一个具有三个元素的整 型数组(该数组一共有2行,每行由3个元素组成的数据类型 二维数组名[行][列])

指针

一级指针

指针的概念: 一个专门用来存放内存地址的变量,指针也就是指针变量

地址:比如 &a 是一个地址,也是一个指针,&a 指向变量 a,专门用于存储地址的变量,又称指针变量。

指针的尺寸指的是指针所占内存的字节数 ,指针所占内存,取决于地址的长度,而地址的长度则取决 于系统寻址范围,即字长 。

32位系统,指针的大小占用4字节

64位系统,指针的大小占用8字节

//指针定义
// 用于存储 int 型数据的地址,p1 被称为 int 型指针,或称整型指针
int *p1;
// 用于存储 char 型数据的地址,p2 被称为 char 型指针,或称字符指针
char *p2;
// 用于存储double型数据的地址,p3 被称为 double 型指针
double *p3;

//指针赋值:赋给指针的地址,类型需跟指针的类型相匹配。
int a = 100;
p1 = &a; // 将一个整型地址,赋值给整型指针p1
char c = 'x';
p2 = &c; // 将一个字符地址,赋值给字符指针p2
double f = 3.14;
p3 = &f; // 将一个浮点地址,赋值给浮点指针p3

//指针的索引:通过指针,取得其指向的目标
*p1 = 200; // 将 p1 指向的目标(即a)修改为200,等价于
a = 200;
*p2 = 'y'; // 将 p2 指向的目标(即c)修改为'y',等价于
c = 'y';
*p3 = 6.6; // 将 p3 指向的目标(即f)修改为6.6,等价于
f = 6.6;

多级指针

如果一个指针变量p1存储的地址,是另一个普通变量a的地 址,那么称p1为一级指针

如果一个指针变量p2存储的地址,是指针变量p1的地址,那 么称p2为二级指针

如果一个指针变量 p3 存储的地址,是指针变量 p2 的地址, 那么称 p3 为三级指针

以此类推,p2、p3等指针被称为多级指针

int a = 100;
int *p1 = &a; // 一级指针,指向普通变量
int **p2 = &p1; // 二级指针,指向一级指针
int ***p3 = &p2; // 三级指针,指向二级指针

指针万能拆解法

任意的指针,不管有多复杂,其定义都是由两部分组成

  • 第一部分:指针所指向的数据类型,可以是任意类的类型

  • 第二部分:指针的名字

char (*p1); // 第2部分:*p1; 第1部分:char;
char *(*p2); // 第2部分:*p2; 第1部分:char *;
char **(*p3); // 第2部分:*p3; 第1部分:char**;
char (*p4)[3]; // 第2部分:*p4; 第1部分:char[3];
char (*p5)(int, float); // 第2部分:*p5; 第1部分:char (int, float);

上述示例中,p1、p2、p3、p4、p5本质上并无区别,它们均是指针

上述示例中,p1、p2、p3、p4、p5唯一的不同,是它们所指向的数据类型不同

第1部分的声明语句,如果由多个单词组成,C语言规定需要将其拆散写到第2部分的两边

野指针

概念:指向一块未知区域的指针,被称为野指针。野指针是危险的。 (定义且暂时不用的需先指向NULL)

危害:

  1. 引用野指针,相当于访问了非法的内存,常常会导致段错误
  2. 引用野指针,可能会破坏系统的关键数据,导致系统崩溃 等严重后果

产生原因:

  1. 指针定义之后,未初始化

  2. 指针所指向的内存,被系统回收

  3. 指针越界

如何防止:

  1. 指针定义时,及时初始化

  2. 绝不引用已被系统回收的内存

  3. 确认所申请的内存边界,谨防越界

空指针

概念:空指针即保存了零地址的指针,亦即指向零地址的指 针。

NULL地址其实就是 (void *)0,就是0x0000 0000

// 1,刚定义的指针,让其指向零地址以确保安全:
char *p1 = NULL;
int *p2 = NULL;
// 2,被释放了内存的指针,让其指向零地址以确保安全:
char *p3 = malloc(100); // a. 让 p3 指向一块大小为
100个字节的内存
free(p3); // b. 释放这块内存,此时 p3
相当于指向了一块非法内存
p3 = NULL; // c. 让 p3 指向零地址

指针运算

int a = 100;
int *p = &a; // 指针 p 指向整型变量 a
int *k1 = p + 2; // 向上移动 2 个目标(2个int型数据)
int *k2 = p - 3; // 向下移动 3 个目标(3个int型数据)

// 几乎不用,容易出现越界
int (*q)[5] = &a;
printf("%p\n",(q+1));// 越界(数组名表示数组整体)
printf("%d\n",*(q+1));

*与 &是互逆运算

int a = 10;
int *p;
//int *p = &a;  //*单独使用为解引用,所以 *p =&a; 错误
p = &a;
printf("*(&a) value:%d\n",*(&a));// * 与 &是互逆运算
*p = 300; //*p 此时相当于变量a

数组指针

数组的指针的本质为指,此指针保存的是数组的地址,说白了就是指针指向数组名,此类指针称为数组指针 。

int arr[3][4] = {{1,2,3,4},{10,20,30,40},
{11,22,33,44}};
定义一个指针p存储数组的名字//数组指针 int a = 5;
int (*p)[4] = arr;
通过arr p 将所有获取到1的数据的方法罗列出来
通过arr p 将所有获取到20的数据方法罗列出来
int (*p)[4] = arr;
int (*q)[3][4] = &arr;
int arr[3][4] = {
                    {1,2,3,4},
                    {10,20,30,40},
                    {11,22,33,44}
				};
//定义一个指针p存储数组的名字//数组指针
int (*p)[4] = arr;
//通过arr p 将所有获取到1的数据的方法罗列出来
printf("arr[0][0] = %d\n",p[0][0]);
printf("arr[0][0] = %d\n",(*(p+0))[0]);
printf("arr[0][0] = %d\n",*((*(p+0))+0));
printf("arr[0][0] = %d\n",**p);
//通过arr p 将所有获取到20的数据方法罗列出来
printf("arr[1][1] = %d\n",p[1][1]);
printf("arr[1][1] = %d\n",(*(p+1))[1]);
printf("arr[1][1] = %d\n",*((*(p+1))+1));

指针数组

指针数组的本质为数组,数组里面存放的内容为指针,而一般指针是指向的地址为字符串居多,我们把此类型称为指针数组。

char *p[5];
char *p = "jack";
char *p[5] = {"jack","rose","ken","tony","tom"};
printf("%s\n",p[2]);
printf("%c\n",p[3][3]);
//"hello"是字符串常量,同时"hello"也是一个匿名数组的空间首地址
char *str[3] = {"abc","def","hij"};

字符串与指针

字符串常量在内存中实际就是一个匿名数组

匿名数组满足数组的两个条件

第一个含义,表示整个数组

第二个含义,表示首元素地址

char buf[] = "abcd";
printf("%c\n",buf[1]);
printf("%c\n","abcd"[1]);
char *p = "abcd"; // 将p指向一块匿名数组的一个首地址
printf("%p,%p\n","abcd",&"abcd"[0]);
// 讲"jack"字符串存放在buf的空间里面
char buf[] = "jack";
// 定义指针p指向buf的首地址
char *p = buf;
// 定义指针q指向"jack"的首地址
char *q = "jack";
printf("%s,%s,%s,%d\n",buf,p,q,sizeof(q));

附录

字节序

大端模式:高位数据存放在内存的低地址端,低位数据存 放在内存的高地址端

小端模式:高位数据存放在内存的高地址端,低位数据存 放在内存的低地址端

int a = 0x11223344;
char *p = (char *)&a;
if(*p == 0x11)
	printf("大端模式\n");
else
	printf("小端模式\n");

段错误原因:没提供空间,访问非法空间,访问常量区修改常量

未完待续,发现错误请留言指正(万分感谢)。

  • 28
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值