c数组与指针
0.数组和指针并不是相同的
我们声明数组时,同时分配了一些内存空间,用于容纳数组元素,但是当我们声明一个指针时,只分配了用于容纳指针本身的内存空间。
从这个方面也可以理解sizeof后面跟数组名和指针名的不同。
什么时候数组和指针相同呢?
c语言标准对此做了如下说明
规则1.表达式中的数组名被编译器当作一个指向该数组的一个元素的指针
规则2.下标总是与指针和偏移量相同
规则3.在函数参数的声明中(形式参数),数组名被编译器当作指向该数组第一个元素的指针
1.数组名是一个常量指针,并不是一个左值
#include<stdio.h>
int main(int argc,char **argv)
{
int array[]={1,2,3,4};
array++;
return 0;
}
test.c|5| error: lvalue required as increment operand
数组名不是常量指针的情况只有两种,就是当数组名是sizeof和&的操作数时,前者产生整个数组的占用的字节数,后者产生一个指向数组的指针
2.下标引用和间接操作是一样的
#include<stdio.h>
int main(int argc,char **argv)
{
int array[]={1,2,3,4};
int *b=array+1;
printf("%d\n",b[1]);
printf("%d\n",*(b+1));
printf("%d\n",b[-1]);
printf("%d\n",b[10]);
printf("%d\n",1[b]);
return 0;
}
输出
3
3
1
32595
3
这个例子说明了几个很有意思的事实,b是指针,但是b还是可以使用下标操作符,c在处理下标操作符时把b[1]看成*(b+1)
这也是为什么1[b]是合法的原因,1[b]被看成了*(1+b),在编译器看来b[1]和1[b]并没有区别
并且c语言不进行下标检查,这是基于相信coder的设计思想,并且检查下标要消耗一定的资源
3.当要传递一个数组是形参可以是两种形式
int strlen(char *string);
int strlen(char string[]);
//甚至是下面的方法都可以
int strlen(char string[100]);//里面的数字随意
void print1(int *a)
{
int i;
for(i=0;i < SIZE;i++)
printf("%d\n",a[i]);
}
void print2(int a[])
{
int i;
for(i=0;i< 5;i++)
printf("%d\n",a[i]);
}
//数组的下标使用变量是可以的
调用时,实参只能是一个指针
int a[]={1,2,3,4,5};
print1(a);
print2(a);
print2(&a[1]);//这样传过来就是数组的一部分。
实参即使不是真的数组都是合法的
int b=10;
print2(&b);
实际上传递的就是指针
这个相等行暗示指针和数组名是相等的,但千万不要被它糊弄了,这个声明确实相等,但是只存在于当前的这个上下文环境中。
sizeof 的不同
char *a = "hello";
char b[]= {'h','e','l','l','o'};
//sizeof(a)是指针占的内存地址大小,所有类型的指针的大小都一样,是系统的字长,32位系统就是4个字节,64位系统就是8个字节
//sizeof(b)是数组b占的字节数,这里是5个char加上一个‘\0',是6个字节
注意指针在这里的用法,只能对字符串常量采用char *a = "hello";
int *a = {1,2,3}; //error: (twice)excess element in scalar
initializer, initialization makes pointer from interger without a cast
char *b = {'h','e','l','l','o'};//error:(four times)excess element in
scalar initializer, initialization makes pointer from interger without a
cast
char *c = 'h'; //error: initialization makes pointer form interger without a cast
char *d = "h"; //correct
char *e ="hello" //correct
4.由第三条引出了第四条
先讲一下历史
由于 char message[]={'h','e','l','l','o'};这种初始化字符数组的方式用于比较长的字符数组显得比较傻,c语言标准提出了一种快速方法用于初始化字符数组
char message[]=“hello"; 尽管它看上去有点像字符串常量,但是它并不是,它只是前例的初始化列表的一种写法。
一个字符串常量在用于初始化一个字符数组时,它是一个初始化列表,在其它任何情况下,他都是一个字符串常量
注意区分
char message1[] = "hello";
//message1是个数组,message1也是一个常量指针,不能改变message1的指向,"hello"是数组的初始化列表,可以通过下标或者间接操作来改变值
char *message2 = "hello";
//message2指向字符串常量,message2是一个变量指针,可以改变message2的指向。不能通过下标或者间接操作来改变值。如果硬要改变的话是编译通过但运行出错
5.指向数组的指针
int matrix[3][10];
matrix可以看作成是一个一维数组,它包含3个元素,每个元素是包含10个整形元素的数组(这个是在人看来的,对计算机而言matrix就是个指针,
计算机没有数组的概念,它只懂得指针,数组和下标提出来只不过是方便人理解,这是我到目前为止的看法)。(如同df -h)
matrix:一个常量指针,指向它的第一个元素,所以matrix是一个指向包含10个整形元素的数组的指针
matrix+1:仍然是一个指针,在matrix的地址的基础上向后移了4*10=40个字节。但是这里不是matrix[1],maxtrix[1]相当于*(matrix+1),再次强调下标和间接操作是一样的。
matrix[0]和
*matrix:maxtrix是一个指向数组的指针,那么*maxtrix就是一个数组,包含(*maxtrix)[0]到(*matrix)[9]这
么10个元素,由于数组名是第一个元素的地址,所以*matrix相当于&(*matrix)[0]也就是&matrix[0][0],
这是一个指向整数的指针。
*(*(matrix+1)+1):有了前面的基础,这个就相当简单了,matrix[1][1]。
但是很有意思的是matrix和matrix[0]的值竟然是一样的,都是&matrix[0][0],换句话说&matrix[0]和matrix[0]是一样的(这块我暂时不能解释,以后解决了再说)
#include<stdio.h>
int main(int argc,char **argv)
{
int matrix[3][10]={
{1,2,3,4,5,6,7,8,9,10},
{10,9,8,7,6,5,4,3,2,1},
{5,4,3,2,1,6,7,8,9,10}
};
printf("%p\t%p\n",matrix,matrix[0]);
printf("%p\t%p\n",matrix+1,matrix[1]);
printf("%p\t%p\n",matrix+2,matrix[2]);
return 0;
}
//结果
0x7ffff4609f50 0x7ffff4609f50
0x7ffff4609f78 0x7ffff4609f78
0x7ffff4609fa0 0x7ffff4609fa0
我们发现下面的都比上面的多出4*10=40个字节的地址
那么指向数组的指针怎么表示呢
int (*p)[10]=maxtrix;
看一下怎么使用
int a[10];
int (*pa)[10] = &a;
由于对于数组来说&a和a的值是一样的,所以也可以写成
int(*pa)[10]=a;//但是这样写在gcc上会得到一个warning,所以还是不要这样写的好
值虽然一样但是类型不一样,这点很重要。
那么int *pa=a;和int (*pa)[10]=&a;有什么区别呢
这两个pa的值虽然是一样的,但是类型不一样,还是这句话,第一个pa指向的是一个整数,第二个pa指向的是一个数组,第一个++pa的步长是4,第二个++pa的步长是40
?
int a1[3];
int a2[2][3];
int (*ap)[3];
ap=&a1;
ap=a2;
用法也不一样
int a[10]={1,2};
int (*pa)[10]=&a;
int *pb=a;
printf("%p\t%p\t%p\t%p\n",pa,pb,a,&a);
printf("%d\t%d\n",(*pa)[1],pb[1]);//很奇怪,其实声明已经很清除了(*pa)[10]这样才得到一个int,pa[1]编译器显示是int *类型
输出:
0x7fffa745c2c0 0x7fffa745c2c0 0x7fffa745c2c0 0x7fffa745c2c0
2 2
这个声明看起来比我们见到过的所有声明都复杂,但事实上并不是很难。你只要假定它是一个表达式,并对它求值。下标引用的优先级高于间接访问,但由于括号的
存在,首先执行的还是间接访问,所以p是一个指针,那么它指向什么呢?接下来是下标引用,所以它指向的是某种类型的数组(包含10个元素),这个表达式没
有更多的操作,所以p是一个指向整形数组的指针。
那么上面matrix的类型是 int (*)[10],而matrix[0]的类型是int *
这里讲一句题外话,数组不能整体赋值给另外一个数组,只能一个一个元素赋值。
6.指针数组
注意和上面的指向数组的指针区别
int *p[10]
下标引用的优先级高于间接访问,首先执行下标引用,因此p是某种类型的数组(它包含的元素数为10)。在取得一个值以后,进行了间接访问操作,这个表达式
不再有其它操作,得到的是一个int型。总结一下:对数组的某个元素进行间接操作后得到了一个int型,所以p是一个数组,它包含的元素是指向整型的指
针。
?
#include<stdio.h>
int main(void)
{
const char *keyword_table[5]={
"do",
"while",
"if",
"else",
"switch"
};
printf("%p\t%p\t%p\t%p\t%p\t%p\t%p\t%c\n",&keyword_table,keyword_table,&keyword_table[0],&"do","do",keyword_table[0],&keyword_table[0][0],*keyword_table[0]);
return 0;
}
输出:
0x7fff46c5f790 0x7fff46c5f790 0x7fff46c5f790 0x4006ac 0x4006ac 0x4006ac 0x4006ac d
c数组与指针的更多相关文章
- 把《c++ primer》读薄(4-2 c和c++的数组 和 指针初探)
督促读书,总结精华,提炼笔记,抛砖引玉,有不合适的地方,欢迎留言指正. 问题1.我们知道,将一个数组赋给另一个数组,就是将一个数组的元素逐个赋值给另一数组的对应元素,相应的,将一个vector 赋给另 ...
- C语言核心之数组和指针详解
指针 相信大家对下面的代码不陌生: int i=2; int *p; p=&i;这是最简单的指针应用,也是最基本的用法.再来熟悉一下什么是指针:首先指针是一个变量,它保存的并不是平常的数据,而 ...
- 《征服 C 指针》摘录3:数组 与 指针
一.数组 和 指针 的微妙关系 数组 是指将固定个数.相同类型的变量排列起来的对象. 正如之前说明的那样,给指针加 N,指针前进“当前指针指向的变量类型的长度 X N”. 因此,给指向数组的某个元素的 ...
- C指针-数组和指针的归一
int bArr[] = {1,2,3}; int *iarr = bArr; *iarr = 6; printf("%d\n",*iarr); printf("%d\n ...
- 《C专家编程》第四章——令人震惊的事实:数组和指针并不相同
数组和指针是C语言里相当重要的两部分内容,也是新手程序员最容易搞混的两个地方,本章我们锁定指针与数组,探讨它们的异同点. 首先来看指针与数组在声明上的区别: int a[10]; int *p; 很明 ...
- (C语言)数组与指针的区别
以前常常听过这种说法,说数组和指针这两者比较像,但是不能混淆,可是一直没能理解.刚刚在李云的<专业嵌入式软件开发>中,看了讲述数组与指针区别的一章,似乎有所领悟.本着知乎上看到的这张图,我 ...
- C语言教学--二维数组和指针的理解
对于初学者对二维数组和指针的理解很模糊, 或者感觉很难理解, 其实我们和生活联系起来, 这一切都会变得清晰透彻. 我们用理解一维数组的思想来理解二维数组, 对于一维数组,每个箱子里存放的是具体的苹果, ...
- 【C语言学习】《C Primer Plus》第10章 数组和指针
学习总结 1.数组初始化方式: int a[]={1,2,3} int a[SIZE]={1,2,3} //SIZE是宏定义,数组初始化个数不能大于SIZE,否则报错:当个数小 //SIZE,自动补0 ...
- C语言学习004:数组与指针
在C语言中,字符串实际上就是字符数组,在内存中字符串"Shatner"存储的形式是这样的
- C语言--指向多维数组的指针和指针数组
#include <stdio.h> //void show(char *p[]); ]); int main(){ ][]={","abc","x ...
随机推荐
- 推荐5 款WordPress主题后台选项开发框架
在开发WordPress 主题的时候,借用成熟的WordPress 主题后台选项开发框架可以为我们省下不少功夫.相信你接触过不少国人做的所谓“原创”主题,一看后台都是千篇一律的界面(连CSS 都懒得改 ...
- 转: Python集合(set)类型的操作
python的set和其他语言类似, 是一个无序不重复元素集, 基本功能包括关系测试和消除重复元素. 集合对象还支持union(联合), intersection(交), difference(差)和 ...
- JavaScript、jQuery、HTML5、Node.js实例大全-读书笔记4
5.2.2 让瀑布流动起来 打好基建之后,就需要写JavaScript代码.首先如果数据不够显示一屏幕的情况,就用新数据来补足它,在补充的时候是根据4列中最矮的那一个为优先补充,因为高矮尺寸一般只有在 ...
- [Apache Maven Shade Plugin] [example] [001] 官方例子:includes-excludes
链接地址:[Selecting Contents for Uber JAR](http://maven.apache.org/plugins/maven-shade-plugin/examples/i ...
- Android开发:碎片Fragment完全解析fragment_main.xml/activity_main.xml(转)
注明:这个转的,见谅未能标明原始出处 我们都知道,Android上的界面展示都是通过Activity实现的,Activity实在是太常用了,我相信大家都已经非常熟悉了,这里就不再赘述. 但是Activ ...
- 求单链表倒数第m个结点
问题:求单链表倒数第m个结点,要求不准求链表的长度,也不许对链表进行逆转 解:设置两个指针p和q,p.q指向第一个结点.让p先移动到链表的第m个结点,然后p和q同时向后移动,直到p首先到达尾结点.此时 ...
- AnyCAD脚本模型
采用AnyCAD建立的模型, 基于AnyCAD.Net SDK在C#中展示模型:
- 【风马一族_Android】Android 前端内容
Android 前端内容 4.1 View 类概述 4.1.1 关于 View //类型说明 view(视图)指的是用户界面组件的基本构建基块.一个视图占据屏幕上的矩形区域,负责绘图和事件处理.视图是 ...
- 使用 Time Machine 恢复 .ssh等隐藏文件夹
重装MAC系统后,要恢复.ssh等文件夹内容,而其在“Finder”中又是默认隐藏的,这时我们可以先在“Finder”中使用“前往文件夹功能…”进入指定文件夹,然后再进入“Time Machine”进 ...
- hadoop2.2.0伪分布式搭建
1.准备Linux环境 1.0点击VMware快捷方式,右键打开文件所在位置 -> 双击vmnetcfg.exe -> VMnet1 host-only ->修改subnet ...