《C和指针》笔记

#include<stdio.h>的意思就是讲预处理器用名叫stdio.h的库函数头文件的内容替换#include指令语句

这个整条语句叫做预处理指令  stdio.h才叫头文件

C语言中数组参数是以引用(reference)形式进行传递的,也就是传地址

1puts()函数和printf()函数输出字符串的区别:

puts()函数,输出一串字符串,并且会在末尾进行换行操作

使用printf()输出字符串就不会进行换行

2、特殊字符表示:

三字母词:几个字符的序列,表示另外一个字符

例如:??(  表示一个[字符   ??)表示 ]

             ??<  表示一个{      ??>表示}

有的编译环境也许会不处理三字母词,vs2017就不处理

转义字符:

如果在某个地方想要使用某个特定字符,但是这个字符在这里有其他的意义,导致不可以使用该字符,这是就应该使用转义字符,将特定字符作为单个字符来处理,

\”   表示” 

\\   表示\

\r   表示回车

\t   表示制表符,一个制表符代表8个空格

\’   表示

\b  表示退格键(backspace

3#if  0

   .........

   #endif      也可以进行注释代码,功能要比/**/强大的多

4、注释/**/里面不可以有单独的  */必须是成对出现的

5、长整形和整形以及短整形:

长整形并不一定比整形大,短整形也不一定比整形小

规定:长整形至少应该和整形一样长,短整形最大和整形一样大

Char 0------127

Sign char  -127----127

Unsigned char  0------255

Short int -32767------32767

int     -32767-------32767

Unsigned short int 0-------65535

Long int

 

6、枚举类型:

enum{ cup,yellow ,blue,black

}name;

大括号中的单词分别代表的是0 1 2 3

声明为枚举类型的变量实际上是数类型, enum name; 你实际上可以给name赋值为数字都可以

另外枚举类型的变量的大小都是4字节

7、浮点类型:

       Double 类型至少和float类型一样长

所有的浮点类型至少能够容纳从10^-3710^37之间的任何值

8、指针:

int *a;

变量名是a 类型是int*

int* a;int *a;

int* a,b,c; 写成这样很容易误认为b,c也是指针变量,实际上不是的

定义指针变量最好定义成 int  *a;

Char *pArr=张三;将字符串常量赋值给char*类型的指针变量pArr

9、typedef

     Typedef    char*  ptr_to_char;

ptr_to_char   a;  a是一个指向char类型的指针变量

使用typedef #define 的区别:

使用#define 重命名指针类型是无效的,typedef是可以的

例如:

#define    d_ptr_to_char  char*

Typedef char *ptr_to_char

声明变量:

d_ptr_to_char   a,b; 这样执行之后a被成功声明成了指针类型的变量,但是b仍然是字符变量

ptr_to_char a,b;这样执行之后a,b两个 变量全是指针变量

 

10、常量:

   Const 声明的常量样子和变量一样,只是他们的值是不允许修改的

常量的声明:

int    const a;

const  int     a;  两种方式是一样的

那么既然常量的值是不可以修改的,那么就需要在声明是直接初始化

或者作为函数中形参的值,由实参进行赋值

指针和const 混合使用:

只需要记住const修饰的是其后面的变量,就像下面的例子:

int const *a;  也可以写成 const int *a;    //因为*a是被const修饰的,所以*a是一个常量,也就是说指针所指向的值是个常量

Int *const a;  不可以写成const *int a;      //因为a是被const修饰的,所以a是个常量,又因为a是一个指针,所以指针是个常量

Int const *const a; //这种情况,因为后面的const修饰的a,但是因为a是一个指针,所以指针是个常量,又因为前面的const修饰的是*a,所以*a是个常量,也就是指针所指向的值十个常量,所以这种用法是:指针和指针所指向的值都是常量


11、作用域:

四种不同类型的作用域:

1文件作用域

2函数作用域

3代码块作用域

4原型作用域

12、c语言内存四区:

内存四区模型:
操作系统给C/C++编写的程序分配内存,通常将分配的内存划分为以下四个区域:
1.栈区:存放局部变量,用完由操作系统自动释放
2.堆区:动态分配给程序的内存区域,由程序员手动释放
3.数据区:
分为两种:
1)常量区:存放常量,一般是字符串常量
2)全局区/静态区:存放全局变量和静态变量
4.代码区:
存放可执行代码的区域

13:goto语句:

 

14getchar()返回的是int类型的值

 

15、逗号操作符:


16、指针:

每个计算机的内存有数以亿计的位组成,每个位可以容纳01,但是由于每个位所能表示的值的范围太有限,所以由几个位组成一个单位,来进行容纳范围更大的值

间接访问操作符*

具有从右往左结合的结合性

指针的指针:

int   a  =  12;

int  *b  =  &a;

int  **c =   &b;

只要从右往左结合性一步步看就很好理解

A  相当于  12

b          &a

*b         a,12  

C          &b

*c         *b, b,&a

**c        *b , a,12

指针的类型:去掉变量名剩下的就是指针的类型 int*

指针指向的类型: 去掉变量名和*剩下的就是   int

*的运算级别高于+的运算级别,但是小于++的运算级别

char str[10] = "ABCDE";

char *p = str;

               单独执行下面的一个语句:

printf("*p=%c\n",*p);       结果是A

printf("*p++=%c\n",*p++);   结果也是A

printf("*++p=%c\n", *++p);   B     

printf("++*p=%c\n", ++*p);   B  

需要注意的是++p得到的是p后面的那一个数的地址,不是仅仅只用p的地址加1,而是p的地址加上 类型所占的字节数*1

也就是说++p   《===》   p+(sizeof(int)*1)

