竞博体育 > 软件 > 竞博体育带有const的一级指针四种合法定义如下,带有const的一级指针四种合法定义如下

竞博体育带有const的一级指针四种合法定义如下,带有const的一级指针四种合法定义如下

终于总结完了这个知识点,现在拿出来和大家分享,如果有不正确的地方希望给出宝贵的意见.

带有const的一级指针四种合法定义如下:

注:个人针对于课本的易错点进行了相关的整理。整理的不专业,多多见谅。

 

const char *, char const *, char *const, const char *const

C语言中的易出错的点

 

 

这个笔记综合了

带有const的一级指针四种合法定义如下:

const char *p;

0. 常量&变量

const char *, char const *, char *const, const char *const

char const *p;

常量

 

这两个定义是完全一样的,而规范的写法是 const char *p,都是指向静态字符的指针。

整型常量

const char *p;

P是可以改变的,但是*p不能改变的。即:pointer to const char.

-345,1000,0

char const *p;

 

实型常量

这两个定义是完全一样的,而规范的写法是 const char *p,都是指向静态字符的指针。

char *const p;

1)   十进制小数形式,-123.456

P是可以改变的,但是*p不能改变的。即:pointer to const char.

说实在的我本以为这种定义是违法的,但是实质上这种定义是用来限定指针p是不能被改变的,而*p是可以改变的。即:const pointer to char.

2)   指数形式-10.34E-12

 

 

字符常量

char *const p;

const char *const p;

普通字符

说实在的我本以为这种定义是违法的,但是实质上这种定义是用来限定指针p是不能被改变的,而*p是可以改变的。即:const pointer to char.

这是一级指针最严格的定义。指针p不能改变,而且*p也不能改变。即:const pointer to const char.

一个字符,单撇号’ a’’Z’’?’’#’

 

 

转义字符

const char *const p;

理解这几种定义和其含义并不难,但是需要区分一些不合法定义,就需要下一些工夫了。另外这里所说的静态,是指不能通过此指针改变。

表格0.1转移字符表

这是一级指针最严格的定义。指针p不能改变,而且*p也不能改变。即:const pointer to const char.

比如: const char *p;

字符串常量

 

              char        A;

“123”   “boy”

理解这几种定义和其含义并不难,但是需要区分一些不合法定义,就需要下一些工夫了。另外这里所说的静态,是指不能通过此指针改变。

              char       B;

符号常量

比如: const char *p;

 

#define  PI  3.1415926

              char        A;

              A=2;

#define  PRINCE  40  

              char       B;

              B =3;

//注意结尾没有分号,尽量大写

 

             

注意:符号常量占用内存,只是一个临时的符号而已。预编译后这个符号就不存在了,故不能对他赋新值。;

              A=2;

              P = &A:   //这个是合法的,此时 P指向地址的值为2

变量

              B =3;

              P = &B;   //这个是合法的,此时 P指向地址的值为3

变量在程序中使用时,必须预先说明它们的存储类型和数据类型。

             

              B = 5;     //这个是合法的,此时 P指向地址的值为5

<存储类型>    <数据类型 >    <变量名> ;

              P = &A:   //这个是合法的,此时 P指向地址的值为2

              *P = 6;   //这个是不合法的 

<存储类型>可以是关键词auto、register、static和extern之一;

              P = &B;   //这个是合法的,此时 P指向地址的值为3

也就是说虽然这个定义说P指向地址的值不能改变,其实是不能通过P来改变,并没有说不能通过其它方式改变。

register : 寄存器,是cpu内存的存储空间

              B = 5;     //这个是合法的,此时 P指向地址的值为5

 

static    : 静态区  , C 程序把存储控件分成3部分, 堆区,栈区,静态区

              *P = 6;   //这个是不合法的 

总结:在一级const指针定义中,指针的值和指针指向地址存放的值,此两个值的状态是可以改变的。

extern    : 表示这个变量是外部变量

也就是说虽然这个定义说P指向地址的值不能改变,其实是不能通过P来改变,并没有说不能通过其它方式改变。

 

auto     : 是变量的默认形式

 

带有const二级指针四种合法定义:

常变量

总结:在一级const指针定义中,指针的值和指针指向地址存放的值,此两个值的状态是可以改变的。

const char **p; chat const **p; char *const *p; const char *const *p

const float pi=3.1415926 ;// 定义常变量

 

 

#define PI 3.1415926    //定义符号变量

带有const二级指针四种合法定义:

const char **p;

注意:常变量占用内存,只是值不能被改变。常变量具有符号常量的优点,使用方便

const char **p; chat const **p; char *const *p; const char *const *p

char const **p;

标识符

 

两者意义相同,指的是p,*p都是可以改变的,但是**p是不能被改变的。

C的合法的符号: (各种名字)

const char **p;

 

    1) 标识符由一个或多个字母、数字或下划线组成   : 组成:字母数字下划线

char const **p;

char *const *p;

    2)标识符的第一个字符必须是字母或下划线       : 第一个字符不能是数字

