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数组与指针的更多相关文章

  1. 把《c++ primer》读薄(4-2 c和c++的数组 和 指针初探)

    督促读书,总结精华,提炼笔记,抛砖引玉,有不合适的地方,欢迎留言指正. 问题1.我们知道,将一个数组赋给另一个数组,就是将一个数组的元素逐个赋值给另一数组的对应元素,相应的,将一个vector 赋给另 ...

  2. C语言核心之数组和指针详解

    指针 相信大家对下面的代码不陌生: int i=2; int *p; p=&i;这是最简单的指针应用,也是最基本的用法.再来熟悉一下什么是指针:首先指针是一个变量,它保存的并不是平常的数据,而 ...

  3. 《征服 C 指针》摘录3:数组 与 指针

    一.数组 和 指针 的微妙关系 数组 是指将固定个数.相同类型的变量排列起来的对象. 正如之前说明的那样,给指针加 N,指针前进“当前指针指向的变量类型的长度 X N”. 因此,给指向数组的某个元素的 ...

  4. C指针-数组和指针的归一

    int bArr[] = {1,2,3}; int *iarr = bArr; *iarr = 6; printf("%d\n",*iarr); printf("%d\n ...

  5. 《C专家编程》第四章——令人震惊的事实:数组和指针并不相同

    数组和指针是C语言里相当重要的两部分内容,也是新手程序员最容易搞混的两个地方,本章我们锁定指针与数组,探讨它们的异同点. 首先来看指针与数组在声明上的区别: int a[10]; int *p; 很明 ...

  6. (C语言)数组与指针的区别

    以前常常听过这种说法,说数组和指针这两者比较像,但是不能混淆,可是一直没能理解.刚刚在李云的<专业嵌入式软件开发>中,看了讲述数组与指针区别的一章,似乎有所领悟.本着知乎上看到的这张图,我 ...

  7. C语言教学--二维数组和指针的理解

    对于初学者对二维数组和指针的理解很模糊, 或者感觉很难理解, 其实我们和生活联系起来, 这一切都会变得清晰透彻. 我们用理解一维数组的思想来理解二维数组, 对于一维数组,每个箱子里存放的是具体的苹果, ...

  8. 【C语言学习】《C Primer Plus》第10章 数组和指针

    学习总结 1.数组初始化方式: int a[]={1,2,3} int a[SIZE]={1,2,3} //SIZE是宏定义,数组初始化个数不能大于SIZE,否则报错:当个数小 //SIZE,自动补0 ...

  9. C语言学习004:数组与指针

    在C语言中,字符串实际上就是字符数组,在内存中字符串"Shatner"存储的形式是这样的

  10. C语言--指向多维数组的指针和指针数组

    #include <stdio.h> //void show(char *p[]); ]); int main(){ ][]={","abc","x ...

随机推荐

  1. Android语音识别(本地+第三方)

    语音识别主要的功能就是在用户不方便输入的时候找一个替代输入的选择. 1.本地语音识别 下面的代码首先创建SpeechRecognizer对象,并设置回调函数监听器.当在点击监听器中调用doSpeech ...

  2. MDNavBarView下拉导航菜单(仿美团导航下拉菜单)

    说到下拉导航菜单这个东西用得还真不少,细心算一下做开发以来这样的菜单已经写了第三次了,但基本都是不能复用.感觉特累人.不经意看到同事写一个类似的下拉菜单,但他引用了开源库仿大众菜单的库,大致看了一下, ...

  3. 慎用memset();

    <span style="font-family: Arial, Helvetica, sans-serif;">void *(memset) (void *s,int ...

  4. postfix部署多个Content Filter的方法

    Postfix邮件服务器可以在接收邮件时使用content_filter来扫描邮件(病毒,广告等).通过整合一个集中化的电子邮件内容过滤器,比如amavis或mailscanner,Postfix可以 ...

  5. swift-自定义无限轮播图

    一  前言 1.之前一直在用OC编程,最近才开始接触使用swift就发现使用OC越来越不习惯,感觉已经爱上了swift. 2.这个自定义轮播图只是对之前OC版本进行了翻译,欢迎指正. 3.我决定一步步 ...

  6. Swiper API

    本文分享自 http://www.cnblogs.com/scavengers/p/3760449.html 最近使用Swipe.js,发现中文的资料很少,试着翻译了一下.能力有限,翻译难免错漏,欢迎 ...

  7. Objective-C 【This is ARC】

    ------------------------------------------- ARC的概念及原理 (1)指针分类 强指针:默认情况下,所有的指针都是强指针,关键字strong 弱指针:_ _ ...

  8. 《锋利的jQuery》心得笔记--Three Sections

    第六章 1.    JavaScript的Ajax的实现步骤: 1)         定义一个函数用来异步获取信息 function Ajax(){ } 2)         声明: var xmlH ...

  9. linux kernel with param

    Linux kernel support pass param to kernel, this params can be assigned at load time by insmod or mod ...

  10. LLVM language 参考手册(译)(6)

    模块级内联汇编(Module-Level Inline Assembly) 模块包含“module-level inline assembly”块,这与GCC中的“file scope inline ...