下面为什么说复制的是关于指针和引用说法正确的是,而不是字符串?

指针的地址在内存中存储是是以数值的方式存储的,但这就并不代表指针或者说是地址就没有类型,只能说是它存储的时候本质是以没有类型来存储的,因为实质上内存空间上的内容在存储的时候都是没有类型的,但它读取时是有类型的,所以将一个非0数值赋予一个指针的操作时错的,必须要将这个数组进行(指针类型)的强制转换后,变成一个有类型的地址后才可以进行指针的赋值,这样通过指针进行间接访问和赋值时才知道要操作多大的空间,不过0和NULL就不用了,因为它本身就被系统宏定义为void *类型了,而void *类型能自动转换为各种类型,但注意非0数字并不是void *,所以还要对非0数字进行地址类型的赋予,如下:

C定义了零指针常量(即 NULL)的概念:一个具有0值的整形常量表达式,或者0值表达式被强制转换为void *类型,则称为空指针常量,它可以用来初始化或赋给任何类型的指针(即NULL指针)。也就是说,我们可以将00L'/0'2–20*5以及(void*)0赋给一个任何类型的指针,此后这个指针就成为一个空指针,由系统保证空指针不指向任何对象或函数。ANSI C为此定义了一个NULL,用来表示空指针常量,但不能对NULL指针进行解引用(*p)——会报错!——如此就保证了不会对野指针进行操作,因为在对任何被赋值为NULL的指针进行解引用时一定会报错!!!——所以一定要将野指针赋值为NULL,以避免误用!不过char

void *型指针作为一种通用的指针,可以和其它任何类型的指针(函数指针除外)相互转化而不需要类型强制转换,但不能对void *指针进行解引用及下标操作(因为无类型大小,无法访问!);

//指向const对象的一般指针---是不允许的 !所以也====绝不存在指向const 变量的一般指针,因为不能将const 变量的地址赋给一般指针!!!!!

//指向常量的指针只能是指向字符串常量的指针,而不能是指向const 的指针。

//---------即:指向常量的一般指针存在,但只能是指向常量字符串的char *指针,而指向const常量的一般指针根本就不可能出现!

NULL指针(以上有述)、


指针常量、有的书上又称为常指针)--------不可改变指向
常量指针---------操作权限受限,仅能“读”的指针,不可对指向对象的内容进行写

指向常量的指针-------指针变量,指向可变,只是指向的是常量,不可使用指针对常量的内容进行改变字符串常量(出现在表达式中,除了sizeof ,& ,给数组初始化的情况,其它的都可以看成是一个地址——第一个字符的地址)、


指向数组的指针(数组指针)、

(二维数组名 与 指向数组的指针 和 指向指针的指针不同 !!)、

指向了不确定的内存区域的指针,通常对这种指针进行操作会使程序发生不可预知的错误。

防止出现野指针必须要做到三大点:-----原理:对野指针乱指的不确定空间被误用是无法察觉的,反而是对NULL进行解引用的话编译器会发出报错!

1在声明指针时将其初始化为NULL或零指针常量------ 防止其乱指,指向的不确定空间被误用了而无法察觉

2free一个指针后,一定要将这个指针设置为NULL或零指针常量,除非能保证后面对这个free的指针不再使用,但及时对一个未指向的指针赋值NULL是一个良好的习惯!------在释放完指向的空间后,指针变成野指针,防止误用其乱指的空间而无法察觉

(1.5)函数返回指针---------- 要保证返回 的地址上的空间内容没有被释放,保证是值传递,只要空间内容还在,那么传递的这个地址就是 有效的。

3函数返回指针时,只能返回静态局部变量的地址、或函数malloc分配的指针、或传递的形参指针,只要保证返回的这个指针所指向的空间没被(手动、自动)释放、这个指向的地址空间的内容值还有效就行。------ 函数返回的地址是存在的,但是当函数返回之后,函数内部的空间就会自动释放,返回的地址仍存在,但是地址上存储的空间内容却被隐形释放了,这就会导致我们在对返回的地址进行解引用时还误以为此地址上的空间内容还存在,对获得的内容其实已经是不确定的无效值了还不自知,而这个也是编译器无法察觉的。所以对函数返回指针时一定要严加注意,一定要保证返回的指针所指向的空间还是有效的。