两者意义相同,指的是p,*p都是可以改变的,但是**p是不能被改变的。

指的是p和**p可以给改变,但是*p是不能被改变的。

    3)标识符不能与任何关键字相同                 :不能和系统的关键字重名         

 

const char *const *p;

 

char *const *p;

指的是p可以被改变,但是*p和**p都是不能被改变的。

空字符null

’’

‘’

?

?

\

a

Alert

b

Backspace

f

Form feed 换下页的开头

n

 换行

r

回车

t

水平制表

v

垂直指标

oo  o ooo               o表示一个八进制数字

与该八进制对应的ASC码

xh[h..]                    H表示一个十六进制数字

与该十六进制对应的ASC码

指的是p和**p可以给改变,但是*p是不能被改变的。

 

 

const char *const *p;

总结:做了这么多的实验终于总结出了一条规律就是:在只有1个const的指针定义中,指针变量定义在程序中是不能被改变的。例如const char *p; *p是不能被二次赋值的;

表格0.1转移字符表

指的是p可以被改变,但是*p和**p都是不能被改变的。

char *const p; 则p是不能被二次赋值的。char const **p; 则**p是不能被重复赋值的。  

1.  数据类型: 

 

如果还感觉到不好记忆话不防在看看以下三条规定:

基本类型

总结:做了这么多的实验终于总结出了一条规律就是:在只有1个const的指针定义中,指针变量定义在程序中是不能被改变的。例如const char *p; *p是不能被二次赋值的;

1,  在定义指针是第一个字符不能为*;

整型的数据

char *const p; 则p是不能被二次赋值的。char const **p; 则**p是不能被重复赋值的。  

2,  在基本数据类型不能被*直接修饰;

类型

字节数

取值范围

[signed] Int

4

-32768~32767            3万

Unsigned int

4

0~65535(0~2e16-1)       6.5万

[signde]short

2

-32768~32767            3万            

Unsigned short

2

0~65535(0~2e16-1)       6.5万

[signed] long [int]

4

21亿

Unsigned long[int]

4

0~42亿

[unsigned]long long[int]

8

-2e63~2e63-1            92兆

Unsigned longlong [int]

8

0~2e64-1

如果还感觉到不好记忆话不防在看看以下三条规定:

3,  定义时不是直接修饰变量的*,不能连续出现。

字符型char

1,  在定义指针是第一个字符不能为*;

 

Signed char

2,  在基本数据类型不能被*直接修饰;

谈到这里还有一个问题没有解决,就是如何使一个二级指针的指向具有静态性呢?

1个字节

3,  定义时不是直接修饰变量的*,不能连续出现。

我不知道,你知道能告诉我吗?

-128~127

 

 

Unsigned char

谈到这里还有一个问题没有解决,就是如何使一个二级指针的指向具有静态性呢?

下面讨论一下指针的近亲数组和const的关系:

1个字节

我不知道,你知道能告诉我吗?

const char arry[2]; const char arry[2][4]; ……

0~255

竞博体育 , 

char const arry[2]; char const arry[2][4]; ……

在编译的时候C标准未规定默认的类型,不同于int的,所以是具体看系统自己默认的。

下面讨论一下指针的近亲数组和const的关系:

想必大家对这个比较熟悉,其含义是数组中的内容是不能被改变的。不论是一维,二维还是更高的维数的数组。即arry[],arry[][]等等是不能二次赋值的。

例如领char c=255;

const char arry[2]; const char arry[2][4]; ……

 

        Printf(“%dn”,c);如果VC++输出了就警告,所以默认就是signed char

char const arry[2]; char const arry[2][4]; ……

const char *arry[2];

a)   字符和字符代码在ASCII表格中A~Za~z0~9!等

想必大家对这个比较熟悉,其含义是数组中的内容是不能被改变的。不论是一维,二维还是更高的维数的数组。即arry[],arry[][]等等是不能二次赋值的。

与指针类似,这里的*arry[0],*arry[1]不能二次赋值。(不要告诉我你不知道*arry[0]表示的是

b)   字符变量

 

什么意思)

char a=’?’;

const char *arry[2];

 

Printf(“%d %cn”);

与指针类似,这里的*arry[0],*arry[1]不能二次赋值。(不要告诉我你不知道*arry[0]表示的是

char *const arry[2];

63 ?

什么意思)

这个定义没有意义。因为arryp[]不能被赋值。

布尔型bool

 

 

浮点类型

char *const arry[2];

const char *const arry[2];

A.  浮点数据

这个定义没有意义。因为arryp[]不能被赋值。

这个定义也没有意义。

具有小数点的实数,3.1455  0.31455e12

 

float

4

6

0 以及1.2e-38~3.4*e38

double

8

15

0以及2.3e-308.7e308

long double

8gcc

16vc

15

19

 

const char *const arry[2];

 

这个定义也没有意义。

枚举类型

指针类型 *

 

数组类型 []

 

结构体类型struct

 