如果p的地址是      10011

那么++p的地址就是   10015

17、 如何在函数中使用可变参数?

头文件stdarg.h   va_list类型的变量 访问可变参数

                 Va_startva_arg   va_end

 

18、 数组

int b[10] ,b[0]int类型的  b是什么类型呢?因为数组名表示数组的地址,所以在这里b就是指向int类型的指针常量,注意不是指针变量,所以你无法修改b的值,因为数组的地址是一定的,因此数组名的值是一个指针常量

所以不可以使用a++。必须使用a+1

int a[10];

Int *b=a;int *b=&a[0],有区别吗?

回答:因为数组名表示的就是数组的首个元素的地址,所以上面的两条赋值语句是完全一样的

除了优先级之外,下标引用和间接访问是完全一样的

数组中看着很奇怪但是是正确的语句:

5[a]     是和    a[5]  完全一样的

因为a[5]  如果使用间接访问访问的写法就是  *a+5)那么和 *(5+a)不是一样的嘛

所以也是可以写成  5[a] 是完全没有问题的

指针与下标的运行效率比较:

多维数组数组名表示含义:

Int a[5][10];

数组名a可以看做是一个一维数组,它包含五个元素,只是每个元素又包含十个整形元素

因为a是指向数组的第一个元素,也就是数组首元素的地址,所以a是一个指向一个包含十个整形元素的数组的指针,简单说a就是数组指针

例如int a[3][5]

数组名a指向第一行   是数组指针

a+1指向第二行

a+2 指向第三行

*a表示 a[0]这个指向5个元素的一维数组   是数组

*(a+1) 表示 a[1]  是个数组

*(a+2) 表示 a[2]  是个数组

使用间接访问表示a[2][4]这个元素 *(*(a+2)+4)

其他语言允许写成a[3,5]

但是在C语言中标a[3,5]仅仅是表示a[5] ,如果没有越界的话还好

指向数组的指针:

例如

int a[5], *p=a;

int a[3][5], *p=a;

第一行语句是合法的,p是一个指向一维数组的指针

但是第二句就是非法的,因为 a本身就是一个指向数组的指针,将一个数组指针赋给一个指针,是不可以的 除非p也是一个数组指针  可以这样写 int (*p)[5] =a; 下标的优先级高于间接访问所以要加括号    

作为函数参数的多维数组;

Int a[5];

Fun(a);

这样的一维数组 函数原型可以是一下这样的

Void funint a[]

       或者 void fun(int * a)   因为一维数组名只是首元素的地址,所以形参使用一个指针变量即可

那么对于这样的二维数组呢?

Int a[3][5];

Fun(a);

函数原型可以是一下两种:

 Void fun(int a[][5])

或者 void fun(int (*p)[5])  

这是因为二维数组名,表示的是一个数组指针,所以形参必须也是数组指针

那么为什么需要第二个数字5,而不是第一个数字3呢?因为编译器必须知道第二个及以后各维的长度,才能对各下标进行求值

对于多维数组如何初始化:

   二维数组:

int b[3][5] =

{

    {1,2,3,4,5},

    {1,1,1,1,1},

    {1,1,1,1,1},

};

   三维数组:

int a[2][3][4] =

{

    {

        {1,1,1,1},

        {1,1,1,1},

        {1,1,1,1},

    },

    {

        {2,2,2,2},

        {3,3,3,3},

        {4,4,4,4},

    },

};

四维数组:

对于多维数组初始化时从前面的中括号往后面看的

数组指针:

          表示是一个指针,指向的是一个数组的地址

     int (*p)[5]

指针数组:

  表示是一个数组,只是数组的元素存放的是地址

除了类型之外,指针变量和普通变量很相似,只是普通变量是存放的数字,指针变量是存放的变量的地址罢了,

那么有数组,当然也可以有指针数组,数组元素中是存放的地址罢了

int * p[10];  p是一个int* 类型的指针数组,可以存放十个整形变量的地址

一维数组名取地址表示什么?

int a[5];

&a和a 和&a[0] 的值都是一样的,但是&a的类型是 int (*)[5] 是一个数组指针

a+1表示 数组的首地址加上 1*sizeof(int)

然而 &a+1表示 数组的首地址加上 1*sizeof(a) ,也就是加上的一个数组的占的所有的字节长度

 

int a[4][5]; 假设数组的首地址为1000的话

那么  a=1000

A+1=1020

A[1]=1020

*(a+1) =1020

A[1]-1=1016

指针函数与函数指针:

返回值是某一种指针的函数叫做指针函数

指针指向函数的地址叫做函数指针、

函数指针除了将函数名用函数指针代替,其他和函数一样

函数指针的定义和初始化:和函数原型一样

Int min(int x,iny y);  函数原型

Int (*p) (int x,int y);

函数指针:

 

字符串:

要注意strlen()函数返回的是无符号整形类型的值

第十章:结构和联合:

数组是相同元素的集合

结构也是一些值的集合,这些值称为他的成员

结构的声明:

Struct  tag

{

member-list

} variable-list;  

下标引用和点操作符的优先级是一样的,他们的结合性都是从左往右

->优先级高于*也高于 &

++高于*  但是+小于*

动态申请内存:

Malloc() 成功返回的是void*类型的指针,失败返回NULL(相当于0

申请的是一块连续的内存

Int *p=malloc(25*sizeof(int));   //申请100字节的内存(假设int类型占4个字节)

初始化:

forint i=0;i<25;i++

 *p++=0;

或者

for(int i=0;i<25;i++)

  P[i]=0;

Free() 释放内存函数,必须是释放一整块,不能是一部分