数组和指针,原本不想在写了,觉得这部分差不多了,但是自己在写程序的时候还是发现了一个错误。首先说一下我的要求:

给一个函数传递一个二维数组,然后我想在这个函数里面计算这个数组的行数。

写个类似的错误DEMO代码弄上来:

#include <stdio.h>
#include <stdlib.h> void func(int a[][])
{
printf("%p\n", a);
printf("%p\n", a[]);
printf("%p\n", &a); printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a[]));
printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a)/sizeof(a[]));
printf("%d\n", a[][]);
}
int main()
{
int a[][] = {{,,},{,,}}; printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a[]));
printf("%d\n", sizeof(a)/sizeof(a[])); func(a); system("pause"); return ;
}

数组名本身是个地址常量,但是某些特殊情况下它的语义可以发生改变。例如sizeof(a),这时a表示整个数组对象(这里指语法对象,不是指类的实例)而不是这个常量本身。
基于这个语义,对数组名取地址也是合法的,对于数组a来说&a的结果等于a这个地址常量本身的值。这是C/C++标准委员会为了维护语法对象a作为一个左值(l-value)总可以取地址这条原则的

妥协。

#include <stdio.h>
int main(void)
{
char str[] = "world";
char * pstr = "world";
printf("%d %d",sizeof(str),sizeof(pstr));
getchar();
return ;
}

运行结果6 4。
解释:
char str[] = "world";
这里初始化不限定长度,而"world"包含结束符'\0'后为6个字符,因此初始化str的长度是6;又因为char数组中每个元素(char变量)占用1个字节的空间,所以str[]数组的大小是6字节。
char *pstr = "world";
由于pstr是指针,无论是否指向字符串,指向什么字符串,sizeof(pstr)等于sizeof(int),32位平台上等于4。
造成差别的原因:
这里char str[] = "world";声明并定义了一个数组str[](当然,C语言的语法不允许在定义之外这样引用整个数组,以下这样的写法只是为了区分语义),之后标识符str有双重语义:一是类

型为char* const的地址常量,它的值等于数组中首个元素的地址,即str等价于(char* const)&str[0];二是表示整个str[]数组这个语法对象。在sizeof(str)中,str表示的含义是str[],因

此返回整个数组的大小(这个大小在之前的数组定义中已经确定了);而pstr只是个指针,sizeof(pstr)只能返回指针本身占用的字节数而不能确定为它指向的内容分配的空间的大小。
(注意,地址常量绝不是指针,类型不同!虽然在函数的参数传递过程中,地址常量可以退化成对应的指针。这里LZ和2L显然由于这个错误理解导致对数组的sizeof()结果判断有误。)
关于数组名语义规定以及“数组名实际上就表示一个指针(错的!!!!)”的原因 以后需要注意哈···

数组什么时候会"退化":

下面是C99中原话:
Except when it is the operand of the sizeof operator or the unary & operator, or is a string literal used to initialize an array, an expression that has type ‘‘array

of type’’ is converted to an expression with type ‘‘pointer to type’’ that points to the initial element of the array object and is not an lvalue.

上面这句话说的很清楚了, 数组在除了3种情况外, 其他时候都要"退化"成指向首元素的指针.
比如对 char s[10] = "china";
这3中例外情况是:
(1) sizeof(s)
(2) &s;
(3) 用来初始化s的"china";

除了上述3种情况外,s都会退化成&s[0], 这就是数组变量的操作方式。

下面分析前面的那个程序:

#include <stdio.h>
#include <stdlib.h> void func(int a[][3])//数组名退化为一个普通的指针int* (*a)[3],所以a就是一个普通的指针,是一个数组指针。
{
printf("%p\n", a);
printf("%p\n", a[0]);
printf("%p\n", &a); printf("%d\n", sizeof(a));//a是一个数组指针,sizeof当然是一个指针的大小4
printf("%d\n", sizeof(a[0]));//数组指针a有2个元素,因为int a[2][3],每个元素都是一个int*,这个int*的a[0]是数组名,它是内层的,并没有退化 printf("%d\n", a[1][2]);
}
int main()
{
int a[2][3] = {{1,2,3},{4,5,6}}; printf("%d\n", sizeof(a));
printf("%d\n", sizeof(a[0])); int (*p)[3] = a; printf("%d\n", sizeof(p));//p是一个数组指针,它是个指针,所以为4
printf("%d\n", sizeof(p[0]));//p[0]相当于*(p+0)也就是*p,p指向a[0],所以*p就是a[0] func(a); system("pause"); return 0;
}

  