共用体类型union

 

函数类型

 

 

     怎么确定常量的类型?

1)   整型的常量。没有小数点,int,long int ,

2)   浮点型常量。C编译系统中把浮点型常量double按照双精度处理。

           float a = 3.14159  f4字节,但是3.14159按照d8字节,编译会有警告。

           可以在常量后面计入强制的转化类型。float a =3.1.159f;

 

Long double a = 1.23L;后者1.23l

2. 运算符的优先级: 记忆技巧:“出 单、算、 关、落 条 幅 。逗”

初等运算符()[]  - >   .

单目运算    !   -- ++   & * ~    sizeof

算术运算符  

关系运算符   >,>=,<,<= (高)      ==  !=(低)

逻辑运算符    &  ^   |     &&     || (高---低)

条件运算符   ? :

赋值运算符 = /= *= %= -= +=    <<=左  >>=右       &=  ^=  |=

逗号运算符  ,

出单、算关、罗 条  幅

 3.  数据赋值语句

1)   a=(a=b)=3*4  错误  因为a是左值,(a=b)不是左值。

2)   赋值语句也可以出现在其他语句中(如输出语句,循环语句等)如printf(”%d”,a=b);

  • 4. 赋值过程中的类型转换

   f/d->int  舍去小数点后的数

   int->f/d  数值不变23  23.0

   d->f     超出的时候显示err

   char->int   ASCII码

   int(多)->int(少 )只保留低位。

5. int a=3,b=3,c=3   不能是int a=b=c=3;

6. 常用的函数:

  •  

printf(“%d,%cn”,I,c)

di xX o u c s Ee f gG %% ntvf

L #(x o efg ) - 0  空格+   m.n

Scanf(“a=%f,b=%f,c=%f”,&a,&b,&c)

 

-----------

----------*

  1. 用“%c”格式符时,空格和转义字符作为有效字符输入。
  2. 输入的格式必须与scanf的格式相一致。
  3. 进行抑制符输入的scanf(”%c%*c”,&a);
  4. .输入数据时,遇以下情况认为该数据结束:

遇空格、TAB、或回车

遇宽度结束

遇非法输入

 

puts(char数组名)     

gets(char数组名)

 

 

getchar(‘a’or‘n’or  ’101’ or x )  putchar()

putchar(getchar());

printf(“%c”,getchar());

 

 

swith(表达式)

//表达式的值只能是整数 ,或则字符

{

case 常量1 : 语句;

case 常量2 : 语句;

case 常量3 : 语句;

……..

case 常量n : 语句;

default :     语句;

}

 

break

在循环体中:跳出循环体, 结束所在循环,接着进行循环体后面的语句。

在switch中, 结束switch选择,执行switch之外的语句。

注意:只能用在循环语句中或者switch语句中,不能单独使用

continue

结束本次循环,继续下次循环。

即掉过循环体中下面上尚未循环的语句,转到循环体结束之前,接着执行for语句中的表达式三,然后进行下一次是否执行循环的判断。后面的代码就不再执行了。

   return

在函数体内, 函数返回。

如果在main函数内, main函数返回, 程序结束.

7. 各种排序法比较

8. 二维数组

                a[2-1][2*8-1] 不能a[2-1,2*8-1

9. 字符串数组

     结束表字符‘’空符号,在ASC码为0,不显示,空操作符,表示什么也不操作,仅供辨别,不会产生其他作用。

       char c[]=”I am happy”11个字符=10个显示字符+1个‘’

                  等价于char c[]={‘i’,’ ‘,’a’,’m’, ‘h’,’p’,’y’,’y’,’’}11个

                   不等价于char c[]={‘i’,’ ‘,’a’,’m’, ‘h’,’p’,’y’,’y’   }10个

10. 字符串数组函数

puts(str)只能是一个数组gets(str)仅仅是一个数组

strcat(str1,str2)

格式:strcat(字符数组1,字符数组2)

功能:把字符数组2连到字符数组1后面

返值:返回字符数组1的首地址

说明:?字符数组1必须足够大

            ?连接前,两串均以‘’结束;连接后,串1的‘’取消,

               新串最后加‘’

        char *strncat(char *dest, const char *src, size_t n);

strcpy(str1,str2)   把二给一  strncpy(str1,str2)把2 的前N个给1的前n个

字符串拷贝函数strcpy

格式:strcpy(字符数组1,字符串2)

功能:将字符串2,拷贝到字符数组1中去

返值:返回字符数组1的首地址

说明:?     字符数组1必须足够大

         ?      拷贝时‘’一同拷贝

         ?      不能使用赋值语句为一个字符数组赋值

char *strcpy(char *dest, const char *src);

char *strncpy(char *dest, const char *src, size_t n);

strcmp(str1,str2)字符串比较函数,

格式:strcmp(字符串1,字符串2)    if(strcmp(str1,str2)>0) printf(“yes”);

功能:比较两个字符串

比较规则:对两串从左向右逐个字符比较(ASCII码),

                    直到遇到不同字符或‘’为止

返值:返回int型整数, a. 若字符串1< 字符串2, 返回负整数

                         b. 若字符串1> 字符串2, 返回正整数

                         c. 若字符串1== 字符串2, 返回零

说明:字符串比较不能用“==”,必须用strcmp

strlen计算实际长度 不包括‘’

          格式:strlen(字符数组)

          功能:计算字符串长度

          返值:返回字符串实际长度,不包括‘’在内

           0 =’’  ASC码对应的 0

           ‘0’字符零   就是asc码的48

strlwr(字符串) 转换为小写

strupy(字符串)转换为大写

11. 函数 :定义  声明   调用(嵌套or递归)

定义:有参变量=声明部分+语句部分

         类型名 函数名(参数列表

          {

           }

          函数类型决定返回值类型

12. 数组作为函数参数

       数组元素作函数实参,不能做形参。 数据传递方式: 值传递。

       数组名做实参or形参(数组名or指针)

                       地址传递方式 :改变形参的值,可以对实参进行改变 :形参可以改变实参的值

        多维数组名作为函数参数

13.  局部变量和全局变量

变量分为局部变量和全局变量(外部变量)建议在非必要时间不要定义全局变量。

 

 类型

注意点

 

局部变量

auto变量

1)   auto变量将来存放动态存储类别不在静态区,调用后就立即释放,到栈。

2)   每次都重新赋值,每次都执行赋初值语句。