(2)指针常量---------- 只是指向被限制了而已 !,在定义时要马上初始化

int *const p2;//错误!!!因为不可再重新赋值了,此时给它初始化的值是一个不确定的随机无效值!

只是这个指针被声明为常量了,但它指向的空间内容并没有指为常量,所以可以对其指向的空间内容做变化,不能变的只是此指针的指向,一但初始化了就不能在赋予其其它的指针了,所以说在定义时就一定要用我们想要的地址值去初始化它有多必要。

但是要注意的是:但数组名并不是一个指针常量 ,只是可以看做一个指针常量!!!------- (具体可看(5)数组名的解释)

(3)常量指针 ----------- 只是其对指向的空间的操作权限(修改内容的权限)被限制了而已(只能访问,不能修改) !

常量指针可以指向任何地址,也可以对它进行重定向(其指针的值可以被重新赋值),但无法通过对常量指针的解引用(*p 或p[] )来对其指向的地址空间内容进行修改,只能有访问的权限。但其指向的空间内容本身是什么就是什么,不受常量指针的影响,该可改就可改(非const变量),该不可改就不可改(const变量) !

常量指针常用于函数间的指针参数传递,以防止在函数内部对指针指向的内容进行修改,用常量指针作为形参,用一般指针作为实参,可以起到函数内部对传递的空间内容只有访问权限,从而保护了传递的地址空间内容,如 int strcmp(const char * str1 , const char * str2) 就是如此用法,即完成了比较,防止了被修改的隐患!

另外,常会对字符串常量返回的指针误以为是常量指针,但根据指针间的赋值原则来看,由char * s = "abcd"; 可得“abcd"返回的指针只能是char * 型一般指针,也就是说字符串常量在表达式中返回的指针仅仅只是一个指向字符常量的一般字符指针,而不是什么常量指针!

在静态存储区分配地址空间,并且会返回一个指向字符的地址

当用在表达式中时,除了一下三种情况:sizeof(”abcd“),&"abcd"(返回的是第一个字符的地址,这里的“abcd”仅当做字符来解释) ,给字符数组赋值char a[] = "abcd")时,其它情况下字符串常量"abde.."都可以当做一个指针处理,且是一个指向字符(指向字符串常量第一个字符)的一般指针,无法通过此常量指针对字符串常量的内容进行修改,此常量指针指向字符串常量的第一个字符。

常会对字符串常量返回的指针误以为是常量指针,但常量指针的定义表示式是const char *p,而根据指针间的赋值原则来看,由char * s = "abcd"; 可得“abcd"返回的指针只能是char * 型一般指针,也就是说字符串常量在表达式中返回的指针仅仅只是一个指向字符的一般指针(其实就只是静态分配区中的一个地址而已),而不是什么常量指针,字符串常量返回的确实是一个指向常量的一般指针,但却是指向一个字符串常量的首字符的指针,而不是指向const常量的指针而不是常量指针。不过倒时可以说字符串常量返回的指针是一个“指向常量的指针常量”,其实它并不是什么指针,就是一个常量取的地址而已,然后赋值给一般指针 char * , 而char *却不是指针常量,它只是一个普通指针,还可以改变指向的。要记得指向常量的一般指针存在,但只能是指向常量字符串的char *指针,而指向const常量的一般指针根本就不可能出现!

char s[] = "abcde"; //只是将字符串"abcde"赋给数组s的地址空间上,而字符串常量”abcde“从始至终都不存在,这里的”abcde“只是作为单纯的一个字符串,与字符串常量没关系,更谈不上返回一个常量指针了;

p[2] = 'k';// 不允许!!!因为指向的内容是一个常量,虽然p是一个一般指针,对指向的空间内容有访问和修改操作权限,但这操作是否能够实现还要看这 个指向的空间内容自己本身的属性(看是否能够被修改),但当这个指向的空间内容本身是一个常量的时候,这时试图通过指针来修改 这个空间内容就是非法的操作了

”abcd“[2] = 'k';//不允许!!!因为指向的是一个常量字符,而且"abcd"返回的还是一个常量指针,怎么样都不允许这个改变内容值的操作!

