c语言深度 笔记

一、关键字

int m; 定义创建了对象,并为该对象分配了内存

extern int m;  声明内存,告诉编译器,这个名字已经被预订了

数据从内存拿出来后,先放在寄存器中,然后CPU再从寄存器中读取数据,进行处理。

寄存器是一块块小的存储空间,读取速度比内存快得多。(更贵)

1.1  static

1)  static 修饰变量,作用域仅限于变量被定义的文件中,从定义处开始,到文件结束

               其他文件 即使用extern也不能使用该变量

Eg:static int j;

void fun1(void)

{

         staticint i=0;

         i++;

         printf("i=%d\n",i);

}

void fun2(void)

{

         j=0;

         j++;

         printf("j=%d\n",j);

}

int main()

{

         intk=0;

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

         {

                   fun1();

                   fun2();

         }

         return0;

}

运行完后,i=10; j=1;

全局变量,静态变量,如果不赋值,编译器给他赋上0.

如果定义了值,就在编译的时候就确定下来了

http://blog.sina.com.cn/s/blog_615ec1630102uwxk.html

2)static 修饰函数  不指存储方式

                 而是函数的作用域仅仅在本文件(???内部函数)

1.2  数据类型

32位系统,

char   1 byte(字节)       

short  2 bytes

int    4 bytes(unsigned or signed)

long   4 bytes

float   4 bytes

double 2 bytes

1.3  sizeof

int i=0;

(A)sizeof(int)  (B)sizeof(i)   (C)sizeof int   (D)sizeof  i

A,B,D 正确,为4   C错误

Sizeof 计算变量所占的内存空间时,括号可以省略

     计算类型时,必须加括号

Eg:

         int*p=NULL;

         sizeof(p);       //4

         sizeof(*p);      //4

         inta[100];

         sizeof(a);       //400 a数组占空间

         sizeof(a[100]);   //4  a数组中第100个内存地址

         sizeof(&a);      //4

         sizeof(&a[0]);    //4

         intb[100];

void fun(int b[100])  //传递是数组第一个元素的地址  等同于void fun(int b[])

{

   sizeof(b);

}

1.4  signed、unsigned 数据范围

编辑器默认是signed 类型

unsigned char 0~(2^8-1);

signed char: -2^7 ~ 2^7-1;

          0 1 2 … 127 -128  -127  -126

Eg:

1、char c=128;

        Printf(“c=%d”,c);//-128

    2、  int i;

              for(i=9;i>=0;i--)

                    printf("%d   ",i); //输出9 8 7 6 5 4 3 2 1 0

3、上面程序i的定义改为

     Signed int I;  变成了死循环,无符号值均大于0

在用i作为循环次数时,一定要用signed int 类型

 

1.5  零值比较--BOOL,int,float,指针变量与零值比较的if语句

BOOL flag 与“零值”比较

    正确   if( flag )
if ( !flag )

错误   if(flag == TRUE) 
if (flag == 1 ) 
if (flag == FALSE) 
if (flag == 0) 

TRUE 的值究竟是什么并没有统一的标准。例如Visual C++ 将TRUE 定义为1,而Visual Basic 则将TRUE 定义为-1。所以我们不可以将布尔变量直接与TRUE、FALSE 或者1、0 进行比较。

float x 与“零值”比较

正确  const float EPSINON= 0.00001;
if ((x >= - EPSINON) && (x <=EPSINON)

错误  if (x == 0.0) 

 整型变量与零值比较

     正确   if (value == 0)

if(value != 0)

     错误 if (value) // 会让人误解 value是布尔变量

指针变量与零值比较

            int *p=NULL;

     正确   if(NULL==p)

            If(NULL!=p);  

1.6   switch ,case

switch(variable)  //variable只能是 整形、字符型常量,或者常量表达式

{

 case  value1: // program code

               break;

case  value2: // program code

               break;

default: break;

}

Case使用顺序: 按字母或数字顺序

               正常放前面,异常放后面

               按执行频率排序

               简化case语句操作

1.7  break、continue

break  终止本层循环,该层循环体内的语句不执行

continue:终止本次循环,该次循环体内 后面不执行

 

1.8  void

void * 空类型,任何类型的变量可以对它赋值,反之no

eg:

1、  float *p1;

      Int *p2;

      P1=p2; //错误,类型不匹配

      P1=(float *)P2;

2、  void *p1;

      Int *p2;

      P1=p2; //ok

      p2= p1;//no 空类型无法赋值

不加返回值类型限定的函数,就会被编辑器作为返回整形值。与void不同

1.9  return

 终止一个函数,并且返回其后面跟的值。

               没有返回值也可以,此时return返回的是一个不确定的值

1.10        const 只读变量

1)  const 修饰的只读变量必须在定义同时初始化

      声明时,需要与原变量保持一致,不能重新定义值

      不可以用在case后面  (case expression notconstant)

      eg: const int Max=100; //right

          extern const int j=10;  //error