3)   如果不对这个变量初始化,这个变量初始值为随机数。

静态存储区,动态存储区,cpu寄存器

 

 

static局部变量

1)   static局部变量,在静态存储区,结束后不消失,继续保留其原值,即占用的存储单元不释放,

2)   (引用切不改变值)static只赋初值一次以后每次调用都比在重新赋值,只是保留上次函数调用时结束的值。该变量已有值(就是上一次函数调用结束的值)。

3)   如果不对这个变量初始化,这个变量初始值为0或者空字符‘’.

4)   静态变量在函数调用后结束后仍然存在,虽然其值存在,但是只能是本函调用,其他函数调用不了。

寄存器register变量

1)  Cpu  Register:当一个变量使用频繁,需要很多次循环时(比如运算的时候),每次存储浪费时间,所以用register int f;

 

外部extern变量

 

全局变量

 

全局变量是定义在函数体外的变量, 这个变量将来存放到静态区,如果不对这个变量初始化,这个变量的初始值为0;

1)   在一个文件内扩展外部变量的作用域,

int main(){ extern int A,B,C;//把外部函数变量作用域从此开始。本来main中不能引用外部变量abc的,现在main中的extern对ABC进行了外部变量声明,把ABC作用域扩展到该位置,这样在main中就能合法的使用全局变量ABC 了。

提倡是吧外部函数的定义放在引用他的所有函数之前没这样可以尽量避免在函数中多加一个extern声明。Extern声明外部变量时,类型名可以写,也可以不用写,例如extern A,B,C;因为他不是定义变量步指定类型,只是写出外部变量名即可。

2)   将外部变量的作用域扩展到其他文件。

一个文件中的变量想引用另一个文件中的变量;在一个文件中定义外部变量NUM,而在另一个文件中用extern对NUM作为“外部变量声明”。既extern NUM

例如file1.c

IntA ;

Int main()

File 2.c

Extern A;

Int power(intA)

这种方法扩展全局变量的作用域应该十分慎重,因为在执行一个文件中操作中,可能会改变该全局变量的值,会影响到另一个文件中全局变量的值,从而影响到另一个文件中的值。

Extern找到就找到,找不到就报错。

3)   将外部变量的作用域限制在本文件中。

只把限制在本文件而不能被其他文件引用    static声明(在局部变量前是把他分配到静态区)

File 1.c                 file 2.c

Static int A;             extern A;

Int main() {}             void fun (int n){   A=A*N;//出错}

 

静态存储区

注意点

如果函数体内的局部变量和全局变量重名

 

 

 

 

 

 

 

 

 

 

 

 

 

13. 变量的声明和定义

      声明部分和执行语句

         声明部分的作用是对有关的表示符号(变量,函数,结构体,共用体等)的属性进行声明。

         对于函数而言,声明和定义的区别是明显的,函数的声明是函数的原型,而函数的定义是对函数功能的定义。对被     调用的函数的声明是放在主调函数的声明部分中的,而函数的定义明显不在声明部分的范围,他是一个独立的模块。

          声明部分出现的变量有两种情况,一种是建立存储空间的如int a   定义性声明--------定义

                                  一种是不需要建立存储空间的 extern a   引用性声明----声明

       注意:有一个简单的结论,

               在函数中出现的对变量的声明(除了用extern声明的以外)都是定义。

               在函数中对其他函数的声明不是函数定义。

               int main()

               {
                  extern A;//这个是声明,不是定义,是指定了A 的作用域从这里开始。
                  …

                }

              int A;//这个是定义

14. 内部函数和外部函数

      函数能否被其他源文件调用,分为内部函数和局部函数。

      内部函数:Static 类型名 函数名 (形参表);内部函数又称为静态函数。

      通常把只能由本文件使用的函数和外部变量放在文件的开头,前面都冠以static使之局部化,其他文件不能引用。这就提高了程序的可靠性。

       外部函数:extern int fun (int a ,int b);在main()函数中的声明

               extern int  fun (int a ,int b)

15. 指针

      定义->引用->做函数参数viod swap(int *p1,int *p2);

数组元素的指针

int a[10]={1,2,3,4,5,6,7,8,9}

Int *p=&a[0]; 等价于int *p;p=&a[0]; 

                         // p=&a[0]**等价于p=a;**

 

引用数组元素时指针的运算

1)  P+1===++p==&a[1]    p++====&a[0],p=1

