指针
内存和地址怎么理解呢?
机器中有一些位置,每一个位置被称为【字节】/byte,许多现代机器上,每个字节包含8个位。更大内存单位【字】,通常包含2个或4个字节组成。
一个字包含4个字节,它的地址是什么?
他仍然只有一个地址,是最左边还是最右边的那个字节的位置,取决于机器。
机器事实-关于整型的起始位置:
在要求边界对齐(boundary alignment)的机器上,整型存储的起始位置只能是某些特定的字节,通常是2或4的倍数。
变量名和地址关系?
所有高级语言的特性之一,就是通过名字而不是地址来访问内存的位置。那么名字和地址是怎么关联的呢?关联关系不是硬件所提供的,它是由编译器实现的,硬件仍然通过地址访问内存位置。
存储在内存的一个32位值,到底是整型还是浮点呢?
什么类型取决于使用方式,如果使用整型算术指令,这个值就被解释为整数,如果使用浮点型指令,他就是个浮点数。
指针变量他的地址对应的内存中存的是什么内容?
存储是一个值,这个值,通常被当做地址来使用。
对于int a=112; int *d = &a; 为什么编译器最终将a的地址写入d对应的内存空间,而不是把112写入d的内存空间呢?
原因应该是这样设计灵活,且效率高,更重要的是硬件的设计如:RAM 有地址这个概念
符号* 什么含义?
用来执行间接访问操作。对一个变量间接访问,声明含义?就是不把这块内存值直接使用,而是把它用作地址,把这个地址对应的内容取出。
指针未初始化是什么含义,有什么风险?对未初始化的指针访问会发生什么?如int *a ; *a=12;
这个操作可能会有下面情景发生
1. 运气好a 的初始值是非法地址,出错,终止程序,在unix系统上这个错误memory fault ,在windows出现一般保护性异常(general protection exception)
2.更严重的情况,这个指针偶尔可能包含一个合法地址,即是这样的情况:引发错误的代码,与原先用于操作那个值得代码完全不相干。
编译器能检测出来初始化的指针p, *p = 12,这样,并给出警告吗?
标准定义的NULL指针什么含义?我怎么定义一个NULL指针变量?怎么判断一个指针是不是NULL指针。
表示不指向任何东西。定义一个变量赋值为零。与零值比较来判断指针是不是NULL指针。
在机器层面NULL指针内容到底是什么呢?
NULL 指针可能不是0,在此情况,编译器将负责零值和内部值之间的翻译和转换。
对一个NULL指针间接访问会出现什么情况?
因编译器而异,有的不报错。有的发生错误,并终止程序。
怎么定义一个指针变量才是好习惯?
如果你已经知道指针将被初始化为什么地址,就把它初始化为该地址,否则把它初始化为NULL。
在对一个变量赋值时,编译器会校验,检查什么?
校验,使用变量类型和应该使用的变量的类型不一致时,编译器会报错。
分析下*&a = 25; 非重点
指针常量是什么能不能访问操作并赋值?
*100 = 25; *100 就是指针常量。这条语句是非法的,需要强制类型转换如:
*(int *) 100 = 25;
指针常量什么样情况才能用到?
如:与输入输出设备控制器通信。
指针的指针怎么用示意图画明白?
指针定义和使用的分析:
定义(包括声明和初始化)
声明的分析:有* 就是指针;初始化,左值使用,*【变量】代表是将右边的值写入指针变量内存中(【变量】);
**【变量】左值使用,赋值,*【变量】表示地址内存打开,打开结果还是地址,**【变量】表示多次打开,还是代表打开地址,注意编译器能知道这种变化,**两次就是二级指针
使用:*【变量】是右值表达式,*代表间接访问,把【】中当做地址使用,找到这个地址对应的内存单元,把值拿出来,就是*【】表达式的值了
注意* 是右结合性
Int a =12;
Int *b = &a;
Int **c = &b;
画图要点:
1.箭头===》是地址
2. 虚线。表示关系
3. 实线。表示*访问
粗椭圆 当做右值使用,使用表达式的值。
粗方框 当做左值使用,指地址。
#include <stdlib.h>
#include <stdio.h>
int
main(){
int a=12;
int *b=&a;
int **c= &b;
printf("%p %p %p \n",a,b,c);
printf("%d %d %d \n",sizeof(a),sizeof(b),sizeof(c));
// printf("%s %s %s \n",a,*b,*c);
printf("%d %d %d \n",a,*b,**c);
}
结果:
分析结果: 指针占用八个字节,地址占用八个字节,实际上用6个字节表示地址(0x开头)
例子 :指针要用对等的地址
#include <stdlib.h>
#include <stdio.h>
int
main(){
int a=12;
int *b= &a;
int **c= &a;
printf("%p %p \n",a,c);
printf("%d %g \n",a,*c);
printf("%d %d \n",sizeof(a),sizeof(c));
}
结果:编译器报错!
结果分析:指针要用对等的地址(或低一级指针赋值如&b, 或者同级别指针&d)来赋值,编译器会检验出不符合的情况
为什么&ch 不能用作左值?
&ch结果会存储在某个地方/某个内存,但是我无法知道他位于何处,这个表达式,看不出他在机器中那个特定位置,所以它不是一个合法的左值。
什么是左值,右值?
左值可以再“=”号左侧,也可以在等号右侧,而右值只能出现在等号右侧。
分析清楚一个指针表达式是左值还是右值有什么好处?
分析表达式?
Char ch = ‘a’;
Char *cp = &ch;
示意图要求1. 标出求值的顺序,2中间专题是虚线,椭圆,方框
表达式
&ch
cp
&cp
*cp
*cp+1
*(cp+1)
++cp
Cp++
*++cp
*cp++
++*cp
(*cp)++
++*++cp
++*cp++
实例,strlen.c 必会默写
指针+/- 整数 在什么情况下可以这样?
只能用于指向数组中某个元素的指针,也适用于使用malloc函数动态分配获得的内存。
指针指向数组最后一个元素的后面的那个内存位置,有什么问题?允许这样,一般不允许对指向这个位置的指针进行间接访问。
指针-指针什么情况下允许这样?
只有两个指针指向同一个数组中的元素时,相减的结果是ptrdiff_t(有符号整数类型)
< <= > >= 可以比较的两个指针有什么限制?
他们都指向同一个数组中的元素,但是两个任意的指针可以执行相等或不相等测试。
数组
数组和指针的区别与联系?
数组和指针有着本质的不同,指针是一个标量值。数组名却有很多属性如:数组元素的数量。只有当数组名在表达式中使用时,编译器才会为它产生一个指针常量。
先来搞明白,什么是声明,什么是定义?
c 语言中的对象有且只有一个定义,但它可以有多个extern 声明。这里的对象,只是跟链接器有关的“东西”比如函数和变量。
定义只能出现一个地方,声明可以出现多次,声明相当于普通的声明,它说明并非本身,而是描述其他地方创建的对象。定义相当于特殊的声明,它为对象分配内存
左值在编译时可知,左值标示存储结果的地方。右值直到运行时才知,如无特殊说明,右值表示的内容。
C语言中有“可修改的左值”,很奇怪!意味着还有不可可以修改的左值,这个奇怪的术语是为与数组名(是左值)区分。
数组和指针是如何访问的?
char a[9] = “abcdefgh”; ….. c= a[i];
编译器符号表具有一个地址9980
运行时步骤1: 取i的值,将它与9980相加
运行时步骤2: 取地址(9980+i)的内容
char *p ; ……… c= *p;
编译符号表有一个符号p, 它的地址为4624;
运行时步骤1: 取4624的内容,就是5081
运行时步骤2: 取地址5081 的内容
指定的访问要灵活得多,但需要增加一次额外的提取。
定义为指针,但以数组方式引用时会发生什么?
指针当然可以p[i] 用,
char *p = “abcdefgh”; c=p[i];
编译器符号表具有一个p, 地址为4624
运行步骤1 :取地址4624的内容,即5081,
运行步骤2: 取得i的值,并将它与5081相加
运行步骤3: 取得地址[ 5081 + i] 的内容
指定的访问要灵活得多,但需要增加一次额外的提取。(和数组a对比)
注意写代码时,声明与定义相匹配
指针 |
数组 |
通常用于动态数据结构 |
通常用于存储固定数目且数据类型相同的元素 |
相关的函数malloc(), free(). |
隐式分配和删除 |
通常指向匿名数据 |
自身即为数据名 |
间接访问 |
直接访问 |
定义指针时,编译器并不为指针指向的对象分配空间,它只是分配指针本身的空间,除非在定义时同时赋给指针一个字符串常量进行初始化。如:
char *p = “break”;
float *pip = 3.14; //错误!!!!
什么时候数组和指针相同?
数组声明,包含三种
- extern , 如 extern char a[] ; 不能改写成指针的形式
- 定义,如 char a[10]; 不能改写成指针的形式
- 函数的参数,如func(char a[]); 选择数组形式,或者指针形式
数组,在表达式中使用
如c = a[i]; 选择数组形式 或指针形式
规则1: 表达式中的数组名被编译器当作一个指向该数组第一个元素的指针。
例外情况如: a 数组作为sizeof()的操作数;b 使用& 操作符取数组的地址;c数组是一个字符串,常量初始值。???
如:
int a[10], i=2;
a[i]; 相当于*(a+i); *(i+a);i[a]; 2[a];
规则2: 在函数参数的声明中,数组名被编译器当作指向该数组第一个元素的指针。
规则3: 下标总是与指针的偏移量相同
事实上,下标范围检查被认为并不值得加入C语言中。
指针总是有类型限制?p+2,p+i ; 是的,因为编译器要直到步长啊。
数组行参是如何被引用的?