当程序中的多处都出现同一相等的字符串常量"abcdef"时,因为字符串常量本身不可修改,只能起到访问的作用,所以即使是在多处出现同样的字符串常量,但在整个程序中的静态存储区中只会存在一份字符串常量,即多处出现的相等的字符串常量返回的常量地址都相等,等于第一个出现时编译器为其分配的地址。

程序占用的内存5个区---------用户空间的内存分布7个段

静态存储区其实就是数据段data 和 bss 段 , 常量区其实就是text 段

左上图可知,程序占用的内存被分了以下几部分.

所谓的文字常量区和程序代码区都放在进程空间的text代码段中,所以不能对字符串常量进行修改,因为text不允许被修改。

常量不一定就只是存储在常量区(text)中,也可存在于栈中,如const常量;

而变量也不一定就都存储于栈(局部变量和函数形参)中,也可存在于静态存储区(全局变量和static变量----data和bss)中,如static变量;

而堆区即不用来存储变量也不来存储常量,而是专门用于人工、手动、动态开辟内存malloc用的 !

(5)数组名----------很重要,看完这些全部,就能明白很多本质上的东西,对一些规则和叫法也就更加理解是为什么

一维数组名的类型是——指向基本数据类型的指针 如:int *

二维数组名的类型是——指向数组的指针 如:int (*p) [ ],而不是什么指向指针的指针int **。

数组名其实就一个指向数组第一个元素的指针常量,而不是什么数组类型的指针,也不是针对数组,真正针对数组、指向数组、数组类型的指针是如以下(6)的数组指针;

数组名只是一个与数组空间相关联的符号并且实质上是使用用数组的第一个元素的地址与之相关联的。