浅谈C中的指针和数组(六)的更多相关文章

  1. 浅谈C中的指针和数组(一)

    本文转载地址:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242138.html 在原文的基础上加入自己的想法作为修改. 指针是C/C ...

  2. 浅谈C中的指针和数组(五)

    前面写了一些C指针和数组的一些知识,但是还有一些很重要的知识没有交代,这里做一个补充. 首先看一下,普通变量(指针也是变量)和数组名查看地址的方式是不同的. 查看数组变量的地址,不需要使用 & ...

  3. 浅谈C中的指针和数组(二)

    原文转载地址:http://see.xidian.edu.cn/cpp/html/475.html 在原文的基础上增加自己的想法作为修改 很多初学者弄不清指针和数组到底有什么样的关系.我现在就告诉你: ...

  4. 浅谈C中的指针和数组(三)

    上一个博客我们得到了一个结论: 指针和数组根本就是两个完全不一样的东西.只是它们都可以“以指针形式”或“以下标形式”进行访问.一个是完全的匿名访问,一个是典型的具名+匿名访问.一定要注意的是这个“以X ...

  5. 浅谈C中的指针和数组(四)

    原文转载地址:http://see.xidian.edu.cn/cpp/html/476.html 在原文的基础上增加自己的思想作为自己的修改 指针数组和数组指针的内存布局 初学者总是分不出指针数组与 ...

  6. 浅谈C中的指针和数组(七)

    现在到揭露数组名本质的时候了,先给出三个结论: (1)数组名的内涵在于其指代实体是一种数据结构,这种数据结构就是数组: (2)数组名的外延在于其可以转换为指向其指代实体的指针,而且是一个指针常量: ( ...

  7. 浅谈C语言 extern 指针与数组

    /* * d.c * * Created on: Nov 15, 2011 * Author: root */ #include "apue.h" int a[] = {3,2}; ...

  8. 转: 浅谈C/C++中的指针和数组(二)

    转自:http://www.cnblogs.com/dolphin0520/archive/2011/11/09/2242419.html 浅谈C/C++中的指针和数组(二) 前面已经讨论了指针和数组 ...

  9. 转:浅谈C/C++中的指针和数组(一)

    再次读的时候实践了一下代码,结果和原文不一致 error C2372: 'p' : redefinition; different types of indirection 不同类型的间接寻址 /// ...

随机推荐

  1. C++ 顶层 const

    我的主力博客:半亩方塘 本文的主要參考来源来自于:C++ Primer 中文版(第 5 版) 第 57 面至第 58 面 1. 顶层 const 与底层 const 概念 我们知道,指针本身是一个对象 ...

  2. 如何用SQL SERVER 2005连接SQL SERVER 2008

    原先使用sql server 2005数据库,后来由于工作需要升级为sql server 2008 开发版,升级过程很简单,基本没有什么问题 下面主要说说,如何使用sql server 2005 st ...

  3. js 检验密码强度

    html 代码如下: <!DOCTYPE HTML> <html lang="en"> <head> <meta charset=&quo ...

  4. linux-telnet服务配置

    Telnet服务的配置:一.安装telnet软件包(通常要两个)1. telnet-client (或 telnet),这个软件包提供的是 telnet 客户端程序: 2. telnet-server ...

  5. C# in Depth阅读笔记1:C#1特性

    1.委托 委托是对包含返回值和参数的行为的一种封装,类似于单一方法的接口. 委托是不易变的(就像string),system.delegate下的combine和remove方法都只能产生一个新的委托 ...

  6. Map集合重要练习

    重要练习:将字符串中的字母按如下格式显示: a(1)b(2)...... 代码及思路如下: /* 获取字符串中字母的次数,并打印出如下格式a(1)b(2)c(3)...... 思路: 先定义一个方法, ...

  7. HTML3层叠样式表

    层叠样式表:CSS Cascading Style Sheet.V2.1  控制页面样式外观.   一.样式表分三类: 1.内联样式表.--放在元素的开始标记中.--只对当前元素起作用. <in ...

  8. vc++ internet

    1.用VC开发ActiveX文档服务器 MFC 4.2不支持开发ActiveX容器,但支持ActiveX服务器.只要在使用MFC AppWizard生成应用程序框架时选择支持Active Docume ...

  9. Android 圆形背景shape定义

    <?xml version="1.0" encoding="utf-8"?> <shape xmlns:android="http: ...

  10. listView 单选实现

    上一篇知道可以使用android自带的listview的chiocemode的单选模式实现.但那个布局是系统自带的checkedTextView,有时候我们需要自己实现布局,那么下面我们开始实现   ...