C与指针

1. 在有些语言中,注释有时候用于把一段代码注释掉,也就是使这段代码在程序中不起作用,但并不将其真正从源文件删除。在C语言中,这可不是好的方法,如果你试图在一段代码的首尾分别加上/*和*/符号来注释掉这段代码,你不一定能如愿。如果这段代码内部原先就有注释存在,这样做就会出问题。要从逻辑上删除这段代码,更好的办法是使用#if指令。  注释不能嵌套!  注意/*或*/出现在字符串字面值内部,就不再起注释定界符的作用。

     # if 0

statements

    #endif

2.在C语言中,数组的参数是以引用reference形式进行传递的,也就是传址调用,而标量和常量则是按值传递的。在函数中对标量参数的任何修改都会在函数返回时丢失,因此,被调用函数无法修改调用函数以传值形式传递给它的参数。然而,当被调用函数修改数组参数的其中一个元素时,调用函数所传递的数组就会实际的修改。


3.scanf函数实现原理,所有标量参数的前面必须加上一个“&”符号。数组参数前面不需要加上"&"符号。但是,数组参数中如果出现了下表引用,也就是说实际参数是莫个特定元素,那么它的前面也必须加上“&”符号。


4.ch = getchar()  中ch定义为int型,而我们事实上需要它来读取字符。答案是EOF是一个整型值,它的位数比字符类型要多,把ch声明为整型可以防止从输入读取的字符意外的被解释为EOF。但同时,这也意味着接受字符的ch必须足够大,足以容纳EOF,这就是ch使用整型值的原因。字符只是小整型数而已,所以用一个整型变量容纳字符值并不会引起任何问题。


5.把字符串常量和指针放在一起讨论,是因为在程序中使用字符串常量会生成一个指向字符的常量指针。当一个字符串常量出现于一个表达式中时,表达式所使用的值就是这些字符所存储的地址,而不是这些字符本身。因此,你可以把字符串常量赋值给一个指向字符的指针,后者指向这些字符所存储的地址。但是,你不能把字符串常量赋值给一个字符数组,因为字符串常量的直接值是一个指针,而不是这些字符本身。


6.break和continue这两条语句的任何一条如果出现于嵌套的循环内部,它只对最内层的循环起作用,你无法使用break或continue语句影响外层循环的执行。如果想跳出多层循环可以使用goto或者return。


7.学了几年的C,写了若干的for但真正理解还要算今天,鄙视一下自己。for(expression1,expression2,expression3)中expression3称为调整部分,它在循环体没错执行完毕,在条件部分即将执行之前执行。所有三个表达式都是可选的,都可以省略。如果省略条件部分,表示测试的值始终为真。


8.由于C并不具备布尔类型,所以这些语句在测试时用的都是整型表达式。零值被解释为假,非零值被解释为真。c并不具备任何输入输出语句,I/O是通过调用库函数实现的。C也不具备任何异常处理语句,它们也是通过调用库函数来完成的。


9.a*f(x) = a*f(x) + 1与a*f(x) += 1区别:前者表达式必须书写两次,一次在赋值号的左边,另一次在赋值号的右边。由于编译器无从知道函数f是否具有副作用,所以它必须两次计算表达式的值。第二种形式效率更高,因为下标只计算一次。


10.sizeof (x) 这是因为括号在表达式中总是合法的。判断表达式的长度并不需要对表达式进行求值,所以sizeof(a = b + 1)并没有向a赋任何值。


11.++a = 10  式子是错误的,原因是++a的结果是a值的拷贝,并不是变量本身,你无法向一个值进行赋值


12.->和.操作符用于访问一个结构的成员。如果S是个结构变量,那么S.a就访问名叫a的成员。当你拥有一个指向结构的指针而不是结构本身,且欲访问它的成员时,就需要使用->操作符而不是.。


13.  (运算法则好好的研究一下)

 

14.一个极为常见的错误

       int  *a ;

      *a  =  12;

     这个声明创建一个名叫a的指针变量,后面那条赋值语句把12储存在a所指向的内存的位置

 

15.如果你已近知道指针将被初始化什么地址,就把它初始化为该地址,否则就把它初始化为NULL。风格良好的程序会在指针解引用之前对它进行检查。

 