2)  P=&a[0];p+i和a+i就是数组元素a[i]的地址

3)  *(p+i)*(a+1)是p+I 或者a+i所指向的数组元素 即a[i]     *(p+5) *(a+5) 即a[5];

加一个整数

P+1    指向这个数组的下一个元素    +  +=

减一个整数

P-1         -  -=

自加

P++   ++p

自减

P--  --p

两指针相减

P1-p2(只有p1p2只想同一个数组中的元素才会有意义)不能相加没意义

如果指针变量p1和p2都指向同一个数组,如执行p2-p1,结果p2-p1的值(两个地址之差)除以数组元素的长度 ,结果也就是两个指针之间的相差的元素的个数。

假设p2指向实型数组元素a[5],p2值是2020,p1指向a[3],值是2012,则p2-p1的结果是(2020-2012)/4=2

 

通过指针引用数组元素

a[i]===*(a+i)===*(p+i)

  1.   下标法:如a[i]形式  
  2.   指针法*(a+i)或者*(p+i)  等价a[i]

               for(i=0;i<10;i++);

               printf(“%d”,*(a+i))等价于printf(”%d”,a[i]);

       3.  用指针变量指向数组元素。 就是地址的偏移

              for(p=a;p<(a+10);P++)

               printf(“%d”,*p);

            第三种的速度快

注意:

        1)  通过改变指针变量的值来指向不同的元素。

        2)  如果不用p++而是用数组名a变化的方法是不可以的!因为a是一个指针常量,是&a[0],比如                                                 for(p=a;a<(p+10);a++)       Printf(“%d”,*a)是不行的。

        3) **注意指变量的当前值。**

练习8.6 有一个整形数组a,10个元素,要求输出数组的全部元素。

下标法:如a[i]形式

指针法*(a+i)或者*(p+i)

用指针变量指向数组元素

#include<stdio.h>

Int main()

{
int a[10]={0};

Int *p=a//Int *p=&a[0];

Int i;

Printf(“please input 10 integer num:n”);

For (i=0;i<10;i++)

{scanf(“%dn”,&a[i]);}
for(i=0;i<10;I++)

 {printf(“%d”,a[i]);}

Printf(“ %n”);

Return 0;

}

#include<stdio.h>

Int main()

{
int a[10]={0};

Int *p=a//Int *p=&a[0];

Int i;

Printf(“please input 10 integer num:n”);

For (i=0;i<10;i++)

{scanf(“%dn”,&a[i]);}
for(i=0;i<10;I++)

 {printf(“%d”,a[i],*(a+i));}

Printf(“ %n”);

Return 0;

}

#include<stdio.h>

Int main()

{
int a[10]={0};

Int *p=a//Int *p=&a[0];

Int i;

Printf(“please input 10

integer num:n”);

for(i=0;i<10;I++)

{scanf(“%dn”,&a[i]);}

 For (i=0;i<10;i++)
For(p=a;p<(a+10);p++)

 {printf(“%d”,a[i],*p);}

Printf(“ %n”);

Return 0;

}

这是首地址,末地址,偏移

*p输出。

思路输入&输出

for(i=0;i<10;i++) scanf(“%d”,*p++)

for(i=0;i<10;i++,p++)

printf(“%d”,*p)

for(I=0;I<10;i++)  printf(“%d”,*p++);

     注意的是p++ *p 和++p  *p的区别:

 

用数组名作函数参数

1)  当数组名作为参数的时候,如果形参数组中的个元素的值发生改变,实参数组元素的值随之改变。

           原因是:

             数组元素做参数值传递:Int swap(int x,int y)。  把值给了xy但是a[1]a[2]不变。

                            Swap(a[1],a[2]) 这里是做实参;

              数组名做参数:地址传递:f(int x[],int n)===f(int*p,int n)改变实际参数中具体的值。

Int main()

{void fun (int arr[],int n );

Int arry[10];

..

Fun(aarry,10);

Return 0;

}
void fun (int arr[],int n){…}

 

数组名做参数

 fun(int arr[],iint n); 等价于Fun(int *arr,int n);将形参数据名作为指针来处理 *arr就是指向arry[0],arr+1就是arry[1]    *(a+1)====arry[1]  *(a+i)=====arry[i]

