c语言指针和数组的联系,C语言学习笔记--指针和数组的关系

1.数组的本质

(1)数组是一段连续的内存空间

(2)数组的空间大小:sizeof(array_type)*array_size;

(3)数组名可看做指向数组第一个元素的常量指针

(4)数组声明时编译器自动分配一片连续的内存空间 ,而指针声明时只分配了用于容纳地址值的 4 字节空间

2.指针的运算

(1)指针是一种特殊的变量,与整数的运算规则为:

p + n == (unsigned int)p + n * sizeof(*p);

当指针 p 指向一个同类型的数组的元素时,p+1 指向当前元素的下一个元素,p-1 指向上一个元素。

(2)指针之间只支持减法运算且参与减法运算的指针类型必须相同。

p1 - p2 = ((unsigned int)p1 – (unsigned int)p2)/sizeof(type)

①只有当两个指针指向同一个数组中的元素时,指针相减才有意义,其意义为指针所指元 素的下标差。

②当两个指针指向的元素不在同一个数组中时,结果未定义。

指针运算的应用

#include

//统计元素的个数

#define DIM(a) (sizeof(a) / sizeof(*a))

int main()

{

char s[]={'H','e','l','l','o'};//栈

char* pBegin = s;

char* pEnd = s + DIM(s); //关键点,数组名 + n

char* p = NULL;

printf("pBegin = %p\n",pBegin); //第 1 个元素的地址

printf("pEnd = %p\n",pEnd); //最后 1 个元素的地址

printf("Size: %d\n",pEnd - pBegin); //5

//在同一行中打印出:Hello

for (p = pBegin;p < pEnd; p++)

{

printf("%c",*p);

}

printf("\n");

return ;

}

3.数组的访问方式

(1)以下标的形式访问数组中的元素:如 a[i];

(2)以指针的形式访问数组中的元素:如*(a+i)

(3)下标形式与指针形式的转换:a[n]==*(a+n)==*(n+a)==n[a];

#include

int main()