16.假设a存储于位置100,那么*100 = 25 ,它看上去像是把25赋值给a,因为a是位置100所存储的变量。但是,这是错的,这条语句实际上是非法的,因为字面值100的类型是整型,而间接访问操作只能作用于指针类型表达式。如果确实想把25存储于位置100,必须使用强制类型转化: *(int *)100 = 25

 

17.  char   ch  = ‘a’; 

 &CH不是一个合法的左值是因为当表达式&ch进行求值时,它的结果应该存储于计算机的什么地方呢? 它肯定会位于某个地方,但你无法知道它位于何处。这个表达式并未标识任何机器内存的特定位置,所以它不是一个合法的左值。


18. char ** strings;

       char * string;

while((string = * strings ++) != NULL)   完成三项任务: (1) 它把strings当前所指向的指针复制到变量string中。 (2) 它增加strings的值,使它指向下一个值。  (3)它测试string 是否为NULL.


19.指针p1  - p2 ,如果两个指针都指向同一个数组中的元素,这个表达式就是合法的。如果两个指针所指向的不是同一个数组中的元素,那么它们之间相减是未定义的。实际上,绝大数编译器都不会检查指针表达式的结果是否位于合法的边界之内。越界指针和指向未知值的指针是两个常见的错误根源。


20.  for( vp = &values[max -1];  vp>= &values[0];  vp --)

            *vp = 0;

    现在vp指向数组最后一个元素,它的自减操作放在for语句的调整部分进行。这个循环存在一个问题!!!!!!    标准允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针进行比较,但不允许与指向数组第一个元素之前的那个内存位置的指针进行比较。  切记。。。。


21.无论是程序员还是计算机都无法通过值的位模式来判断它的类型。类型是通过值的使用方法隐式确定的。编译器能够保证值的声明和值的使用之间的关系是确当的,从而帮助我们确定值的类型。


22.函数参数传递规则:

 1. 传递给参数的标量参数是传值调用的。

 2.传递给函数的数组参数在行为上就像他们是通过传址调用的那样。


23.  在C中,几乎所有使用数组名的表达式中,数组名的值是一个指针常量,也就是数组第一个元素的地址,它的类型取决于数组元素的类型。如果他们是int类型,那么数组名的类型就是指向int的常量指针,如果它们是其它类型,那么数组名的类型就是指向其它类型的常量指针。


24.只有在两种场合下,数组名并不用指针常量来表示 就是当数组名作为sizeof操作符或单目操作符&的操作符时sizeof返回整个数组的长度,而不是指向数组的指针的长度。取一个数组名的地址所产生的是一个指向数组的指针,而不是一个指向某个指针常量值的指针。


25.函数原型中的一维数组形参无需写明它的元素数目,因为函数并不为数组参数分配内存空间。形参只是一个指针,它指向的是已经在其他地方分配好的内存空间。这个事实解释了为什么数组形参可以与任何长度的数组匹配,它实际传递的只是指向数组第一个元素的指针。另一方面,这种实现方法使函数无法知道数组的长度。如果函数需要知道数组的长度,它必须作为一个显式的参数传递给函数。



 

26. int matrix[3][10] ,*mp = matrix;   这个声明是非法的,它正确地创建了matrix数组,并把mp声明为一个指向整型的指针。但是,mp的初始化是不正确的,因为matrix并不是一个指向整型的指针,而是一个指向整型数组的指针。   int   (*p)[10];  这个声明比我们以前见过的所有声明更为复杂,但它事实上并不是很难。你只要假定它是一个表达式并对它求值。


27. NUL字节是字符串的终止符,但它本身并不是字符串的一部分,所以字符串的长度的长度并不包括NUL字节

 

28.注意strlen返回一个类型为size_t的值。这个类型是在头文件stddef.h中定义的,它是一个无符号整数类型。在表达式中使用无符号数可能导致不可预料的结果。

 

29.  C提供了两种类型的聚合数据类型,数组和结构。数组是相同类型的元素的集合,它的每个元素是通过下标引用或指针间接访问来选择。结构也是一些值的集合,这些值称为它的成员,但一个结构各个成员可能具有不同的类型。

 