2) const  在程序运行过程中只有一份备份(因为它是全局的只读变量,放在静态去)

          编译时候,确定值

          修饰变量有特定类型

 define  宏在内存中有若干个备份

         预编译阶段进行替换

         没有类型

eg:

define M 3      //宏常量

    const int N=5;    //此时未将N放入内存中

    int i=N;          //为N分配内存,以后不再分配

    int I=M;         //预编译期间进行宏替换,分配内存

    int j=N          //未分配内存

    int j=M;         //宏替换,分配内存

3) 修饰指针(将类型名忽略)

const int *p ;    //p可变,p指向的对象不可变

int  const *p;   //p可变,p指向的对象不可变

int * const p;    //p不可变,p指向的对象可变

const  int * const p; //p和p指向的对象都不可变

1.11        volatile

定义:修饰的变量可以被编译器未知的因素更改,如操作系统,硬件或者其他线程等。

     编译器不进行代码优化,对特殊地址的稳定访问。

Eg: int j=10;

     Inti=j; //(1)

     Intk=j; //(2)

(1)语句之后i值没有丢掉,在(2)语句中继续用,而不从内存中取i的值

若改为 volatileint j=10; 每次使用时,汇编代码会从j的地址取值

1.12        struct关键字

可以将相关联的数据打包成一个整体

    在C++中struct成员默认是public,class默认是private

1) 空结构体的大小

struct student

{

}stu;

Sizeof(stu)=1;

2) 柔性数组

结构体中包含一个大小可变的数组

   Eg: typedefstruct st_type

        {

            int i;

            int a[];

}type_a;

sizeof(type_a)=4;    //不包括柔性数组内存

type_a *p=( type_a)malloc(sizeof(type_a)+100*sizeof(int) );

此时sizeof(type_a)=4;  //柔性数组 算不得结构体的 正式成员

3) union 联合体

union所有数据成员具有相同的起始地址,同一时间只能用一个数据成员

如何用程序确认当前系统的储存模式(大小端)

       Int checksystem()

{

  union check

{

  int I;

  char ch;

}c;

c.i=1;

return (c.ch==1);

}

        返回为1,小端模式。(数据低字节放在低地址,高字节放在高地址)

        返回为0,大端模式。(高字节放在低地址)

        注意内存的增长是从低到高,大端先放高字节,小端反之。

1.13        enum关键字

枚举常量,一般大写

如果有一个赋值,后面不赋值变量从赋值变量后 一次加1

如果都不复制,从0开始递增

         Eg:  enum Color

        {

                      GREEN=1      //1

                      RED           //2

                      BLUE          //3

                      GREEN_RED=10,

                      GREEN_BLUE      //11

              }ColorVal;

        sizeof(ColorVal)=4;  //????? Enum 默认整形

1.14        typedef

1)  给一个已经存在的数据类型取一个别名,而非定义一个新的数据类型

  Eg:  typedef struct  student

      {

        //

}Stu_st, *Stu_pst;

(a)    struct  student stu1 和 Stu_st stu1; 相同

(b)    struct  student * stu2和Stu_st * stu2和Stu_pst  stu2 相同

(c)    const  Stu_pst stu3;

(d)    Stu_pst  const stu4;

Const修饰stu3,stu4,这两个不能变

将Stu_pst作为一种新的类型,const修饰时,将数据类型去掉

2)  Typedef与define区别

(e)#define INT32 int

   Unsigned  INT32 i=10;

//right 预编译的时候 已经替换

(F)typedef int INT32

   Unsigned int32 j=10;

   //typedef定义了一种类型,不支持类型扩展。(unsigned与int结合???)

(G)#definePCHAR char*

PCHAR p3,p4;  //p4不是指针,仅仅是字符变量

(H)  typedef char * pchar;

pchar p1,p2;  // p1, p2均是字符指针

二、符号

2.1 注释符号