{

int a[] = {};

int* p = a;

int i = ;

for(i=; i

{

p[i] = i + ; //利用指针给数组元素赋值

}

for(i=; i

{

//以指针形式来访问数组元素

printf("a[%d] = %d\n",i,*(a+i));

}

for(i=; i

{

//以下标形式来访问数组元素

i[a] = i + ; //比较另类,相当于 a[i] = i + 10

}

for(i=; i

{

//通过指针来访问数组元素

printf("p[%d] = %d\n",i,p[i]);

}

return ;

}

指针和数组不同

//ext.c

//本文件应为.c,而不能是头文件。因为头文件会被直接包含在相应文件中,而.c 文件是分别编译的

int a[] = {, , , , }; //在该文件中,a 被看作一个数组

#include

//编译这两个文件:gcc test.c ext.c

int main()

{

//外部文件中 a 被定义成一个数组。int a[5] = {1, 2, 3, 4, 5};

extern int a[]; //本文件,如果这样声明,a 仍被看作一个数组

printf("&a = %p\n", &a); //这里的&a 为整个数组的地址

printf("a = %p\n",a); //a 为首元素的地址,数值上等于&a

printf("*a = %d\n",*a); //打印出第 1 个元素,即 1

/*

extern int* a; //如果这样声明,编译器将 a 当成是一个 int 型的指针来使用。

printf("&a = %p\n", &a); //会从符号表中查到指针 a 的地址,指向数组

printf("a = %p\n",a); //从指针 a 处,取一个 int 型的数据,即第 1 个元素,为 1

printf("*a = %d\n",*a); //试图从地址 0x01 处取出数据,违规内存访问错误。

*/

return ;

}

4.a 和&a 的区别

(1)a 为数组首元素的地址

(2)&a 为整个数组的地址,即可将数组看成是一种数据结据。如 int[5];

(3)a 和&a 的区别在在指针运算

a + 1 == (unsigned int)a + sizeof(*a);//a 代表首元素地址,*a 表示第 1 个元素

&a + 1 == (unsigned int)(&a) + sizeof(*&a) == (unsigned int)(&a) + sizeof(a);

指针运算经典问题

#include

int main()

{

int a[] = {, , , , };

int* p1 = (int*)(&a + ); //指向数组最后面,即第5个元素的后面

int* p2 = (int*)((int)a + );//指向数组的起始地址+1byte偏移处

int* p3 = (int*)(a + ); //指向第2个元素

printf("p1[-1] = %d\n",p1[-]);//输出第5个元素

//数组a在内存中从低地址到高地址的分布如下:(小端模式,低字节放入低地址)

//01 00 00 00,02 00 00 00,03 00 00 00,04 00 00 00,05 00 00 00

//p2指向数组起始地址+1字节的偏移处,即01的后面,从这个地方开始读

//出4个字节,00 00 00 02,根据小端模式规则,该值为0x02 00 00 00,

printf("p2[0] = 0x%X,p2[0] = %d\n",p2[],p2[]);//0x02000000==33554432

printf("p3[1] = %d\n",p3[]); //输出第3个元素,即3

return ;

}

5.数组参数

(1)数组作为函数参数时,编译器将其编译为对应的指针。因此,一般情况下,当定义的函数中有数组参数时,需要定义另一个参数来标示数组的大小。

void f(int a[])等价于 void f(int* a);

void f(int a[5])等价于 void f(int* a); //就是一个指针,丢失了数组长度的信息

#include

void func1(char a[]) //编译时,a被编译为一个指针,丢失了数组长度的信息

{

printf("In func1:sizeof(a) = %d\n",sizeof(a));//a退化为指针,4

*a = 'a';

/*对指针所指向的内存进行存取(注意数组名也可以这样对内存

进行访问,如访问第1次元素*a,对i个元素*(a+i);这说明数组名

在使用上很像指针,但数组名并不是指针,见下面的分析*/

//再次说明a是指针,而不是数组名

a = NULL;//编译通过,a是指针类型。(而不是数组名,数组名不能作为左值)

}

void func2(char b[]) //b被编译成一个指针,与数组小大有没有被指定无关。

{

printf("In func2:sizeof(b) = %d\n",sizeof(b));//b退化为指针,仍为4

*b = 'b';

b = NULL;//编译通过,b是指针类型

}

int main()

{

char array[] = {};

func1(array);

printf("array[0] = %c\n",array[]); //array[0] = a;

func2(array);

printf("array[0] = %c\n",array[]); //array[0] = b;

return ;

}

参考资料:

www.dt4sw.com

http://www.cnblogs.com/5iedu/category/804081.html

c语言学习笔记 - 指针和数组

结合内存存储数据的机制,c语言里指针的出现和使用也就不奇怪了,如果先学了内存的一些知识,以及程序运行机制,到了c指针这块就会清晰很多. #include int mai ...

Go语言学习笔记八: 数组

Go语言学习笔记八: 数组 数组地球人都知道.所以只说说Go语言的特殊(奇葩)写法. 我一直在想一个人参与了两种语言的设计,但是最后两种语言的语法差异这么大.这是自己否定自己么,为什么不与之前统一一下 ...

C语言学习笔记之成员数组和指针

成员数组和指针是我们c语言中一个非常重要的知识点,记得以前在大学时老师一直要我们做这类的练习了,但是最的还是忘记了,今天来恶补一下.     单看这文章的标题,你可能会觉得好像没什么意思.你先别下这个 ...

c语言学习笔记 - 指针和字符串

前面学习了字符串是一种字符数组,又知道了指针变量和数组的关系,这里来看一下指针和字符串的关系. #include int main(void){ char str = ...

C语言学习笔记--指针阅读技巧

1. 指针阅读技巧:右左法则 (1)从最里层的圆括号中未定义的标示符看起 (2)首先往右看,再往左看 (3)遇到圆括号或方括号时可以确定部分类型,并调转方向 (4)重复 2.3 步骤,直到阅读结束 注 ...

C语言学习笔记--多维数组和多维指针

1. 指向指针的指针 (1)指针的本质是变量,会占用一定的内存空间 (2)可以定义指针的指针来保存指针变量的地址值 (3)指针是个变量,同样也存在传值调用与传址调用 重置动态空间的大小 #includ ...

c语言学习笔记.指针.

指针: 一个变量,其值为另一个变量的地址,即,内存位置的直接地址. 声明: int *ptr; /* 一个整型的指针,指针指向的类型是整型 */ double *ptr; /* 一个 double 型 ...

吴裕雄--天生自然C++语言学习笔记:C++ 数组

C++ 支持数组数据结构,它可以存储一个固定大小的相同类型元素的顺序集合.数组是用来存储一系列数据,但它往往被认为是一系列相同类型的变量. 数组的声明并不是声明一个个单独的变量,比如 number0. ...

C语言学习笔记之动态分配数组空间

本文为原创文章,转载请标明出处 高级语言写多了,再拿起C语言的时候,自己已经傻了... C语言中数组大小不能为变量,即使这个变量已经被赋过值了,应该使用malloc方法进行数组空间动态分配. 如下: ...

随机推荐

js轮播(qq幻灯片效果)

javaweb 中的路径问题汇总

路径问题汇总  http://localhost/day10/AServlet     request.getRequestDispatcher("/AServlet") ==&g ...

run() 和 start() 的区别

1) start: 用start方法来启动线程,真正实现了多线程运行,这时无需等待run方法体代码执行完毕而直接继续执行下面(指主线程下面)的代码.通过调用Thread类的start()方法来启动一个 ...

CentOS6.5搭建LNMP

1:查看环境: ? 1 2 [root@10-4-14-168 html]# cat /etc/redhat-release CentOS release 6.5 (Final) 2:关掉防火墙 ...

java jvm学习笔记十二(访问控制器的栈校验机制)

欢迎装载请说明出处:http://blog.csdn.net/yfqnihao 本节源码:http://download.csdn.net/detail/yfqnihao/4863854 这一节,我们 ...

Swift - 炫酷放射弹出按钮菜单(改造自AwesomeMenu)

这个是一个第三方按钮菜单组件,原版是使用Objective-C编写的名为AwesomeMenu的组件,地址是:https://github.com/levey/AwesomeMenu 这里改造成了Sw ...

Android开发事件总线之EventBus运用和框架原理深入理解

[Android]事件总线之EventBus的使用背景 在我们的android项目开发过程中,经常会有各个组件如activity,fragment和service之间,各个线程之间的通信需求:项目中用 ...

Mac下nvm管理node.js版本问题

本篇文章主要是针对已经安装了node.js和nvm管理工具小伙伴遇到的问题. 管理工具有两个,一个是nvm,还有一个是nnvm的好处就是可以管理多个node版本,而且可以切换想要的版本,可以安装一个稳 ...

CentOS6/7快捷使用gcc5

Centos6/7自带的gcc为4.x版本,可通过devtoolset工具集安装gcc5.x版本 1. 添加yum源 1)CentOS6 [hhorak-devtoolset--rebuild-boo ...

Servlet 随记:

API 1)init(ServletConfig config) 何时执行:servlet对象创建的时候执行 ServletConfig : 代表的是该servlet对象的配置信息 2)service ...

你可能感兴趣的:(c语言指针和数组的联系)