30.void func( struct compx *cp)   可以用(*cp).f    或cp->f  来调用。

 

31.系统禁止编译器在一个结构的起始位置跳过几个字节来满足边界对齐要求,因此所有结构的起始存储位置必须是结构中边界要求最严格的数据类型所要求的位置

 

32  struct aling2 {

        char   a;

        int    b:

        char c;

}

   struct aling2 {

        int    b:

        char   a;

        char    c;

}   所包含的成员如上,但它只占有8个字节的空间。两个字符可以紧挨着存储

 

33.位段的声明和结构类似,但它的成员是一个或多个位的字段。这些不同长度的字段实际上存储一个或多个整型变量中。位段的声明和任何普通的结构成员声明相同,但有两个例外。首先,位段成员必须声明为int、signed int 或unsigned int类型。其次,在成员名的后面是一个冒号和一个整数,这个整数指定该位段所占用的位的数目。使用位段的理由是它能够把长度为奇数的数据包装在一起,节省存储空间。另外是由于它可以方便地访问一个整型值得部分内容。

 

34.联合 union 的声明和结构相似,但它的行为方式却和结构不同。联合的所有成员引用的是内存中的相同位置。当你想不同的时刻把不同的东西存储于同一个位置时,就可以使用联合。  不是太明白待研究。。。。

 

35.malloc 和calloc 函数都用于动态分配一块内存,并返回一个指向该块内存的指针。malloc的参数就是需要分配的内存的字节数。和它不同的是,calloc的参数是你需要分配的元素的个数和每个元素的长度。calloc函数在返回前把内存初始化为零,而malloc函数返回时内存并未以任何方式进行初始化。

 

36.函数指针

   int  f(int);

   int   (*p)(int) = &f;

  初始化表达式中的&操作符石可选的,因为函数名被使用时总是由编译器把它转换为函数指针。&操作符只是显示地说明了编译器将隐式执行任务。

 

37.回调函数的使用 

   因为用户把一个函数指针作为参数传递其他函数,后者将回调用户的函数。我们无法再这个上下文环境中为回调函数编写一个准确的原型,因为我们并不知道进行比较的值得类型。事实上,我们需要查找函数能作用于任何类型的值。解决这个难题的方法是把参数类型声明为void*,表示一个指向未知类型的指针

 

38.C程序的main函数具有两个形参。第一个通常称为argc,它表示命令行参数的数目。第二个通常称为argv,它指向一组参数值。由于参数的数目并没有内在的限制,所以argv指向这组参数值的第一个元素。这些元素的每个都是指向一个参数文本的指针。

 

39.字符串常量 “xyz”+1   对于大多数程序员而言,它就是垃圾。但是,当你记得记得字符串常量实际上是个指针时,它的意义就变得很清楚了。这个表达式计算指针值加上1的值,它的结果就是个指针,指向字符串中的第二个字符:y

 

40.*“xyz”  对一个指针执行间接访问操作时,其结果就是指针所指向的内容。字符串常量的类型是指向字符的指针,所以这个间接访问的结果就是它所指向的字符:x

 

41.“xyz”[2],直接能够推断出这个表达式的值就是字符Z

 

42.#define max(a,b)   ((a)>(b)?(a):(b))

     x =5;

     y=8;

    z = Max(x++,y++);

结果是x=6,y=10,z=9

 

43.#undef  name

    如果一个现存的名字需要被重新定义,那么它的旧定义首先必须用#undef移除。

 

44.  #ifdef

       #else

      #endif   使用

 

45.#ifndef _headername

    #define _headername

    #endif

   多重包含的危险就被消除了

 

46. perror函数简化向用户报告这些特定错误的过程。它的原型定义在于stdio.h  

       void   perror(char const *message);

       如果message不是NULL并且指向一个非空的字符串,perror函数就打印这个字符串,后面跟一个分号和一个空格,然后打印出一条用于解释errno当前错误代码的信息。

 

47.exit它用于终止一个程序的执行,它的原型定于于stdlib.h,

     void exit(int status);  status参数返回给操作系统,用于提示程序是否正常完成。这个值和main函数返回的整型状态值相同。

  

 标准库以及输入输出流将会单独列出文章篇幅。

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值