我们声明一个变量和数组的过程是(可看出它们的本质)先保留一个内存空间,空间大小与变量类型或数组大小相对应,然后再创建一个符号表,在符号表上记录所保留的空间与变量名或数组名的绑定关系。只不过与数组名关联的内存空间是整个数组的空间(但实质是用数组的第一个元素的地址相关联),而与变量名关联的内存空间是变量名(也可称为变量)自己所占的内存空间【但不同的类型的变量,所占的内存空间大小却又是各不相同,(而变量占不同的空间大小体现了它将来存储其它数据的容量的能力,所以这里更可以把变量看成一个容器,它的本质即为容器的本质——需要占空间,又可存储其它数据如字符变量占1个字节,整形和指针变量都是占4个字节,而指针这个容器所容纳的数据又是另一个内存空间的地址,所以也可以看成指针与两个地址空间相”关联“,一个即是它自己本身所占的内存空间的地址(用&取得),一个是它所存储的另一内存空间的地址(即是它自己的值p)。这样之后就可以通过变量名或数组名来访问空间内容,后者是通过符号表上的关联关系,后者是通过它的值(即容器所容纳的另一内存空间的地址),但它们自身的本质都是通过记录的符号表上的关系来确定的,它们的本质可通过它们的大小sizeof(变量名或数组名)来体现!

数组名与指针不同,数组名只是一个符号,是不占空间的数组名通过一个符号表的记录与整个数组空间相关联(实际上是用数组的第一个元素的地址相关联),所以数组名的大小sizeof(a)是整个数组的大小//而指针名所代表的是一个变量,是占用空间的,它所关联的地址即是它所占空间,并且这种关联关系也是通过一个符号表来记录的所以指针变量(名)的大小sizeof(p)仅是它(指针变量)所占的空间大小4,即使这个指针变量的内存空间存储的是另一个内存空间的地址(又是另一种关联)。

所以与数组名关联的地址只有一个,即整个数组的首地址(所以是与整个数组关联)。而与指针相关联的的地址却有两个,一个是它(指针变量)自身所占内存空间的地址(与它所占的整个4个字节的内存空间相关联),一个是它所占的内存空间的存储的值,即它的指向;前者与数组名关联的数组地址一样,表现的是它的本质,所以sizeof(指针名)=4 ,sizeof(数组名)=数组的大小,而后者表现的是作为变量的属性(——容器)上所存储的值,通过指针来访问这个容器上所存储的另一内存空间地址上所存储的值又是另一个变量的容器表现——可用容器将变量与一段段存储有效值的内存空间对应起来(除了malloc的内存空间,它只能返回一个地址,而无法返回一个变量,即只能通过间接访问这个malloc的空间,而无法通过变量来直接访问)——叫做间接访问(通过指针变量),而通过与之相关联的的变量名来访问这个内存空间上存储的内容——叫做直接访问(通过变量名,直接取变量的值即可)。

数组一般有三种全局/静态范围的数组局部变量数组申请堆空间来创建数组

其中,全局数组和局部静态变量数组、局部动态变量都属于静态数组,前两个为静态区空间,最末一个在栈空间。

静态数组的变量本身就是数组第一个元素的地址,所谓静态数组的变量即是数组名,就是一个符号,直接与数组的第一个元素的首地址进行关联,只要进行一次寻址,所以更

动态数组的变量存放的是一根指向到申请空间的首址指针,与指针直接关联的是指针本身的空间地址,申请堆空间数组的首地址要对指针进行间接访问才能得到,需要进行两次寻址,所以更

但数组名与指针有着类似的用法,即都可以通过指针名的解引用*(a+i) 或 a[i]来访问数组内容,但与指针不同的是,数组名只是一个符号,他不具有变量的属性(即使它能与一个地址相关联),但数组名与它相关联的地址的绑定关系不可再改变,所以也就表现出“数组名不可再重新指向”性质",而指针是一个变量,而变量的容器内的内容是可以改变的(存入什么就变成什么),所以往指针这个容器内存入什么地址,则指针的指向即指向什么地址,所以说指针的指向是可改变的。

因为数组名只是一个符号,而不是一个变量,更不是一个“所谓的”指针常量,所以对数组名取地址并不像对指针取地址那样返回一个变量的地址,而是返回的是数组的首地址,但却又是不同于数组名所关联的地址——虽然他们返回的地址值是相等的,但意义完全不一样。数组名返回的地址只是数组中第一个元素的地址(因为虽然数组名的本质是与真个数组空间相关联,但当初实际上也是用第一个元素的地址去关联它的,所以体现的是第一个元素的地址,也就是以数组元素为内存单位,只不过值为第一个元素的地址)——级别上与数组元素的地址是同一级别,而&数组名返回的地址却是作为以整个数组为内存单位的地址——级别上与指向数组的指针是同一级别(如果数组名是一维指针的话,则&数组名也可看成是二维指针,也就是与二维数组名同一级别)(&a)++跨越的是整个数组的大小,如同aa++ , 而a++跨越的仅仅只是一个元素的大小------具体的在如下的(6)中有所述。

我们常常还用数组名形式的形参作为参数进行传递,但要记得那也是值传递(第一个数组元素的地址),而用数组名形式的形参在函数内部也会转换成指针的形式,如 void myfun( int a[] ,int size)中的a就变成了一个有着独立地址的指针变量a,作用域为整个函数内部,属于自动变量,并且用实参传递过来的第一个数组元素的地址来初始化它,在函数内部 sizeof(a)= 4 而不是数组的大小,因为a不是数组名因为传递的是指针值,所以用一个指针来存储它,而不是一个真实存在着的数组名,因为那样的话就不是值传递了,而是变成内容传递了,将整个数组内容传递过来了,而那时不对的。而实在上以指针来传递数组即能节省传递(copy)的数据量,同时也能达到在函数内部也能操作函数外部空间的数组的存储的元素内容的能力,一举两得。但如果是要在一个函数的内部改变函数外部空间的一个指针的指向的话,那么要传递的就是指针的地址了,即如

若a是一维数组名,aa是二维数组名,p是指向一维数组元素的指针(p=a;) :

数组的属性和指针的属性不相同,在声明数组(声明数组名)时,分配了用于容纳数组元素的空间;而声明一个指针时,只分配了用于容纳指针本身的空间。

在值上:a = &a ; 在意义上:a是第一元素的首地址,与数组元素的指针同级别;&a是数组的首地址,与二维数组aa(指向数组的指针)同级别//

一维数组名的类型是——指向基本数据类型的指针 如:int *

二维数组名的类型是——指向数组的指针 如:int (*p) [ ],而不是什么指向指针的指针int **。

数组名不是指针常量,是一个符号,所以也同指针常量一样表现出不可再"重新指向"的性质(如a++,a+=3都是错的!);而指针可重新指向 。

数组形式的形参在函数内部不会以一个数组名形式存在,而是以一个独立地址的指针自动变量存在,传递的只是数组的第一个元素的首地址。

(6)指向数组的指针(数组指针)

指向数组的指针 vs 二维数组名 vs 指向指针的指针:-----------很重要!!!

数组名并非是一个简单的指向指针的指针!

二维数组名与指向数组的指针是同一类的,因为他们的跨度都是一个数组长度(也可以说出元素都为一个数组),而指向指针的指针,跨度仅为一个指针的大小,它的元素为一个基本数据类型对象的指针,所以说用二级指针来指向二维数组只能得到二维数组的最基本元素,而指向指针的指针来指向一个二维数组,则得到的是一个一维数组,它相当于二维数组的直接元素——一维数组的数组名(指向一维数组元素的指针)。所以说将一个二维数组名赋予一个二级指针是不正确的,这时编译器会发出指针类型不匹配的信号(因为一个的指针的元素为一个数组,而一个指针的元素为基本对象的指针);而正确的做法应该是:将二维数组名赋予指向数组的指针,如同

 //只要是指针变量,那么它用来容纳的就是另一个地址,地址都是4个字节的,所以只要是指针,其大小就为4,而数组名不同,它是一个符号(严格说来并不是指针变量),大小整个数组说明指针变量的大小与指针是什么类型的无关,仅与它是不是指针有关

所以,p +1 相当于aa+1 ,p[1] 相当 aa[1] ,因为指针的步长等于指针类型的大小,而指向数组的指针的类型为一个数组,所以p 的一个跨度为整个数组(即 sizeof(int)*10的大小);

虽然P的一个跨度为一个数组的大小,但指向数组的指针p仍是一个指针,int (*p)[6] ; sizeof(p)=4, 它的大小仍为4 ,如下所示:-----------很重要!!!

指针的跨度与指针的类型有关,即也是与指针被赋予的地址类型有关,而指针的大小仅与是不是指针相关,与该指针为什么类型无关,所有类型指针的大小都为4

malloc开辟的是堆区(专门用于malloc)的地址空间 ,需要用户指定开辟空间的大小,同时在开辟后会立即返回此开辟空间的首地址,返回类型为void * ,这样开辟的地址就可以用于各种类型数据的存放,那么对应的返回的void * 指针也要转变成各种指针类型。因为数据在内存中的存放实际上是没有类型之说的,有类型的只是我们的操作,只是我们在读取内存或存放数据至内存时,是根据变量或指针的类型宽度来指定读取内存的大小和分配存放数据的空间大小的,我们依照我们定义的变量或指针类型来访问空间,而空间本身是没有类型的,只是我们在访问时指定了访问的类型。所以,对于malloc返回的指针void * , 我们直接使用的话是没有意义的,因为它对应的是空类型void ,没有指定类型宽度,所以我们访问起来也是没办法指定对内存操作的宽度的,所以我们需要对malloc返回的指针类型进行类型转换。

所谓的类型转换有以下两种方式:

因为指针的步长等于指针类型的大小(即sizeof( type)), 所以我们可以通过移动指针p+1或p+i 来达到存取相应类型数据的目的。同样的,这个也适用与由malloc返回的经过强制类型转换的指针。这样的话,也就可以使用malloc构建一个数组,而返回的指针p相当于数组名使用;也可以用来开辟一个字符串空间,用于存储字符串,这样p就相当于一个普通的指向字符串的指针char *。不过,更经常的是用malloc

因为malloc返回的地址空间上的内容的初始值是不确定的,所以为确保用的时候不会出问题,最好在每个malloc后面都要 memset(p,0,size);一下。

还有就是最终都别忘了free 和 赋为NULL

以下是这三种情况下的通常用法:

a、用于动态数组:----- 用malloc创建的数组称为动态数组,大小在运行时才知道,在运行时分配空间;

     用a[ ]下标创建的数组称为静态数组,大小在编译时就知道了,在编译时分配空间;

     全局变量数组、局部静态变量数组、局部动态变量数组都——称作静态数组,前两个在静态区,最后一个在栈区。

——常与指向数组的指针如int (*p)[ ]连用——用于创建动态二维数组:

//int **pb = &b;//本身是没错,但是要作为模拟二维数组的话就是错的,因为二维数组名根本就不是int **类型!!!

//int (*pbb)[5] = &b;//错误!!! &b是二级指针,而pbb 是二维数组名类别的,这就是对一个指向第一元素的指针取地址和对一个数组名取地址的区别——地址名并不是一个指针而只是一个符号,虽然关联的都是第一元素的地址,但很多数组名的性质就是不能用于一般指针!!!

  经由系统分配了(变量或数组的地址)或是手动开辟(malloc得到的空间地址)过了,否则会出现段错误!

    strcpy(s,"abcd"); // 错误!!--- 会出现不可预知的错误,因为这时的s是野指针!---- 编译会通过,因为编译器无法察觉野指针,但执行时会段错误!

有没有被覆盖掉,或者是cpy时没有将原字符串的尾部‘\0’赋值过来,如果这样的话,要手工给新字符串的末尾添一个‘\0’ ! ! ! !

printf("%s\n",s1);//正确,因为s1在初始化时除了前面4个,后面的都被初始化为了0 即 '\0' ,所以第5个位置被覆盖为‘5’,也还有第6个为'\0'

要注意:stu1 和 stu2 并不一样,不一样在于name的构建不同,前者只是构建一个指针,而后者构建了一个数组,对字符串的构建方式不同,所以这两种结构体类型大小也不同,之后初始化的方式也不同,这个要注意一下:

因为Stu1中的name只是一个指针,指针只占4个字节 ; 而Stu2中的name为一个数组,编译时编译器已经为其开辟了空间,所以为大小20个字节

字节对齐:结构体类型的大小不仅与成员有关,还与成员的顺序有关,涉及到字节对齐问题,

初始化:结构体变量的初始化只能在定义结构体变量的时候同时进行,因为不允许对结构体的单个成员进行初始化。

结构体初始化的两种方式如下所示:

在函数内单独定义结构体变量时:

struct Stu1 stu1 = {s,11,{}};这里初始化name时即可以用另一字符串指针也可用字符串常量,因为在函数体内定义以及可以获 得字符串指针了,而上面的情况因为是在体外初始化无法获得指针变量,所以只能用”Elain“了 !

而又因为结构体变量和其他类型变量一样,可以在定义的同时完成系统默认的初始化(实质是成员各自按照全局时的默认初始化),所以我们也可以不在定义的时候马上去初始化它,让它自动初始化,然后在之后再一一对每个成员进行赋值就可以了,这样也完成了类似的初始化功能啦!

所以以下使用的是对成员一一赋值的方法:

因为stu1中的name 仅仅只是一个指针,还没有开辟存储字符串的空间,所以在对stu1->name进行赋值前要先对name进行malloc;

而stu2中的name 因为是一个数组,所以空间已经是开辟好了的,所以直接使用即可。

对比指向数组的指针int(*p) [100],注意后面的区别,不要搞混了;

 ------ 用相同类型(返回值和形参列表相同)函数的函数名或函数指针

指向函数的指针(函数指针)指向的是函数,它所指向的函数也是有特定类型的,函数的类型由它的返回值类型以及形参列表确定,和函数名无关。

要注意的是:如果指向函数的指针只有在被明确为赋值指向某个函数了,才能安全地用来调用函数,因为只样才给了函数的实现(通过函数入口进入)。

函数名,与数组名类似(注意:只是类似),是一个符号用来标识一个函数的入口地址

定义为一种函数指针类型的别名,它和函数指针类型int (*) (double*, char);等价,也就是说ptf现在也是一种类型

即作为返回值又作为函数参数:

};// 通过下标来选择要调用的函数,可把一类操作放在同一个函数指针数组里面,或是将对同一个对象的操作放在同一个函数数组里面