实参类型

变量

数组名

要求形参的类型

变量名

数组名或者指针变量

传递的信息

变量的值

实参数组首元素的地址

通过函数调用能否改变实参的值

不能

能改变实参数组的值

实参数组名 代表一个固定的地址或者指针常量

形参数组名并不是一个固定的地址,而是按照指针变量处理。

 

形参和实参都用数组名         F(a,10)...           Int f(int x[],int n )

实参用数组名,形参用指针变量For(a,10)       Void f(int *x,int n)

实参形参都用指针变量         F(p,10)       Void f(int *x ,int)

实参为指针变量,形参为数组名。F(p,10); Void f(int x[],int n)

注意:

如果在main函数中不设数组,只设指针变量,编译会出错,原因是指针变量arr没有确定值,谈不上指向那个变量,如果要使用指针变量的话,指针变量需要有确定值。

 

通过指针引用多维数组

 

多维数组元素的地址

1)  int a[3][4];

2)  二维数组首元素的地址:对于二维数组而言,a代表的是二维数组首元素的地址,即是由四个整形元素组成的 一维数组。因此a代表的首行。 a[0]

  a+1是&a[1]

3)  a第0行的第1列元素的地址*(a+0)+1

 

表现形式

含义

地址

a

二维数组名,指向一维数组a[0],即0行首地址

2000

a[0]

*(a+0)

*a

一维数组名,0行0列元素地址a[0][0]

2000

a+1

&a[1]

二维数组名,指向一维数组a[1],即1行首地址

2016

 

a[1]

*(a+1)

1行0列的元素a[1][0]的地址

2016

 

a[1]+2

*(a+1)+2

&a[1][2]

1行2列的元素a[1][2]的地址

2024

 

*(a[1]+2)

*(*(a+1)+2)

a[1][2]

1行2列元素a[1][2]的地址

13

 

 

 

 

 

 

 

指向多维数组的指针变量

1)  指向数组元素的指针

  例子8.12  3*4的二维数组

  for(p=a[0];p<a[0]+12;p++)

  {if ((p-a[0])%4==0)printf(“n”);

    Printf(“*4d”,*p);

  }

  Printf(“n”);

2)  指向由m个元素组成的一维数组的指针变量。

 

 

 

 

 

16.  const型指针 

    const类型修饰符可以将指针变量常量化,主要有

下面三种形式。

   (1).第一种:

    const <数据类型>*<指针变量名称>[= <指针运算表达式>] ;

    常量化指针目标是限制通过指针改变其目标的数值。 

    例如: int a  = 100;

    const int *p = &a

    主要目的是: 不允许通过指针改变a 的值

    (2).第二种:

     <数据类型>  *const <指针变量名称>= <指针运算表达式>;

     例如: int a = 100;

     int * const p = &a

     主要目的: 只要修饰指针, 指针的内容不能修改,变量的内容可以改变

    (3).第三种:

     const  <数据类型>  * const    <指针变量名> = <指针运算表达式>  ;

     例如:

     例如: int a = 100;

     const int * const p = &a

     主要目的: 既要修饰指针, 指针的内容不能修改,也要修饰变量,变量的内容也不可以改变

17.void类型的指针、空指针、野指针

   void型的指针变量是一种不确定数据类型的指针变量,它可以通过强制类型转换让该变量指向任何数据类型的变量或数组。

一般形式为:

   void   *<指针变量名称> ;

   例如: void * p = &a

野指针:定义指针打不给他付初值

空指针 #define null 0;

空指针与野指针

   在LInux的C中, NULL被定义为空指针  , NULL的值就是0

   #define NULL ((void *)0)

   NULL与0之间的关系,  NULL是void * 类型的数据

                        0 只是一个数,

  野指针: 定义一个局部指针变量不对其赋值, 这个指针的值是一个随机数,这种指针就是野指针

            定义一个全局指针变量不对其赋值, 这个指针的值是0

 

18. 指针引用字符串

    int *p=”I love you”        int a[14]=“I love you”

   1)     之后指针指向了数组地址才可以被改变值。要指针指向了具体的字符串的时候,作为常量不能被改变了。

   2)  字符指针指示指向了一个字符的地址,而不是具体的内容。当局部变量释放了后,输出的指针的内容只是个随机的地址

   3)  指针p可以++,但是数组名a是常量&a[0]不能进行++运算

   4)        在输出的时候只能是字符数组的printf(“%s”,string)可以自动输出全部,但是int型不可以。

实参类型

变量名

数组名

要求的形参类型

变量名

数组名或指针变量

传递的信息

变量的值

实参数组的首元素地址

通过函数调用能否改变实参的值

不能

19. 函数指针和指针函数

        函数指针:char (*mystrcpy)(char *dst,char *src)

        指针函数:是指一个函数的返回值为地址量的函数 char * mystrcpy(char *dst,char *src)

  int (*p)(int,int);

  p =max;

  c=*(a,b);

  c=(*p)(a,b)

  对指向函数的指针变量不能进行算术均算,如p+n,p++