(a) int/*….*/i;           //编译器会将注释的内容 用空格代替,变成int  i ;

(b) char * s=”abcd       //hijkl\\m”;//其中//是字符串的内容,\\表示‘\’

(c) //Is it a \

Valid comment?          // \ 连接符,后面不能有空格,下一行开头也不能有空格

(d) in/*…*/t i ;           //error

(e) /*this is /* illegal*/ */  //注释不能嵌套

(f)y=x/*p;           //error /*被认为是注释,直到出现*/,

     Y=x/  *p;  or  y=x/(*p);  //right / *有空格就不是注释

2.2 ‘’,“”

‘a’字符常量,1字节

“a”字符串常量,2个字节(\0结束)

2.3 运输符

1)&& 、|| 逻辑运算符

   inti=0;

  int j=0;

  for( (++i>0) || (++j>0) )

{

  Printf(“%d  %d”,i,j); //i=1,j=0;

}

2) << 、>>

0x01<<2+3;  //算术运算级的优先级 高于 逻辑优先级,结果为32

3)++、--

Eg:int x;

 int i=3;

 x=(++i,i++,i+10);

 printf("%d  %d",i,x);   //x,遇到逗号,任务本计算单位已经结束。

3)  for循环的顺序  1,2,程序;3,2,程序

for(1;2;3)

{

   程序

}

三、预处理

3.1 文件包含

1)include<stdio.h>尖括号,预处理到系统路径中获取文件(C编译系统提供的并存放在指定子目录下的头文件)

2)include “filename”  双引号,在当前目录中查找该文件。

3.2 内存对齐(如下图)

3.3  #

# 可以将语言符号转换为字符串

3.4 ## 粘合剂

#define XNAME(n)  x##n

XNAME(8);

展开为:x8

四、指针、数组

4.1 指针

1)指针的定义

正确:    int *p=NULL;

//等价于int *p; p=NULL;  是把p的值设为0x00000000(NULL被宏定义为0)

错误: int *p;

*p=NULL; //p指向内存中保存的是int类型数据,p保存的可能是一个非法地址

2)将数值存储在指定内存处

往内存地址0x12ff7c上存入一个整型数0x100:

方法一、int *p=(int *)0x12ff7c; //将0x12ff7c强制转换为int型指针,赋给指针变量p

        *p=0x100;

方法二、*((int *)0x12ff7c)=0x100;

???0x12ff7c而不是其他地址,可以通过定义变量int i,查找i的地址

3)指针访问

Eg: char *p=“abcdef”;

     指针形式访问:*(p+4)

     数组形式访问:p[4]  //取出p里存储的地址,再加上4个偏移元素个数,不是字节数

4.2 数组

4.2.1  定义  

内存上连续存储

Eg:int a[5];

sizeof(a) 值为sizeof(int) *5,32位系统下为20

sizeof(a[0]) 值为sizeof(int)=5;

sizeof(a[5]) 值为32系统下为4,sizeof关键字是在编译的时候运行,不是运行,不去真正访问a[5]。

4.2.2数组访问

Eg: char a[] =”123456”;

 *(a+4),a[4]均可以访问。

4.2.3 a 与&a 区别

地址的强制转换

       指针变量 +- 整数:指针变量的地址加减元素个数,而不是字节数

       Eg:

           struct Test

{

         int Num;

                     char *pcName;

         short sDate;

                     char cha[2];

                     short sBa[4];

}*p;

假设p值为0x100 000,求下面表达式的值

p+0x1=???    //p+0x1*(sizeof(p))=p+20=0x100014(转化为16进制数)

(unsigned long)p+0x1=??? //p+(unsigned long)0x1= 0x100 001;

(unsigned int *)p+0x1=??? //p+(unsigned int *)0x01=p+4=0x100 004;

Eg:

1、

    voidmain()

{

         inta[5]={1,2,3,4,5};

         int*ptr1=(int *)(&a+1);

int *ptr2=(int *)(a+1);      //输出5,2

//int *ptr2=(int *)((int)a+1); //????输出5,2000 000

         printf("%x,%x",ptr1[-1],*ptr2);

}

a 数组首元素的首地址,a+1是数组下一元素

&a数组的首地址,&a+1是下一数组的首地址

2、

void main()

