C语言之数组与指针的易混淆知识点
一、指针与数组
指针:指针本身也是一个变量,它的内容是指向的内容的地址。指针同样有类型的区分,char 的指针只能指向char型数据,int 指针指向int型数据。但是指针所占内存单元的大小(即其内容)是跟操作系统的地址位数有关,比如32位地址的系统,那么指针所占的内存单元就是4个字节,16位就是2个字节,因此,指针的类型只是限定它所指向的变量的类型,其内容所占的单元大小是与操作系统的地址位数相关,与变量类型无关。
在32位地址系统中,比如:
int a = 10; //int型一般长度为4字节,所以sizeof(a)的值为4
int * p =a; // p中存放的是a的地址,所以sizeof(p)的长度是4字节
char b = 'c'; // char型一般长度为1个字节,所以sizeof(b)值为1
char * d = c; // d中存放的依然是a的地址,所以sizeof(d)值为
// 4(而不是 1 !)
注意:void * 指针是未确定的指针,它可以指向任何类型的变量。
数组:数组是一个连续占有一段内存单元的变量集合。
比如:
int a[10]; // a:数组名,代表的是当前数组所在的首地址。
a = 3; // 错误;数组名a不是一个变量,它是一个地址常量,因
// 此对于a的赋值或自增是会报错的;
// a[0]:表示的是当前首地址下存储的内容;
//&a[0]:表示第一个元素的地址,这时候与a是一样,即首地址
指针与数组区别:
1、指针是一个单独变量,只是指向了其他变量的地址(相当于汇编中的间接寻址,与地址寄存器类似);
数组是一串元素序列并且真实的存储元素内容,它的数组名可以相当一个指针(与指针的用法“基本”一样),代表数组的首地址;
比如:
int a[10]; //系统实实在在分配了10*4个字节的连续内存单元
int* p=a; //也可以写成 int* p = &a[0];p指向数组a[10]的首地址a[0];
//p只是一个变量,只占据4个内存单元,存储的是数组a的首地址
其实:p=p[0]=a[0]=*a;(p+i)=p[i]=a[i]=*(a+i);指针和数
组的取值可以互换。
2、 sizeof对于指针变量和数组的处理是不一样的。拿上面的指针p 和数组a[10]来说,对于一个32位地址的系统。
sizeof(p)的值为 4 个字节;
sizeof(a)的值为 40 个字节;
原因是因为,p是一个指针变量其内容存储的是4个字节的地址;而数组名a并不是一个变量,它是一个常量的地址,sizeof将其视为整个数组的代表,因此计算的时候会计算整个数组的大小。
但是下面的情况又会不同:
void computesize(int *a,int b[], int c[10]);
int main(){
int a[10];
computesize(a,a,a);
}
void computesize(int *a,int b[],int c[10]){
printf("a = %d , b = %d , c = %d",sizeof(a), sizeof(b),sizeof(c));
}
这时执行strlen(a,a,a); 假设是32位地址系统。
输出的值并不是4、40、40,而是4、4、4,那是不是有矛盾了呢?并不是的,因为这牵扯到了另一个知识点——函数参数的传递。我们知道,传递参数有值传递和地址传递。上面的情况就属于地址传递,无论形参是*a、b[]还是c[10],传入的时候都是将首地址传给指针变量a 、b、c。b与c的不同就是b没有指定长度,而c指定了长度。这时候其实a、b、c相当于一个指针变量而不是之前的数组了,否则每一次传递都要重新弄一个副本,系统会吃不消的。
二、指针与函数
1、指针与函数参数:指针和数组作为参数传递的时候,其实是传递的一个地址。
比如:
int main(){
int b[10];
int* a = b;
printf(" %d ",sizeof(b));//输出40
printf(" %d ",sizeof(a));//输出4
fun(a,b);
}
void fun(int *a,int b[]){
printf(" %d ",sizeof(b));//输出4
printf(" %d ",sizeof(a));//输出4
};
传进去的都是一个地址变量,sizeof计算出来的大小是一样的(但是在定义他们的地方sizeof的长度是不一样的)。
2、字符指针与函数:(重点,容易忽略)
//定义一个数组,内存中只有数组,存放在堆栈中(注意,此时字符串不是
//常量字符串,不在静态区而是在数组所在的堆栈内存中)。
char aMeg[] = "I am a boy."; //定义一个指针变量,内存中具有一个指针变量和一个字符串常量,并且字
//符串常量存放在静态区中,指针在堆栈中分配。
char * pMeg = "I am a boy.";
三、指针数组、数组指针、指向指针的指针
1、指针数组:
形如:int * p[10]; //一个指针数组,数组里面有10个元素,每一个元素都是一个int型的指针
数组内的每一个元素都是一个指针变量(这时注意每个元素所占的内存单元大小是地址的长度而不是类型长度)
2、数组指针:形如:int (* b)[10];//一个数组指针,指针指向一个列长度为十的一个二维数组的第一行的行地址。
也称作行指针,该指针指向了一个长度为10的数组的行首地址;
如:b表示第一行的首地址;b+1表示第二行首地址
*(b) = b[0][0]; *(b+1) = b[1][0]; *(*(b+i)+j) = b[i][j]
3、指向指针的指针:形如: int **c = p;//二级指针c,指向了指针数组p的首地址&p[0],即指向了指针数组的第一个指针的地址
该指针大小也是取决于操作系统,它跟一级指针其实本质上是没有差别的,只是说是有连环指向的这种感觉。
c是指向指针的指针,因此它使用间接取址符时需要两次才能取出目标的内容。
比如:*c表示的是p[0]的地址,而**c表示的是p[0]地址中的内容
c == &p[0]; // 指针数组中第一个指针元素的地址
*c == p[0]; // 指针数组中第一个指针元素地址的内容,即目标变量的地址
**c == *p[0]; // 指针数组中第一个指针元素指向的目标变量值
四、程序陷阱
1、*与++运算符的优先级相同,而且都是右结合的(一元都是的)。
a = *p++ :表示先将指针p自增,然后再取指针的内容赋值给a;
a =(*p)++: 表示先取指针内容赋值给a,然后指针再自增
2、/、*、%等二元运算符具有相同的优先级,而且结合性是自左向右的。
a = 1/2*a: 因为是自左向右结合的,故表示的是 0.5*a,然后再赋值给a。
a = 1/(2*a): 这才表示求2*a的倒数,然后赋值给a。
3、字符串数组和指针
当指针指向字符串常量时,通过指针是不能修改字符串常量的值的。如:
char * p1 = "Hello World";
p1[5] = ','; //错误,这是一种C语言标准未定义的操作
对于p1[5] = ‘,’;不同系统会给出不同结果,在Turbo C环境下,可能会完成赋值过程,但是对于像VC++、Dev-C++来说这是一个错误的操作,因为“Hello World”是一个字符串常量,存储在常量区,从C语言的概念和定义上讲,是没有这个标准的。
4、利用malloc分配内存
如果想将某一个字符串复制到某一个控件,一定要记得分配足够的空间大小,不要忘记”\0”结束符。 比如:
char * strSrc = "Hello Boy.";
char * strDest;
//错误,strlen并未计算"\0"结束符,赋值后的指针末尾指向未知空间。
strDest = (char *) malloc(strlen(strSrc)); //正确,为"\0"留出空间。
strDest = (char *) malloc(strlen(strSrc)+1);
strcpy(strDest,strSrc);
5、空指针和空字符串的差别
空指针是指向0(NULL)的指针,C语言保证对空指针的操作是安全的。如下:
char * p = NULL; // #define NULL 0 ,这是C语言定义的NULL
而空字符串则是一个只有一个’\0’结束符的字符串,它在内存中是有存储空间的。比如:
char p[] ="";//之战一个字符空间,\0
char p1[] = "\0";//与上面不同,这里占据两个字符空间,\0\0
char a[10];
printf("%d , %d \n",sizeof(p),strlen(p)); //输出为1,0 说明占有一个字节空间,注意此时strlen(p)为0,
printf("%d , %d \n",sizeof(p1),strlen(p1)); //输出为2,0 说明占有两个字节空间,注意此时strlen(p1)为0!
6、strlen与sizeof的区别
sizeof:
1、计算所有变量类型的占用字节大小
2、在计算字符串的时候,会将字符串后面的’\0’的大小也计算上来。
3、计算的是字节内存的大小
4、计算数组名的时候特殊,会计算整个数组的长度。其他的均计算单个变量strlen:
1、计算的是字符串的长度大小
3、计算字符串长度时,将会忽略’\0’结束符,遇到’\0’字符就会结束。
7、使用未初始化的指针
int x,*p;
x=10;
*p = x; // 错误,p并未指向一个确定的地方,它并没有被初始化。
8、NULL指针
NULL指针并不指向任何对象,在赋值和比较运算意外的其他运算符都是非法的。由于标准并未对NULL指针赋值运算、比较运算意外的运算进行定义,因此这些运算都将得到一个不确定的结果。有时候可能给系统带来灾难性的后果。 如:
int *p = NULL;
int x = 10;
int a = *p * x;
int b = *p * x;
printf("%d %d",a,b); //在 Dev 上运行出错
C语言之数组与指针的易混淆知识点的更多相关文章
- C语言中数组与指针的异同之处!你不知道的编程奥秘~
C语言的数组和指针一直是两个容易混淆的东西,当初在学习的时候,也许为了通过考试会对指针和数组的一些考点进行突击,但是很多极其细节的东西也许并不是那么清楚.本篇侧重点在于分析数组与指针的关系,什么时候数 ...
- Java-web易混淆知识点整理
Java-web易混淆知识点 post和get区别 post: 数据不会显示在地址栏 安全 大小无限制 可以提交二进制文件 get: 数据显示在地址栏 不安全 get方式提交有大小限制(约4kb) 相 ...
- [C语言基础] 数组与指针之间的引用
通过指针引用数组,通过数组引用指针,你搞明白了么?通过下面3种情形来了解一下数组和指针 Case 1. unsigned char arry[10]; unsigned char *ptr; unsi ...
- c语言,数组和指针
概要: 1.普通数组与指针 2.数组指针 3.指针的数组 数组是一个由(同一类型)连续元素组成的预先分配的内存块:指针是一个对任何位置的元素的引用. 数组自动分配空间,但不能重分配或改变大小:指针必须 ...
- 由strcat函数引发的C语言中数组和指针问题的思考
问题一 首先,来看一下下面这段代码: #include <stdio.h> #include <string.h> int main() { char *str = " ...
- c语言中数组,指针数组,数组指针,二维数组指针
1.数组和指针 ] = {,,,,};// 定义数组 // 1. 指针和数组的关系 int * pa = array; pa = array; // p[0] == *(p+0) == array[0 ...
- C++易混淆知识点整理
// 1 /////////////////////////////////////////////////////////////////////// // 常量指针:,指针可修改,变量不可修改(只 ...
- c语言字符数组和指针的经典用法
1.字符数组 许多情况下,对字符串的处理使用字符数组会更加方便,比如: 我觉得不改变字符串的原有顺序,对字符串进行删除等操作时,使用字符数组效果会更好. eg:给定字符串(ASCII码0-255)数组 ...
- (四)C语言柔性数组、指针赋值
一.柔性数组 今天看了公司的代码,发现一个很奇怪的问题,后来自己写了类似代码,我先把代码贴出来吧. #include<stdio.h> #include<string.h> # ...
随机推荐
- SWTBOK測试实践系列(5) -- 项目中使用手动和自己主动化的策略
手动測试和自己主动化測试永远是一个非常热门的话题.自己主动化也一直被人们捧上神坛.自己主动化測试和手动測试从技术上来说本质事实上都是測试用例设计.仅仅只是终于形式一个是人工运行,一个是代码运行罢了.这 ...
- 音频单元组件服务参考(Audio Unit Component Services Reference)
目录 了解Audio Unit体系结构 文档结构预览 结构单元介绍 本文主要介绍AudioUnit的组成 本文由自己理解而成,如有错误,请欢迎网友们指出校正. 了解Audio Unit体系结构 开始前 ...
- (updated on Mar 1th)Programming Mobile Applications for Android Handheld Systems by Dr. Adam Porter
本作品由Man_华创作,采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可.基于http://www.cnblogs.com/manhua/上的作品创作. Lab - Inte ...
- Net is as typeof 运行运算符详解 net 自定义泛型那点事
Net is as typeof 运行运算符详解 概述 在了解运行运算符的前提我们需要了解什么是RTTI ,在任何一门面向对象的语言中,都有RTTI这个概念(即 运行时). RTTI(Run-Ti ...
- ARM architecture
http://en.wikipedia.org/wiki/ARM_architecture ARM architecture ARM architectures The ARM logo De ...
- nginx源代码分析--配置信息的继承&合并
这里仅仅讲述http{}模块下的配置: 在ngx_http_block()函数内(这个函数别调用时在ngx_inti_cycle内的ngx_conf_parse函数,这个函数遇到http命令时 回调n ...
- Memory-mapped I/O vs port-mapped I/O
关于MMIO和PIO,我看到的解释最清楚的文章,原文在这里:Memory-mapped I/O vs port-mapped I/O - 2015 Microprocessors normally u ...
- 关于CSS和CSS3的布局小知识(干货)
最近在网站偶然看到的这个网站,进去看了下讲的CSS布局,感觉还不错,讲易懂且实用推荐给大家. http://zh.learnlayout.com/
- Struts2 (三) (转载)
前面一直在说Action可以是一个普通的Java类,与Servlet API完全分离,但是为了实现业务逻辑,Action需要使用HttpServletRequest内容.Struts 2设计的精巧之处 ...
- MVC入门——列表页
创建控制器UserInfoController using System; using System.Collections.Generic; using System.Linq; using Sys ...