VS_C_17/12/17 C的数组的扩充与指针的引入

来源:互联网 发布:数据库怎么查重复数据 编辑:程序博客网 时间:2024/05/17 02:53

数组在上一章已经做过一些解释,接下来对其做一个扩充。并探讨一下指针。
提要:
1,数组中左值与右值。
2,柔性数组。
3,定义指针的要点。
4,void*空类型指针。
5,const与指针的修饰关系。
6,&和*与指针和数组的关系。
7,sizeof与数组的关系。


一:数组扩充,
1.1左值与右值
什么是左值与右值?

int x = 10;int y = 20;x = y; //y作为右值表示y里面的数据x = 20;//当X作为左值表示一个地址

所以左值可以理解为一个存放数据的地址或内存(一般为变量),右值就是需要存放的数据。
在数组中有这种情况:

char ch = 'a';char *p = &ch;

这时&ch = 右值 是错误的。
char *cp;
cp = &ch; 这时是正确的。
char ch = *cp + 1;
*cp + 1 = 右值 是错误的表达。
因为*的优先级高于+所以先进行 简介访问操作,然后对其拷贝值加1,由于表达式的最终结果的储存位置并未清晰定义,所以不是一个合法的左值。
1.2柔性数组:
C99 中,结构中的最后一个元素允许是未知大小的数组,这就叫做柔性数组成员
,但结构中的柔性数组成员前面必须至少一 个其他成员。柔性数组成员允许结构
中包含一个大小可变的数组。sizeof 返回的这种结构大小不包括柔性数组的内存。
包含柔性数组成员的结构用malloc ()函数进行内存的动态分配,并且分配的内存应
该大于结构的大小,以

typedef structst_type  {     int i;     int a[];  }type_a;  sizeof(type_a) = 4;type_a *p = (type_a*)malloc(sizeof(type_a)+100*sizeof(int));`

malloc 返回值为空类型指针,必须强转类型;
分配100*4=400个字节的动态内存;
在定义这个结构体的时候,模子的大小就已经确定不包含柔性数组的内存大小;
当然,上面既然用 malloc 函数分配了内存,肯定就需要用 free 函数来释放内存
:free(p);


二:指针
2.1指针必须有指向,必须初始化。(野指针
野指针:声明一个指针变量并不会自动分配任何内存。在对指针间接访问前,指针必须进行初始化。或使它指向现有内存,或者给它分配动态内存,对未初始化的指针执行间接访问是非法的而且这种错误常常难以预测。而这种未初始化的指针就是所谓的野指针。而野指针的预防也是有方法的。
预防方法:
1,char *p = NULL;//如果初始化没有指向,就赋值为空。
2,学习上面的malloc为指针分配动态内存。
free(ptr); //但跟上面一样用了malloc分配之后需要free释放内存
ptr = NULL;//释放之后将指针置空
其实预防指针也没有特别高效方法,只有通过编程的规范来预防野指针的产生。
这里给出一个博客地址,有具体的预防野指针的编程规范:

http://blog.csdn.net/qq_30594349/article/details/51932810

char *p = &a;
p 里存储的内存地址处的内存称为 p 所指向的内存。指针变量 p 里存储的任何数据都将被当作地址来处理。“*”号前面的数据类型只是说明指针所指向的内存里存储的数据类型。所以,在 32 位系统下,不管什么样的指针类型,其大小都为 4byte

2.2 void*空类型指针
void 关键字意为“空类型”
void* 意为“空类型”的指针。
E.g:float *p1;
int *p2;
p1 = p2; 这时会出错;
void *p1;
int *p2;
p1 = p2; 这时不会出错;
void* 可以指向任何类型的数据,void* 可以无需强制类型转换的赋值
其他类型的指针。因为“空类型”可以包容“有类型”,而“有类型”
则不能包容“空类型”
E.g:
void *p1;
int *p2;
p2 = p1; 这时会出错,p1不能赋值给p2;
void 指针:按照ANSI标准,不能对void指针进行算法操作;
E.g:
void *p;
p++; 错误
p–; 错误
应为进行算法操作的指针必须是确定知道其指向数据类型的大小的,即必须知道内存目的地址的确定值。
2.3【规则 1- -36】如果函数的参数可以是任意类型指针,那么应声明其参数为 void *。
但在GNU中,void 空指针的算法操作与char*一致。
const int *p; // p 可变,p 指向的对象不可变
int const *p; // p 可变,p 指向的对象不可变
int *const p; // p 不可变,p 指向的对象可变
const int* const p; //指针 p 和 p 指向的对象都不可变
先忽略类型名(编译器解析的时候也是忽略类型名),我们看 const 离哪个近。离谁近就修饰谁。
去掉类型名之后
const *p; //const 修饰*p,p 是指针,*p 是指针指向的对象,不可变
const *p; //const 修饰*p,p 是指针,*p 是指针指向的对象,不可变
*const p; //const 修饰 p,p 不可变,p 指向的对象可变
const * const p; //前一个 const 修饰*p,后一个 const 修饰 p,指针 p 和 p 指向的对象都不可变。


三:指针与数组的联系
1)& 和 *
&a + 1: 取数组 a 的首地址,该地址的值加上 sizeof(a) 的值,即 &a +5*sizeof(int),也
就是下一个数组的首地址,显然当前指针已经越过了数组的界限。(即二维数组第二行的起始地址)
sizeof(arr) 表示数组长度字节数,int n = sizeof(arr) / sizeof(arr[0])表示数组长度,这句只能接着数组定义写

#include <stdio.h>int main(){int a[5]={1,2,3,4,5};int *ptr1=(int*)(&a+1);int *ptr2=(int*)((int)a+1);printf("%x,%x",ptr1[-1],*ptr2);//当ptr这个指针指向数组首元素的首地址时,ptr1[i]也可以表示return 0;                       这个数组的元素,有点代替arr的作用;}                            **这个打印出来为 52**int*ptr1=(int*)(&a+1); ptr1指向了&arr + 1;即数组的末地址,所以ptr1[-1]代表arr数组的最后一个元素。

2)sizeof
int arr[5] = {1,2,3,4,5};
打印:sizeof(arr[0]),sizeof(&arr[0]),sizeof(arr[5]),sizeof(&arr)
结果都是 4 。
sizeof(*p)无论在括号里的指针指向哪里,都是4 因为指针是int4字节。

原创粉丝点击