20. 指针数组和多重指针

  指针 数组: char *buf[4];

  数组  指针: char (* buf)[4];   指向一位数组的一个指针

  多重指针:存储类型 数据类型  *** ** 指针名字

21.动态分配函数

    (void  * )malloc (unsigned int size)   不成功返回NULL

     (void  * )calloc (unsigned n ,unsigned int size);比较大,n*size字节

     void  free (void * p);

     (void  * )realloc (void *p ,unsigned int size);不改变p值,知识改变size。

22.指针的小结

      p=student**,&a,&arry[1],array,p2,NULL(0)(不是没有赋值,是赋0,不指向任何)。**

     指针可以比较大小,相减,但是不能加.

23.结构体

  有不同类型数据组成的组合型数据结构那叫做结构体

    1)  字对齐: 2字节+2字节--->4  4字节+2字节 --->8字节

    2)  sizeof(str) == strlen(str) + 1

 3)  变量:定义声明 ->初始化->引用

     struct student

  {

    int num;Char name[20];Int age ;Float score;Char addr[30];

  } Struct student     student1,student2 ;

  说明:

  1. 结构体类型和变量不同,只能对变量进行赋值等运算。
  2. 编译的时候,对类型不分配空间,只对变量分配空间。
  3. 结构体类型的成员名可以与程序中的变量名相同,但是两者表示不同的对象。
  4. 对结构体的变量中的成员(即‘域’),可单独使用,他的作用和地位相当于普通的变量

  4) *  a={10101,”lilin”,’m’,”123beijingroad”}*

      Printf(“NO.:%dnname:%Snnaddr:%Sn”,a.num,a.name,a.sex,a.addr);

    也可以只是对于某一成员进行初始化,其他未初始化数字型默认为0,字符型默认为‘’,指针型的是NULL。

      如:struct student b ={.name = “zhang fang”,.sex=’m’};

    只能对最低级的成员进行赋值或者存取以及运算。

        Student1.num  错误Student1.birthday.month正确

    5)   结构体数组

    {成员列表} 数组名[ 数组长度]

      struct Person leader[3];

 

    6)   结构体指针

      1)   指向结构体变量的指针。

        跟我们普通的定义和理解是一样的

        struct student stu_1;//struct student类型的变量stu——1

        struct student *p;//指向struct student类型数据的指针变量p

        p=&stu_1;//p指向stu_1;

        stu.成员名字 如stu.num

         (*p).成员名   如(*p)student

            p->成员名    如p->num;

      2)   指向结构体数组的指针

        例子: 有三个学生信息,放在结构体数组中,要求输出全部学生的信息。

    #include<>

      struct student *p;

      for(P=stu;p<stu+3;p++)

        {}

    注意:

               1. P的初值是stu,p指向stu的第一个元素,p加1后,p就指向下一个元素。

       (++p)->num       p+1        , 然后得到p,指向的元素中的num成员(即10102)

       (p++)->num        p->num 的值,然后得到p+1,指向stu[1]

      2. p是指向stustudent类型的指针,不是指向数组元素的某一个成员,如p=stu[1].name错误

        如果是指向某一个成员的地址给p,需要强制转换。p=(struct student *)stu[0].name

24. 共同体

    1)  union 共用体名字

      {
        成员表列;
      }  变量表列;

Union date

{  int i;Char ch;Float f;

}a,b,c;

 

Union Date

{  int i;Char ch;Float f;

};

Union Data a,b,c;

    2)   共同的区间相同的地址----覆盖---起作用的事最后一次赋值的

     3)  不能对共用体变量名赋值,也不能企图引用变量名来得到一个值。

    例如: a=1;  m=a;都是不对的。但是允许共用体变量之间相互赋值

    4)   其他的用法跟结构体一样

25.枚举类型

定义

 

  1. 一个变量可能有多个可能值。

声明:enum【枚举名】{枚举元素列表}

enum Weekday {sun,mon,tue,thu,fri,sat};

定义变量:enum Weekday workday,weekend;

但是枚举变量workday,weekend只能是枚举元素中的一个值

  1. 也可以不声明有名字的枚举类型,而直接定义枚举变量。

enum{sun,mon,tue,wed,thu,fri,sat}  workday,weekend;

注意

1)   枚举的元素是常量,不能做变量,不能复制。

2)   每一个枚举元素都代表一个整数。默认为sun,mon,tue,wed,thu,fri,sat

0 ,1 ,2,3,4,5,6

如果赋值语句workday=mon;  相当于woekday=1;  printf(“%d”,workday);

也可以认为的指定枚举元素的数值,在定义枚举类型时显式的指定。

enum weekday {sun=7,mon=1,tur,wed,thu,fri,sat}weekday,week_end;

sun是7,mon是1,之后的都加1.

由于枚举变量的值是整数,所以把枚举数据作为整型数据的一种,即用户自行定义的整数类型。