可以用外剥离法找到最里面,用右左法则扩展到最外面的方法

一、常量指针与一般指针与const 对象的地址与指针常量 间的赋值关系:

实际上任何指针(一般指针、指向常量的一般指针、常量指针、指针常量)都可以赋给常量指针;

不能将常量指针(不管指向的是const对象还是非const对象)赋给一般指针,除非强制转换为一般指针;

也不能将const 对象的地址赋给一般指针,除非(强制转换)为一般指针----即:切记一般指针不能指向const 变量。

cpi =pi;//而实际上任何指针(一般指针、常量指针、指针常量)都可以赋给常量指针
p1 = cpi;//不能将常量指针(不管指向的是const对象还是非const对象)赋给一般指针,除非强制转换为一般指针
p2 = cpc;//不能将常量指针(不管指向的是const对象还是非const对象)赋给一般指针,除非强制转换为一般指针
p1 = &bb;//不能将const 对象的地址赋给一般指针,除非强制转换为一般指针----即切记一般指针不能指向const 变量
p2 = &bb;//不能将const 对象的地址赋给一般指针,除非强制转换为一般指针

       C语言中对于指针的赋值操作(而不是对象的复制操作)(包括实参与形参之间的传递)应该满足:

应该满足:两个操作数都是指向有限定符 或 指向无限定符的类型相兼容 或 左边指针所指向的类型具有右边指针所指向的类型的全部限定符

