Pointers on C(第八章 第九章 第十章 第十一章第十二章)

    技术2022-05-19  24

    第八章 数组

     

    1、数组名是一个指针常量,也就是数组第一个元素的地址,而不是指针变量。

     

    2、sizeof 数组名:返回整个数组的长度,而不是指向数组的指针的长度。

     

       sizeof string:返回的是指向字符的指针的长度,而不是数组的长度。

     

    3、不能使用赋值符把一个数组的所有元素赋值到另一个数组。

       如:int a[10];       int b[10];       b=a; //错误

     

    4、一个神秘和离题的例子:

       2[array] 是合法的。相当于array[2].

     

    5、函数原型中的一维数组形参无需写明它的元素数目,因为函数并不为数组参数分配内存空间。

       在多维数组中,只有一维数组才能根据初始化列表缺省地提供,剩余的几维必须显示地写出,这样编译器就能推断出每个子数组维数的长度

     

    6、不完整的初始化,只允许省略最后几个初始值。

       如:int vector[4]={1,5};

     

    7、int d[3][6][10];

       d是一个3排6列10行的整型三维数组。

       多维数组的元素存储顺序按照最右边的下标率先变化的原则,---行主序。

       可以把d看成1个1维数组,有3个元素,每个数组元素是又一个数组,含有6个元素,这每个元素又是一个数组,含有   10个元素。

     

    8、int matrix[3][10];

     

       1)、matirx[1][5]

     

       2)、matirx         它的类型是指向包含10个整型元素的数组的指针。指向包含10个整型元素的第一个子数组。

     

       3)、matrix+1       指向包含10个整型元素的第二个子数组。

     

       4)、*(matrix+1)       类型:指向整型的指针。       相当于matrix[1]      5)、*(matrix+1)+5       类型:指向整型值的指针       相当于&matrix[1][5]

     

       6)、*(*(matrix+1)+5)==matrix[1][5];

     

    9、指向数组的指针

       int matrix[3][10];   int (*p)[10]=matrix;        //p指向matrix的第一行      p是一个指向(拥有10个元素整型元素的数组)的指针。

       警告: int (*p)[]=matrix;          p仍然是指向一个整型数组的指针,但是数组长度却不见了。当某个整数与种类型的指针执行指针运算时,          它的值将根据空数组的长度进行调整,也就是说与0相乘。结果可能不是你所设想的。

     

    10、指针数组

     

        int *p[10];

        p是一个数组,含有10个元素,每个元素是指向整型值的指针。

     

    11、只要有可能,函数的指针形参都应该声明为const。

     

    12、在有些环境中,使用register关键字提高程序的运行效率。

     

    13、在多维数组的初始值列表中使用完整的多层花括号能提高可读性。

     

    第九章  字符串、字符和字节

     

    1、NULL字节是字符串的终止符,但它本身不是字符串的一部分,所以字符串的长度并不包括NULL字节。

       字符串的长度就是它所包含的字符数。

     

    2、size_t  strlen(char const *string);

       size_t是一个无符号整数类型。

     

       看下面两个表达式:   if(strlen(x)>=strlen(y)) ...   if(strlen(x)-strlen(y)>=0)...

       看似相等却不等!

       第一条语句会如你想象的那样运行。

       但是第二条语句将永远为真:因为无符号数-无符号数,结果永远都是无符号数,无符号数是不可能为负的

     

    3、警告:表达式中如果同时出现了符号数和无符号数,可能会产生奇怪的结果。   如:      if(strlen(x)>=10)...      if(strlen(x)-10>=0)...

       如果把strlen的返回值强制转换成int,就可以消除这个问题。

     

    4、strncpy 调用的结果可能不是一个字符串,因此它的结果不会以NULL字节结尾。

     

    5、提示:直接测试或操纵字符会降低程序的可移植性。

       例如:      if(ch>='A'&&ch<='Z')

       这条语句在使用ASCII字符集的机器上能够运行,但是在使用EBCDIC字符集的机器上将会失败 。

       下面这条语句:   if(isupper(ch))

       无论机器使用哪个字符集,它都能顺利运行。

     

    6、任何类型的指针都可以转换成void*类型的指针。

     

     

     

    第十章、结构和联合

    1、  struct   {    int a;    char b;    float c;   }x;     struct   {    int a;    char b;    float c;   }y[20],*z;

        z=&x;  //这是非法的。

     上面这两个声明被编译器当做两种截然不同的类型,即使它们的成员列表完全相同。

    2、点操作符的结合性是从左到右的。下标引用和点操作符具有相同的优先级,也是自左向右的结合性。它们的优先级均高于间接访问操作符。

    3、struct SELF_REF{

              int a;   struct SELF_REF b;   int c;   };  //这种类型的自引用是非法的。

     

      因为这样b中又有一个结构SELF_REF,以此类推,无限下去,没有结尾。

       struct SELF_REF{

              int a;   struct SELF_REF *b;   int c;   };  //这种类型的自引用是合法的。

     

       这个声明和前面那个声明区别在于b现在是一个指针而不是一个结构,编译器在结构的长度确定之前就已经知道指针的长度了。

       它实际上是指向同一种类型的不同结构。(这句话是什么意思?)

     

    4、不完整的声明:   struct B;   struct A{      struct B;     };

       struct B{       struct A;   };

     

    5、优先级高到低:  ->  .  *

     

    6、结构的存储分配:

       编译器按照成员列表的顺序一个接一个地给每个成员分配内存。只有当存储成员时需要满足正确的边界对齐要求时,成员之间才可能出现用于填充的额外内存空间。

     

      struct ALIGN{       char a;       int b;       char c;    };

     

     如果某个机器的整型值是4个字节长度,并且它的起始存储位置必须能够被4整除,那么

      aXXXbcXXX

     

    每个结构将占据12字节的空间,但实际上只使用了其中个6个,浪费啊。。。

     

    改成这样:   struct ALIGN{          int b;          char a;          char c;  };

    但它值包含了8个字节的存储空间。

    两个字符字节可以紧挨着存储,所以只有结构最后面需要跳过的两个字节才被浪费掉。

     

    sizeof操作符能够得出一个结构的整体长度,包括因边界对齐而跳过的那些字节。

    7、什么时候你应该向函数传递一个结构而不是一个指向结构的指针呢?很少有这种情况!

     

    8、位段

     

       位段成员必须声明为int、signed int、unsigned int类型。

       在成员的后面是一个冒号和一个整数。这个整数指定该位段所占用的位的数目。

       位段的声明本质上是不可移植的。

      struct CHAR{

             unsigned ch : 7;         unsigned font:6;         unsigned size:19;  };

     

     

    9、联合

       联合的所有成员引用的是内存中的相同位置。当你想在不同时刻把不同的东西存储与同一个位置时,就可以使用联合。

     

       如果联合的各个成员具有不同的长度,联合的长度就是它最长的成员的长度。

     

     

     

    第十一章 动态内存分配

     

     

    1、malloc实际分配的内存可能比你请求的稍微多一点。但是,这个行为是由编译器定义的,所以不能指望它肯定会分配比你的请求更多的内

     

    存。 如果操作系统无法向malloc提供更多的内存,malloc就返回一个NULL指针。因此,动态内存分配最常见的错误就是忘记检查所请求的

     

    内存是成功分配。

     

    2、一个void *类型的指针可以转换为其他任何类型的指针。

     

    3、malloc和calloc之间最主要的区别就是后者在返回指向内存指针之前把它初始化为0.使用realloc可以对一块内存扩大或者缩小。

     

    4、释放内存的一部分是不允许的,动态分配的内存必须整块一起释放。但是realloc函数可以缩小一块动态分配的内存,有效地释放它尾部的部分内存。

     

    5、分配内存但在使用完毕后不将其释放将会引起内存泄露(memory leak).

     

    6、如果一个指针不是从早先的malloc、calloc、realloc函数返回的,它是不能作为参数传递给free函数的。(int *p=&a;呢?)

     

     

    第十二章 使用结构和指针

     

    1、Pascal语言中的指针哲学:

       使用锤子可能会伤着你自己,所以我们不给你锤子。

     

       C语言中的指针哲学:给你锤子,实际上你可以使用好几种锤子,祝你好运!

     

    2、语句提炼是简化程序的一种技巧,其方法是消除程序中冗余的句子。

     

     

     


    最新回复(0)