3)   枚举元素可以用来判断比较

If(workday==mon)…

If (workday>sun)…

枚举比较是按指定的整数(或者默认规则来处理的)数据来比较的。

    

例子

口袋有三个红黄蓝白黑5中颜色的球若干个。每次从口袋中先后取出三个球,问得到3种不同颜色的球的可能取法,输出每种排列的情况。

 

 

 

26.typedef  old   new;

  typedef  int   haha;

  typedef student{结构体} haha;

  typedef int NUM[100];//声明NUM为整形数组类型名

  typedef char *  string;           //声明string为字符指针类型

  string  p,a[10]; //p字符指针,a[10]char指针数组

  typedef  int(*pointer)();//pointer指向函数的指针类型,返回值为整数值。

               pointer p1,p2;//pw为pointer型指针

27.文件

文件

1)        文件名:   路径    文件名主干  .   文件后缀

2)        文件的分类:ASCII 文件和二进制文件

3)        文件缓冲区:(高级磁盘io)标准IO:(全缓存行缓存不带缓存),文件io

4)        文件类型指针 FILE *fp;

文件指针变量不是指向外部介质上的数据文件的开头,而是指向内存中文件信息区的开头。

打开、关闭文件

fopen()     fclose()

r    rb

打开只读,文件存在

r+   r+b

打开可读写,文件存在

w   wb

打开只写文件,若存在则文件长度清为0;擦拭些以前文件的内容。

不存在,则建立该文件

w+  w+b

打开可读写文件,若存在则文件长度清为0;擦拭些以前文件的内容。

不存在,则建立该文件

a   ab

以附加方式打开只写文件,

若文件不存在,建立文件

若文件存在;写入的数据正在文件尾,即文件原先的内容被保留

a+   a+b     ab+

以附加方式打开可读写文件,

若文件不存在,建立文件

若文件存在;写入的数据正在文件尾,即文件原先的内容被保留

Umask改变权限

 

顺序读写数据文件

字符

fgetc(fp)

从fp指向的文件中读入一个字符

读成功,返回字符

失败,返回EOF,(即-1)

fputc(fp)

把字符ch写到文件指针变量fp指向的文件中

输出成功,返回值是输入的字符。

输出失败,则返回EOF(即-1)

 

字符串

fgets(str,n,fp)

从fp指向的文件中读入一个长度(n-1)字符串,放在字符数组str中。

读成功,返回地址str

失败,返回NULL

注意,它包含了‘’

区别:

fgets 包含’n’

gets 不包含”n”

fputs(str,n,fp)

把str所指向的字符串写到文件指针变量fp指向的文件中

输出成功,返回0。

输出失败,则返回非0值EOF(即-1)。

 

格式化的方式读写文件

 

fprintf(文件指针,格式字符串,输出列表);

fprintf(fp,’’%d,%6.2f’’,I,f);

fscanf(文件指针,格式字符串,输入列表);

fscanf(fp,”%d%f”,&i,&f);

比较方便、容易理解,但由于输入时将字符与二进制保存,输出时是反向的,所以是要比较多的花时间。

 

二进制行形式向文件读写一组数据

fread(buffer,size,count,fp);

fread(&stud[i],sizeof(struct Student_type),1,fp);

返回:读取对象数

fwrite(buffer,size,count,fp)

fwrite(&stud[i],sizeof(struct Student_type),1,fp);

buffer:地址

size:要读写的字节数

count:要读写多少个数据项(每个数据项有size个字节)

fp:FILE类型指针

 

 

随机读写数据文件

文件位置标示及其定位

文件位置标记:

rewind函数

使文件的位置标记重新返回文件的开头,因此函数没有返回值。

fseek函数

fseek(文件类型指针,位移量,起始点)

fseek(fp,100L,0)

起始点:

文件开始位置

SEEK_SET

0

当前

SEEK_CUR

1

末尾

SEEK_END

2

 

ftell函数

测定文件位置标记的当前位置

i=ftell(fp);

 

文件读写的出错检测

ferror(fp)函数

注意:

对同一个文件每一次进行输入输出函数式,都会产生一个新的ferror函数值。因此在调用一个输入输出函数后立即检查ferror函数的值,否则信息丢失。

在执行ferror函数时,ferror函数的初始值自动置0

clearerr函数

使文件错误标志和文件结束标志置为0

在出错误时,ferror函数值为一个非零数,,应该立即调用clearer(fp),使ferror(fp)的值为0;以便再进行下一次检验。

只要出现文件读写错误标志,他就一直保留,直到对同一个文件调用clearerr函数或者rewind函数,或者任何一个其他输入输出函数。

 

练习

从键盘中读入若干个字符串,对他们按字符大小排序,然后把排好顺序的字符送到磁盘文件中保存。

练习2

键盘中输入10个学生的有关数据,然后把他们转存到磁盘文件上去。

 

28.printf(“%s”,p);

 

  • 首页
  • 电话
  • 软件