即:只要满足左边指针指向的空间类型(包含)右边指针所指向的空间类型即可!!!

比较的应该是 : 赋值操作符两边的指针所指向的空间类型,与指针本身是否是常量无关,所以int * const p1 看的只是int * ,而与const没有关系, 因为const修饰的是指针而不是指针所指向的空间。

int型变量的指针,类型和p_to_const一样。p_to_const所指向的类型为const int,而p所指向的类型为int,p在赋值操作符左边,p_to_const在赋值操作符右边,左边指针所指向的类型并不具有右边指针所指向类型的全部限定符,所以会出错。

所以,按照赋值操作符两边的指针所指向的空间的类型相包含(左包含右)的原则 :

一维数组名的类型是——指向基本数据类型的指针 如:int *

二维数组名的类型是——指向数组的指针 如:int (*p) [ ],而不是什么指向指针的指针int **。

用二级指针来指向二维数组只能得到二维数组的最基本元素,而指向指针的指针来指向一个二维数组,则得到的是一个一维数组,它相当于二维数组的直接元素——一维数组的数组名(指向一维数组元素的指针)。所以说将一个二维数组名赋予一个二级指针是不正确的,这时编译器会发出指针类型不匹配的信号(因为一个的指针的元素为一个数组,而一个指针的元素为基本对象的指针);而正确的做法应该是:将二维数组名赋予指向数组的指针,即:

C语言中,指针是最难理解的一部分,尤其是Const指针变量。

Const指针变量只保护其Value值不被改变,并不能保护指针所指向的地址中的值不被改变,下面是针对Const指针的一个小练习:

pc = &z; //可以,pc是一个指向常量的指针,不能通过该指针去修改指针所指向的内存空间的值,但是,该指针可以指向别的变量

// *pc = 10; //不可以,*pc所指向的地址为const变量,其值不可更改 pc是一个指向常量的指针,不能通过该指针去修改指针所指向的内存空间的值

pc = pp; //可以,pc为指针,其值可被改变 pc是一个指向常量的指针,pp是一个普通指针,用一个普通指针给一个指向常量的指针赋值是可以的
// pp = pc; //用一个指向常量的指针,赋值给一个普通指针,不可以。如果可以,那么就可以通过普通的指针去修改内存的值

*cp = x; //可以,通过常指针去修改指针所指向变量的值,原则上来讲是可以的,如果指向的变量是const的,那么不同编译器会有不同的结果

// cp = &x; //不可以,cp为常指针,指针的值不能被修改,给常指针赋值是错误的

// pp = &x; //不可以,pp是非const指针,原则上来讲给它赋值是可以的,在不同的编译器下有不同的结果
// pp = pc; //不可以,指向常量的指针不能赋值给一个普通的指针
pp = cp; //可以,常指针可以赋值给一个普通指针

0;所以不能对NULL指针(零地址)进行解引用(*p)(访问)——因为对零地址进行访问会报错!不过char *p

void *型指针作为一种通用的指针,可以和其它任何类型的指针(函数指针除外)相互转化而不需要类型强制转换, 但不能对它进行解引用及下标操作(因为void *无类型大小,无法访问!);

指向const对象的一般指针---是不允许的, 只允许存在指向const对象的常量指针。

虽然指向const对象的一般指针时不允许的,但指向常量字符串的一般指针时允许的,虽然都是常量,但一个是const修饰的常量,一个是静态存储区(txt)常量,虽然性质都是不允许修改的常量性质,但从其存储上看,其本质还是不同的。

我要回帖

更多关于 关于指针和引用说法正确的是 的文章

 

随机推荐