{

char a[5]={'A','B','C','D'};

  char (*p3)[5]=&a;

char (*p4)[5]=a; // warning C4048: different arraysubscripts : 'char (*)[10]' and 'char (*)[5]'

 

         char (*p3)[3]=&a;

         char (*p4)[3]=a;//warning

 

 

    char (*p3)[10]=&a;

         char (*p4)[10]=a;//warning

         printf("%x,%x",p3+1,p4+1);

}

a 的地址为0x0012ff78,p3,p4 也指向该地址。

4.3 指针数组、数组指针

定义

指针数组:首先是个数组,其次数组中的元素都是指针

                Eg: int*p1[10];   // [] 优先级比* 高

数组指针:首先是个指针,它指向数组。

           Eg:  int (*p1)[10];  //()优先级比[] 高

4.4 多维数组和指针

二维数组的定义

    1) inta[3][2]={ {0,1},{2.3},{4.5} };  //中的一维数组也是{ }

           若定义为这样呢?

          int a[3][2]={ (0.1),(2,3),(4,5) }; //小括号从左往右依次计算,取最右侧的值

           此时 a[0]=1;

2)第一个参数可省,第二个不可省(否则不知道一行有几个,无法确定)

Eg: a[][4] ok;  a[4][] error

&p[4][2]-&a[4][2]值

Eg:int a[5][5];

    Int  (*p)[4]

    P=a;

    问&p[4][2]-&a[4][2]值为多少?

 

P是指向一个包含4个元素的数组的指针,即p+1表示指针向后移动了一个“包含4个int类型元素的数组”。

4.5 无法向函数传递数组

Eg:

      void fun1(char a[])  //ok

{

              char c=a[3];

              printf("%c",c);

}

void fun2(char a[10])  //实际传递数组的大小 与函数形参数组大小 没有关系

{

              char c=a[3];

    printf("%c",c);

}

void fun3(char *p)   //ok

{

char c=p[3];

               printf("%c",c);

}

int main()

{

              char b[100]="abcdef";

    fun1(b);

              fun2(b);

              fun3(b);

              return 0;

}

当一维数组作为函数的参数,编译器总是把它解析成 指向该数组首元素首地址 的指针。

所有非数组形式的数据实参 均以传值形式(对实参做备份,传递给被调用函数,不能修改实参的值)。

main 函数内的变量 不是全局变量,而是局部变量,只不过它生命周期和全局变量(定义在函数外面)一样。

4.6 函数指针

Char * (* fun1)(char *p1,char *p2); //fun1不是函数名,而是指向函数 的指针

五、内存管理

5.1 如何避免野指针

指针

定义时,最好初始化为NULL。

eg:char * p=NULL;(NULL是地址为0x0处,核心系统位置,此时指针内容不允许修改,                     

    报错)

5.2 栈、堆和静态区

堆     定义:malloc系列函数 或new操作符分配内存,由free或delete结束,释放内存。

          在没有释放前一直存在,直到程序结束。

特点:使用灵活,空间较大,但是容易出错(野指针)。

Eg:

free(p); //指针p保存的地址没有改变,被释放的内存中值也没变,但是p无法使用该内存了

p=NULL; //p 指向地址为0

 

栈     定义:保存局部变量。栈上内容只在函数范围内存在,函数运行结束,自动销毁。

特点:效率高,但空间大小有限。

 

静态区保存全局变量和static变量(包括static全局和局部变量)

      由编译器在编译的时候分配空间,在整个程序的都存在。

5.3 常见的内存错误及策略

5.3.1 指针指向合法内存

struct student
{
   char *name;
   int score;
}stu,*pstu;

void main()
{
 
  //指针赋值,大小要合适
  char *p1="abcdefg";
  char *p2=(char *)malloc(sizeof(char)*(strlen(p1)+1));
  strcpy(p2,p1);
  printf("%s ",p2);*/

  pstu=(struct student *)malloc(sizeof(struct student));//结构体指针分配内存空间
  //strcpy(pstu->name,"Jimy");//error 结构体变量 没有分配空间。编译无误,运行错误

  pstu->name=(char *)malloc(10*sizeof(char));
  strcpy(pstu->name,"Jimy");//right 

  pstu->score=99;
  printf("%s  %d",pstu->name,pstu->score);
  free(pstu);
  return 0;
}

六、函数

1)fun() 不要省略

   若函数没有返回值,应声明为void类型

   若无返回值,函数默认返回为int 类型

2)  return 不可返回函数内部变量

char *Func(void)

{

  char str[30];

  ...

  return str;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值